state: add more tests for block validation (#3674)

* Expose priv validators for use in testing

* Generalize block header validation test past height 1

* Remove ineffectual assignment

* Remove redundant SaveState call

* Reorder comment for clarity

* Use the block executor ApplyBlock function instead of implementing a stripped-down version of it

* Remove commented-out code

* Remove unnecessary test

The required tests already appear to be implemented (implicitly) through
the TestValidateBlockHeader test.

* Allow for catching of specific error types during TestValidateBlockCommit

* Make return error testable

* Clean up and add TestValidateBlockCommit code

* Fix formatting

* Extract function to create a new mock test app

* Update comment for clarity

* Fix comment

* Add skeleton code for evidence-related test

* Allow for addressing priv val by address

* Generalize test beyond a single validator

* Generalize TestValidateBlockEvidence past first height

* Reorder code to clearly separate tests and utility code

* Use a common constant for stop height for testing in state/validation_test.go

* Refactor errors to resemble existing conventions

* Fix formatting

* Extract common helper functions

Having the tests littered with helper functions makes them less easily
readable imho, so I've pulled them out into a separate file. This also
makes it easier to see what helper functions are available during
testing, so we minimize the chance of duplication when writing new
tests.

* Remove unused parameter

* Remove unused parameters

* Add field keys

* Remove unused height constant

* Fix typo

* Fix incorrect return error

* Add field keys

* Use separate package for tests

This refactors all of the state package's tests into a state_test
package, so as to keep any usage of the state package's internal methods
explicit.

Any internal methods/constants used by tests are now explicitly exported
in state/export_test.go

* Refactor: extract helper function to make, validate, execute and commit a block

* Rename state function to makeState

* Remove redundant constant for number of validators

* Refactor mock evidence registration into TestMain

* Remove extraneous nVals variable

* Replace function-level TODOs with file-level TODO and explanation

* Remove extraneous comment

* Fix linting issues brought up by GolangCI (pulled in from latest merge from develop)
This commit is contained in:
Thane Thomson 2019-06-21 17:29:29 -04:00 committed by Ethan Buchman
parent 59497c362b
commit 228bba799d
12 changed files with 636 additions and 370 deletions

View File

@ -1,22 +1,20 @@
package state
package state_test
import (
"context"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/abci/example/kvstore"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/mock"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
@ -34,10 +32,10 @@ func TestApplyBlock(t *testing.T) {
require.Nil(t, err)
defer proxyApp.Stop()
state, stateDB := state(1, 1)
state, stateDB, _ := makeState(1, 1)
blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(),
mock.Mempool{}, MockEvidencePool{})
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(),
mock.Mempool{}, sm.MockEvidencePool{})
block := makeBlock(state, 1)
blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
@ -58,7 +56,7 @@ func TestBeginBlockValidators(t *testing.T) {
require.Nil(t, err)
defer proxyApp.Stop()
state, stateDB := state(2, 2)
state, stateDB, _ := makeState(2, 2)
prevHash := state.LastBlockID.Hash
prevParts := types.PartSetHeader{}
@ -84,7 +82,7 @@ func TestBeginBlockValidators(t *testing.T) {
// block for height 2
block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
_, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB)
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB)
require.Nil(t, err, tc.desc)
// -> app receives a list of validators with a bool indicating if they signed
@ -111,7 +109,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
require.Nil(t, err)
defer proxyApp.Stop()
state, stateDB := state(2, 12)
state, stateDB, _ := makeState(2, 12)
prevHash := state.LastBlockID.Hash
prevParts := types.PartSetHeader{}
@ -145,7 +143,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 = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB)
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB)
require.Nil(t, err, tc.desc)
// -> app must receive an index of the byzantine validator
@ -213,7 +211,7 @@ func TestValidateValidatorUpdates(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := validateValidatorUpdates(tc.abciUpdates, tc.validatorParams)
err := sm.ValidateValidatorUpdates(tc.abciUpdates, tc.validatorParams)
if tc.shouldErr {
assert.Error(t, err)
} else {
@ -307,9 +305,9 @@ func TestEndBlockValidatorUpdates(t *testing.T) {
require.Nil(t, err)
defer proxyApp.Stop()
state, stateDB := state(1, 1)
state, stateDB, _ := makeState(1, 1)
blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, MockEvidencePool{})
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{})
eventBus := types.NewEventBus()
err = eventBus.Start()
@ -365,8 +363,8 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
require.Nil(t, err)
defer proxyApp.Stop()
state, stateDB := state(1, 1)
blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, MockEvidencePool{})
state, stateDB, _ := makeState(1, 1)
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{})
block := makeBlock(state, 1)
blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
@ -381,90 +379,3 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
assert.NotEmpty(t, state.NextValidators.Validators)
}
//----------------------------------------------------------------------------
// make some bogus txs
func makeTxs(height int64) (txs []types.Tx) {
for i := 0; i < nTxsPerBlock; i++ {
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
}
return txs
}
func state(nVals, height int) (State, dbm.DB) {
vals := make([]types.GenesisValidator, nVals)
for i := 0; i < nVals; i++ {
secret := []byte(fmt.Sprintf("test%d", i))
pk := ed25519.GenPrivKeyFromSecret(secret)
vals[i] = types.GenesisValidator{
Address: pk.PubKey().Address(),
PubKey: pk.PubKey(),
Power: 1000,
Name: fmt.Sprintf("test%d", i),
}
}
s, _ := MakeGenesisState(&types.GenesisDoc{
ChainID: chainID,
Validators: vals,
AppHash: nil,
})
// save validators to db for 2 heights
stateDB := dbm.NewMemDB()
SaveState(stateDB, s)
for i := 1; i < height; i++ {
s.LastBlockHeight++
s.LastValidators = s.Validators.Copy()
SaveState(stateDB, s)
}
return s, stateDB
}
func makeBlock(state State, height int64) *types.Block {
block, _ := state.MakeBlock(height, makeTxs(state.LastBlockHeight), new(types.Commit), nil, state.Validators.GetProposer().Address)
return block
}
//----------------------------------------------------------------------------
type testApp struct {
abci.BaseApplication
CommitVotes []abci.VoteInfo
ByzantineValidators []abci.Evidence
ValidatorUpdates []abci.ValidatorUpdate
}
var _ abci.Application = (*testApp)(nil)
func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
return abci.ResponseInfo{}
}
func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
app.CommitVotes = req.LastCommitInfo.Votes
app.ByzantineValidators = req.ByzantineValidators
return abci.ResponseBeginBlock{}
}
func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
return abci.ResponseEndBlock{ValidatorUpdates: app.ValidatorUpdates}
}
func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
return abci.ResponseDeliverTx{Events: []abci.Event{}}
}
func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
return abci.ResponseCheckTx{}
}
func (app *testApp) Commit() abci.ResponseCommit {
return abci.ResponseCommit{}
}
func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
return
}

