Skip to content

Commit

Permalink
feat(abci): move timeout_commit into FinalizeBlockResponse (comet…
Browse files Browse the repository at this point in the history
…bft#3089)

as `next_block_delay`

ADR-115: cometbft#2966
Closes cometbft#2655

---

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

---------

Co-authored-by: Sergio Mena <[email protected]>
  • Loading branch information
2 people authored and teddyding committed Jun 19, 2024
1 parent c8beeea commit a84d857
Show file tree
Hide file tree
Showing 17 changed files with 223 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `[config]` Move `timeout_commit` into the ABCI `FinalizeBlockResponse`
([\#2655](https://github.com/cometbft/cometbft/issues/2655))
4 changes: 3 additions & 1 deletion abci/types/application.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types

import "context"
import (
"context"
)

//go:generate ../../scripts/mockery_generate.sh Application

Expand Down
54 changes: 54 additions & 0 deletions abci/types/types.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,12 @@ func (cfg *Config) ValidateBasic() error {
return nil
}

// CheckDeprecated returns any deprecation warnings. These are printed to the operator on startup
// CheckDeprecated returns any deprecation warnings. These are printed to the operator on startup.
func (cfg *Config) CheckDeprecated() []string {
var warnings []string
if cfg.Consensus.TimeoutCommit != 0 {
warnings = append(warnings, "[consensus.timeout_commit] is deprecated. Use `next_block_delay` in the ABCI `FinalizeBlockResponse`.")
}
return warnings
}

Expand Down Expand Up @@ -996,6 +999,7 @@ type ConsensusConfig struct {
// height (this gives us a chance to receive some more precommits, even
// though we already have +2/3).
// NOTE: when modifying, make sure to update time_iota_ms genesis parameter
// Deprecated: use `next_block_delay` in the ABCI application's `FinalizeBlockResponse`.
TimeoutCommit time.Duration `mapstructure:"timeout_commit"`

// Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
Expand Down Expand Up @@ -1078,6 +1082,7 @@ func (cfg *ConsensusConfig) Precommit(round int32) time.Duration {

// Commit returns the amount of time to wait for straggler votes after receiving +2/3 precommits
// for a single block (ie. a commit).
// Deprecated: use `next_block_delay` in the ABCI application's `FinalizeBlockResponse`.
func (cfg *ConsensusConfig) Commit(t time.Time) time.Time {
return t.Add(cfg.TimeoutCommit)
}
Expand Down
4 changes: 2 additions & 2 deletions consensus/reactor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -637,9 +637,9 @@ func TestReactorWithTimeoutCommit(t *testing.T) {
N := 4
css, cleanup := randConsensusNet(t, N, "consensus_reactor_with_timeout_commit_test", newMockTickerFunc(false), newKVStore)
defer cleanup()
// override default SkipTimeoutCommit == true for tests
// override default NextBlockDelay == 0 for tests
for i := 0; i < N; i++ {
css[i].config.SkipTimeoutCommit = false
css[i].state.NextBlockDelay = 1 * time.Second
}

reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N-1)
Expand Down
25 changes: 16 additions & 9 deletions consensus/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -718,15 +718,20 @@ func (cs *State) updateToState(state sm.State) {
cs.updateHeight(height)
cs.updateRoundStep(0, cstypes.RoundStepNewHeight)

timeoutCommit := state.NextBlockDelay
// If the ABCI app didn't set a delay, use the deprecated config value.
if timeoutCommit == 0 {
timeoutCommit = cs.config.TimeoutCommit //nolint:staticcheck

Check failure on line 724 in consensus/state.go

View workflow job for this annotation

GitHub Actions / golangci-lint

directive `//nolint:staticcheck` is unused for linter "staticcheck" (nolintlint)
}
if cs.CommitTime.IsZero() {
// "Now" makes it easier to sync up dev nodes.
// We add timeoutCommit to allow transactions
// to be gathered for the first block.
// And alternative solution that relies on clocks:
// cs.StartTime = state.LastBlockTime.Add(timeoutCommit)
cs.StartTime = cs.config.Commit(cmttime.Now())
//
// We add timeoutCommit to allow transactions to be gathered for
// the first block. An alternative solution that relies on clocks:
// `cs.StartTime = state.LastBlockTime.Add(timeoutCommit)`
cs.StartTime = cmttime.Now().Add(timeoutCommit)
} else {
cs.StartTime = cs.config.Commit(cs.CommitTime)
cs.StartTime = cs.CommitTime.Add(timeoutCommit)
}

cs.Validators = validators
Expand Down Expand Up @@ -1042,7 +1047,7 @@ func (cs *State) handleTxsAvailable() {

// Enter: `timeoutNewHeight` by startTime (commitTime+timeoutCommit),
//
// or, if SkipTimeoutCommit==true, after receiving all precommits from (height,round-1)
// or, if NextBlockDelay==0, after receiving all precommits from (height,round-1)
//
// Enter: `timeoutPrecommits` after any +2/3 precommits from (height,round-1)
// Enter: +2/3 precommits for nil at (height,round-1)
Expand Down Expand Up @@ -2211,7 +2216,8 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error
cs.evsw.FireEvent(types.EventVote, vote)

// if we can skip timeoutCommit and have all the votes now,
if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() {
skipTimeoutCommit := cs.state.NextBlockDelay == 0 && cs.config.TimeoutCommit == 0 //nolint:staticcheck

Check failure on line 2219 in consensus/state.go

View workflow job for this annotation

GitHub Actions / golangci-lint

directive `//nolint:staticcheck` is unused for linter "staticcheck" (nolintlint)
if skipTimeoutCommit && cs.LastCommit.HasAll() {
// go straight to new round (skip timeout commit)
// cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight)
cs.enterNewRound(cs.Height, 0)
Expand Down Expand Up @@ -2369,7 +2375,8 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error

if !blockID.IsNil() {
cs.enterCommit(height, vote.Round)
if cs.config.SkipTimeoutCommit && precommits.HasAll() {
skipTimeoutCommit := cs.state.NextBlockDelay == 0 && cs.config.TimeoutCommit == 0 //nolint:staticcheck

Check failure on line 2378 in consensus/state.go

View workflow job for this annotation

GitHub Actions / golangci-lint

directive `//nolint:staticcheck` is unused for linter "staticcheck" (nolintlint)
if skipTimeoutCommit && precommits.HasAll() {
cs.enterNewRound(cs.Height, 0)
}
} else {
Expand Down
2 changes: 2 additions & 0 deletions consensus/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2761,6 +2761,7 @@ func (n *fakeTxNotifier) Notify() {
func TestStartNextHeightCorrectlyAfterTimeout(t *testing.T) {
config.Consensus.SkipTimeoutCommit = false
cs1, vss := randState(4)
cs1.state.NextBlockDelay = 10 * time.Millisecond
cs1.txNotifier = &fakeTxNotifier{ch: make(chan struct{})}

vs2, vs3, vs4 := vss[1], vss[2], vss[3]
Expand Down Expand Up @@ -2825,6 +2826,7 @@ func TestResetTimeoutPrecommitUponNewHeight(t *testing.T) {

config.Consensus.SkipTimeoutCommit = false
cs1, vss := randState(4)
cs1.state.NextBlockDelay = 10 * time.Millisecond

vs2, vs3, vs4 := vss[1], vss[2], vss[3]
height, round := cs1.Height, cs1.Round
Expand Down
51 changes: 24 additions & 27 deletions docs/app-dev/indexing-transactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ transferBalance200FinalizeBlock12 1
transferNodeNothingFinalizeBlock12 1
```

The event number is a local variable kept by the indexer and incremented when a new event is processed.
It is an `int64` variable and has no other semantics besides being used to associate attributes belonging to the same events within a height.
This variable is not atomically incremented as event indexing is deterministic. **Should this ever change**, the event id generation
Expand Down Expand Up @@ -174,32 +173,30 @@ Example:

```go
func (app *Application) FinalizeBlock(_ context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {

//...
tx_results[0] := &types.ExecTxResult{
Code: CodeTypeOK,
// With every transaction we can emit a series of events. To make it simple, we just emit the same events.
Events: []types.Event{
{
Type: "app",
Attributes: []types.EventAttribute{
{Key: "creator", Value: "Cosmoshi Netowoko", Index: true},
{Key: "key", Value: key, Index: true},
{Key: "index_key", Value: "index is working", Index: true},
{Key: "noindex_key", Value: "index is working", Index: false},
},
},
{
Type: "app",
Attributes: []types.EventAttribute{
{Key: "creator", Value: "Cosmoshi", Index: true},
{Key: "key", Value: value, Index: true},
{Key: "index_key", Value: "index is working", Index: true},
{Key: "noindex_key", Value: "index is working", Index: false},
},
},
},
}
tx_results[0] := &types.ExecTxResult{
Code: CodeTypeOK,
// With every transaction we can emit a series of events. To make it simple, we just emit the same events.
Events: []types.Event{
{
Type: "app",
Attributes: []types.EventAttribute{
{Key: "creator", Value: "Cosmoshi Netowoko", Index: true},
{Key: "key", Value: key, Index: true},
{Key: "index_key", Value: "index is working", Index: true},
{Key: "noindex_key", Value: "index is working", Index: false},
},
},
{
Type: "app",
Attributes: []types.EventAttribute{
{Key: "creator", Value: "Cosmoshi", Index: true},
{Key: "key", Value: value, Index: true},
{Key: "index_key", Value: "index is working", Index: true},
{Key: "noindex_key", Value: "index is working", Index: false},
},
},
},
}

block_events = []types.Event{
{
Expand Down
3 changes: 3 additions & 0 deletions docs/guides/go-built-in.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,13 @@ func (app *KVStoreApplication) FinalizeBlock(_ context.Context, req *abcitypes.R

return &abcitypes.ResponseFinalizeBlock{
TxResults: txs,
NextBlockDelay: 1 * time.Second,
}, nil
}
```

`NextBlockDelay` is a delay between the time when the current block is committed and the next height is started. Normally you don't need to change the default value (1s). Please refer to the [spec](../../spec/abci/abci++_methods.md#finalizeblock) for more information.

Transactions are not guaranteed to be valid when they are delivered to an application, even if they were valid when they were proposed.

This can happen if the application state is used to determine transaction validity.
Expand Down
2 changes: 2 additions & 0 deletions docs/guides/go.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ func (app *KVStoreApplication) FinalizeBlock(_ context.Context, req *abcitypes.R
}
```

`NextBlockDelay` is a delay between the time when the current block is committed and the next height is started. Normally you don't need to change the default value (1s). Please refer to the [spec](../../spec/abci/abci++_methods.md#finalizeblock) for more information.

Transactions are not guaranteed to be valid when they are delivered to an application, even if they were valid when they were proposed.

This can happen if the application state is used to determine transaction validity. The application state may have changed between the initial execution of `CheckTx` and the transaction delivery in `FinalizeBlock` in a way that rendered the transaction no longer valid.
Expand Down
7 changes: 7 additions & 0 deletions proto/tendermint/abci/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import "tendermint/types/params.proto";
import "tendermint/types/validator.proto";
import "google/protobuf/timestamp.proto";
import "gogoproto/gogo.proto";
import "google/protobuf/duration.proto";

// NOTE: When using custom types, mind the warnings.
// https://github.com/cosmos/gogoproto/blob/master/custom_types.md#warnings-and-issues
Expand Down Expand Up @@ -363,6 +364,12 @@ message ResponseFinalizeBlock {
tendermint.types.ConsensusParams consensus_param_updates = 4;
// app_hash is the hash of the applications' state which is used to confirm that execution of the transactions was deterministic. It is up to the application to decide which algorithm to use.
bytes app_hash = 5;
// delay between the time when this block is committed and the next height is started.
// previously `timeout_commit` in config.toml
google.protobuf.Duration next_block_delay = 6 [
(gogoproto.nullable) = false,
(gogoproto.stdduration) = true
];
}

//----------------------------------------
Expand Down
Loading

0 comments on commit a84d857

Please sign in to comment.