package state_test import ( "bytes" "fmt" "testing" "time" dbm "github.com/tendermint/tm-db" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/internal/test" tmstate "github.com/tendermint/tendermint/proto/tendermint/state" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "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 } func newTestApp() proxy.AppConns { app := &testApp{} cc := proxy.NewLocalClientCreator(app) return proxy.NewAppConns(cc, proxy.NopMetrics()) } 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, test.MakeNTxs(height, 10), lastCommit, evidence, proposerAddr) partSet, err := block.MakePartSet(types.BlockPartSizeBytes) if err != nil { return state, types.BlockID{}, err } if err := blockExec.ValidateBlock(state, block); err != nil { return state, types.BlockID{}, err } blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: partSet.Header()} state, err = blockExec.ApplyBlock(state, blockID, block) if err != nil { return state, types.BlockID{}, err } return state, blockID, nil } func makeBlock(state sm.State, height int64, c *types.Commit) *types.Block { return state.MakeBlock( height, test.MakeNTxs(state.LastBlockHeight, 10), c, nil, state.Validators.GetProposer().Address, ) } 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(int32(i)) vote, err := types.MakeVote(height, blockID, vals, privVals[val.Address.String()], chainID, time.Now()) if err != nil { return nil, err } sigs = append(sigs, vote.CommitSig()) } return types.NewCommit(height, 0, blockID, sigs), nil } 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() stateStore := sm.NewStore(stateDB, sm.StoreOptions{ DiscardABCIResponses: false, }) if err := stateStore.Save(s); err != nil { panic(err) } for i := 1; i < height; i++ { s.LastBlockHeight++ s.LastValidators = s.Validators.Copy() if err := stateStore.Save(s); err != nil { panic(err) } } return s, stateDB, privVals } 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 makeHeaderPartsResponsesValPubKeyChange( t *testing.T, state sm.State, pubkey crypto.PubKey, ) (types.Header, types.BlockID, *tmstate.ABCIResponses) { block := makeBlock(state, state.LastBlockHeight+1, new(types.Commit)) abciResponses := &tmstate.ABCIResponses{ BeginBlock: &abci.ResponseBeginBlock{}, 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(), PartSetHeader: types.PartSetHeader{}}, abciResponses } func makeHeaderPartsResponsesValPowerChange( t *testing.T, state sm.State, power int64, ) (types.Header, types.BlockID, *tmstate.ABCIResponses) { block := makeBlock(state, state.LastBlockHeight+1, new(types.Commit)) abciResponses := &tmstate.ABCIResponses{ BeginBlock: &abci.ResponseBeginBlock{}, 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(), PartSetHeader: types.PartSetHeader{}}, abciResponses } func makeHeaderPartsResponsesParams( t *testing.T, state sm.State, params tmproto.ConsensusParams, ) (types.Header, types.BlockID, *tmstate.ABCIResponses) { block := makeBlock(state, state.LastBlockHeight+1, new(types.Commit)) abciResponses := &tmstate.ABCIResponses{ BeginBlock: &abci.ResponseBeginBlock{}, EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: ¶ms}, } return block.Header, types.BlockID{Hash: block.Hash(), PartSetHeader: 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 Misbehavior []abci.Misbehavior 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.Misbehavior = req.ByzantineValidators return abci.ResponseBeginBlock{} } func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { return abci.ResponseEndBlock{ ValidatorUpdates: app.ValidatorUpdates, ConsensusParamUpdates: &tmproto.ConsensusParams{ Version: &tmproto.VersionParams{ App: 1}}} } 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{RetainHeight: 1} } func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { return } func (app *testApp) ProcessProposal(req abci.RequestProcessProposal) abci.ResponseProcessProposal { for _, tx := range req.Txs { if len(tx) == 0 { return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} } } return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT} }