62
state/export_test.go Normal file
View File

@ -0,0 +1,62 @@
package state
import (
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/types"
)
//
// TODO: Remove dependence on all entities exported from this file.
//
// Every entity exported here is dependent on a private entity from the `state`
// package. Currently, these functions are only made available to tests in the
// `state_test` package, but we should not be relying on them for our testing.
// Instead, we should be exclusively relying on exported entities for our
// testing, and should be refactoring exported entities to make them more
// easily testable from outside of the package.
//
const ValSetCheckpointInterval = valSetCheckpointInterval
// UpdateState is an alias for updateState exported from execution.go,
// exclusively and explicitly for testing.
func UpdateState(
state State,
blockID types.BlockID,
header *types.Header,
abciResponses *ABCIResponses,
validatorUpdates []*types.Validator,
) (State, error) {
return updateState(state, blockID, header, abciResponses, validatorUpdates)
}
// ValidateValidatorUpdates is an alias for validateValidatorUpdates exported
// from execution.go, exclusively and explicitly for testing.
func ValidateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, params types.ValidatorParams) error {
return validateValidatorUpdates(abciUpdates, params)
}
// CalcValidatorsKey is an alias for the private calcValidatorsKey method in
// store.go, exported exclusively and explicitly for testing.
func CalcValidatorsKey(height int64) []byte {
return calcValidatorsKey(height)
}
// SaveABCIResponses is an alias for the private saveABCIResponses method in
// store.go, exported exclusively and explicitly for testing.
func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) {
saveABCIResponses(db, height, abciResponses)
}
// 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 types.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)
}

280
state/helpers_test.go Normal file
View File

@ -0,0 +1,280 @@
package state_test
import (
"bytes"
"fmt"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
type paramsChangeTestCase struct {
height int64
params types.ConsensusParams
}
// always returns true if asked if any evidence was already committed.
type mockEvPoolAlwaysCommitted struct{}
func (m mockEvPoolAlwaysCommitted) PendingEvidence(int64) []types.Evidence { return nil }
func (m mockEvPoolAlwaysCommitted) AddEvidence(types.Evidence) error { return nil }
func (m mockEvPoolAlwaysCommitted) Update(*types.Block, sm.State) {}
func (m mockEvPoolAlwaysCommitted) IsCommitted(types.Evidence) bool { return true }
func newTestApp() proxy.AppConns {
app := &testApp{}
cc := proxy.NewLocalClientCreator(app)
return proxy.NewAppConns(cc)
}
func makeAndCommitGoodBlock(
state sm.State,
height int64,
lastCommit *types.Commit,
proposerAddr []byte,
blockExec *sm.BlockExecutor,
privVals map[string]types.PrivValidator,
evidence []types.Evidence) (sm.State, types.BlockID, *types.Commit, error) {
// A good block passes
state, blockID, err := makeAndApplyGoodBlock(state, height, lastCommit, proposerAddr, blockExec, evidence)
if err != nil {
return state, types.BlockID{}, nil, err
}
// Simulate a lastCommit for this block from all validators for the next height
commit, err := makeValidCommit(height, blockID, state.Validators, privVals)
if err != nil {
return state, types.BlockID{}, nil, err
}
return state, blockID, commit, nil
}
func makeAndApplyGoodBlock(state sm.State, height int64, lastCommit *types.Commit, proposerAddr []byte,
blockExec *sm.BlockExecutor, evidence []types.Evidence) (sm.State, types.BlockID, error) {
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr)
if err := blockExec.ValidateBlock(state, block); err != nil {
return state, types.BlockID{}, err
}
blockID := types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}
state, err := blockExec.ApplyBlock(state, blockID, block)
if err != nil {
return state, types.BlockID{}, err
}
return state, blockID, nil
}
func makeVote(height int64, blockID types.BlockID, valSet *types.ValidatorSet, privVal types.PrivValidator) (*types.Vote, error) {
addr := privVal.GetPubKey().Address()
idx, _ := valSet.GetByAddress(addr)
vote := &types.Vote{
ValidatorAddress: addr,
ValidatorIndex: idx,
Height: height,
Round: 0,
Timestamp: tmtime.Now(),
Type: types.PrecommitType,
BlockID: blockID,
}
if err := privVal.SignVote(chainID, vote); err != nil {
return nil, err
}
return vote, nil
}
func makeValidCommit(height int64, blockID types.BlockID, vals *types.ValidatorSet, privVals map[string]types.PrivValidator) (*types.Commit, error) {
sigs := make([]*types.CommitSig, 0)
for i := 0; i < vals.Size(); i++ {
_, val := vals.GetByIndex(i)
vote, err := makeVote(height, blockID, vals, privVals[val.Address.String()])
if err != nil {
return nil, err
}
sigs = append(sigs, vote.CommitSig())
}
return types.NewCommit(blockID, sigs), nil
}
// make some bogus txs
func makeTxs(height int64) (txs []types.Tx) {
for i := 0; i < nTxsPerBlock; i++ {
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
}
return txs
}
func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) {
vals := make([]types.GenesisValidator, nVals)
privVals := make(map[string]types.PrivValidator, nVals)
for i := 0; i < nVals; i++ {
secret := []byte(fmt.Sprintf("test%d", i))
pk := ed25519.GenPrivKeyFromSecret(secret)
valAddr := pk.PubKey().Address()
vals[i] = types.GenesisValidator{
Address: valAddr,
PubKey: pk.PubKey(),
Power: 1000,
Name: fmt.Sprintf("test%d", i),
}
privVals[valAddr.String()] = types.NewMockPVWithParams(pk, false, false)
}
s, _ := sm.MakeGenesisState(&types.GenesisDoc{
ChainID: chainID,
Validators: vals,
AppHash: nil,
})
stateDB := dbm.NewMemDB()
sm.SaveState(stateDB, s)
for i := 1; i < height; i++ {
s.LastBlockHeight++
s.LastValidators = s.Validators.Copy()
sm.SaveState(stateDB, s)
}
return s, stateDB, privVals
}
func makeBlock(state sm.State, height int64) *types.Block {
block, _ := state.MakeBlock(height, makeTxs(state.LastBlockHeight), new(types.Commit), nil, state.Validators.GetProposer().Address)
return block
}
func genValSet(size int) *types.ValidatorSet {
vals := make([]*types.Validator, size)
for i := 0; i < size; i++ {
vals[i] = types.NewValidator(ed25519.GenPrivKey().PubKey(), 10)
}
return types.NewValidatorSet(vals)
}
func makeConsensusParams(
blockBytes, blockGas int64,
blockTimeIotaMs int64,
evidenceAge int64,
) types.ConsensusParams {
return types.ConsensusParams{
Block: types.BlockParams{
MaxBytes: blockBytes,
MaxGas: blockGas,
TimeIotaMs: blockTimeIotaMs,
},
Evidence: types.EvidenceParams{
MaxAge: evidenceAge,
},
}
}
func makeHeaderPartsResponsesValPubKeyChange(state sm.State, pubkey crypto.PubKey) (types.Header, types.BlockID, *sm.ABCIResponses) {
block := makeBlock(state, state.LastBlockHeight+1)
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
// If the pubkey is new, remove the old and add the new.
_, val := state.NextValidators.GetByIndex(0)
if !bytes.Equal(pubkey.Bytes(), val.PubKey.Bytes()) {
abciResponses.EndBlock = &abci.ResponseEndBlock{
ValidatorUpdates: []abci.ValidatorUpdate{
types.TM2PB.NewValidatorUpdate(val.PubKey, 0),
types.TM2PB.NewValidatorUpdate(pubkey, 10),
},
}
}
return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses
}
func makeHeaderPartsResponsesValPowerChange(state sm.State, power int64) (types.Header, types.BlockID, *sm.ABCIResponses) {
block := makeBlock(state, state.LastBlockHeight+1)
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
// If the pubkey is new, remove the old and add the new.
_, val := state.NextValidators.GetByIndex(0)
if val.VotingPower != power {
abciResponses.EndBlock = &abci.ResponseEndBlock{
ValidatorUpdates: []abci.ValidatorUpdate{
types.TM2PB.NewValidatorUpdate(val.PubKey, power),
},
}
}
return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses
}
func makeHeaderPartsResponsesParams(state sm.State, params types.ConsensusParams) (types.Header, types.BlockID, *sm.ABCIResponses) {
block := makeBlock(state, state.LastBlockHeight+1)
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(&params)},
}
return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses
}
func randomGenesisDoc() *types.GenesisDoc {
pubkey := ed25519.GenPrivKey().PubKey()
return &types.GenesisDoc{
GenesisTime: tmtime.Now(),
ChainID: "abc",
Validators: []types.GenesisValidator{
{
Address: pubkey.Address(),
PubKey: pubkey,
Power: 10,
Name: "myval",
},
},
ConsensusParams: types.DefaultConsensusParams(),
}
}
//----------------------------------------------------------------------------
type testApp struct {
abci.BaseApplication
CommitVotes []abci.VoteInfo
ByzantineValidators []abci.Evidence
ValidatorUpdates []abci.ValidatorUpdate
}
var _ abci.Application = (*testApp)(nil)
func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
return abci.ResponseInfo{}
}
func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
app.CommitVotes = req.LastCommitInfo.Votes
app.ByzantineValidators = req.ByzantineValidators
return abci.ResponseBeginBlock{}
}
func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
return abci.ResponseEndBlock{ValidatorUpdates: app.ValidatorUpdates}
}
func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
return abci.ResponseDeliverTx{Events: []abci.Event{}}
}
func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
return abci.ResponseCheckTx{}
}
func (app *testApp) Commit() abci.ResponseCommit {
return abci.ResponseCommit{}
}
func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
return
}

