package state_test
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"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"
|
|
tmrand "github.com/tendermint/tendermint/libs/rand"
|
|
"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 (m mockEvPoolAlwaysCommitted) IsPending(types.Evidence) bool { return false }
|
|
|
|
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{Total: 3, Hash: tmrand.Bytes(32)}}
|
|
state, _, err := blockExec.ApplyBlock(state, blockID, block)
|
|
if err != nil {
|
|
return state, types.BlockID{}, err
|
|
}
|
|
return state, blockID, 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 := 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
|
|
}
|
|
|
|
// 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{
|
|
MaxAgeNumBlocks: evidenceAge,
|
|
MaxAgeDuration: time.Duration(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
|
|
}
|