tendermint/state/store_test.go
Anton Kaliaev 50b87c3445 state: Use last height changed if validator set is empty (#3560)
What happened:

New code was supposed to fall back to last height changed when/if it
failed to find validators at checkpoint height (to make release
non-breaking).

But because we did not check if validator set is empty, the fall back
logic was never executed => resulting in LoadValidators returning an
empty validator set for cases where `lastStoredHeight` is checkpoint
height (i.e. almost all heights if the application does not change
validator set often).

How it was found:

one of our users - @sunboshan reported a bug here
https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833

* use last height changed in validator set is empty
* add a changelog entry
2019-04-15 16:53:38 +02:00

84 lines
2.4 KiB
Go

package state
import (
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
cfg "github.com/tendermint/tendermint/config"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/types"
)
func TestStoreLoadValidators(t *testing.T) {
stateDB := dbm.NewMemDB()
val, _ := types.RandValidator(true, 10)
vals := types.NewValidatorSet([]*types.Validator{val})
// 1) LoadValidators loads validators using a height where they were last changed
saveValidatorsInfo(stateDB, 1, 1, vals)
saveValidatorsInfo(stateDB, 2, 1, vals)
loadedVals, err := LoadValidators(stateDB, 2)
require.NoError(t, err)
assert.NotZero(t, loadedVals.Size())
// 2) LoadValidators loads validators using a checkpoint height
// TODO(melekes): REMOVE in 0.33 release
// https://github.com/tendermint/tendermint/issues/3543
// for releases prior to v0.31.4, it uses last height changed
valInfo := &ValidatorsInfo{
LastHeightChanged: valSetCheckpointInterval,
}
stateDB.Set(calcValidatorsKey(valSetCheckpointInterval), valInfo.Bytes())
assert.NotPanics(t, func() {
saveValidatorsInfo(stateDB, valSetCheckpointInterval+1, 1, vals)
loadedVals, err := LoadValidators(stateDB, valSetCheckpointInterval+1)
if err != nil {
t.Fatal(err)
}
if loadedVals.Size() == 0 {
t.Fatal("Expected validators to be non-empty")
}
})
// ENDREMOVE
saveValidatorsInfo(stateDB, valSetCheckpointInterval, 1, vals)
loadedVals, err = LoadValidators(stateDB, valSetCheckpointInterval)
require.NoError(t, err)
assert.NotZero(t, loadedVals.Size())
}
func BenchmarkLoadValidators(b *testing.B) {
const valSetSize = 100
config := cfg.ResetTestRoot("state_")
defer os.RemoveAll(config.RootDir)
dbType := dbm.DBBackendType(config.DBBackend)
stateDB := dbm.NewDB("state", dbType, config.DBDir())
state, err := LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
if err != nil {
b.Fatal(err)
}
state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
SaveState(stateDB, state)
for i := 10; i < 10000000000; i *= 10 { // 10, 100, 1000, ...
saveValidatorsInfo(stateDB, int64(i), state.LastHeightValidatorsChanged, state.NextValidators)
b.Run(fmt.Sprintf("height=%d", i), func(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := LoadValidators(stateDB, int64(i))
if err != nil {
b.Fatal(err)
}
}
})
}
}