13
state/main_test.go Normal file
View File

@ -0,0 +1,13 @@
package state_test
import (
"os"
"testing"
"github.com/tendermint/tendermint/types"
)
func TestMain(m *testing.M) {
types.RegisterMockEvidencesGlobal()
os.Exit(m.Run())
}

View File

@ -43,7 +43,7 @@ type EvidencePool interface {
IsCommitted(types.Evidence) bool
}
// MockEvidencePool is an empty implementation of a Mempool, useful for testing.
// MockEvidencePool is an empty implementation of EvidencePool, useful for testing.
type MockEvidencePool struct{}
func (m MockEvidencePool) PendingEvidence(int64) []types.Evidence { return nil }

View File

@ -1,4 +1,4 @@
package state
package state_test
import (
"bytes"
@ -10,23 +10,22 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
sm "github.com/tendermint/tendermint/state"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/types"
)
// setupTestCase does setup common to all test cases.
func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, State) {
func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, sm.State) {
config := cfg.ResetTestRoot("state_")
dbType := dbm.DBBackendType(config.DBBackend)
stateDB := dbm.NewDB("state", dbType, config.DBDir())
state, err := LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
assert.NoError(t, err, "expected no error on LoadStateFromDBOrGenesisFile")
tearDown := func(t *testing.T) { os.RemoveAll(config.RootDir) }
@ -59,7 +58,7 @@ func TestMakeGenesisStateNilValidators(t *testing.T) {
Validators: nil,
}
require.Nil(t, doc.ValidateAndComplete())
state, err := MakeGenesisState(&doc)
state, err := sm.MakeGenesisState(&doc)
require.Nil(t, err)
require.Equal(t, 0, len(state.Validators.Validators))
require.Equal(t, 0, len(state.NextValidators.Validators))
@ -73,9 +72,9 @@ func TestStateSaveLoad(t *testing.T) {
assert := assert.New(t)
state.LastBlockHeight++
SaveState(stateDB, state)
sm.SaveState(stateDB, state)
loadedState := LoadState(stateDB)
loadedState := sm.LoadState(stateDB)
assert.True(state.Equals(loadedState),
fmt.Sprintf("expected state and its copy to be identical.\ngot: %v\nexpected: %v\n",
loadedState, state))
@ -92,15 +91,15 @@ func TestABCIResponsesSaveLoad1(t *testing.T) {
// Build mock responses.
block := makeBlock(state, 2)
abciResponses := NewABCIResponses(block)
abciResponses := sm.NewABCIResponses(block)
abciResponses.DeliverTx[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Events: nil}
abciResponses.DeliverTx[1] = &abci.ResponseDeliverTx{Data: []byte("bar"), Log: "ok", Events: nil}
abciResponses.EndBlock = &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{
types.TM2PB.NewValidatorUpdate(ed25519.GenPrivKey().PubKey(), 10),
}}
saveABCIResponses(stateDB, block.Height, abciResponses)
loadedABCIResponses, err := LoadABCIResponses(stateDB, block.Height)
sm.SaveABCIResponses(stateDB, block.Height, abciResponses)
loadedABCIResponses, err := sm.LoadABCIResponses(stateDB, block.Height)
assert.Nil(err)
assert.Equal(abciResponses, loadedABCIResponses,
fmt.Sprintf("ABCIResponses don't match:\ngot: %v\nexpected: %v\n",
@ -155,24 +154,24 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
// Query all before, this should return error.
for i := range cases {
h := int64(i + 1)
res, err := LoadABCIResponses(stateDB, h)
res, err := sm.LoadABCIResponses(stateDB, h)
assert.Error(err, "%d: %#v", i, res)
}
// Add all cases.
for i, tc := range cases {
h := int64(i + 1) // last block height, one below what we save
responses := &ABCIResponses{
responses := &sm.ABCIResponses{
DeliverTx: tc.added,
EndBlock: &abci.ResponseEndBlock{},
}
saveABCIResponses(stateDB, h, responses)
sm.SaveABCIResponses(stateDB, h, responses)
}
// Query all before, should return expected value.
for i, tc := range cases {
h := int64(i + 1)
res, err := LoadABCIResponses(stateDB, h)
res, err := sm.LoadABCIResponses(stateDB, h)
assert.NoError(err, "%d", i)
assert.Equal(tc.expected.Hash(), res.ResultsHash(), "%d", i)
}
@ -186,26 +185,26 @@ func TestValidatorSimpleSaveLoad(t *testing.T) {
assert := assert.New(t)
// Can't load anything for height 0.
v, err := LoadValidators(stateDB, 0)
assert.IsType(ErrNoValSetForHeight{}, err, "expected err at height 0")
v, err := sm.LoadValidators(stateDB, 0)
assert.IsType(sm.ErrNoValSetForHeight{}, err, "expected err at height 0")
// Should be able to load for height 1.
v, err = LoadValidators(stateDB, 1)
v, err = sm.LoadValidators(stateDB, 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 = LoadValidators(stateDB, 2)
v, err = sm.LoadValidators(stateDB, 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
saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
vp0, err := LoadValidators(stateDB, nextHeight+0)
sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
vp0, err := sm.LoadValidators(stateDB, nextHeight+0)
assert.Nil(err, "expected no err")
vp1, err := LoadValidators(stateDB, nextHeight+1)
vp1, err := sm.LoadValidators(stateDB, 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")
@ -234,13 +233,13 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
changeIndex++
power++
}
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, i, power)
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, power)
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
require.NoError(t, err)
state, err = updateState(state, blockID, &header, responses, validatorUpdates)
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
require.NoError(t, err)
nextHeight := state.LastBlockHeight + 1
saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
}
// On each height change, increment the power by one.
@ -258,7 +257,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
}
for i, power := range testCases {
v, err := LoadValidators(stateDB, int64(i+1+1)) // +1 because vset changes delayed by 1 block.
v, err := sm.LoadValidators(stateDB, 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)
@ -405,12 +404,12 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) {
block := makeBlock(state, state.LastBlockHeight+1)
blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
abciResponses := &ABCIResponses{
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
require.NoError(t, err)
updatedState, err := updateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState, err := sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
assert.NoError(t, err)
curTotal := val1VotingPower
// one increment step and one validator: 0 + power - total_power == 0
@ -422,7 +421,7 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) {
updateAddVal := abci.ValidatorUpdate{PubKey: types.TM2PB.PubKey(val2PubKey), Power: val2VotingPower}
validatorUpdates, err = types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{updateAddVal})
assert.NoError(t, err)
updatedState2, err := updateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState2, err := sm.UpdateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates)
assert.NoError(t, err)
require.Equal(t, len(updatedState2.NextValidators.Validators), 2)
@ -461,7 +460,7 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) {
// this will cause the diff of priorities (77)
// to be larger than threshold == 2*totalVotingPower (22):
updatedState3, err := updateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState3, err := sm.UpdateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates)
assert.NoError(t, err)
require.Equal(t, len(updatedState3.NextValidators.Validators), 2)
@ -516,13 +515,13 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
block := makeBlock(state, state.LastBlockHeight+1)
blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
// no updates:
abciResponses := &ABCIResponses{
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
require.NoError(t, err)
updatedState, err := updateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState, err := sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
assert.NoError(t, err)
// 0 + 10 (initial prio) - 10 (avg) - 10 (mostest - total) = -10
@ -537,7 +536,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
validatorUpdates, err = types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{updateAddVal})
assert.NoError(t, err)
updatedState2, err := updateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState2, err := sm.UpdateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates)
assert.NoError(t, err)
require.Equal(t, len(updatedState2.NextValidators.Validators), 2)
@ -574,7 +573,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
require.NoError(t, err)
updatedState3, err := updateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState3, err := sm.UpdateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates)
assert.NoError(t, err)
assert.Equal(t, updatedState3.Validators.Proposer.Address, updatedState3.NextValidators.Proposer.Address)
@ -598,13 +597,13 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
// no changes in voting power and both validators have same voting power
// -> proposers should alternate:
oldState := updatedState3
abciResponses = &ABCIResponses{
abciResponses = &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
require.NoError(t, err)
oldState, err = updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
oldState, err = sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
assert.NoError(t, err)
expectedVal1Prio2 = 1
expectedVal2Prio2 = -1
@ -613,13 +612,13 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
for i := 0; i < 1000; i++ {
// no validator updates:
abciResponses := &ABCIResponses{
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
require.NoError(t, err)
updatedState, err := updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
assert.NoError(t, err)
// alternate (and cyclic priorities):
assert.NotEqual(t, updatedState.Validators.Proposer.Address, updatedState.NextValidators.Proposer.Address, "iter: %v", i)
@ -660,7 +659,7 @@ func TestLargeGenesisValidator(t *testing.T) {
oldState := state
for i := 0; i < 10; i++ {
// no updates:
abciResponses := &ABCIResponses{
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
@ -669,7 +668,7 @@ func TestLargeGenesisValidator(t *testing.T) {
block := makeBlock(oldState, oldState.LastBlockHeight+1)
blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
updatedState, err := updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
require.NoError(t, err)
// no changes in voting power (ProposerPrio += VotingPower == Voting in 1st round; than shiftByAvg == 0,
// than -Total == -Voting)
@ -689,18 +688,18 @@ func TestLargeGenesisValidator(t *testing.T) {
firstAddedVal := abci.ValidatorUpdate{PubKey: types.TM2PB.PubKey(firstAddedValPubKey), Power: firstAddedValVotingPower}
validatorUpdates, err := types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{firstAddedVal})
assert.NoError(t, err)
abciResponses := &ABCIResponses{
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{firstAddedVal}},
}
block := makeBlock(oldState, oldState.LastBlockHeight+1)
blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
updatedState, err := updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
require.NoError(t, err)
lastState := updatedState
for i := 0; i < 200; i++ {
// no updates:
abciResponses := &ABCIResponses{
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
@ -709,7 +708,7 @@ func TestLargeGenesisValidator(t *testing.T) {
block := makeBlock(lastState, lastState.LastBlockHeight+1)
blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
updatedStateInner, err := updateState(lastState, blockID, &block.Header, abciResponses, validatorUpdates)
updatedStateInner, err := sm.UpdateState(lastState, blockID, &block.Header, abciResponses, validatorUpdates)
require.NoError(t, err)
lastState = updatedStateInner
}
@ -734,26 +733,26 @@ func TestLargeGenesisValidator(t *testing.T) {
validatorUpdates, err := types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{addedVal})
assert.NoError(t, err)
abciResponses := &ABCIResponses{
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{addedVal}},
}
block := makeBlock(oldState, oldState.LastBlockHeight+1)
blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
state, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
state, err = sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
require.NoError(t, err)
}
require.Equal(t, 10+2, len(state.NextValidators.Validators))
// remove genesis validator:
removeGenesisVal := abci.ValidatorUpdate{PubKey: types.TM2PB.PubKey(genesisPubKey), Power: 0}
abciResponses = &ABCIResponses{
abciResponses = &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{removeGenesisVal}},
}
block = makeBlock(oldState, oldState.LastBlockHeight+1)
blockID = types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
require.NoError(t, err)
updatedState, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState, err = sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
require.NoError(t, err)
// only the first added val (not the genesis val) should be left
assert.Equal(t, 11, len(updatedState.NextValidators.Validators))
@ -764,14 +763,14 @@ func TestLargeGenesisValidator(t *testing.T) {
count := 0
isProposerUnchanged := true
for isProposerUnchanged {
abciResponses := &ABCIResponses{
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
require.NoError(t, err)
block = makeBlock(curState, curState.LastBlockHeight+1)
blockID = types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
curState, err = updateState(curState, blockID, &block.Header, abciResponses, validatorUpdates)
curState, err = sm.UpdateState(curState, blockID, &block.Header, abciResponses, validatorUpdates)
require.NoError(t, err)
if !bytes.Equal(curState.Validators.Proposer.Address, curState.NextValidators.Proposer.Address) {
isProposerUnchanged = false
@ -787,7 +786,7 @@ func TestLargeGenesisValidator(t *testing.T) {
proposers := make([]*types.Validator, numVals)
for i := 0; i < 100; i++ {
// no updates:
abciResponses := &ABCIResponses{
abciResponses := &sm.ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
@ -796,7 +795,7 @@ func TestLargeGenesisValidator(t *testing.T) {
block := makeBlock(updatedState, updatedState.LastBlockHeight+1)
blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
updatedState, err = updateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates)
updatedState, err = sm.UpdateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates)
require.NoError(t, err)
if i > numVals { // expect proposers to cycle through after the first iteration (of numVals blocks):
if proposers[i%numVals] == nil {
@ -814,15 +813,15 @@ func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) {
defer tearDown(t)
state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
SaveState(stateDB, state)
sm.SaveState(stateDB, state)
nextHeight := state.LastBlockHeight + 1
v0, err := LoadValidators(stateDB, nextHeight)
v0, err := sm.LoadValidators(stateDB, nextHeight)
assert.Nil(t, err)
acc0 := v0.Validators[0].ProposerPriority
v1, err := LoadValidators(stateDB, nextHeight+1)
v1, err := sm.LoadValidators(stateDB, nextHeight+1)
assert.Nil(t, err)
acc1 := v1.Validators[0].ProposerPriority
@ -838,28 +837,27 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
require.Equal(t, int64(0), state.LastBlockHeight)
state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
SaveState(stateDB, state)
sm.SaveState(stateDB, state)
_, valOld := state.Validators.GetByIndex(0)
var pubkeyOld = valOld.PubKey
pubkey := ed25519.GenPrivKey().PubKey()
const height = 1
// Swap the first validator with a new one (validator set size stays the same).
header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(state, height, pubkey)
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 = updateState(state, blockID, &header, responses, validatorUpdates)
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
require.Nil(t, err)
nextHeight := state.LastBlockHeight + 1
saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
// Load nextheight, it should be the oldpubkey.
v0, err := LoadValidators(stateDB, nextHeight)
v0, err := sm.LoadValidators(stateDB, nextHeight)
assert.Nil(t, err)
assert.Equal(t, valSetSize, v0.Size())
index, val := v0.GetByAddress(pubkeyOld.Address())
@ -869,7 +867,7 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
}
// Load nextheight+1, it should be the new pubkey.
v1, err := LoadValidators(stateDB, nextHeight+1)
v1, err := sm.LoadValidators(stateDB, nextHeight+1)
assert.Nil(t, err)
assert.Equal(t, valSetSize, v1.Size())
index, val = v1.GetByAddress(pubkey.Address())
@ -879,14 +877,6 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
}
}
func genValSet(size int) *types.ValidatorSet {
vals := make([]*types.Validator, size)
for i := 0; i < size; i++ {
vals[i] = types.NewValidator(ed25519.GenPrivKey().PubKey(), 10)
}
return types.NewValidatorSet(vals)
}
func TestStateMakeBlock(t *testing.T) {
tearDown, _, state := setupTestCase(t)
defer tearDown(t)
@ -932,14 +922,14 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
changeIndex++
cp = params[changeIndex]
}
header, blockID, responses := makeHeaderPartsResponsesParams(state, i, cp)
header, blockID, responses := makeHeaderPartsResponsesParams(state, cp)
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
require.NoError(t, err)
state, err = updateState(state, blockID, &header, responses, validatorUpdates)
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
require.Nil(t, err)
nextHeight := state.LastBlockHeight + 1
saveConsensusParamsInfo(stateDB, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
sm.SaveConsensusParamsInfo(stateDB, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
}
// Make all the test cases by using the same params until after the change.
@ -957,32 +947,15 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
}
for _, testCase := range testCases {
p, err := LoadConsensusParams(stateDB, testCase.height)
p, err := sm.LoadConsensusParams(stateDB, testCase.height)
assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", testCase.height))
assert.Equal(t, testCase.params, p, fmt.Sprintf(`unexpected consensus params at
height %d`, testCase.height))
}
}
func makeParams(
blockBytes, blockGas int64,
blockTimeIotaMs int64,
evidenceAge int64,
) types.ConsensusParams {
return types.ConsensusParams{
Block: types.BlockParams{
MaxBytes: blockBytes,
MaxGas: blockGas,
TimeIotaMs: blockTimeIotaMs,
},
Evidence: types.EvidenceParams{
MaxAge: evidenceAge,
},
}
}
func TestApplyUpdates(t *testing.T) {
initParams := makeParams(1, 2, 3, 4)
initParams := makeConsensusParams(1, 2, 3, 4)
cases := [...]struct {
init types.ConsensusParams
@ -998,14 +971,14 @@ func TestApplyUpdates(t *testing.T) {
MaxGas: 55,
},
},
makeParams(44, 55, 3, 4)},
makeConsensusParams(44, 55, 3, 4)},
3: {initParams,
abci.ConsensusParams{
Evidence: &abci.EvidenceParams{
MaxAge: 66,
},
},
makeParams(1, 2, 3, 66)},
makeConsensusParams(1, 2, 3, 66)},
}
for i, tc := range cases {
@ -1013,61 +986,3 @@ func TestApplyUpdates(t *testing.T) {
assert.Equal(t, tc.expected, res, "case %d", i)
}
}
func makeHeaderPartsResponsesValPubKeyChange(state State, height int64,
pubkey crypto.PubKey) (types.Header, types.BlockID, *ABCIResponses) {
block := makeBlock(state, state.LastBlockHeight+1)
abciResponses := &ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
// If the pubkey is new, remove the old and add the new.
_, val := state.NextValidators.GetByIndex(0)
if !bytes.Equal(pubkey.Bytes(), val.PubKey.Bytes()) {
abciResponses.EndBlock = &abci.ResponseEndBlock{
ValidatorUpdates: []abci.ValidatorUpdate{
types.TM2PB.NewValidatorUpdate(val.PubKey, 0),
types.TM2PB.NewValidatorUpdate(pubkey, 10),
},
}
}
return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses
}
func makeHeaderPartsResponsesValPowerChange(state State, height int64,
power int64) (types.Header, types.BlockID, *ABCIResponses) {
block := makeBlock(state, state.LastBlockHeight+1)
abciResponses := &ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
// If the pubkey is new, remove the old and add the new.
_, val := state.NextValidators.GetByIndex(0)
if val.VotingPower != power {
abciResponses.EndBlock = &abci.ResponseEndBlock{
ValidatorUpdates: []abci.ValidatorUpdate{
types.TM2PB.NewValidatorUpdate(val.PubKey, power),
},
}
}
return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses
}
func makeHeaderPartsResponsesParams(state State, height int64,
params types.ConsensusParams) (types.Header, types.BlockID, *ABCIResponses) {
block := makeBlock(state, state.LastBlockHeight+1)
abciResponses := &ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(&params)},
}
return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses
}
type paramsChangeTestCase struct {
height int64
params types.ConsensusParams
}

View File

@ -1,4 +1,4 @@
package state
package state_test
import (
"fmt"
@ -10,6 +10,7 @@ import (
cfg "github.com/tendermint/tendermint/config"
dbm "github.com/tendermint/tendermint/libs/db"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
@ -19,9 +20,9 @@ func TestStoreLoadValidators(t *testing.T) {
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)
sm.SaveValidatorsInfo(stateDB, 1, 1, vals)
sm.SaveValidatorsInfo(stateDB, 2, 1, vals)
loadedVals, err := sm.LoadValidators(stateDB, 2)
require.NoError(t, err)
assert.NotZero(t, loadedVals.Size())
@ -30,13 +31,13 @@ func TestStoreLoadValidators(t *testing.T) {
// 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,
valInfo := &sm.ValidatorsInfo{
LastHeightChanged: sm.ValSetCheckpointInterval,
}
stateDB.Set(calcValidatorsKey(valSetCheckpointInterval), valInfo.Bytes())
stateDB.Set(sm.CalcValidatorsKey(sm.ValSetCheckpointInterval), valInfo.Bytes())
assert.NotPanics(t, func() {
saveValidatorsInfo(stateDB, valSetCheckpointInterval+1, 1, vals)
loadedVals, err := LoadValidators(stateDB, valSetCheckpointInterval+1)
sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval+1, 1, vals)
loadedVals, err := sm.LoadValidators(stateDB, sm.ValSetCheckpointInterval+1)
if err != nil {
t.Fatal(err)
}
@ -46,9 +47,9 @@ func TestStoreLoadValidators(t *testing.T) {
})
// ENDREMOVE
saveValidatorsInfo(stateDB, valSetCheckpointInterval, 1, vals)
sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval, 1, vals)
loadedVals, err = LoadValidators(stateDB, valSetCheckpointInterval)
loadedVals, err = sm.LoadValidators(stateDB, sm.ValSetCheckpointInterval)
require.NoError(t, err)
assert.NotZero(t, loadedVals.Size())
}
@ -60,20 +61,20 @@ func BenchmarkLoadValidators(b *testing.B) {
defer os.RemoveAll(config.RootDir)
dbType := dbm.DBBackendType(config.DBBackend)
stateDB := dbm.NewDB("state", dbType, config.DBDir())
state, err := LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
if err != nil {
b.Fatal(err)
}
state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
SaveState(stateDB, state)
sm.SaveState(stateDB, state)
for i := 10; i < 10000000000; i *= 10 { // 10, 100, 1000, ...
saveValidatorsInfo(stateDB, int64(i), state.LastHeightValidatorsChanged, state.NextValidators)
sm.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))
_, err := sm.LoadValidators(stateDB, int64(i))
if err != nil {
b.Fatal(err)
}

View File

@ -1,4 +1,4 @@
package state
package state_test
import (
"os"
@ -7,11 +7,10 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
func TestTxFilter(t *testing.T) {
@ -34,10 +33,10 @@ func TestTxFilter(t *testing.T) {
for i, tc := range testCases {
stateDB := dbm.NewDB("state", "memdb", os.TempDir())
state, err := LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
require.NoError(t, err)
f := TxPreCheck(state)
f := sm.TxPreCheck(state)
if tc.isErr {
assert.NotNil(t, f(tc.tx), "#%v", i)
} else {
@ -45,13 +44,3 @@ func TestTxFilter(t *testing.T) {
}
}
}
func randomGenesisDoc() *types.GenesisDoc {
pubkey := ed25519.GenPrivKey().PubKey()
return &types.GenesisDoc{
GenesisTime: tmtime.Now(),
ChainID: "abc",
Validators: []types.GenesisValidator{{Address: pubkey.Address(), PubKey: pubkey, Power: 10, Name: "myval"}},
ConsensusParams: types.DefaultConsensusParams(),
}
}

View File

@ -94,10 +94,7 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block
}
} else {
if len(block.LastCommit.Precommits) != state.LastValidators.Size() {
return fmt.Errorf("Invalid block commit size. Expected %v, got %v",
state.LastValidators.Size(),
len(block.LastCommit.Precommits),
)
return types.NewErrInvalidCommitPrecommits(state.LastValidators.Size(), len(block.LastCommit.Precommits))
}
err := state.LastValidators.VerifyCommit(
state.ChainID, state.LastBlockID, block.Height-1, block.LastCommit)

View File

@ -1,31 +1,30 @@
package state
package state_test
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/mock"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/libs/log"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
// TODO(#2589):
// - generalize this past the first height
// - add txs and build up full State properly
// - test block.Time (see #2587 - there are no conditions on time for the first height)
const validationTestsStopHeight int64 = 10
func TestValidateBlockHeader(t *testing.T) {
var height int64 = 1 // TODO(#2589): generalize
state, stateDB := state(1, int(height))
proxyApp := newTestApp()
require.NoError(t, proxyApp.Start())
defer proxyApp.Stop()
blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), nil, nil, nil)
// A good block passes.
block := makeBlock(state, height)
err := blockExec.ValidateBlock(state, block)
require.NoError(t, err)
state, stateDB, privVals := makeState(3, 1)
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{})
lastCommit := types.NewCommit(types.BlockID{}, nil)
// some bad values
wrongHash := tmhash.Sum([]byte("this hash is wrong"))
@ -43,7 +42,7 @@ func TestValidateBlockHeader(t *testing.T) {
{"Version wrong2", func(block *types.Block) { block.Version = wrongVersion2 }},
{"ChainID wrong", func(block *types.Block) { block.ChainID = "not-the-real-one" }},
{"Height wrong", func(block *types.Block) { block.Height += 10 }},
{"Time wrong", func(block *types.Block) { block.Time = block.Time.Add(-time.Second * 3600 * 24) }},
{"Time wrong", func(block *types.Block) { block.Time = block.Time.Add(-time.Second * 1) }},
{"NumTxs wrong", func(block *types.Block) { block.NumTxs += 10 }},
{"TotalTxs wrong", func(block *types.Block) { block.TotalTxs += 10 }},
@ -62,78 +61,145 @@ func TestValidateBlockHeader(t *testing.T) {
{"Proposer invalid", func(block *types.Block) { block.ProposerAddress = []byte("wrong size") }},
}
for _, tc := range testCases {
block := makeBlock(state, height)
tc.malleateBlock(block)
err := blockExec.ValidateBlock(state, block)
require.Error(t, err, tc.name)
// Build up state for multiple heights
for height := int64(1); height < validationTestsStopHeight; height++ {
proposerAddr := state.Validators.GetProposer().Address
/*
Invalid blocks don't pass
*/
for _, tc := range testCases {
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, proposerAddr)
tc.malleateBlock(block)
err := blockExec.ValidateBlock(state, block)
require.Error(t, err, tc.name)
}
/*
A good block passes
*/
var err error
state, _, lastCommit, err = makeAndCommitGoodBlock(state, height, lastCommit, proposerAddr, blockExec, privVals, nil)
require.NoError(t, err, "height %d", height)
}
}
/*
TODO(#2589):
- test Block.Data.Hash() == Block.DataHash
- test len(Block.Data.Txs) == Block.NumTxs
*/
func TestValidateBlockData(t *testing.T) {
}
/*
TODO(#2589):
- test len(block.LastCommit.Precommits) == state.LastValidators.Size()
- test state.LastValidators.VerifyCommit
*/
func TestValidateBlockCommit(t *testing.T) {
}
proxyApp := newTestApp()
require.NoError(t, proxyApp.Start())
defer proxyApp.Stop()
/*
TODO(#2589):
- test good/bad evidence in block
*/
func TestValidateBlockEvidence(t *testing.T) {
var height int64 = 1 // TODO(#2589): generalize
state, stateDB := state(1, int(height))
state, stateDB, privVals := makeState(1, 1)
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{})
lastCommit := types.NewCommit(types.BlockID{}, nil)
wrongPrecommitsCommit := types.NewCommit(types.BlockID{}, nil)
badPrivVal := types.NewMockPV()
blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), nil, nil, nil)
for height := int64(1); height < validationTestsStopHeight; height++ {
proposerAddr := state.Validators.GetProposer().Address
if height > 1 {
/*
#2589: ensure state.LastValidators.VerifyCommit fails here
*/
// should be height-1 instead of height
wrongHeightVote, err := makeVote(height, state.LastBlockID, state.Validators, privVals[proposerAddr.String()])
require.NoError(t, err, "height %d", height)
wrongHeightCommit := types.NewCommit(state.LastBlockID, []*types.CommitSig{wrongHeightVote.CommitSig()})
block, _ := state.MakeBlock(height, makeTxs(height), wrongHeightCommit, nil, proposerAddr)
err = blockExec.ValidateBlock(state, block)
_, isErrInvalidCommitHeight := err.(types.ErrInvalidCommitHeight)
require.True(t, isErrInvalidCommitHeight, "expected ErrInvalidCommitHeight at height %d but got: %v", height, err)
// make some evidence
addr, _ := state.Validators.GetByIndex(0)
goodEvidence := types.NewMockGoodEvidence(height, 0, addr)
/*
#2589: test len(block.LastCommit.Precommits) == state.LastValidators.Size()
*/
block, _ = state.MakeBlock(height, makeTxs(height), wrongPrecommitsCommit, nil, proposerAddr)
err = blockExec.ValidateBlock(state, block)
_, isErrInvalidCommitPrecommits := err.(types.ErrInvalidCommitPrecommits)
require.True(t, isErrInvalidCommitPrecommits, "expected ErrInvalidCommitPrecommits at height %d but got: %v", height, err)
}
// A block with a couple pieces of evidence passes.
block := makeBlock(state, height)
block.Evidence.Evidence = []types.Evidence{goodEvidence, goodEvidence}
block.EvidenceHash = block.Evidence.Hash()
err := blockExec.ValidateBlock(state, block)
require.NoError(t, err)
/*
A good block passes
*/
var err error
var blockID types.BlockID
state, blockID, lastCommit, err = makeAndCommitGoodBlock(state, height, lastCommit, proposerAddr, blockExec, privVals, nil)
require.NoError(t, err, "height %d", height)
// A block with too much evidence fails.
maxBlockSize := state.ConsensusParams.Block.MaxBytes
maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBlockSize)
require.True(t, maxNumEvidence > 2)
for i := int64(0); i < maxNumEvidence; i++ {
block.Evidence.Evidence = append(block.Evidence.Evidence, goodEvidence)
/*
wrongPrecommitsCommit is fine except for the extra bad precommit
*/
goodVote, err := makeVote(height, blockID, state.Validators, privVals[proposerAddr.String()])
require.NoError(t, err, "height %d", height)
badVote := &types.Vote{
ValidatorAddress: badPrivVal.GetPubKey().Address(),
ValidatorIndex: 0,
Height: height,
Round: 0,
Timestamp: tmtime.Now(),
Type: types.PrecommitType,
BlockID: blockID,
}
err = badPrivVal.SignVote(chainID, goodVote)
require.NoError(t, err, "height %d", height)
wrongPrecommitsCommit = types.NewCommit(blockID, []*types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()})
}
block.EvidenceHash = block.Evidence.Hash()
err = blockExec.ValidateBlock(state, block)
require.Error(t, err)
_, ok := err.(*types.ErrEvidenceOverflow)
require.True(t, ok)
}
// always returns true if asked if any evidence was already committed.
type mockEvPoolAlwaysCommitted struct{}
func TestValidateBlockEvidence(t *testing.T) {
proxyApp := newTestApp()
require.NoError(t, proxyApp.Start())
defer proxyApp.Stop()
func (m mockEvPoolAlwaysCommitted) PendingEvidence(int64) []types.Evidence { return nil }
func (m mockEvPoolAlwaysCommitted) AddEvidence(types.Evidence) error { return nil }
func (m mockEvPoolAlwaysCommitted) Update(*types.Block, State) {}
func (m mockEvPoolAlwaysCommitted) IsCommitted(types.Evidence) bool { return true }
state, stateDB, privVals := makeState(3, 1)
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{})
lastCommit := types.NewCommit(types.BlockID{}, nil)
for height := int64(1); height < validationTestsStopHeight; height++ {
proposerAddr := state.Validators.GetProposer().Address
proposerIdx, _ := state.Validators.GetByAddress(proposerAddr)
goodEvidence := types.NewMockGoodEvidence(height, proposerIdx, proposerAddr)
if height > 1 {
/*
A block with too much evidence fails
*/
maxBlockSize := state.ConsensusParams.Block.MaxBytes
maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBlockSize)
require.True(t, maxNumEvidence > 2)
evidence := make([]types.Evidence, 0)
// one more than the maximum allowed evidence
for i := int64(0); i <= maxNumEvidence; i++ {
evidence = append(evidence, goodEvidence)
}
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr)
err := blockExec.ValidateBlock(state, block)
_, ok := err.(*types.ErrEvidenceOverflow)
require.True(t, ok, "expected error to be of type ErrEvidenceOverflow at height %d", height)
}
/*
A good block with several pieces of good evidence passes
*/
maxBlockSize := state.ConsensusParams.Block.MaxBytes
maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBlockSize)
require.True(t, maxNumEvidence > 2)
evidence := make([]types.Evidence, 0)
// precisely the amount of allowed evidence
for i := int64(0); i < maxNumEvidence; i++ {
evidence = append(evidence, goodEvidence)
}
var err error
state, _, lastCommit, err = makeAndCommitGoodBlock(state, height, lastCommit, proposerAddr, blockExec, privVals, evidence)
require.NoError(t, err, "height %d", height)
}
}
func TestValidateFailBlockOnCommittedEvidence(t *testing.T) {
var height int64 = 1
state, stateDB := state(1, int(height))
state, stateDB, _ := makeState(1, int(height))
blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), nil, nil, mockEvPoolAlwaysCommitted{})
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), nil, nil, mockEvPoolAlwaysCommitted{})
// A block with a couple pieces of evidence passes.
block := makeBlock(state, height)
addr, _ := state.Validators.GetByIndex(0)
@ -145,12 +211,3 @@ func TestValidateFailBlockOnCommittedEvidence(t *testing.T) {
require.Error(t, err)
require.IsType(t, err, &types.ErrEvidenceInvalid{})
}
/*
TODO(#2589):
- test unmarshalling BlockParts that are too big into a Block that
(note this logic happens in the consensus, not in the validation here).
- test making blocks from the types.MaxXXX functions works/fails as expected
*/
func TestValidateBlockSize(t *testing.T) {
}

