mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-24 14:22:16 +00:00
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:
parent
59497c362b
commit
228bba799d
@ -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
62
state/export_test.go
Normal 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
280
state/helpers_test.go
Normal 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(¶ms)},
|
||||
}
|
||||
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
13
state/main_test.go
Normal 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())
|
||||
}
|
@ -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 }
|
||||
|
@ -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(¶ms)},
|
||||
}
|
||||
return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses
|
||||
}
|
||||
|
||||
type paramsChangeTestCase struct {
|
||||
height int64
|
||||
params types.ConsensusParams
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
41
types/errors.go
Normal 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)
|
||||
}
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user