mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-24 22:32:15 +00:00
Delay validator set changes by 1 block.
This commit is contained in:
parent
ce0d0b312f
commit
a5b7ea93c4
4
Makefile
4
Makefile
@ -132,11 +132,11 @@ vagrant_test:
|
||||
### go tests
|
||||
test:
|
||||
@echo "--> Running go test"
|
||||
@go test $(PACKAGES)
|
||||
@GOCACHE=off go test -p 1 $(PACKAGES)
|
||||
|
||||
test_race:
|
||||
@echo "--> Running go test --race"
|
||||
@go test -v -race $(PACKAGES)
|
||||
@GOCACHE=off go test -p 1 -v -race $(PACKAGES)
|
||||
|
||||
|
||||
########################################
|
||||
|
@ -174,14 +174,14 @@ func TestReactorRecordsBlockParts(t *testing.T) {
|
||||
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
|
||||
}
|
||||
|
||||
// Test we record votes from other peers
|
||||
// Test we record votes from other peers.
|
||||
func TestReactorRecordsVotes(t *testing.T) {
|
||||
// create dummy peer
|
||||
// Create dummy peer.
|
||||
peer := p2pdummy.NewPeer()
|
||||
ps := NewPeerState(peer).SetLogger(log.TestingLogger())
|
||||
peer.Set(types.PeerStateKey, ps)
|
||||
|
||||
// create reactor
|
||||
// Create reactor.
|
||||
css := randConsensusNet(1, "consensus_reactor_records_votes_test", newMockTickerFunc(true), newPersistentKVStore)
|
||||
reactor := NewConsensusReactor(css[0], false) // so we dont start the consensus states
|
||||
reactor.SetEventBus(css[0].eventBus)
|
||||
|
@ -264,15 +264,15 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
|
||||
stateBlockHeight := state.LastBlockHeight
|
||||
h.logger.Info("ABCI Replay Blocks", "appHeight", appBlockHeight, "storeHeight", storeBlockHeight, "stateHeight", stateBlockHeight)
|
||||
|
||||
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain
|
||||
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain.
|
||||
if appBlockHeight == 0 {
|
||||
validators := types.TM2PB.Validators(state.Validators)
|
||||
nvals := types.TM2PB.Validators(state.Validators) // state.Validators would work too.
|
||||
csParams := types.TM2PB.ConsensusParams(h.genDoc.ConsensusParams)
|
||||
req := abci.RequestInitChain{
|
||||
Time: h.genDoc.GenesisTime.Unix(), // TODO
|
||||
ChainId: h.genDoc.ChainID,
|
||||
ConsensusParams: csParams,
|
||||
Validators: validators,
|
||||
Validators: nvals,
|
||||
AppStateBytes: h.genDoc.AppStateJSON,
|
||||
}
|
||||
res, err := proxyApp.Consensus().InitChainSync(req)
|
||||
@ -280,9 +280,7 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if the app returned validators
|
||||
// or consensus params, update the state
|
||||
// with the them
|
||||
// If the app returned validators or consensus params, update the state.
|
||||
if len(res.Validators) > 0 {
|
||||
vals, err := types.PB2TM.Validators(res.Validators)
|
||||
if err != nil {
|
||||
@ -296,7 +294,7 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
|
||||
sm.SaveState(h.stateDB, state)
|
||||
}
|
||||
|
||||
// First handle edge cases and constraints on the storeBlockHeight
|
||||
// First handle edge cases and constraints on the storeBlockHeight.
|
||||
if storeBlockHeight == 0 {
|
||||
return appHash, checkAppHash(state, appHash)
|
||||
|
||||
|
@ -74,7 +74,6 @@ type ConsensusState struct {
|
||||
privValidator types.PrivValidator // for signing votes
|
||||
|
||||
// services for creating and executing blocks
|
||||
// TODO: encapsulate all of this in one "BlockManager"
|
||||
blockExec *sm.BlockExecutor
|
||||
blockStore sm.BlockStore
|
||||
mempool sm.Mempool
|
||||
|
@ -64,22 +64,22 @@ func TestStateProposerSelection0(t *testing.T) {
|
||||
|
||||
startTestRound(cs1, height, round)
|
||||
|
||||
// wait for new round so proposer is set
|
||||
// Wait for new round so proposer is set.
|
||||
<-newRoundCh
|
||||
|
||||
// lets commit a block and ensure proposer for the next height is correct
|
||||
// Commit a block and ensure proposer for the next height is correct.
|
||||
prop := cs1.GetRoundState().Validators.GetProposer()
|
||||
if !bytes.Equal(prop.Address, cs1.privValidator.GetAddress()) {
|
||||
t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address)
|
||||
}
|
||||
|
||||
// wait for complete proposal
|
||||
// Wait for complete proposal.
|
||||
<-proposalCh
|
||||
|
||||
rs := cs1.GetRoundState()
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:]...)
|
||||
|
||||
// wait for new round so next validator is set
|
||||
// Wait for new round so next validator is set.
|
||||
<-newRoundCh
|
||||
|
||||
prop = cs1.GetRoundState().Validators.GetProposer()
|
||||
|
@ -27,6 +27,7 @@ func initializeValidatorState(valAddr []byte, height int64) dbm.DB {
|
||||
LastBlockHeight: 0,
|
||||
LastBlockTime: time.Now(),
|
||||
Validators: valSet,
|
||||
NextValidators: valSet.CopyIncrementAccum(1),
|
||||
LastHeightValidatorsChanged: 1,
|
||||
ConsensusParams: types.ConsensusParams{
|
||||
EvidenceParams: types.EvidenceParams{
|
||||
|
@ -109,7 +109,7 @@ func validatorAtHeight(h int64) *types.Validator {
|
||||
|
||||
privValAddress := pubKey.Address()
|
||||
|
||||
// if we're still at height h, search in the current validator set
|
||||
// If we're still at height h, search in the current validator set.
|
||||
if lastBlockHeight == h {
|
||||
for _, val := range vals {
|
||||
if bytes.Equal(val.Address, privValAddress) {
|
||||
@ -118,12 +118,11 @@ func validatorAtHeight(h int64) *types.Validator {
|
||||
}
|
||||
}
|
||||
|
||||
// if we've moved to the next height, retrieve the validator set from DB
|
||||
// If we've moved to the next height, retrieve the validator set from DB.
|
||||
if lastBlockHeight > h {
|
||||
vals, err := sm.LoadValidators(stateDB, h)
|
||||
if err != nil {
|
||||
// should not happen
|
||||
return nil
|
||||
return nil // should not happen
|
||||
}
|
||||
_, val := vals.GetByAddress(privValAddress)
|
||||
return val
|
||||
|
@ -80,18 +80,18 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
// save the results before we commit
|
||||
// Save the results before we commit.
|
||||
saveABCIResponses(blockExec.db, block.Height, abciResponses)
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
// update the state with the block and responses
|
||||
// Update the state with the block and responses.
|
||||
state, err = updateState(state, blockID, block.Header, abciResponses)
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("Commit failed for application: %v", err)
|
||||
}
|
||||
|
||||
// lock mempool, commit app state, update mempoool
|
||||
// Lock mempool, commit app state, update mempoool.
|
||||
appHash, err := blockExec.Commit(block)
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("Commit failed for application: %v", err)
|
||||
@ -102,13 +102,13 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
// update the app hash and save the state
|
||||
// Update the app hash and save the state.
|
||||
state.AppHash = appHash
|
||||
SaveState(blockExec.db, state)
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
// events are fired after everything else
|
||||
// Events are fired after everything else.
|
||||
// NOTE: if we crash between Commit and Save, events wont be fired during replay
|
||||
fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses)
|
||||
|
||||
@ -164,7 +164,7 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus,
|
||||
txIndex := 0
|
||||
abciResponses := NewABCIResponses(block)
|
||||
|
||||
// Execute transactions and get hash
|
||||
// Execute transactions and get hash.
|
||||
proxyCb := func(req *abci.Request, res *abci.Response) {
|
||||
switch r := res.Value.(type) {
|
||||
case *abci.Response_DeliverTx:
|
||||
@ -186,7 +186,7 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus,
|
||||
|
||||
signVals, byzVals := getBeginBlockValidatorInfo(block, lastValSet, stateDB)
|
||||
|
||||
// Begin block
|
||||
// Begin block.
|
||||
_, err := proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{
|
||||
Hash: block.Hash(),
|
||||
Header: types.TM2PB.Header(block.Header),
|
||||
@ -198,7 +198,7 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Run txs of block
|
||||
// Run txs of block.
|
||||
for _, tx := range block.Txs {
|
||||
proxyAppConn.DeliverTxAsync(tx)
|
||||
if err := proxyAppConn.Error(); err != nil {
|
||||
@ -206,7 +206,7 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus,
|
||||
}
|
||||
}
|
||||
|
||||
// End block
|
||||
// End block.
|
||||
abciResponses.EndBlock, err = proxyAppConn.EndBlockSync(abci.RequestEndBlock{block.Height})
|
||||
if err != nil {
|
||||
logger.Error("Error in proxyAppConn.EndBlock", "err", err)
|
||||
@ -307,26 +307,25 @@ func updateValidators(currentSet *types.ValidatorSet, abciUpdates []abci.Validat
|
||||
func updateState(state State, blockID types.BlockID, header *types.Header,
|
||||
abciResponses *ABCIResponses) (State, error) {
|
||||
|
||||
// copy the valset so we can apply changes from EndBlock
|
||||
// and update s.LastValidators and s.Validators
|
||||
prevValSet := state.Validators.Copy()
|
||||
nextValSet := prevValSet.Copy()
|
||||
// Copy the valset so we can apply changes from EndBlock
|
||||
// and update s.LastValidators and s.Validators.
|
||||
nValSet := state.NextValidators.Copy()
|
||||
|
||||
// update the validator set with the latest abciResponses
|
||||
// Update the validator set with the latest abciResponses.
|
||||
lastHeightValsChanged := state.LastHeightValidatorsChanged
|
||||
if len(abciResponses.EndBlock.ValidatorUpdates) > 0 {
|
||||
err := updateValidators(nextValSet, abciResponses.EndBlock.ValidatorUpdates)
|
||||
err := updateValidators(nValSet, abciResponses.EndBlock.ValidatorUpdates)
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("Error changing validator set: %v", err)
|
||||
}
|
||||
// change results from this height but only applies to the next height
|
||||
lastHeightValsChanged = header.Height + 1
|
||||
// Change results from this height but only applies to the next next height.
|
||||
lastHeightValsChanged = header.Height + 1 + 1
|
||||
}
|
||||
|
||||
// Update validator accums and set state variables
|
||||
nextValSet.IncrementAccum(1)
|
||||
// Update validator accums and set state variables.
|
||||
nValSet.IncrementAccum(1)
|
||||
|
||||
// update the params with the latest abciResponses
|
||||
// Update the params with the latest abciResponses.
|
||||
nextParams := state.ConsensusParams
|
||||
lastHeightParamsChanged := state.LastHeightConsensusParamsChanged
|
||||
if abciResponses.EndBlock.ConsensusParamUpdates != nil {
|
||||
@ -336,7 +335,7 @@ func updateState(state State, blockID types.BlockID, header *types.Header,
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("Error updating consensus params: %v", err)
|
||||
}
|
||||
// change results from this height but only applies to the next height
|
||||
// Change results from this height but only applies to the next height.
|
||||
lastHeightParamsChanged = header.Height + 1
|
||||
}
|
||||
|
||||
@ -348,7 +347,8 @@ func updateState(state State, blockID types.BlockID, header *types.Header,
|
||||
LastBlockTotalTx: state.LastBlockTotalTx + header.NumTxs,
|
||||
LastBlockID: blockID,
|
||||
LastBlockTime: header.Time,
|
||||
Validators: nextValSet,
|
||||
NextValidators: nValSet,
|
||||
Validators: state.NextValidators.Copy(),
|
||||
LastValidators: state.Validators.Copy(),
|
||||
LastHeightValidatorsChanged: lastHeightValsChanged,
|
||||
ConsensusParams: nextParams,
|
||||
|
@ -24,7 +24,7 @@ var (
|
||||
// Instead, use state.Copy() or state.NextState(...).
|
||||
// NOTE: not goroutine-safe.
|
||||
type State struct {
|
||||
// Immutable
|
||||
// immutable
|
||||
ChainID string
|
||||
|
||||
// LastBlockHeight=0 at genesis (ie. block(H=0) does not exist)
|
||||
@ -38,6 +38,7 @@ type State struct {
|
||||
// so we can query for historical validator sets.
|
||||
// Note that if s.LastBlockHeight causes a valset change,
|
||||
// we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1
|
||||
NextValidators *types.ValidatorSet
|
||||
Validators *types.ValidatorSet
|
||||
LastValidators *types.ValidatorSet
|
||||
LastHeightValidatorsChanged int64
|
||||
@ -50,7 +51,7 @@ type State struct {
|
||||
// Merkle root of the results from executing prev block
|
||||
LastResultsHash []byte
|
||||
|
||||
// The latest AppHash we've received from calling abci.Commit()
|
||||
// the latest AppHash we've received from calling abci.Commit()
|
||||
AppHash []byte
|
||||
}
|
||||
|
||||
@ -64,6 +65,7 @@ func (state State) Copy() State {
|
||||
LastBlockID: state.LastBlockID,
|
||||
LastBlockTime: state.LastBlockTime,
|
||||
|
||||
NextValidators: state.NextValidators.Copy(),
|
||||
Validators: state.Validators.Copy(),
|
||||
LastValidators: state.LastValidators.Copy(),
|
||||
LastHeightValidatorsChanged: state.LastHeightValidatorsChanged,
|
||||
@ -93,24 +95,20 @@ func (state State) IsEmpty() bool {
|
||||
return state.Validators == nil // XXX can't compare to Empty
|
||||
}
|
||||
|
||||
// GetValidators returns the last and current validator sets.
|
||||
func (state State) GetValidators() (last *types.ValidatorSet, current *types.ValidatorSet) {
|
||||
return state.LastValidators, state.Validators
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Create a block from the latest state
|
||||
|
||||
// MakeBlock builds a block with the given txs and commit from the current state.
|
||||
func (state State) MakeBlock(height int64, txs []types.Tx, commit *types.Commit) (*types.Block, *types.PartSet) {
|
||||
// build base block
|
||||
// Build base block.
|
||||
block := types.MakeBlock(height, txs, commit)
|
||||
|
||||
// fill header with state data
|
||||
// Fill header with state data.
|
||||
block.ChainID = state.ChainID
|
||||
block.TotalTxs = state.LastBlockTotalTx + block.NumTxs
|
||||
block.LastBlockID = state.LastBlockID
|
||||
block.ValidatorsHash = state.Validators.Hash()
|
||||
block.NextValidatorsHash = state.NextValidators.Hash()
|
||||
block.AppHash = state.AppHash
|
||||
block.ConsensusHash = state.ConsensusParams.Hash()
|
||||
block.LastResultsHash = state.LastResultsHash
|
||||
@ -175,6 +173,7 @@ func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) {
|
||||
LastBlockID: types.BlockID{},
|
||||
LastBlockTime: genDoc.GenesisTime,
|
||||
|
||||
NextValidators: types.NewValidatorSet(validators).CopyIncrementAccum(1),
|
||||
Validators: types.NewValidatorSet(validators),
|
||||
LastValidators: types.NewValidatorSet(nil),
|
||||
LastHeightValidatorsChanged: 1,
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// setupTestCase does setup common to all test cases
|
||||
// setupTestCase does setup common to all test cases.
|
||||
func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, State) {
|
||||
config := cfg.ResetTestRoot("state_")
|
||||
dbType := dbm.DBBackendType(config.DBBackend)
|
||||
@ -72,7 +72,7 @@ func TestABCIResponsesSaveLoad1(t *testing.T) {
|
||||
|
||||
state.LastBlockHeight++
|
||||
|
||||
// build mock responses
|
||||
// Build mock responses.
|
||||
block := makeBlock(state, 2)
|
||||
abciResponses := NewABCIResponses(block)
|
||||
abciResponses.DeliverTx[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Tags: nil}
|
||||
@ -89,7 +89,7 @@ func TestABCIResponsesSaveLoad1(t *testing.T) {
|
||||
loadedABCIResponses, abciResponses))
|
||||
}
|
||||
|
||||
// TestResultsSaveLoad tests saving and loading abci results.
|
||||
// TestResultsSaveLoad tests saving and loading ABCI results.
|
||||
func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||
tearDown, stateDB, _ := setupTestCase(t)
|
||||
defer tearDown(t)
|
||||
@ -97,8 +97,8 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cases := [...]struct {
|
||||
// height is implied index+2
|
||||
// as block 1 is created from genesis
|
||||
// Height is implied to equal index+2,
|
||||
// as block 1 is created from genesis.
|
||||
added []*abci.ResponseDeliverTx
|
||||
expected types.ABCIResults
|
||||
}{
|
||||
@ -132,14 +132,14 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// query all before, should return error
|
||||
// Query all before, this should return error.
|
||||
for i := range cases {
|
||||
h := int64(i + 1)
|
||||
res, err := LoadABCIResponses(stateDB, h)
|
||||
assert.Error(err, "%d: %#v", i, res)
|
||||
}
|
||||
|
||||
// add all cases
|
||||
// Add all cases.
|
||||
for i, tc := range cases {
|
||||
h := int64(i + 1) // last block height, one below what we save
|
||||
responses := &ABCIResponses{
|
||||
@ -149,7 +149,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||
saveABCIResponses(stateDB, h, responses)
|
||||
}
|
||||
|
||||
// query all before, should return expected value
|
||||
// Query all before, should return expected value.
|
||||
for i, tc := range cases {
|
||||
h := int64(i + 1)
|
||||
res, err := LoadABCIResponses(stateDB, h)
|
||||
@ -165,34 +165,30 @@ func TestValidatorSimpleSaveLoad(t *testing.T) {
|
||||
// nolint: vetshadow
|
||||
assert := assert.New(t)
|
||||
|
||||
// can't load anything for height 0
|
||||
// Can't load anything for height 0.
|
||||
v, err := LoadValidators(stateDB, 0)
|
||||
assert.IsType(ErrNoValSetForHeight{}, err, "expected err at height 0")
|
||||
|
||||
// should be able to load for height 1
|
||||
// Should be able to load for height 1.
|
||||
v, err = LoadValidators(stateDB, 1)
|
||||
assert.Nil(err, "expected no err at height 1")
|
||||
assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match")
|
||||
|
||||
// increment height, save; should be able to load for next height
|
||||
// Should be able to load for height 2.
|
||||
v, err = 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, state.LastHeightValidatorsChanged, state.Validators)
|
||||
v, err = LoadValidators(stateDB, nextHeight)
|
||||
saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
|
||||
vp0, err := LoadValidators(stateDB, nextHeight+0)
|
||||
assert.Nil(err, "expected no err")
|
||||
assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match")
|
||||
|
||||
// increment height, save; should be able to load for next height
|
||||
state.LastBlockHeight += 10
|
||||
nextHeight = state.LastBlockHeight + 1
|
||||
saveValidatorsInfo(stateDB, nextHeight, state.LastHeightValidatorsChanged, state.Validators)
|
||||
v, err = LoadValidators(stateDB, nextHeight)
|
||||
vp1, err := LoadValidators(stateDB, nextHeight+1)
|
||||
assert.Nil(err, "expected no err")
|
||||
assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match")
|
||||
|
||||
// should be able to load for next next height
|
||||
_, err = LoadValidators(stateDB, state.LastBlockHeight+2)
|
||||
assert.IsType(ErrNoValSetForHeight{}, err, "expected err at unknown height")
|
||||
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")
|
||||
}
|
||||
|
||||
// TestValidatorChangesSaveLoad tests saving and loading a validator set with changes.
|
||||
@ -200,38 +196,37 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
|
||||
tearDown, stateDB, state := setupTestCase(t)
|
||||
defer tearDown(t)
|
||||
|
||||
// change vals at these heights
|
||||
// Change vals at these heights.
|
||||
changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20}
|
||||
N := len(changeHeights)
|
||||
|
||||
// build the validator history by running updateState
|
||||
// with the right validator set for each height
|
||||
// Build the validator history by running updateState
|
||||
// with the right validator set for each height.
|
||||
highestHeight := changeHeights[N-1] + 5
|
||||
changeIndex := 0
|
||||
_, val := state.Validators.GetByIndex(0)
|
||||
power := val.VotingPower
|
||||
var err error
|
||||
for i := int64(1); i < highestHeight; i++ {
|
||||
// when we get to a change height,
|
||||
// use the next pubkey
|
||||
// When we get to a change height, use the next pubkey.
|
||||
if changeIndex < len(changeHeights) && i == changeHeights[changeIndex] {
|
||||
changeIndex++
|
||||
power++
|
||||
}
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, i, power)
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, power)
|
||||
state, err = updateState(state, blockID, header, responses)
|
||||
assert.Nil(t, err)
|
||||
nextHeight := state.LastBlockHeight + 1
|
||||
saveValidatorsInfo(stateDB, nextHeight, state.LastHeightValidatorsChanged, state.Validators)
|
||||
saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
|
||||
}
|
||||
|
||||
// on each change height, increment the power by one.
|
||||
// On each height change, increment the power by one.
|
||||
testCases := make([]int64, highestHeight)
|
||||
changeIndex = 0
|
||||
power = val.VotingPower
|
||||
for i := int64(1); i < highestHeight+1; i++ {
|
||||
// we we get to the height after a change height
|
||||
// use the next pubkey (note our counter starts at 0 this time)
|
||||
// We get to the height after a change height use the next pubkey (note
|
||||
// our counter starts at 0 this time).
|
||||
if changeIndex < len(changeHeights) && i == changeHeights[changeIndex]+1 {
|
||||
changeIndex++
|
||||
power++
|
||||
@ -240,7 +235,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, power := range testCases {
|
||||
v, err := LoadValidators(stateDB, int64(i+1))
|
||||
v, err := 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)
|
||||
@ -255,25 +250,41 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
|
||||
func TestManyValidatorChangesSaveLoad(t *testing.T) {
|
||||
const valSetSize = 7
|
||||
tearDown, stateDB, state := setupTestCase(t)
|
||||
require.Equal(t, int64(0), state.LastBlockHeight)
|
||||
state.Validators = genValSet(valSetSize)
|
||||
state.NextValidators = state.Validators.CopyIncrementAccum(1)
|
||||
SaveState(stateDB, state)
|
||||
defer tearDown(t)
|
||||
|
||||
const height = 1
|
||||
pubkey := crypto.GenPrivKeyEd25519().PubKey()
|
||||
// swap the first validator with a new one ^^^ (validator set size stays the same)
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(state, height, pubkey)
|
||||
_, valOld := state.Validators.GetByIndex(0)
|
||||
var pubkeyOld = valOld.PubKey
|
||||
var pubkey = crypto.GenPrivKeyEd25519().PubKey()
|
||||
|
||||
// Swap the first validator with a new one (validator set size stays the same).
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(state, pubkey)
|
||||
|
||||
// Save state etc.
|
||||
var err error
|
||||
state, err = updateState(state, blockID, header, responses)
|
||||
require.Nil(t, err)
|
||||
nextHeight := state.LastBlockHeight + 1
|
||||
saveValidatorsInfo(stateDB, nextHeight, state.LastHeightValidatorsChanged, state.Validators)
|
||||
saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
|
||||
|
||||
v, err := LoadValidators(stateDB, height+1)
|
||||
// Load nextheight, it should be the oldpubkey.
|
||||
v0, err := LoadValidators(stateDB, nextHeight)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, valSetSize, v.Size())
|
||||
assert.Equal(t, valSetSize, v0.Size())
|
||||
index, val := v0.GetByAddress(pubkeyOld.Address())
|
||||
assert.NotNil(t, val)
|
||||
if index < 0 {
|
||||
t.Fatal("expected to find old validator")
|
||||
}
|
||||
|
||||
index, val := v.GetByAddress(pubkey.Address())
|
||||
// Load nextheight+1, it should be the new pubkey.
|
||||
v1, err := LoadValidators(stateDB, nextHeight+1)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, valSetSize, v1.Size())
|
||||
index, val = v1.GetByAddress(pubkey.Address())
|
||||
assert.NotNil(t, val)
|
||||
if index < 0 {
|
||||
t.Fatal("expected to find newly added validator")
|
||||
@ -294,12 +305,12 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
|
||||
tearDown, stateDB, state := setupTestCase(t)
|
||||
defer tearDown(t)
|
||||
|
||||
// change vals at these heights
|
||||
// Change vals at these heights.
|
||||
changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20}
|
||||
N := len(changeHeights)
|
||||
|
||||
// each valset is just one validator
|
||||
// create list of them
|
||||
// Each valset is just one validator.
|
||||
// create list of them.
|
||||
params := make([]types.ConsensusParams, N+1)
|
||||
params[0] = state.ConsensusParams
|
||||
for i := 1; i < N+1; i++ {
|
||||
@ -307,20 +318,19 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
|
||||
params[i].BlockSize.MaxBytes += i
|
||||
}
|
||||
|
||||
// build the params history by running updateState
|
||||
// with the right params set for each height
|
||||
// Build the params history by running updateState
|
||||
// with the right params set for each height.
|
||||
highestHeight := changeHeights[N-1] + 5
|
||||
changeIndex := 0
|
||||
cp := params[changeIndex]
|
||||
var err error
|
||||
for i := int64(1); i < highestHeight; i++ {
|
||||
// when we get to a change height,
|
||||
// use the next params
|
||||
// When we get to a change height, use the next params.
|
||||
if changeIndex < len(changeHeights) && i == changeHeights[changeIndex] {
|
||||
changeIndex++
|
||||
cp = params[changeIndex]
|
||||
}
|
||||
header, blockID, responses := makeHeaderPartsResponsesParams(state, i, cp)
|
||||
header, blockID, responses := makeHeaderPartsResponsesParams(state, cp)
|
||||
state, err = updateState(state, blockID, header, responses)
|
||||
|
||||
require.Nil(t, err)
|
||||
@ -328,13 +338,13 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
|
||||
saveConsensusParamsInfo(stateDB, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
|
||||
}
|
||||
|
||||
// make all the test cases by using the same params until after the change
|
||||
// Make all the test cases by using the same params until after the change.
|
||||
testCases := make([]paramsChangeTestCase, highestHeight)
|
||||
changeIndex = 0
|
||||
cp = params[changeIndex]
|
||||
for i := int64(1); i < highestHeight+1; i++ {
|
||||
// we we get to the height after a change height
|
||||
// use the next pubkey (note our counter starts at 0 this time)
|
||||
// We get to the height after a change height use the next pubkey (note
|
||||
// our counter starts at 0 this time).
|
||||
if changeIndex < len(changeHeights) && i == changeHeights[changeIndex]+1 {
|
||||
changeIndex++
|
||||
cp = params[changeIndex]
|
||||
@ -419,16 +429,16 @@ func TestApplyUpdates(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func makeHeaderPartsResponsesValPubKeyChange(state State, height int64,
|
||||
pubkey crypto.PubKey) (*types.Header, types.BlockID, *ABCIResponses) {
|
||||
func makeHeaderPartsResponsesValPubKeyChange(state State, pubkey crypto.PubKey) (
|
||||
*types.Header, types.BlockID, *ABCIResponses) {
|
||||
|
||||
block := makeBlock(state, height)
|
||||
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.Validators.GetByIndex(0)
|
||||
// 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.Validator{
|
||||
@ -441,16 +451,16 @@ func makeHeaderPartsResponsesValPubKeyChange(state State, height int64,
|
||||
return block.Header, types.BlockID{block.Hash(), types.PartSetHeader{}}, abciResponses
|
||||
}
|
||||
|
||||
func makeHeaderPartsResponsesValPowerChange(state State, height int64,
|
||||
power int64) (*types.Header, types.BlockID, *ABCIResponses) {
|
||||
func makeHeaderPartsResponsesValPowerChange(state State, power int64) (
|
||||
*types.Header, types.BlockID, *ABCIResponses) {
|
||||
|
||||
block := makeBlock(state, height)
|
||||
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.Validators.GetByIndex(0)
|
||||
// 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.Validator{
|
||||
@ -462,10 +472,10 @@ func makeHeaderPartsResponsesValPowerChange(state State, height int64,
|
||||
return block.Header, types.BlockID{block.Hash(), types.PartSetHeader{}}, abciResponses
|
||||
}
|
||||
|
||||
func makeHeaderPartsResponsesParams(state State, height int64,
|
||||
params types.ConsensusParams) (*types.Header, types.BlockID, *ABCIResponses) {
|
||||
func makeHeaderPartsResponsesParams(state State, params types.ConsensusParams) (
|
||||
*types.Header, types.BlockID, *ABCIResponses) {
|
||||
|
||||
block := makeBlock(state, height)
|
||||
block := makeBlock(state, state.LastBlockHeight+1)
|
||||
abciResponses := &ABCIResponses{
|
||||
EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(¶ms)},
|
||||
}
|
||||
@ -476,14 +486,3 @@ type paramsChangeTestCase struct {
|
||||
height int64
|
||||
params types.ConsensusParams
|
||||
}
|
||||
|
||||
func makeHeaderPartsResults(state State, height int64,
|
||||
results []*abci.ResponseDeliverTx) (*types.Header, types.BlockID, *ABCIResponses) {
|
||||
|
||||
block := makeBlock(state, height)
|
||||
abciResponses := &ABCIResponses{
|
||||
DeliverTx: results,
|
||||
EndBlock: &abci.ResponseEndBlock{},
|
||||
}
|
||||
return block.Header, types.BlockID{block.Hash(), types.PartSetHeader{}}, abciResponses
|
||||
}
|
||||
|
@ -86,7 +86,14 @@ func SaveState(db dbm.DB, state State) {
|
||||
|
||||
func saveState(db dbm.DB, state State, key []byte) {
|
||||
nextHeight := state.LastBlockHeight + 1
|
||||
saveValidatorsInfo(db, nextHeight, state.LastHeightValidatorsChanged, state.Validators)
|
||||
// If first block, save validators for block 1.
|
||||
if nextHeight == 1 {
|
||||
lastHeightVoteChanged := int64(1) // Due to Tendermint validator set changes being delayed 1 block.
|
||||
saveValidatorsInfo(db, nextHeight, lastHeightVoteChanged, state.Validators)
|
||||
}
|
||||
// Save next validators.
|
||||
saveValidatorsInfo(db, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
|
||||
// Save next consensus params.
|
||||
saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
|
||||
db.SetSync(stateKey, state.Bytes())
|
||||
}
|
||||
|
@ -13,12 +13,12 @@ import (
|
||||
// Validate block
|
||||
|
||||
func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
|
||||
// validate internal consistency
|
||||
// Validate internal consistency.
|
||||
if err := block.ValidateBasic(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate basic info
|
||||
// Validate basic info.
|
||||
if block.ChainID != state.ChainID {
|
||||
return fmt.Errorf("Wrong Block.Header.ChainID. Expected %v, got %v", state.ChainID, block.ChainID)
|
||||
}
|
||||
@ -33,7 +33,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
|
||||
}
|
||||
*/
|
||||
|
||||
// validate prev block info
|
||||
// Validate prev block info.
|
||||
if !block.LastBlockID.Equals(state.LastBlockID) {
|
||||
return fmt.Errorf("Wrong Block.Header.LastBlockID. Expected %v, got %v", state.LastBlockID, block.LastBlockID)
|
||||
}
|
||||
@ -42,7 +42,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
|
||||
return fmt.Errorf("Wrong Block.Header.TotalTxs. Expected %v, got %v", state.LastBlockTotalTx+newTxs, block.TotalTxs)
|
||||
}
|
||||
|
||||
// validate app info
|
||||
// Validate app info
|
||||
if !bytes.Equal(block.AppHash, state.AppHash) {
|
||||
return fmt.Errorf("Wrong Block.Header.AppHash. Expected %X, got %v", state.AppHash, block.AppHash)
|
||||
}
|
||||
@ -55,6 +55,9 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
|
||||
if !bytes.Equal(block.ValidatorsHash, state.Validators.Hash()) {
|
||||
return fmt.Errorf("Wrong Block.Header.ValidatorsHash. Expected %X, got %v", state.Validators.Hash(), block.ValidatorsHash)
|
||||
}
|
||||
if !bytes.Equal(block.NextValidatorsHash, state.NextValidators.Hash()) {
|
||||
return fmt.Errorf("Wrong Block.Header.NextValidatorsHash. Expected %X, got %v", state.NextValidators.Hash(), block.NextValidatorsHash)
|
||||
}
|
||||
|
||||
// Validate block LastCommit.
|
||||
if block.Height == 1 {
|
||||
@ -73,6 +76,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate all evidence.
|
||||
// TODO: Each check requires loading an old validator set.
|
||||
// We should cap the amount of evidence per block
|
||||
// to prevent potential proposer DoS.
|
||||
|
@ -197,6 +197,7 @@ type Header struct {
|
||||
|
||||
// hashes from the app output from the prev block
|
||||
ValidatorsHash cmn.HexBytes `json:"validators_hash"` // validators for the current block
|
||||
NextValidatorsHash cmn.HexBytes `json:"next_validators_hash"` // validators for the next block
|
||||
ConsensusHash cmn.HexBytes `json:"consensus_hash"` // consensus params for current block
|
||||
AppHash cmn.HexBytes `json:"app_hash"` // state after txs from the previous block
|
||||
LastResultsHash cmn.HexBytes `json:"last_results_hash"` // root hash of all results from the txs from the previous block
|
||||
@ -223,6 +224,7 @@ func (h *Header) Hash() cmn.HexBytes {
|
||||
"LastCommit": aminoHasher(h.LastCommitHash),
|
||||
"Data": aminoHasher(h.DataHash),
|
||||
"Validators": aminoHasher(h.ValidatorsHash),
|
||||
"NextValidators": aminoHasher(h.NextValidatorsHash),
|
||||
"App": aminoHasher(h.AppHash),
|
||||
"Consensus": aminoHasher(h.ConsensusHash),
|
||||
"Results": aminoHasher(h.LastResultsHash),
|
||||
@ -245,6 +247,7 @@ func (h *Header) StringIndented(indent string) string {
|
||||
%s LastCommit: %v
|
||||
%s Data: %v
|
||||
%s Validators: %v
|
||||
%s NextValidators: %v
|
||||
%s App: %v
|
||||
%s Consensus: %v
|
||||
%s Results: %v
|
||||
@ -259,6 +262,7 @@ func (h *Header) StringIndented(indent string) string {
|
||||
indent, h.LastCommitHash,
|
||||
indent, h.DataHash,
|
||||
indent, h.ValidatorsHash,
|
||||
indent, h.NextValidatorsHash,
|
||||
indent, h.AppHash,
|
||||
indent, h.ConsensusHash,
|
||||
indent, h.LastResultsHash,
|
||||
|
@ -46,7 +46,14 @@ func NewValidatorSet(vals []*Validator) *ValidatorSet {
|
||||
return vs
|
||||
}
|
||||
|
||||
// incrementAccum and update the proposer
|
||||
// Increment Accum and update the proposer on a copy, and return it.
|
||||
func (valSet *ValidatorSet) CopyIncrementAccum(times int) *ValidatorSet {
|
||||
copy := valSet.Copy()
|
||||
copy.IncrementAccum(times)
|
||||
return copy
|
||||
}
|
||||
|
||||
// Increment Accum and update the proposer.
|
||||
func (valSet *ValidatorSet) IncrementAccum(times int) {
|
||||
// Add VotingPower * times to each validator and order into heap.
|
||||
validatorsHeap := cmn.NewHeap()
|
||||
|
Loading…
x
Reference in New Issue
Block a user