41
types/errors.go Normal file
View File

@ -0,0 +1,41 @@
package types
import "fmt"
type (
// ErrInvalidCommitHeight is returned when we encounter a commit with an
// unexpected height.
ErrInvalidCommitHeight struct {
Expected int64
Actual int64
}
// ErrInvalidCommitPrecommits is returned when we encounter a commit where
// the number of precommits doesn't match the number of validators.
ErrInvalidCommitPrecommits struct {
Expected int
Actual int
}
)
func NewErrInvalidCommitHeight(expected, actual int64) ErrInvalidCommitHeight {
return ErrInvalidCommitHeight{
Expected: expected,
Actual: actual,
}
}
func (e ErrInvalidCommitHeight) Error() string {
return fmt.Sprintf("Invalid commit -- wrong height: %v vs %v", e.Expected, e.Actual)
}
func NewErrInvalidCommitPrecommits(expected, actual int) ErrInvalidCommitPrecommits {
return ErrInvalidCommitPrecommits{
Expected: expected,
Actual: actual,
}
}
func (e ErrInvalidCommitPrecommits) Error() string {
return fmt.Sprintf("Invalid commit -- wrong set size: %v vs %v", e.Expected, e.Actual)
}

View File

@ -594,10 +594,10 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height i
return err
}
if vals.Size() != len(commit.Precommits) {
return fmt.Errorf("Invalid commit -- wrong set size: %v vs %v", vals.Size(), len(commit.Precommits))
return NewErrInvalidCommitPrecommits(vals.Size(), len(commit.Precommits))
}
if height != commit.Height() {
return fmt.Errorf("Invalid commit -- wrong height: %v vs %v", height, commit.Height())
return NewErrInvalidCommitHeight(height, commit.Height())
}
if !blockID.Equals(commit.BlockID) {
return fmt.Errorf("Invalid commit -- wrong block id: want %v got %v",