- package state
-
- 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"
- cmn "github.com/tendermint/tendermint/libs/common"
- dbm "github.com/tendermint/tendermint/libs/db"
- "github.com/tendermint/tendermint/libs/log"
- tmtime "github.com/tendermint/tendermint/types/time"
-
- "github.com/tendermint/tendermint/proxy"
- "github.com/tendermint/tendermint/types"
- )
-
- var (
- chainID = "execution_chain"
- testPartSize = 65536
- nTxsPerBlock = 10
- )
-
- func TestApplyBlock(t *testing.T) {
- cc := proxy.NewLocalClientCreator(kvstore.NewKVStoreApplication())
- proxyApp := proxy.NewAppConns(cc)
- err := proxyApp.Start()
- require.Nil(t, err)
- defer proxyApp.Stop()
-
- state, stateDB := state(1, 1)
-
- blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(),
- MockMempool{}, MockEvidencePool{})
-
- block := makeBlock(state, 1)
- blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()}
-
- state, err = blockExec.ApplyBlock(state, blockID, block)
- require.Nil(t, err)
-
- // TODO check state and mempool
- }
-
- // TestBeginBlockValidators ensures we send absent validators list.
- func TestBeginBlockValidators(t *testing.T) {
- app := &testApp{}
- cc := proxy.NewLocalClientCreator(app)
- proxyApp := proxy.NewAppConns(cc)
- err := proxyApp.Start()
- require.Nil(t, err)
- defer proxyApp.Stop()
-
- state, stateDB := state(2, 2)
-
- prevHash := state.LastBlockID.Hash
- prevParts := types.PartSetHeader{}
- prevBlockID := types.BlockID{prevHash, prevParts}
-
- now := tmtime.Now()
- commitSig0 := (&types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType}).CommitSig()
- commitSig1 := (&types.Vote{ValidatorIndex: 1, Timestamp: now}).CommitSig()
-
- testCases := []struct {
- desc string
- lastCommitPrecommits []*types.CommitSig
- expectedAbsentValidators []int
- }{
- {"none absent", []*types.CommitSig{commitSig0, commitSig1}, []int{}},
- {"one absent", []*types.CommitSig{commitSig0, nil}, []int{1}},
- {"multiple absent", []*types.CommitSig{nil, nil}, []int{0, 1}},
- }
-
- for _, tc := range testCases {
- lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: tc.lastCommitPrecommits}
-
- // block for height 2
- block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
-
- _, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), state.Validators, stateDB)
- require.Nil(t, err, tc.desc)
-
- // -> app receives a list of validators with a bool indicating if they signed
- ctr := 0
- for i, v := range app.CommitVotes {
- if ctr < len(tc.expectedAbsentValidators) &&
- tc.expectedAbsentValidators[ctr] == i {
-
- assert.False(t, v.SignedLastBlock)
- ctr++
- } else {
- assert.True(t, v.SignedLastBlock)
- }
- }
- }
- }
-
- // TestBeginBlockByzantineValidators ensures we send byzantine validators list.
- func TestBeginBlockByzantineValidators(t *testing.T) {
- app := &testApp{}
- cc := proxy.NewLocalClientCreator(app)
- proxyApp := proxy.NewAppConns(cc)
- err := proxyApp.Start()
- require.Nil(t, err)
- defer proxyApp.Stop()
-
- state, stateDB := state(2, 12)
-
- prevHash := state.LastBlockID.Hash
- prevParts := types.PartSetHeader{}
- prevBlockID := types.BlockID{prevHash, prevParts}
-
- height1, idx1, val1 := int64(8), 0, state.Validators.Validators[0].Address
- height2, idx2, val2 := int64(3), 1, state.Validators.Validators[1].Address
- ev1 := types.NewMockGoodEvidence(height1, idx1, val1)
- ev2 := types.NewMockGoodEvidence(height2, idx2, val2)
-
- now := tmtime.Now()
- valSet := state.Validators
- testCases := []struct {
- desc string
- evidence []types.Evidence
- expectedByzantineValidators []abci.Evidence
- }{
- {"none byzantine", []types.Evidence{}, []abci.Evidence{}},
- {"one byzantine", []types.Evidence{ev1}, []abci.Evidence{types.TM2PB.Evidence(ev1, valSet, now)}},
- {"multiple byzantine", []types.Evidence{ev1, ev2}, []abci.Evidence{
- types.TM2PB.Evidence(ev1, valSet, now),
- types.TM2PB.Evidence(ev2, valSet, now)}},
- }
-
- commitSig0 := (&types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType}).CommitSig()
- commitSig1 := (&types.Vote{ValidatorIndex: 1, Timestamp: now}).CommitSig()
- commitSigs := []*types.CommitSig{commitSig0, commitSig1}
- lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: commitSigs}
- for _, tc := range testCases {
-
- 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(), state.Validators, stateDB)
- require.Nil(t, err, tc.desc)
-
- // -> app must receive an index of the byzantine validator
- assert.Equal(t, tc.expectedByzantineValidators, app.ByzantineValidators, tc.desc)
- }
- }
-
- func TestValidateValidatorUpdates(t *testing.T) {
- pubkey1 := ed25519.GenPrivKey().PubKey()
- pubkey2 := ed25519.GenPrivKey().PubKey()
-
- secpKey := secp256k1.GenPrivKey().PubKey()
-
- defaultValidatorParams := types.ValidatorParams{[]string{types.ABCIPubKeyTypeEd25519}}
-
- testCases := []struct {
- name string
-
- abciUpdates []abci.ValidatorUpdate
- validatorParams types.ValidatorParams
-
- shouldErr bool
- }{
- {
- "adding a validator is OK",
-
- []abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 20}},
- defaultValidatorParams,
-
- false,
- },
- {
- "updating a validator is OK",
-
- []abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey1), Power: 20}},
- defaultValidatorParams,
-
- false,
- },
- {
- "removing a validator is OK",
-
- []abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 0}},
- defaultValidatorParams,
-
- false,
- },
- {
- "adding a validator with negative power results in error",
-
- []abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: -100}},
- defaultValidatorParams,
-
- true,
- },
- {
- "adding a validator with pubkey thats not in validator params results in error",
-
- []abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(secpKey), Power: -100}},
- defaultValidatorParams,
-
- true,
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- err := validateValidatorUpdates(tc.abciUpdates, tc.validatorParams)
- if tc.shouldErr {
- assert.Error(t, err)
- } else {
- assert.NoError(t, err)
- }
- })
- }
- }
-
- func TestUpdateValidators(t *testing.T) {
- pubkey1 := ed25519.GenPrivKey().PubKey()
- val1 := types.NewValidator(pubkey1, 10)
- pubkey2 := ed25519.GenPrivKey().PubKey()
- val2 := types.NewValidator(pubkey2, 20)
-
- testCases := []struct {
- name string
-
- currentSet *types.ValidatorSet
- abciUpdates []abci.ValidatorUpdate
-
- resultingSet *types.ValidatorSet
- shouldErr bool
- }{
- {
- "adding a validator is OK",
-
- types.NewValidatorSet([]*types.Validator{val1}),
- []abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 20}},
-
- types.NewValidatorSet([]*types.Validator{val1, val2}),
- false,
- },
- {
- "updating a validator is OK",
-
- types.NewValidatorSet([]*types.Validator{val1}),
- []abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey1), Power: 20}},
-
- types.NewValidatorSet([]*types.Validator{types.NewValidator(pubkey1, 20)}),
- false,
- },
- {
- "removing a validator is OK",
-
- types.NewValidatorSet([]*types.Validator{val1, val2}),
- []abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 0}},
-
- types.NewValidatorSet([]*types.Validator{val1}),
- false,
- },
- {
- "removing a non-existing validator results in error",
-
- types.NewValidatorSet([]*types.Validator{val1}),
- []abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 0}},
-
- types.NewValidatorSet([]*types.Validator{val1}),
- true,
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- updates, err := types.PB2TM.ValidatorUpdates(tc.abciUpdates)
- assert.NoError(t, err)
- err = updateValidators(tc.currentSet, updates)
- if tc.shouldErr {
- assert.Error(t, err)
- } else {
- assert.NoError(t, err)
- require.Equal(t, tc.resultingSet.Size(), tc.currentSet.Size())
-
- assert.Equal(t, tc.resultingSet.TotalVotingPower(), tc.currentSet.TotalVotingPower())
-
- assert.Equal(t, tc.resultingSet.Validators[0].Address, tc.currentSet.Validators[0].Address)
- if tc.resultingSet.Size() > 1 {
- assert.Equal(t, tc.resultingSet.Validators[1].Address, tc.currentSet.Validators[1].Address)
- }
- }
- })
- }
- }
-
- // TestEndBlockValidatorUpdates ensures we update validator set and send an event.
- func TestEndBlockValidatorUpdates(t *testing.T) {
- app := &testApp{}
- cc := proxy.NewLocalClientCreator(app)
- proxyApp := proxy.NewAppConns(cc)
- err := proxyApp.Start()
- require.Nil(t, err)
- defer proxyApp.Stop()
-
- state, stateDB := state(1, 1)
-
- blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), MockMempool{}, MockEvidencePool{})
-
- eventBus := types.NewEventBus()
- err = eventBus.Start()
- require.NoError(t, err)
- defer eventBus.Stop()
- blockExec.SetEventBus(eventBus)
-
- updatesSub, err := eventBus.Subscribe(context.Background(), "TestEndBlockValidatorUpdates", types.EventQueryValidatorSetUpdates)
- require.NoError(t, err)
-
- block := makeBlock(state, 1)
- blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()}
-
- pubkey := ed25519.GenPrivKey().PubKey()
- app.ValidatorUpdates = []abci.ValidatorUpdate{
- {PubKey: types.TM2PB.PubKey(pubkey), Power: 10},
- }
-
- state, err = blockExec.ApplyBlock(state, blockID, block)
- require.Nil(t, err)
-
- // test new validator was added to NextValidators
- if assert.Equal(t, state.Validators.Size()+1, state.NextValidators.Size()) {
- idx, _ := state.NextValidators.GetByAddress(pubkey.Address())
- if idx < 0 {
- t.Fatalf("can't find address %v in the set %v", pubkey.Address(), state.NextValidators)
- }
- }
-
- // test we threw an event
- select {
- case msg := <-updatesSub.Out():
- event, ok := msg.Data().(types.EventDataValidatorSetUpdates)
- require.True(t, ok, "Expected event of type EventDataValidatorSetUpdates, got %T", msg.Data())
- if assert.NotEmpty(t, event.ValidatorUpdates) {
- assert.Equal(t, pubkey, event.ValidatorUpdates[0].PubKey)
- assert.EqualValues(t, 10, event.ValidatorUpdates[0].VotingPower)
- }
- case <-updatesSub.Cancelled():
- t.Fatal(fmt.Sprintf("updatesSub was cancelled (reason: %v)", updatesSub.Err()))
- case <-time.After(1 * time.Second):
- t.Fatal("Did not receive EventValidatorSetUpdates within 1 sec.")
- }
- }
-
- //----------------------------------------------------------------------------
-
- // 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{
- pk.PubKey().Address(),
- pk.PubKey(),
- 1000,
- 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(tx []byte) abci.ResponseDeliverTx {
- return abci.ResponseDeliverTx{Tags: []cmn.KVPair{}}
- }
-
- func (app *testApp) CheckTx(tx []byte) 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
- }
|