state: define interface for state store (#5348)

## Description

Make an interface for the state store. 

Closes: #5213
This commit is contained in:
Marko
2020-09-15 07:45:48 +00:00
committed by GitHub
parent bf6bd2cefa
commit 56911ee352
37 changed files with 495 additions and 327 deletions
+20 -18
View File
@@ -5,8 +5,6 @@ import (
"fmt"
"time"
dbm "github.com/tendermint/tm-db"
abci "github.com/tendermint/tendermint/abci/types"
cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
"github.com/tendermint/tendermint/libs/fail"
@@ -26,7 +24,7 @@ import (
// BlockExecutor provides the context and accessories for properly executing a block.
type BlockExecutor struct {
// save state, validators, consensus params, abci responses here
db dbm.DB
store Store
// execute the app against this
proxyApp proxy.AppConnConsensus
@@ -55,7 +53,7 @@ func BlockExecutorWithMetrics(metrics *Metrics) BlockExecutorOption {
// NewBlockExecutor returns a new BlockExecutor with a NopEventBus.
// Call SetEventBus to provide one.
func NewBlockExecutor(
db dbm.DB,
stateStore Store,
logger log.Logger,
proxyApp proxy.AppConnConsensus,
mempool mempl.Mempool,
@@ -63,7 +61,7 @@ func NewBlockExecutor(
options ...BlockExecutorOption,
) *BlockExecutor {
res := &BlockExecutor{
db: db,
store: stateStore,
proxyApp: proxyApp,
eventBus: types.NopEventBus{},
mempool: mempool,
@@ -79,8 +77,8 @@ func NewBlockExecutor(
return res
}
func (blockExec *BlockExecutor) DB() dbm.DB {
return blockExec.db
func (blockExec *BlockExecutor) Store() Store {
return blockExec.store
}
// SetEventBus - sets the event bus for publishing block related events.
@@ -116,7 +114,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
// Validation does not mutate state, but does require historical information from the stateDB,
// ie. to verify evidence from a validator at an old height.
func (blockExec *BlockExecutor) ValidateBlock(state State, block *types.Block) error {
return validateBlock(blockExec.evpool, blockExec.db, state, block)
return validateBlock(blockExec.evpool, state, block)
}
// ApplyBlock validates the block against the state, executes it against the app,
@@ -135,7 +133,7 @@ func (blockExec *BlockExecutor) ApplyBlock(
startTime := time.Now().UnixNano()
abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block,
blockExec.db, state.InitialHeight)
blockExec.store, state.InitialHeight)
endTime := time.Now().UnixNano()
blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000)
if err != nil {
@@ -145,7 +143,9 @@ func (blockExec *BlockExecutor) ApplyBlock(
fail.Fail() // XXX
// Save the results before we commit.
SaveABCIResponses(blockExec.db, block.Height, abciResponses)
if err := blockExec.store.SaveABCIResponses(block.Height, abciResponses); err != nil {
return state, 0, err
}
fail.Fail() // XXX
@@ -182,7 +182,9 @@ func (blockExec *BlockExecutor) ApplyBlock(
// Update the app hash and save the state.
state.AppHash = appHash
SaveState(blockExec.db, state)
if err := blockExec.store.Save(state); err != nil {
return state, 0, err
}
fail.Fail() // XXX
@@ -254,7 +256,7 @@ func execBlockOnProxyApp(
logger log.Logger,
proxyAppConn proxy.AppConnConsensus,
block *types.Block,
stateDB dbm.DB,
store Store,
initialHeight int64,
) (*tmstate.ABCIResponses, error) {
var validTxs, invalidTxs = 0, 0
@@ -283,7 +285,7 @@ func execBlockOnProxyApp(
}
proxyAppConn.SetResponseCallback(proxyCb)
commitInfo, byzVals := getBeginBlockValidatorInfo(block, stateDB, initialHeight)
commitInfo, byzVals := getBeginBlockValidatorInfo(block, store, initialHeight)
// Begin block
var err error
@@ -322,14 +324,14 @@ func execBlockOnProxyApp(
return abciResponses, nil
}
func getBeginBlockValidatorInfo(block *types.Block, stateDB dbm.DB,
func getBeginBlockValidatorInfo(block *types.Block, store Store,
initialHeight int64) (abci.LastCommitInfo, []abci.Evidence) {
voteInfos := make([]abci.VoteInfo, block.LastCommit.Size())
// Initial block -> LastCommitInfo.Votes are empty.
// Remember that the first LastCommit is intentionally empty, so it makes
// sense for LastCommitInfo.Votes to also be empty.
if block.Height > initialHeight {
lastValSet, err := LoadValidators(stateDB, block.Height-1)
lastValSet, err := store.LoadValidators(block.Height - 1)
if err != nil {
panic(err)
}
@@ -359,7 +361,7 @@ func getBeginBlockValidatorInfo(block *types.Block, stateDB dbm.DB,
// We need the validator set. We already did this in validateBlock.
// TODO: Should we instead cache the valset in the evidence itself and add
// `SetValidatorSet()` and `ToABCI` methods ?
valset, err := LoadValidators(stateDB, ev.Height())
valset, err := store.LoadValidators(ev.Height())
if err != nil {
panic(err)
}
@@ -528,10 +530,10 @@ func ExecCommitBlock(
appConnConsensus proxy.AppConnConsensus,
block *types.Block,
logger log.Logger,
stateDB dbm.DB,
store Store,
initialHeight int64,
) ([]byte, error) {
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, stateDB, initialHeight)
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, store, initialHeight)
if err != nil {
logger.Error("Error executing block on proxy app", "height", block.Height, "err", err)
return nil, err
+10 -5
View File
@@ -35,8 +35,9 @@ func TestApplyBlock(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, _ := makeState(1, 1)
stateStore := sm.NewStore(stateDB)
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(),
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
mock.Mempool{}, sm.MockEvidencePool{})
block := makeBlock(state, 1)
@@ -60,6 +61,7 @@ func TestBeginBlockValidators(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // no need to check error again
state, stateDB, _ := makeState(2, 2)
stateStore := sm.NewStore(stateDB)
prevHash := state.LastBlockID.Hash
prevParts := types.PartSetHeader{}
@@ -94,7 +96,7 @@ func TestBeginBlockValidators(t *testing.T) {
// block for height 2
block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB, 1)
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1)
require.Nil(t, err, tc.desc)
// -> app receives a list of validators with a bool indicating if they signed
@@ -122,6 +124,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, privVals := makeState(2, 12)
stateStore := sm.NewStore(stateDB)
prevHash := state.LastBlockID.Hash
prevParts := types.PartSetHeader{}
@@ -163,7 +166,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
block.Time = now
block.Evidence.Evidence = tc.evidence
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB, 1)
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1)
require.Nil(t, err, tc.desc)
// -> app must receive an index of the byzantine validator
@@ -311,9 +314,10 @@ func TestEndBlockValidatorUpdates(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, _ := makeState(1, 1)
stateStore := sm.NewStore(stateDB)
blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
log.TestingLogger(),
proxyApp.Consensus(),
mock.Mempool{},
@@ -381,8 +385,9 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, _ := makeState(1, 1)
stateStore := sm.NewStore(stateDB)
blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
log.TestingLogger(),
proxyApp.Consensus(),
mock.Mempool{},
+4 -10
View File
@@ -1,12 +1,11 @@
package state
import (
dbm "github.com/tendermint/tm-db"
abci "github.com/tendermint/tendermint/abci/types"
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"
)
//
@@ -40,14 +39,9 @@ func ValidateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, params tmproto
return validateValidatorUpdates(abciUpdates, params)
}
// SaveConsensusParamsInfo is an alias for the private saveConsensusParamsInfo
// method in store.go, exported exclusively and explicitly for testing.
func SaveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params tmproto.ConsensusParams) {
saveConsensusParamsInfo(db, nextHeight, changeHeight, params)
}
// SaveValidatorsInfo is an alias for the private saveValidatorsInfo method in
// store.go, exported exclusively and explicitly for testing.
func SaveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) {
saveValidatorsInfo(db, height, lastHeightChanged, valSet)
func SaveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) error {
stateStore := dbStore{db}
return stateStore.saveValidatorsInfo(height, lastHeightChanged, valSet)
}
+7 -2
View File
@@ -115,12 +115,17 @@ func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValida
})
stateDB := dbm.NewMemDB()
sm.SaveState(stateDB, s)
stateStore := sm.NewStore(stateDB)
if err := stateStore.Save(s); err != nil {
panic(err)
}
for i := 1; i < height; i++ {
s.LastBlockHeight++
s.LastValidators = s.Validators.Copy()
sm.SaveState(stateDB, s)
if err := stateStore.Save(s); err != nil {
panic(err)
}
}
return s, stateDB, privVals
+50 -30
View File
@@ -29,10 +29,12 @@ func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, sm.State) {
config := cfg.ResetTestRoot("state_")
dbType := dbm.BackendType(config.DBBackend)
stateDB, err := dbm.NewDB("state", dbType, config.DBDir())
stateStore := sm.NewStore(stateDB)
require.NoError(t, err)
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile())
assert.NoError(t, err, "expected no error on LoadStateFromDBOrGenesisFile")
sm.SaveState(stateDB, state)
err = stateStore.Save(state)
require.NoError(t, err)
tearDown := func(t *testing.T) { os.RemoveAll(config.RootDir) }
@@ -74,13 +76,16 @@ func TestMakeGenesisStateNilValidators(t *testing.T) {
func TestStateSaveLoad(t *testing.T) {
tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t)
stateStore := sm.NewStore(stateDB)
assert := assert.New(t)
state.LastBlockHeight++
state.LastValidators = state.Validators
sm.SaveState(stateDB, state)
err := stateStore.Save(state)
require.NoError(t, err)
loadedState := sm.LoadState(stateDB)
loadedState, err := stateStore.Load()
require.NoError(t, err)
assert.True(state.Equals(loadedState),
fmt.Sprintf("expected state and its copy to be identical.\ngot: %v\nexpected: %v\n",
loadedState, state))
@@ -90,6 +95,7 @@ func TestStateSaveLoad(t *testing.T) {
func TestABCIResponsesSaveLoad1(t *testing.T) {
tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t)
stateStore := sm.NewStore(stateDB)
assert := assert.New(t)
state.LastBlockHeight++
@@ -107,8 +113,9 @@ func TestABCIResponsesSaveLoad1(t *testing.T) {
types.TM2PB.NewValidatorUpdate(ed25519.GenPrivKey().PubKey(), 10),
}}
sm.SaveABCIResponses(stateDB, block.Height, abciResponses)
loadedABCIResponses, err := sm.LoadABCIResponses(stateDB, block.Height)
err := stateStore.SaveABCIResponses(block.Height, abciResponses)
require.NoError(t, err)
loadedABCIResponses, err := stateStore.LoadABCIResponses(block.Height)
assert.Nil(err)
assert.Equal(abciResponses, loadedABCIResponses,
fmt.Sprintf("ABCIResponses don't match:\ngot: %v\nexpected: %v\n",
@@ -121,6 +128,8 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
defer tearDown(t)
assert := assert.New(t)
stateStore := sm.NewStore(stateDB)
cases := [...]struct {
// Height is implied to equal index+2,
// as block 1 is created from genesis.
@@ -169,7 +178,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
// Query all before, this should return error.
for i := range cases {
h := int64(i + 1)
res, err := sm.LoadABCIResponses(stateDB, h)
res, err := stateStore.LoadABCIResponses(h)
assert.Error(err, "%d: %#v", i, res)
}
@@ -181,13 +190,14 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
DeliverTxs: tc.added,
EndBlock: &abci.ResponseEndBlock{},
}
sm.SaveABCIResponses(stateDB, h, responses)
err := stateStore.SaveABCIResponses(h, responses)
require.NoError(t, err)
}
// Query all before, should return expected value.
for i, tc := range cases {
h := int64(i + 1)
res, err := sm.LoadABCIResponses(stateDB, h)
res, err := stateStore.LoadABCIResponses(h)
if assert.NoError(err, "%d", i) {
t.Log(res)
responses := &tmstate.ABCIResponses{
@@ -206,27 +216,30 @@ func TestValidatorSimpleSaveLoad(t *testing.T) {
defer tearDown(t)
assert := assert.New(t)
statestore := sm.NewStore(stateDB)
// Can't load anything for height 0.
_, err := sm.LoadValidators(stateDB, 0)
_, err := statestore.LoadValidators(0)
assert.IsType(sm.ErrNoValSetForHeight{}, err, "expected err at height 0")
// Should be able to load for height 1.
v, err := sm.LoadValidators(stateDB, 1)
v, err := statestore.LoadValidators(1)
assert.Nil(err, "expected no err at height 1")
assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match")
// Should be able to load for height 2.
v, err = sm.LoadValidators(stateDB, 2)
v, err = statestore.LoadValidators(2)
assert.Nil(err, "expected no err at height 2")
assert.Equal(v.Hash(), state.NextValidators.Hash(), "expected validator hashes to match")
// Increment height, save; should be able to load for next & next next height.
state.LastBlockHeight++
nextHeight := state.LastBlockHeight + 1
sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
vp0, err := sm.LoadValidators(stateDB, nextHeight+0)
err = statestore.Save(state)
require.NoError(t, err)
vp0, err := statestore.LoadValidators(nextHeight + 0)
assert.Nil(err, "expected no err")
vp1, err := sm.LoadValidators(stateDB, nextHeight+1)
vp1, err := statestore.LoadValidators(nextHeight + 1)
assert.Nil(err, "expected no err")
assert.Equal(vp0.Hash(), state.Validators.Hash(), "expected validator hashes to match")
assert.Equal(vp1.Hash(), state.NextValidators.Hash(), "expected next validator hashes to match")
@@ -236,6 +249,7 @@ func TestValidatorSimpleSaveLoad(t *testing.T) {
func TestOneValidatorChangesSaveLoad(t *testing.T) {
tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t)
stateStore := sm.NewStore(stateDB)
// Change vals at these heights.
changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20}
@@ -260,8 +274,8 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
require.NoError(t, err)
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
require.NoError(t, err)
nextHeight := state.LastBlockHeight + 1
sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
err := stateStore.Save(state)
require.NoError(t, err)
}
// On each height change, increment the power by one.
@@ -279,7 +293,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
}
for i, power := range testCases {
v, err := sm.LoadValidators(stateDB, int64(i+1+1)) // +1 because vset changes delayed by 1 block.
v, err := stateStore.LoadValidators(int64(i + 1 + 1)) // +1 because vset changes delayed by 1 block.
assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", i))
assert.Equal(t, v.Size(), 1, "validator set size is greater than 1: %d", v.Size())
_, val := v.GetByIndex(0)
@@ -886,18 +900,20 @@ func TestLargeGenesisValidator(t *testing.T) {
func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) {
const valSetSize = 2
tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t)
t.Cleanup(func() { tearDown(t) })
stateStore := sm.NewStore(stateDB)
state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
sm.SaveState(stateDB, state)
err := stateStore.Save(state)
require.NoError(t, err)
nextHeight := state.LastBlockHeight + 1
v0, err := sm.LoadValidators(stateDB, nextHeight)
v0, err := stateStore.LoadValidators(nextHeight)
assert.Nil(t, err)
acc0 := v0.Validators[0].ProposerPriority
v1, err := sm.LoadValidators(stateDB, nextHeight+1)
v1, err := stateStore.LoadValidators(nextHeight + 1)
assert.Nil(t, err)
acc1 := v1.Validators[0].ProposerPriority
@@ -910,10 +926,12 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
const valSetSize = 7
tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t)
stateStore := sm.NewStore(stateDB)
require.Equal(t, int64(0), state.LastBlockHeight)
state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
sm.SaveState(stateDB, state)
err := stateStore.Save(state)
require.NoError(t, err)
_, valOld := state.Validators.GetByIndex(0)
var pubkeyOld = valOld.PubKey
@@ -923,17 +941,17 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(state, pubkey)
// Save state etc.
var err error
var validatorUpdates []*types.Validator
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
require.NoError(t, err)
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
require.Nil(t, err)
nextHeight := state.LastBlockHeight + 1
sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
err = stateStore.Save(state)
require.NoError(t, err)
// Load nextheight, it should be the oldpubkey.
v0, err := sm.LoadValidators(stateDB, nextHeight)
v0, err := stateStore.LoadValidators(nextHeight)
assert.Nil(t, err)
assert.Equal(t, valSetSize, v0.Size())
index, val := v0.GetByAddress(pubkeyOld.Address())
@@ -943,7 +961,7 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
}
// Load nextheight+1, it should be the new pubkey.
v1, err := sm.LoadValidators(stateDB, nextHeight+1)
v1, err := stateStore.LoadValidators(nextHeight + 1)
assert.Nil(t, err)
assert.Equal(t, valSetSize, v1.Size())
index, val = v1.GetByAddress(pubkey.Address())
@@ -972,6 +990,8 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t)
stateStore := sm.NewStore(stateDB)
// Change vals at these heights.
changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20}
N := len(changeHeights)
@@ -1004,8 +1024,8 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
require.Nil(t, err)
nextHeight := state.LastBlockHeight + 1
sm.SaveConsensusParamsInfo(stateDB, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
err := stateStore.Save(state)
require.NoError(t, err)
}
// Make all the test cases by using the same params until after the change.
@@ -1023,7 +1043,7 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
}
for _, testCase := range testCases {
p, err := sm.LoadConsensusParams(stateDB, testCase.height)
p, err := stateStore.LoadConsensusParams(testCase.height)
assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", testCase.height))
assert.EqualValues(t, testCase.params, p, fmt.Sprintf(`unexpected consensus params at
height %d`, testCase.height))
+167 -90
View File
@@ -1,6 +1,7 @@
package state
import (
"errors"
"fmt"
"github.com/gogo/protobuf/proto"
@@ -36,10 +37,52 @@ func calcABCIResponsesKey(height int64) []byte {
return []byte(fmt.Sprintf("abciResponsesKey:%v", height))
}
//----------------------
type Store interface {
// LoadFromDBOrGenesisFile loads the most recent state.
// If the chain is new it will use the genesis file from the provided genesis file path as the current state.
LoadFromDBOrGenesisFile(string) (State, error)
// LoadFromDBOrGenesisDoc loads the most recent state.
// If the chain is new it will use the genesis doc as the current state.
LoadFromDBOrGenesisDoc(*types.GenesisDoc) (State, error)
// Load loads the current state of the blockchain
Load() (State, error)
// LoadValidators loads the validator set at a given height
LoadValidators(int64) (*types.ValidatorSet, error)
// LoadABCIResponses loads the abciResponse for a given height
LoadABCIResponses(int64) (*tmstate.ABCIResponses, error)
// LoadConsensusParams loads the consensus params for a given height
LoadConsensusParams(int64) (tmproto.ConsensusParams, error)
// Save overwrites the previous state with the updated one
Save(State) error
// SaveABCIResponses saves ABCIResponses for a given height
SaveABCIResponses(int64, *tmstate.ABCIResponses) error
// Bootstrap is used for bootstrapping state when not starting from a initial height.
Bootstrap(State) error
// PruneStates takes the height from which to start prning and which height stop at
PruneStates(int64, int64) error
}
//dbStore wraps a db (github.com/tendermint/tm-db)
type dbStore struct {
db dbm.DB
}
var _ Store = (*dbStore)(nil)
// NewStore creates the dbStore of the state pkg.
func NewStore(db dbm.DB) Store {
return dbStore{db}
}
// LoadStateFromDBOrGenesisFile loads the most recent state from the database,
// or creates a new one from the given genesisFilePath.
func LoadStateFromDBOrGenesisFile(stateDB dbm.DB, genesisFilePath string) (State, error) {
state := LoadState(stateDB)
func (store dbStore) LoadFromDBOrGenesisFile(genesisFilePath string) (State, error) {
state, err := store.Load()
if err != nil {
return State{}, err
}
if state.IsEmpty() {
var err error
state, err = MakeGenesisStateFromFile(genesisFilePath)
@@ -53,8 +96,11 @@ func LoadStateFromDBOrGenesisFile(stateDB dbm.DB, genesisFilePath string) (State
// LoadStateFromDBOrGenesisDoc loads the most recent state from the database,
// or creates a new one from the given genesisDoc.
func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) (State, error) {
state := LoadState(stateDB)
func (store dbStore) LoadFromDBOrGenesisDoc(genesisDoc *types.GenesisDoc) (State, error) {
state, err := store.Load()
if err != nil {
return State{}, err
}
if state.IsEmpty() {
var err error
@@ -68,17 +114,17 @@ func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) (
}
// LoadState loads the State from the database.
func LoadState(db dbm.DB) State {
return loadState(db, stateKey)
func (store dbStore) Load() (State, error) {
return store.loadState(stateKey)
}
func loadState(db dbm.DB, key []byte) (state State) {
buf, err := db.Get(key)
func (store dbStore) loadState(key []byte) (state State, err error) {
buf, err := store.db.Get(key)
if err != nil {
panic(err)
return state, err
}
if len(buf) == 0 {
return state
return state, nil
}
sp := new(tmstate.State)
@@ -92,51 +138,72 @@ func loadState(db dbm.DB, key []byte) (state State) {
sm, err := StateFromProto(sp)
if err != nil {
panic(err)
return state, err
}
return *sm
return *sm, nil
}
// SaveState persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database.
// Save persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database.
// This flushes the writes (e.g. calls SetSync).
func SaveState(db dbm.DB, state State) {
saveState(db, state, stateKey)
func (store dbStore) Save(state State) error {
return store.save(state, stateKey)
}
func saveState(db dbm.DB, state State, key []byte) {
func (store dbStore) save(state State, key []byte) error {
nextHeight := state.LastBlockHeight + 1
// If first block, save validators for the block.
if nextHeight == 1 {
nextHeight = state.InitialHeight
// This extra logic due to Tendermint validator set changes being delayed 1 block.
// It may get overwritten due to InitChain validator updates.
saveValidatorsInfo(db, nextHeight, nextHeight, state.Validators)
if err := store.saveValidatorsInfo(nextHeight, nextHeight, state.Validators); err != nil {
return err
}
}
// Save next validators.
saveValidatorsInfo(db, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
if err := store.saveValidatorsInfo(nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators); err != nil {
return err
}
// Save next consensus params.
saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
err := db.SetSync(key, state.Bytes())
if err != nil {
panic(err)
if err := store.saveConsensusParamsInfo(nextHeight,
state.LastHeightConsensusParamsChanged, state.ConsensusParams); err != nil {
return err
}
err := store.db.SetSync(key, state.Bytes())
if err != nil {
return err
}
return nil
}
// BootstrapState saves a new state, used e.g. by state sync when starting from non-zero height.
func BootstrapState(db dbm.DB, state State) error {
func (store dbStore) Bootstrap(state State) error {
height := state.LastBlockHeight + 1
if height == 1 {
height = state.InitialHeight
}
if height > 1 && !state.LastValidators.IsNilOrEmpty() {
saveValidatorsInfo(db, height-1, height-1, state.LastValidators)
if err := store.saveValidatorsInfo(height-1, height-1, state.LastValidators); err != nil {
return err
}
}
saveValidatorsInfo(db, height, height, state.Validators)
saveValidatorsInfo(db, height+1, height+1, state.NextValidators)
saveConsensusParamsInfo(db, height, height, state.ConsensusParams)
return db.SetSync(stateKey, state.Bytes())
if err := store.saveValidatorsInfo(height, height, state.Validators); err != nil {
return err
}
if err := store.saveValidatorsInfo(height+1, height+1, state.NextValidators); err != nil {
return err
}
if err := store.saveConsensusParamsInfo(height, height, state.ConsensusParams); err != nil {
return err
}
return store.db.SetSync(stateKey, state.Bytes())
}
// PruneStates deletes states between the given heights (including from, excluding to). It is not
@@ -147,20 +214,20 @@ func BootstrapState(db dbm.DB, state State) error {
// encoding not preserving ordering: https://github.com/tendermint/tendermint/issues/4567
// This will cause some old states to be left behind when doing incremental partial prunes,
// specifically older checkpoints and LastHeightChanged targets.
func PruneStates(db dbm.DB, from int64, to int64) error {
func (store dbStore) PruneStates(from int64, to int64) error {
if from <= 0 || to <= 0 {
return fmt.Errorf("from height %v and to height %v must be greater than 0", from, to)
}
if from >= to {
return fmt.Errorf("from height %v must be lower than to height %v", from, to)
}
valInfo := loadValidatorsInfo(db, to)
if valInfo == nil {
return fmt.Errorf("validators at height %v not found", to)
valInfo, err := loadValidatorsInfo(store.db, to)
if err != nil {
return fmt.Errorf("validators at height %v not found: %w", to, err)
}
paramsInfo := loadConsensusParamsInfo(db, to)
if paramsInfo == nil {
return fmt.Errorf("consensus params at height %v not found", to)
paramsInfo, err := store.loadConsensusParamsInfo(to)
if err != nil {
return fmt.Errorf("consensus params at height %v not found: %w", to, err)
}
keepVals := make(map[int64]bool)
@@ -173,10 +240,9 @@ func PruneStates(db dbm.DB, from int64, to int64) error {
keepParams[paramsInfo.LastHeightChanged] = true
}
batch := db.NewBatch()
batch := store.db.NewBatch()
defer batch.Close()
pruned := uint64(0)
var err error
// We have to delete in reverse order, to avoid deleting previous heights that have validator
// sets and consensus params that we may need to retrieve.
@@ -185,10 +251,9 @@ func PruneStates(db dbm.DB, from int64, to int64) error {
// params, otherwise they will panic if they're retrieved directly (instead of
// indirectly via a LastHeightChanged pointer).
if keepVals[h] {
v := loadValidatorsInfo(db, h)
if v.ValidatorSet == nil {
vip, err := LoadValidators(db, h)
v, err := loadValidatorsInfo(store.db, h)
if err != nil || v.ValidatorSet == nil {
vip, err := store.LoadValidators(h)
if err != nil {
return err
}
@@ -218,17 +283,23 @@ func PruneStates(db dbm.DB, from int64, to int64) error {
}
if keepParams[h] {
p := loadConsensusParamsInfo(db, h)
p, err := store.loadConsensusParamsInfo(h)
if err != nil {
return err
}
if p.ConsensusParams.Equal(&tmproto.ConsensusParams{}) {
p.ConsensusParams, err = LoadConsensusParams(db, h)
p.ConsensusParams, err = store.LoadConsensusParams(h)
if err != nil {
return err
}
p.LastHeightChanged = h
bz, err := p.Marshal()
if err != nil {
return err
}
err = batch.Set(calcConsensusParamsKey(h), bz)
if err != nil {
return err
@@ -254,7 +325,7 @@ func PruneStates(db dbm.DB, from int64, to int64) error {
return err
}
batch.Close()
batch = db.NewBatch()
batch = store.db.NewBatch()
defer batch.Close()
}
}
@@ -283,8 +354,8 @@ func ABCIResponsesResultsHash(ar *tmstate.ABCIResponses) []byte {
// This is useful for recovering from crashes where we called app.Commit and
// before we called s.Save(). It can also be used to produce Merkle proofs of
// the result of txs.
func LoadABCIResponses(db dbm.DB, height int64) (*tmstate.ABCIResponses, error) {
buf, err := db.Get(calcABCIResponsesKey(height))
func (store dbStore) LoadABCIResponses(height int64) (*tmstate.ABCIResponses, error) {
buf, err := store.db.Get(calcABCIResponsesKey(height))
if err != nil {
return nil, err
}
@@ -311,7 +382,7 @@ func LoadABCIResponses(db dbm.DB, height int64) (*tmstate.ABCIResponses, error)
// Merkle proofs.
//
// Exposed for testing.
func SaveABCIResponses(db dbm.DB, height int64, abciResponses *tmstate.ABCIResponses) {
func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCIResponses) error {
var dtxs []*abci.ResponseDeliverTx
//strip nil values,
for _, tx := range abciResponses.DeliverTxs {
@@ -323,33 +394,36 @@ func SaveABCIResponses(db dbm.DB, height int64, abciResponses *tmstate.ABCIRespo
bz, err := abciResponses.Marshal()
if err != nil {
panic(err)
return err
}
err = db.SetSync(calcABCIResponsesKey(height), bz)
err = store.db.SetSync(calcABCIResponsesKey(height), bz)
if err != nil {
panic(err)
return err
}
return nil
}
//-----------------------------------------------------------------------------
// LoadValidators loads the ValidatorSet for a given height.
// Returns ErrNoValSetForHeight if the validator set can't be found for this height.
func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) {
valInfo := loadValidatorsInfo(db, height)
if valInfo == nil {
func (store dbStore) LoadValidators(height int64) (*types.ValidatorSet, error) {
valInfo, err := loadValidatorsInfo(store.db, height)
if err != nil {
return nil, ErrNoValSetForHeight{height}
}
if valInfo.ValidatorSet == nil {
lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged)
valInfo2 := loadValidatorsInfo(db, lastStoredHeight)
if valInfo2 == nil || valInfo2.ValidatorSet == nil {
panic(
fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)",
valInfo2, err := loadValidatorsInfo(store.db, lastStoredHeight)
if err != nil || valInfo2.ValidatorSet == nil {
return nil,
fmt.Errorf("couldn't find validators at height %d (height %d was originally requested): %w",
lastStoredHeight,
height,
),
)
err,
)
}
vs, err := types.ValidatorSetFromProto(valInfo2.ValidatorSet)
@@ -381,14 +455,14 @@ func lastStoredHeightFor(height, lastHeightChanged int64) int64 {
}
// CONTRACT: Returned ValidatorsInfo can be mutated.
func loadValidatorsInfo(db dbm.DB, height int64) *tmstate.ValidatorsInfo {
func loadValidatorsInfo(db dbm.DB, height int64) (*tmstate.ValidatorsInfo, error) {
buf, err := db.Get(calcValidatorsKey(height))
if err != nil {
panic(err)
return nil, err
}
if len(buf) == 0 {
return nil
return nil, errors.New("value retrieved from db is empty")
}
v := new(tmstate.ValidatorsInfo)
@@ -400,7 +474,7 @@ func loadValidatorsInfo(db dbm.DB, height int64) *tmstate.ValidatorsInfo {
}
// TODO: ensure that buf is completely read.
return v
return v, nil
}
// saveValidatorsInfo persists the validator set.
@@ -408,9 +482,9 @@ func loadValidatorsInfo(db dbm.DB, height int64) *tmstate.ValidatorsInfo {
// `height` is the effective height for which the validator is responsible for
// signing. It should be called from s.Save(), right before the state itself is
// persisted.
func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) {
func (store dbStore) saveValidatorsInfo(height, lastHeightChanged int64, valSet *types.ValidatorSet) error {
if lastHeightChanged > height {
panic("LastHeightChanged cannot be greater than ValidatorsInfo height")
return errors.New("lastHeightChanged cannot be greater than ValidatorsInfo height")
}
valInfo := &tmstate.ValidatorsInfo{
LastHeightChanged: lastHeightChanged,
@@ -420,20 +494,22 @@ func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *type
if height == lastHeightChanged || height%valSetCheckpointInterval == 0 {
pv, err := valSet.ToProto()
if err != nil {
panic(err)
return err
}
valInfo.ValidatorSet = pv
}
bz, err := valInfo.Marshal()
if err != nil {
panic(err)
return err
}
err = db.Set(calcValidatorsKey(height), bz)
err = store.db.Set(calcValidatorsKey(height), bz)
if err != nil {
panic(err)
return err
}
return nil
}
//-----------------------------------------------------------------------------
@@ -441,23 +517,22 @@ func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *type
// ConsensusParamsInfo represents the latest consensus params, or the last height it changed
// LoadConsensusParams loads the ConsensusParams for a given height.
func LoadConsensusParams(db dbm.DB, height int64) (tmproto.ConsensusParams, error) {
func (store dbStore) LoadConsensusParams(height int64) (tmproto.ConsensusParams, error) {
empty := tmproto.ConsensusParams{}
paramsInfo := loadConsensusParamsInfo(db, height)
if paramsInfo == nil {
return empty, ErrNoConsensusParamsForHeight{height}
paramsInfo, err := store.loadConsensusParamsInfo(height)
if err != nil {
return empty, fmt.Errorf("could not find consensus params for height #%d: %w", height, err)
}
if paramsInfo.ConsensusParams.Equal(&empty) {
paramsInfo2 := loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged)
if paramsInfo2 == nil {
panic(
fmt.Sprintf(
"Couldn't find consensus params at height %d as last changed from height %d",
paramsInfo.LastHeightChanged,
height,
),
paramsInfo2, err := store.loadConsensusParamsInfo(paramsInfo.LastHeightChanged)
if err != nil {
return empty, fmt.Errorf(
"couldn't find consensus params at height %d as last changed from height %d: %w",
paramsInfo.LastHeightChanged,
height,
err,
)
}
@@ -467,13 +542,13 @@ func LoadConsensusParams(db dbm.DB, height int64) (tmproto.ConsensusParams, erro
return paramsInfo.ConsensusParams, nil
}
func loadConsensusParamsInfo(db dbm.DB, height int64) *tmstate.ConsensusParamsInfo {
buf, err := db.Get(calcConsensusParamsKey(height))
func (store dbStore) loadConsensusParamsInfo(height int64) (*tmstate.ConsensusParamsInfo, error) {
buf, err := store.db.Get(calcConsensusParamsKey(height))
if err != nil {
panic(err)
return nil, err
}
if len(buf) == 0 {
return nil
return nil, errors.New("value retrieved from db is empty")
}
paramsInfo := new(tmstate.ConsensusParamsInfo)
@@ -484,14 +559,14 @@ func loadConsensusParamsInfo(db dbm.DB, height int64) *tmstate.ConsensusParamsIn
}
// TODO: ensure that buf is completely read.
return paramsInfo
return paramsInfo, nil
}
// saveConsensusParamsInfo persists the consensus params for the next block to disk.
// It should be called from s.Save(), right before the state itself is persisted.
// If the consensus params did not change after processing the latest block,
// only the last height for which they changed is persisted.
func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params tmproto.ConsensusParams) {
func (store dbStore) saveConsensusParamsInfo(nextHeight, changeHeight int64, params tmproto.ConsensusParams) error {
paramsInfo := &tmstate.ConsensusParamsInfo{
LastHeightChanged: changeHeight,
}
@@ -501,11 +576,13 @@ func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params t
}
bz, err := paramsInfo.Marshal()
if err != nil {
panic(err)
return err
}
err = db.Set(calcConsensusParamsKey(nextHeight), bz)
err = store.db.Set(calcConsensusParamsKey(nextHeight), bz)
if err != nil {
panic(err)
return err
}
return nil
}
+27 -16
View File
@@ -23,21 +23,25 @@ import (
func TestStoreLoadValidators(t *testing.T) {
stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB)
val, _ := types.RandValidator(true, 10)
vals := types.NewValidatorSet([]*types.Validator{val})
// 1) LoadValidators loads validators using a height where they were last changed
sm.SaveValidatorsInfo(stateDB, 1, 1, vals)
sm.SaveValidatorsInfo(stateDB, 2, 1, vals)
loadedVals, err := sm.LoadValidators(stateDB, 2)
err := sm.SaveValidatorsInfo(stateDB, 1, 1, vals)
require.NoError(t, err)
err = sm.SaveValidatorsInfo(stateDB, 2, 1, vals)
require.NoError(t, err)
loadedVals, err := stateStore.LoadValidators(2)
require.NoError(t, err)
assert.NotZero(t, loadedVals.Size())
// 2) LoadValidators loads validators using a checkpoint height
sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval, 1, vals)
err = sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval, 1, vals)
require.NoError(t, err)
loadedVals, err = sm.LoadValidators(stateDB, sm.ValSetCheckpointInterval)
loadedVals, err = stateStore.LoadValidators(sm.ValSetCheckpointInterval)
require.NoError(t, err)
assert.NotZero(t, loadedVals.Size())
}
@@ -50,22 +54,27 @@ func BenchmarkLoadValidators(b *testing.B) {
dbType := dbm.BackendType(config.DBBackend)
stateDB, err := dbm.NewDB("state", dbType, config.DBDir())
require.NoError(b, err)
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
stateStore := sm.NewStore(stateDB)
state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile())
if err != nil {
b.Fatal(err)
}
state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
sm.SaveState(stateDB, state)
err = stateStore.Save(state)
require.NoError(b, err)
for i := 10; i < 10000000000; i *= 10 { // 10, 100, 1000, ...
i := i
sm.SaveValidatorsInfo(stateDB, int64(i), state.LastHeightValidatorsChanged, state.NextValidators)
if err := sm.SaveValidatorsInfo(stateDB,
int64(i), state.LastHeightValidatorsChanged, state.NextValidators); err != nil {
b.Fatal(err)
}
b.Run(fmt.Sprintf("height=%d", i), func(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := sm.LoadValidators(stateDB, int64(i))
_, err := stateStore.LoadValidators(int64(i))
if err != nil {
b.Fatal(err)
}
@@ -98,6 +107,7 @@ func TestPruneStates(t *testing.T) {
tc := tc
t.Run(name, func(t *testing.T) {
db := dbm.NewMemDB()
stateStore := sm.NewStore(db)
pk := ed25519.GenPrivKey().PubKey()
// Generate a bunch of state data. Validators change for heights ending with 3, and
@@ -134,19 +144,21 @@ func TestPruneStates(t *testing.T) {
state.LastValidators = state.Validators
}
sm.SaveState(db, state)
err := stateStore.Save(state)
require.NoError(t, err)
sm.SaveABCIResponses(db, h, &tmstate.ABCIResponses{
err = stateStore.SaveABCIResponses(h, &tmstate.ABCIResponses{
DeliverTxs: []*abci.ResponseDeliverTx{
{Data: []byte{1}},
{Data: []byte{2}},
{Data: []byte{3}},
},
})
require.NoError(t, err)
}
// Test assertions
err := sm.PruneStates(db, tc.pruneFrom, tc.pruneTo)
err := stateStore.PruneStates(tc.pruneFrom, tc.pruneTo)
if tc.expectErr {
require.Error(t, err)
return
@@ -158,7 +170,7 @@ func TestPruneStates(t *testing.T) {
expectABCI := sliceToMap(tc.expectABCI)
for h := int64(1); h <= tc.makeHeights; h++ {
vals, err := sm.LoadValidators(db, h)
vals, err := stateStore.LoadValidators(h)
if expectVals[h] {
require.NoError(t, err, "validators height %v", h)
require.NotNil(t, vals)
@@ -167,16 +179,15 @@ func TestPruneStates(t *testing.T) {
require.Equal(t, sm.ErrNoValSetForHeight{Height: h}, err)
}
params, err := sm.LoadConsensusParams(db, h)
params, err := stateStore.LoadConsensusParams(h)
if expectParams[h] {
require.NoError(t, err, "params height %v", h)
require.False(t, params.Equal(&tmproto.ConsensusParams{}))
} else {
require.Error(t, err, "params height %v", h)
require.Equal(t, sm.ErrNoConsensusParamsForHeight{Height: h}, err)
}
abci, err := sm.LoadABCIResponses(db, h)
abci, err := stateStore.LoadABCIResponses(h)
if expectABCI[h] {
require.NoError(t, err, "abci height %v", h)
require.NotNil(t, abci)
+2 -1
View File
@@ -33,7 +33,8 @@ func TestTxFilter(t *testing.T) {
for i, tc := range testCases {
stateDB, err := dbm.NewDB("state", "memdb", os.TempDir())
require.NoError(t, err)
state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
stateStore := sm.NewStore(stateDB)
state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc)
require.NoError(t, err)
f := sm.TxPreCheck(state) // current max size of a tx 1850
+1 -3
View File
@@ -5,8 +5,6 @@ import (
"errors"
"fmt"
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/types"
)
@@ -14,7 +12,7 @@ import (
//-----------------------------------------------------
// Validate block
func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block *types.Block) error {
func validateBlock(evidencePool EvidencePool, state State, block *types.Block) error {
// Validate internal consistency.
if err := block.ValidateBasic(); err != nil {
return err
+8 -4
View File
@@ -29,8 +29,9 @@ func TestValidateBlockHeader(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, privVals := makeState(3, 1)
stateStore := sm.NewStore(stateDB)
blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
log.TestingLogger(),
proxyApp.Consensus(),
memmock.Mempool{},
@@ -99,8 +100,9 @@ func TestValidateBlockCommit(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, privVals := makeState(1, 1)
stateStore := sm.NewStore(stateDB)
blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
log.TestingLogger(),
proxyApp.Consensus(),
memmock.Mempool{},
@@ -212,6 +214,7 @@ func TestValidateBlockEvidence(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, privVals := makeState(4, 1)
stateStore := sm.NewStore(stateDB)
defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
evpool := &mocks.EvidencePool{}
@@ -220,7 +223,7 @@ func TestValidateBlockEvidence(t *testing.T) {
state.ConsensusParams.Evidence.MaxNum = 3
blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
log.TestingLogger(),
proxyApp.Consensus(),
memmock.Mempool{},
@@ -280,6 +283,7 @@ func TestValidateBlockEvidence(t *testing.T) {
func TestValidateDuplicateEvidenceShouldFail(t *testing.T) {
var height int64 = 1
state, stateDB, privVals := makeState(2, int(height))
stateStore := sm.NewStore(stateDB)
_, val := state.Validators.GetByIndex(0)
_, val2 := state.Validators.GetByIndex(1)
ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultTestTime,
@@ -288,7 +292,7 @@ func TestValidateDuplicateEvidenceShouldFail(t *testing.T) {
privVals[val2.Address.String()], chainID)
blockExec := sm.NewBlockExecutor(
stateDB, log.TestingLogger(),
stateStore, log.TestingLogger(),
nil,
nil,
sm.MockEvidencePool{})