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

refactor(consensus): remove not needed commit timeout and unused LastPrecommits #751

Merged
merged 10 commits into from
Mar 12, 2024
4 changes: 1 addition & 3 deletions config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -664,9 +664,7 @@ const testGenesisFmt = `{
"propose": "30000000",
"propose_delta": "50000",
"vote": "30000000",
"vote_delta": "50000",
"commit": "10000000",
"bypass_timeout_commit": true
"vote_delta": "50000"
},
"evidence": {
"max_age_num_blocks": "100000",
Expand Down
1 change: 0 additions & 1 deletion internal/consensus/block_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ func (c *blockExecutor) create(ctx context.Context, rs *cstypes.RoundState, roun
// The commit is empty, but not nil.
commit = types.NewCommit(0, 0, types.BlockID{}, nil, nil)
case rs.LastCommit != nil:
// Make the commit from LastPrecommits
commit = rs.LastCommit

default: // This shouldn't happen.
Expand Down
4 changes: 0 additions & 4 deletions internal/consensus/gossiper.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,6 @@ func (g *msgGossiper) ensurePeerPartSetHeader(blockPartSetHeader types.PartSetHe
// there is a vote to send and (nil,false) otherwise.
func (g *msgGossiper) pickVoteForGossip(rs cstypes.RoundState, prs *cstypes.PeerRoundState) (*types.Vote, bool) {
var voteSets []*types.VoteSet
// if there are lastPrecommits to send
if prs.Step == cstypes.RoundStepNewHeight {
voteSets = append(voteSets, rs.LastPrecommits)
}
if prs.Round != -1 && prs.Round <= rs.Round {
// if there are POL prevotes to send
if prs.Step <= cstypes.RoundStepPropose && prs.ProposalPOLRound != -1 {
Expand Down
14 changes: 0 additions & 14 deletions internal/consensus/gossiper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,6 @@ func (suite *GossiperSuiteTest) TestGossipGossipVote() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

precommitH99 := suite.makeSignedVote(99, 0, tmproto.PrecommitType)
lastPercommits := types.NewVoteSet(factory.DefaultTestChainID, 99, 0, tmproto.PrecommitType, suite.valSet)
_, _ = lastPercommits.AddVote(precommitH99)

prevoteH100R0 := suite.makeSignedVote(100, 0, tmproto.PrevoteType)
prevoteH100R1 := suite.makeSignedVote(100, 1, tmproto.PrevoteType)
prevoteH100R2 := suite.makeSignedVote(100, 2, tmproto.PrevoteType)
Expand All @@ -489,16 +485,6 @@ func (suite *GossiperSuiteTest) TestGossipGossipVote() {
prs cstypes.PeerRoundState
wantMsg *tmproto.Vote
}{
{
rs: cstypes.RoundState{LastPrecommits: lastPercommits},
prs: cstypes.PeerRoundState{
Height: 100,
Round: -1,
ProposalPOLRound: -1,
Step: cstypes.RoundStepNewHeight,
},
wantMsg: precommitH99.ToProto(),
},
{
rs: cstypes.RoundState{Votes: votesH100},
prs: cstypes.PeerRoundState{
Expand Down
7 changes: 0 additions & 7 deletions internal/consensus/msg_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,6 @@ func msgInfoWithCtxMiddleware() msgMiddlewareFunc {
}
}

func logKeyValsWithError(keyVals []any, err error) []any {
if err == nil {
return keyVals
}
return append(keyVals, "error", err)
}

func makeLogArgsFromMessage(msg Message) []any {
switch m := msg.(type) {
case *ProposalMessage:
Expand Down
8 changes: 1 addition & 7 deletions internal/consensus/peer_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func (ps *PeerState) getVoteBitArray(height int64, round int32, votesType tmprot
return nil

case tmproto.PrecommitType:
return ps.PRS.LastPrecommits
return ps.PRS.Precommits
}
}

Expand Down Expand Up @@ -335,10 +335,6 @@ func (ps *PeerState) ensureVoteBitArrays(height int64, numValidators int) {
if ps.PRS.ProposalPOL == nil {
ps.PRS.ProposalPOL = bits.NewBitArray(numValidators)
}
} else if ps.PRS.Height == height+1 {
if ps.PRS.LastPrecommits == nil {
ps.PRS.LastPrecommits = bits.NewBitArray(numValidators)
}
}
}

Expand Down Expand Up @@ -530,10 +526,8 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) {
// Shift Precommits to LastPrecommits.
if psHeight+1 == msg.Height && psRound == msg.LastCommitRound {
ps.PRS.LastCommitRound = msg.LastCommitRound
ps.PRS.LastPrecommits = ps.PRS.Precommits.Copy()
} else {
ps.PRS.LastCommitRound = msg.LastCommitRound
ps.PRS.LastPrecommits = nil
}

// we'll update the BitArray capacity later
Expand Down
5 changes: 3 additions & 2 deletions internal/consensus/reactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,8 @@ func (r *Reactor) handleVoteMessage(ctx context.Context, envelope *p2p.Envelope,
case *tmcons.Vote:
stateData := r.state.stateDataStore.Get()
isValidator := stateData.isValidator(r.state.privValidator.ProTxHash)
height, valSize, lastCommitSize := stateData.Height, stateData.Validators.Size(), stateData.LastPrecommits.Size()
height, valSize := stateData.Height, stateData.Validators.Size()
lastValSize := len(stateData.LastValidators.Validators)

if isValidator { // ignore votes on non-validator nodes; TODO don't even send it
vMsg := msgI.(*VoteMessage)
Expand All @@ -692,7 +693,7 @@ func (r *Reactor) handleVoteMessage(ctx context.Context, envelope *p2p.Envelope,
}

ps.EnsureVoteBitArrays(height, valSize)
ps.EnsureVoteBitArrays(height-1, lastCommitSize)
ps.EnsureVoteBitArrays(height-1, lastValSize)
if err := ps.SetHasVote(vMsg.Vote); err != nil {
return err
}
Expand Down
1 change: 0 additions & 1 deletion internal/consensus/reactor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,6 @@ func TestReactorValidatorSetChanges(t *testing.T) {
validatorUpdates: updates,
consensusParams: factory.ConsensusParams(func(cp *types.ConsensusParams) {
cp.Timeout.Propose = 2 * time.Second
cp.Timeout.Commit = 1 * time.Second
cp.Timeout.Vote = 1 * time.Second
}),
}
Expand Down
5 changes: 3 additions & 2 deletions internal/consensus/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,8 +512,9 @@ func (cs *State) OnStop() {
if cs.GetRoundState().Step == cstypes.RoundStepApplyCommit {
select {
case <-cs.getOnStopCh():
case <-time.After(stateData.state.ConsensusParams.Timeout.Commit):
cs.logger.Error("OnStop: timeout waiting for commit to finish", "time", stateData.state.ConsensusParams.Timeout.Commit)
case <-time.After(stateData.state.ConsensusParams.Timeout.Vote):
// we wait vote timeout, just in case
cs.logger.Error("OnStop: timeout waiting for commit to finish", "time", stateData.state.ConsensusParams.Timeout.Vote)
}
}

Expand Down
7 changes: 4 additions & 3 deletions internal/consensus/state_add_commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ func (c *AddCommitAction) Execute(ctx context.Context, stateEvent StateEvent) er
if err != nil {
return fmt.Errorf("error adding commit: %w", err)
}
if stateData.bypassCommitTimeout() {
_ = stateEvent.Ctrl.Dispatch(ctx, &EnterNewRoundEvent{Height: stateData.Height}, stateData)
}

// We go to next round, as in Tenderdash we don't need to wait for new commits
_ = stateEvent.Ctrl.Dispatch(ctx, &EnterNewRoundEvent{Height: stateData.Height}, stateData)

_ = c.statsQueue.send(ctx, msgInfoFromCtx(ctx))
return nil
}
43 changes: 1 addition & 42 deletions internal/consensus/state_add_vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ func newAddVoteAction(cs *State, ctrl *Controller, statsQueue *chanQueue[msgInfo
statsMw := addVoteStatsMw(statsQueue)
dispatchPrecommitMw := addVoteDispatchPrecommitMw(ctrl)
verifyVoteExtensionMw := addVoteVerifyVoteExtensionMw(cs.privValidator, cs.blockExec, cs.metrics, cs.emitter)
addToLastPrecommitMw := addVoteToLastPrecommitMw(cs.eventPublisher, ctrl)
return &AddVoteAction{
prevote: withVoterMws(
addToVoteSet,
Expand All @@ -62,7 +61,6 @@ func newAddVoteAction(cs *State, ctrl *Controller, statsQueue *chanQueue[msgInfo
dispatchPrecommitMw,
verifyVoteExtensionMw,
validateVoteMw,
addToLastPrecommitMw,
errorMw,
statsMw,
),
Expand Down Expand Up @@ -101,45 +99,6 @@ func addVoteToVoteSetFunc(metrics *Metrics, ep *EventPublisher) AddVoteFunc {
}
}

func addVoteToLastPrecommitMw(ep *EventPublisher, ctrl *Controller) AddVoteMiddlewareFunc {
return func(next AddVoteFunc) AddVoteFunc {
return func(ctx context.Context, stateData *StateData, vote *types.Vote) (bool, error) {
if vote.Height+1 != stateData.Height || vote.Type != tmproto.PrecommitType {
return next(ctx, stateData, vote)
}
logger := log.FromCtxOrNop(ctx)
if stateData.Step != cstypes.RoundStepNewHeight {
// Late precommit at prior height is ignored
logger.Trace("precommit vote came in after commit timeout and has been ignored")
return false, nil
}
if stateData.LastPrecommits == nil {
logger.Debug("no last round precommits on node", "vote", vote)
return false, nil
}
added, err := stateData.LastPrecommits.AddVote(vote)
if !added {
logger.Debug("vote not added to last precommits", logKeyValsWithError(nil, err)...)
return false, nil
}
logger.Trace("added vote to last precommits", "last_precommits", stateData.LastPrecommits)

err = ep.PublishVoteEvent(vote)
if err != nil {
return added, err
}

// if we can skip timeoutCommit and have all the votes now,
if stateData.bypassCommitTimeout() && stateData.LastPrecommits.HasAll() {
// go straight to new round (skip timeout commit)
// c.scheduleTimeout(time.Duration(0), c.Height, 0, cstypes.RoundStepNewHeight)
_ = ctrl.Dispatch(ctx, &EnterNewRoundEvent{Height: stateData.Height}, stateData)
}
return added, err
}
}
}

func addVoteUpdateValidBlockMw(ep *EventPublisher) AddVoteMiddlewareFunc {
return func(next AddVoteFunc) AddVoteFunc {
return func(ctx context.Context, stateData *StateData, vote *types.Vote) (bool, error) {
Expand Down Expand Up @@ -250,7 +209,7 @@ func addVoteDispatchPrecommitMw(ctrl *Controller) AddVoteMiddlewareFunc {
return added, err
}
_ = ctrl.Dispatch(ctx, &EnterCommitEvent{Height: height, CommitRound: vote.Round}, stateData)
if stateData.bypassCommitTimeout() && precommits.HasAll() {
if precommits.HasTwoThirdsMajority() {
_ = ctrl.Dispatch(ctx, &EnterNewRoundEvent{Height: stateData.Height}, stateData)
}
return added, err
Expand Down
33 changes: 4 additions & 29 deletions internal/consensus/state_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,22 +226,16 @@ func (s *StateData) updateToState(state sm.State, commit *types.Commit) {
switch {
case state.LastBlockHeight == 0: // Very first commit should be empty.
s.LastCommit = (*types.Commit)(nil)
s.LastPrecommits = (*types.VoteSet)(nil)
case s.CommitRound > -1 && s.Votes != nil && commit == nil: // Otherwise, use cs.Votes
if !s.Votes.Precommits(s.CommitRound).HasTwoThirdsMajority() {
panic(fmt.Sprintf(
"wanted to form a commit, but precommits (H/R: %d/%d) didn't have 2/3+: %v",
state.LastBlockHeight, s.CommitRound, s.Votes.Precommits(s.CommitRound),
))
}
s.LastPrecommits = s.Votes.Precommits(s.CommitRound)
s.LastCommit = s.LastPrecommits.MakeCommit()
precommits := s.Votes.Precommits(s.CommitRound)
s.LastCommit = precommits.MakeCommit()
case commit != nil:
// We either got the commit from a remote node
// In which Last precommits will be nil
// Or we got the commit from finalize commit
// In which Last precommits will not be nil
s.LastPrecommits = s.Votes.Precommits(s.CommitRound)
s.LastCommit = commit
case s.LastCommit == nil:
// NOTE: when Tendermint starts, it has no votes. reconstructLastCommit
Expand All @@ -266,13 +260,9 @@ func (s *StateData) updateToState(state sm.State, commit *types.Commit) {

if s.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)
s.StartTime = s.commitTime(tmtime.Now())
s.StartTime = tmtime.Now()
} else {
s.StartTime = s.commitTime(s.CommitTime)
s.StartTime = s.CommitTime
}

if s.Validators == nil || !bytes.Equal(s.Validators.QuorumHash, validators.QuorumHash) {
Expand Down Expand Up @@ -314,14 +304,6 @@ func (s *StateData) HeightVoteSet() (int64, *cstypes.HeightVoteSet) {
return s.Height, s.Votes
}

func (s *StateData) commitTime(t time.Time) time.Time {
c := s.state.ConsensusParams.Timeout.Commit
if s.config.UnsafeCommitTimeoutOverride != 0 {
c = s.config.UnsafeProposeTimeoutOverride
}
return t.Add(c)
}

func (s *StateData) proposalIsTimely() bool {
if s.Height == s.state.InitialHeight {
// by definition, initial block must have genesis time
Expand Down Expand Up @@ -459,13 +441,6 @@ func (s *StateData) voteTimeout(round int32) time.Duration {
) * time.Nanosecond
}

func (s *StateData) bypassCommitTimeout() bool {
if s.config.UnsafeBypassCommitTimeoutOverride != nil {
return *s.config.UnsafeBypassCommitTimeoutOverride
}
return s.state.ConsensusParams.Timeout.BypassCommitTimeout
}

func (s *StateData) isValidForPrevote() error {
// Check that a proposed block was not received within this round (and thus executing this from a timeout).
if s.ProposalBlock == nil {
Expand Down
2 changes: 0 additions & 2 deletions internal/consensus/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2733,7 +2733,6 @@ func TestStartNextHeightCorrectlyAfterTimeout(t *testing.T) {

cs1, vss := makeState(ctx, t, makeStateArgs{config: config})
stateData := cs1.GetStateData()
stateData.state.ConsensusParams.Timeout.BypassCommitTimeout = false
err := stateData.Save()
require.NoError(t, err)
cs1.txNotifier = &fakeTxNotifier{ch: make(chan struct{})}
Expand Down Expand Up @@ -2798,7 +2797,6 @@ func TestResetTimeoutPrecommitUponNewHeight(t *testing.T) {

cs1, vss := makeState(ctx, t, makeStateArgs{config: config})
stateData := cs1.GetStateData()
stateData.state.ConsensusParams.Timeout.BypassCommitTimeout = false
err := stateData.Save()
require.NoError(t, err)

Expand Down
6 changes: 2 additions & 4 deletions internal/consensus/types/peer_round_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ type PeerRoundState struct {
Prevotes *bits.BitArray `json:"prevotes"` // All votes peer has for this round
Precommits *bits.BitArray `json:"precommits"` // All precommits peer has for this round
LastCommitRound int32 `json:"last_commit_round"` // Round of commit for last height. -1 if none.
LastPrecommits *bits.BitArray `json:"last_commit"` // All commit precommits of commit for last height.

HasCommit bool `json:"has_commit"`

Expand Down Expand Up @@ -69,7 +68,6 @@ func (prs PeerRoundState) Copy() PeerRoundState {
prs.ProposalPOL = prs.ProposalPOL.Copy()
prs.Prevotes = prs.Prevotes.Copy()
prs.Precommits = prs.Precommits.Copy()
prs.LastPrecommits = prs.LastPrecommits.Copy()
prs.CatchupCommit = prs.CatchupCommit.Copy()

return prs
Expand All @@ -83,15 +81,15 @@ func (prs PeerRoundState) StringIndented(indent string) string {
%s POL %v (round %v)
%s Prevotes %v
%s Precommits %v
%s LastPrecommits %v (round %v)
%s Last commit round %v
%s Catchup %v (round %v)
%s}`,
indent, prs.Height, prs.Round, prs.Step, prs.StartTime,
indent, prs.ProposalBlockPartSetHeader, prs.ProposalBlockParts,
indent, prs.ProposalPOL, prs.ProposalPOLRound,
indent, prs.Prevotes,
indent, prs.Precommits,
indent, prs.LastPrecommits, prs.LastCommitRound,
indent, prs.LastCommitRound,
indent, prs.CatchupCommit, prs.CatchupCommitRound,
indent)
}
Expand Down
1 change: 0 additions & 1 deletion internal/consensus/types/round_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ type RoundState struct {
ValidBlockParts *types.PartSet `json:"valid_block_parts"`
Votes *HeightVoteSet `json:"votes"`
CommitRound int32 `json:"commit_round"`
LastPrecommits *types.VoteSet `json:"last_precommits"`
LastCommit *types.Commit `json:"last_commit"`
LastValidators *types.ValidatorSet `json:"last_validators"`
TriggeredTimeoutPrecommit bool `json:"triggered_timeout_precommit"`
Expand Down
28 changes: 15 additions & 13 deletions internal/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1100,20 +1100,22 @@ func TestStateProto(t *testing.T) {

for _, tt := range tc {
tt := tt
pbs, err := tt.state.ToProto()
if !tt.expPass1 {
assert.Error(t, err)
} else {
assert.NoError(t, err, tt.testName)
}
t.Run(tt.testName, func(t *testing.T) {
pbs, err := tt.state.ToProto()
if !tt.expPass1 {
assert.Error(t, err)
} else {
assert.NoError(t, err, tt.testName)
}

smt, err := sm.FromProto(pbs)
if tt.expPass2 {
require.NoError(t, err, tt.testName)
require.Equal(t, tt.state, smt, tt.testName)
} else {
require.Error(t, err, tt.testName)
}
smt, err := sm.FromProto(pbs)
if tt.expPass2 {
require.NoError(t, err, tt.testName)
require.Equal(t, tt.state, smt, tt.testName)
} else {
require.Error(t, err, tt.testName)
}
})
}
}

Expand Down
Loading
Loading