/
state_enter_precommit.go
158 lines (131 loc) · 5.59 KB
/
state_enter_precommit.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package consensus
import (
"context"
"fmt"
cstypes "github.com/dashpay/tenderdash/internal/consensus/types"
"github.com/dashpay/tenderdash/libs/log"
tmproto "github.com/dashpay/tenderdash/proto/tendermint/types"
"github.com/dashpay/tenderdash/types"
)
type EnterPrecommitEvent struct {
Height int64
Round int32
}
// GetType returns EnterPrecommitType event-type
func (e *EnterPrecommitEvent) GetType() EventType {
return EnterPrecommitType
}
// EnterPrecommitAction ...
// Enter: `timeoutPrevote` after any +2/3 prevotes.
// Enter: `timeoutPrecommit` after any +2/3 precommits.
// Enter: +2/3 precomits for block or nil.
// Lock & precommit the ProposalBlock if we have enough prevotes for it (a POL in this round)
// else, precommit nil otherwise.
type EnterPrecommitAction struct {
logger log.Logger
eventPublisher *EventPublisher
blockExec *blockExecutor
voteSigner *voteSigner
}
// Execute ...
// Enter: `timeoutPrevote` after any +2/3 prevotes.
// Enter: `timeoutPrecommit` after any +2/3 precommits.
// Enter: +2/3 precomits for block or nil.
// Lock & precommit the ProposalBlock if we have enough prevotes for it (a POL in this round)
// else, precommit nil otherwise.
func (c *EnterPrecommitAction) Execute(ctx context.Context, stateEvent StateEvent) error {
event := stateEvent.Data.(*EnterPrecommitEvent)
stateData := stateEvent.StateData
height := event.Height
round := event.Round
logger := c.logger.With("new_height", height, "new_round", round)
if stateData.Height != height || round < stateData.Round || (stateData.Round == round && cstypes.RoundStepPrecommit <= stateData.Step) {
logger.Trace("entering precommit step with invalid args",
"height", stateData.Height,
"round", stateData.Round,
"step", stateData.Step)
return nil
}
logger.Debug("entering precommit step",
"height", stateData.Height,
"round", stateData.Round,
"step", stateData.Step)
defer func() {
// Done enterPrecommit:
stateData.updateRoundStep(round, cstypes.RoundStepPrecommit)
}()
// check for a polka
blockID, ok := stateData.Votes.Prevotes(round).TwoThirdsMajority()
// If we don't have a polka, we must precommit nil.
// From protocol perspective it's an error condition, so we log it on Error level.
if !ok {
if stateData.LockedBlock != nil {
logger.Error("precommit step; no +2/3 prevotes during enterPrecommit while we are locked; precommitting nil")
} else {
logger.Error("precommit step; no +2/3 prevotes during enterPrecommit; precommitting nil")
}
c.voteSigner.signAddVote(ctx, stateData, tmproto.PrecommitType, types.BlockID{})
return nil
}
// At this point +2/3 prevoted for a particular block or nil.
c.eventPublisher.PublishPolkaEvent(stateData.RoundState)
// the latest POLRound should be this round.
polRound, _ := stateData.Votes.POLInfo()
if polRound < round {
panic(fmt.Sprintf("this POLRound should be %v but got %v", round, polRound))
}
// +2/3 prevoted nil. Precommit nil.
if blockID.IsNil() {
// From protocol perspective it's an error condition, so we log it on Error level.
logger.Error("precommit step: +2/3 prevoted for nil; precommitting nil")
c.voteSigner.signAddVote(ctx, stateData, tmproto.PrecommitType, types.BlockID{})
return nil
}
// At this point, +2/3 prevoted for a particular block.
// If we never received a proposal for this block, we must precommit nil
if stateData.Proposal == nil || stateData.ProposalBlock == nil {
logger.Debug("precommit step; did not receive proposal, precommitting nil")
c.voteSigner.signAddVote(ctx, stateData, tmproto.PrecommitType, types.BlockID{})
return nil
}
// If the proposal time does not match the block time, precommit nil.
if !stateData.Proposal.Timestamp.Equal(stateData.ProposalBlock.Header.Time) {
logger.Error("precommit step: proposal timestamp not equal; precommitting nil")
c.voteSigner.signAddVote(ctx, stateData, tmproto.PrecommitType, types.BlockID{})
return nil
}
// If we're already locked on that block, precommit it, and update the LockedRound
if stateData.LockedBlock.HashesTo(blockID.Hash) {
logger.Debug("precommit step: +2/3 prevoted locked block; relocking")
stateData.LockedRound = round
c.eventPublisher.PublishRelockEvent(stateData.RoundState)
c.voteSigner.signAddVote(ctx, stateData, tmproto.PrecommitType, blockID)
return nil
}
// If greater than 2/3 of the voting power on the network prevoted for
// the proposed block, update our locked block to this block and issue a
// precommit vote for it.
if stateData.ProposalBlock.HashesTo(blockID.Hash) {
logger.Debug("precommit step: +2/3 prevoted proposal block; locking", "hash", blockID.Hash)
// we got precommit but we didn't process proposal yet
c.blockExec.mustEnsureProcess(ctx, &stateData.RoundState, round)
// Validate the block.
c.blockExec.mustValidate(ctx, stateData)
stateData.LockedRound = round
stateData.LockedBlock = stateData.ProposalBlock
stateData.LockedBlockParts = stateData.ProposalBlockParts
c.eventPublisher.PublishLockEvent(stateData.RoundState)
c.voteSigner.signAddVote(ctx, stateData, tmproto.PrecommitType, blockID)
return nil
}
// There was a polka in this round for a block we don't have.
// Fetch that block, and precommit nil.
logger.Debug("precommit step: +2/3 prevotes for a block we do not have; voting nil", "block_id", blockID)
if !stateData.ProposalBlockParts.HasHeader(blockID.PartSetHeader) {
stateData.ProposalBlock = nil
stateData.metrics.MarkBlockGossipStarted()
stateData.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader)
}
c.voteSigner.signAddVote(ctx, stateData, tmproto.PrecommitType, types.BlockID{})
return nil
}