use extended vote sets

This commit is contained in:
Callum Waters
2022-11-18 11:01:56 +01:00
parent 1d4e8ca6ec
commit fc96ab888f
4 changed files with 76 additions and 26 deletions
+6 -6
View File
@@ -385,10 +385,10 @@ func subscribeToVoter(cs *State, addr []byte) <-chan tmpubsub.Message {
// consensus states
type makeStateArgs struct {
config *config.Config
consensusParams *types.ConsensusParams
validators int
application abci.Application
config *config.Config
params *types.ConsensusParams
validators int
application abci.Application
}
func makeState(t *testing.T, args makeStateArgs) (*State, []*validatorStub) {
@@ -409,8 +409,8 @@ func makeState(t *testing.T, args makeStateArgs) (*State, []*validatorStub) {
args.config.SetRoot(t.TempDir())
cp := test.ConsensusParams()
if args.consensusParams != nil {
cp = args.consensusParams
if args.params != nil {
cp = args.params
}
state, privVals := makeGenesisState(t, genesisStateArgs{
+47 -14
View File
@@ -562,23 +562,51 @@ func (cs *State) sendInternalMessage(mi msgInfo) {
}
}
// Reconstruct LastCommit from SeenCommit, which we saved along with the block,
// (which happens even before saving the state)
// Reconstruct the LastCommit from either SeenCommit or the ExtendedCommit. SeenCommit
// and ExtendedCommit are saved along with the block. If VoteExtensions are required
// the method will panic on an absent ExtendedCommit or an ExtendedCommit without
// extension data.
func (cs *State) reconstructLastCommit(state sm.State) {
seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight)
if seenCommit == nil {
panic(fmt.Sprintf(
"failed to reconstruct last commit; seen commit for height %v not found",
state.LastBlockHeight,
))
extensionsEnabled := cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(state.LastBlockHeight)
if !extensionsEnabled {
votes, err := cs.votesFromSeenCommit(state)
if err != nil {
panic(fmt.Sprintf("failed to reconstruct last commit; %s", err))
}
cs.LastCommit = votes
return
}
lastPrecommits := types.CommitToVoteSet(state.ChainID, seenCommit, state.LastValidators)
if !lastPrecommits.HasTwoThirdsMajority() {
panic("failed to reconstruct last commit; does not have +2/3 maj")
votes, err := cs.votesFromExtendedCommit(state)
if err != nil {
panic(fmt.Sprintf("failed to reconstruct last extended commit; %s", err))
}
cs.LastCommit = votes
}
func (cs *State) votesFromExtendedCommit(state sm.State) (*types.VoteSet, error) {
ec := cs.blockStore.LoadBlockExtendedCommit(state.LastBlockHeight)
if ec == nil {
return nil, fmt.Errorf("extended commit for height %v not found", state.LastBlockHeight)
}
vs := ec.ToExtendedVoteSet(state.ChainID, state.LastValidators)
if !vs.HasTwoThirdsMajority() {
return nil, errors.New("extended commit does not have +2/3 majority")
}
return vs, nil
}
func (cs *State) votesFromSeenCommit(state sm.State) (*types.VoteSet, error) {
commit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight)
if commit == nil {
return nil, fmt.Errorf("commit for height %v not found", state.LastBlockHeight)
}
cs.LastCommit = lastPrecommits
vs := commit.ToVoteSet(state.ChainID, state.LastValidators)
if !vs.HasTwoThirdsMajority() {
return nil, errors.New("commit does not have +2/3 majority")
}
return vs, nil
}
// Updates State and increments height to match that of state.
@@ -679,7 +707,11 @@ func (cs *State) updateToState(state sm.State) {
cs.ValidRound = -1
cs.ValidBlock = nil
cs.ValidBlockParts = nil
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
if state.ConsensusParams.ABCI.VoteExtensionsEnabled(height) {
cs.Votes = cstypes.NewExtendedHeightVoteSet(state.ChainID, height, validators)
} else {
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
}
cs.CommitRound = -1
cs.LastValidators = state.LastValidators
cs.TriggeredTimeoutPrecommit = false
@@ -1212,6 +1244,7 @@ func (cs *State) createProposalBlock() (*types.Block, error) {
return nil, errors.New("entered createProposalBlock with privValidator being nil")
}
// TODO(sergio): wouldn't it be easier if CreateProposalBlock accepted cs.LastCommit directly?
var lastExtCommit *types.ExtendedCommit
switch {
case cs.Height == cs.state.InitialHeight:
@@ -2290,7 +2323,7 @@ func (cs *State) signVote(
v := vote.ToProto()
err := cs.privValidator.SignVote(cs.state.ChainID, v)
vote.Signature = v.Signature
// append the extension signature if expect it
// append the extension signature if it is expected
if msgType == tmproto.PrecommitType && !vote.BlockID.IsNil() &&
cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(cs.Height) {
// defensively check that the signer correctly signed the vote extension
+4 -2
View File
@@ -1540,7 +1540,7 @@ func TestExtendVoteCalledWhenEnabled(t *testing.T) {
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(r, nil).Maybe()
cs1, vss := makeState(t, makeStateArgs{application: m})
if !testCase.enabled {
cs1.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = 0
cs1, vss = makeState(t, makeStateArgs{application: m, params: types.DefaultConsensusParams()})
}
height, round := cs1.Height, cs1.Round
@@ -1823,7 +1823,9 @@ func TestVoteExtensionEnableHeight(t *testing.T) {
r := &abci.ResponseFinalizeBlock{AgreedAppData: []byte("hashyHash")}
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(r, nil).Maybe()
m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
cs1, vss := makeState(t, makeStateArgs{application: m})
c := test.ConsensusParams()
c.ABCI.VoteExtensionsEnableHeight = testCase.enableHeight
cs1, vss := makeState(t, makeStateArgs{application: m, params: c})
cs1.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.enableHeight
height, round := cs1.Height, cs1.Round
+19 -4
View File
@@ -39,9 +39,10 @@ We let each peer provide us with up to 2 unexpected "catchup" rounds.
One for their LastCommit round, and another for the official commit round.
*/
type HeightVoteSet struct {
chainID string
height int64
valSet *types.ValidatorSet
chainID string
height int64
valSet *types.ValidatorSet
extensionsEnabled bool
mtx sync.Mutex
round int32 // max tracked round
@@ -57,6 +58,15 @@ func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet)
return hvs
}
func NewExtendedHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet {
hvs := &HeightVoteSet{
chainID: chainID,
extensionsEnabled: true,
}
hvs.Reset(height, valSet)
return hvs
}
func (hvs *HeightVoteSet) Reset(height int64, valSet *types.ValidatorSet) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
@@ -105,7 +115,12 @@ func (hvs *HeightVoteSet) addRound(round int32) {
}
// log.Debug("addRound(round)", "round", round)
prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrevoteType, hvs.valSet)
precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet)
var precommits *types.VoteSet
if hvs.extensionsEnabled {
precommits = types.NewExtendedVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet)
} else {
precommits = types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet)
}
hvs.roundVoteSets[round] = RoundVoteSet{
Prevotes: prevotes,
Precommits: precommits,