Browse Source

Delay validator set changes by 1 block.

pull/2296/head
Jae Kwon 7 years ago
parent
commit
a5b7ea93c4
14 changed files with 172 additions and 155 deletions
  1. +2
    -2
      Makefile
  2. +3
    -3
      consensus/reactor_test.go
  3. +5
    -7
      consensus/replay.go
  4. +0
    -1
      consensus/state.go
  5. +4
    -4
      consensus/state_test.go
  6. +1
    -0
      evidence/pool_test.go
  7. +3
    -4
      rpc/core/status.go
  8. +22
    -22
      state/execution.go
  9. +8
    -9
      state/state.go
  10. +78
    -79
      state/state_test.go
  11. +8
    -1
      state/store.go
  12. +8
    -4
      state/validation.go
  13. +21
    -17
      types/block.go
  14. +9
    -2
      types/validator_set.go

+ 2
- 2
Makefile View File

@ -132,11 +132,11 @@ vagrant_test:
### go tests ### go tests
test: test:
@echo "--> Running go test" @echo "--> Running go test"
@go test $(PACKAGES)
@GOCACHE=off go test -p 1 $(PACKAGES)
test_race: test_race:
@echo "--> Running go test --race" @echo "--> Running go test --race"
@go test -v -race $(PACKAGES)
@GOCACHE=off go test -p 1 -v -race $(PACKAGES)
######################################## ########################################


+ 3
- 3
consensus/reactor_test.go View File

@ -174,14 +174,14 @@ func TestReactorRecordsBlockParts(t *testing.T) {
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same") 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) { func TestReactorRecordsVotes(t *testing.T) {
// create dummy peer
// Create dummy peer.
peer := p2pdummy.NewPeer() peer := p2pdummy.NewPeer()
ps := NewPeerState(peer).SetLogger(log.TestingLogger()) ps := NewPeerState(peer).SetLogger(log.TestingLogger())
peer.Set(types.PeerStateKey, ps) peer.Set(types.PeerStateKey, ps)
// create reactor
// Create reactor.
css := randConsensusNet(1, "consensus_reactor_records_votes_test", newMockTickerFunc(true), newPersistentKVStore) css := randConsensusNet(1, "consensus_reactor_records_votes_test", newMockTickerFunc(true), newPersistentKVStore)
reactor := NewConsensusReactor(css[0], false) // so we dont start the consensus states reactor := NewConsensusReactor(css[0], false) // so we dont start the consensus states
reactor.SetEventBus(css[0].eventBus) reactor.SetEventBus(css[0].eventBus)


+ 5
- 7
consensus/replay.go View File

@ -264,15 +264,15 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
stateBlockHeight := state.LastBlockHeight stateBlockHeight := state.LastBlockHeight
h.logger.Info("ABCI Replay Blocks", "appHeight", appBlockHeight, "storeHeight", storeBlockHeight, "stateHeight", stateBlockHeight) 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 { 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) csParams := types.TM2PB.ConsensusParams(h.genDoc.ConsensusParams)
req := abci.RequestInitChain{ req := abci.RequestInitChain{
Time: h.genDoc.GenesisTime.Unix(), // TODO Time: h.genDoc.GenesisTime.Unix(), // TODO
ChainId: h.genDoc.ChainID, ChainId: h.genDoc.ChainID,
ConsensusParams: csParams, ConsensusParams: csParams,
Validators: validators,
Validators: nvals,
AppStateBytes: h.genDoc.AppStateJSON, AppStateBytes: h.genDoc.AppStateJSON,
} }
res, err := proxyApp.Consensus().InitChainSync(req) res, err := proxyApp.Consensus().InitChainSync(req)
@ -280,9 +280,7 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
return nil, err 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 { if len(res.Validators) > 0 {
vals, err := types.PB2TM.Validators(res.Validators) vals, err := types.PB2TM.Validators(res.Validators)
if err != nil { if err != nil {
@ -296,7 +294,7 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
sm.SaveState(h.stateDB, state) 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 { if storeBlockHeight == 0 {
return appHash, checkAppHash(state, appHash) return appHash, checkAppHash(state, appHash)


+ 0
- 1
consensus/state.go View File

@ -74,7 +74,6 @@ type ConsensusState struct {
privValidator types.PrivValidator // for signing votes privValidator types.PrivValidator // for signing votes
// services for creating and executing blocks // services for creating and executing blocks
// TODO: encapsulate all of this in one "BlockManager"
blockExec *sm.BlockExecutor blockExec *sm.BlockExecutor
blockStore sm.BlockStore blockStore sm.BlockStore
mempool sm.Mempool mempool sm.Mempool


+ 4
- 4
consensus/state_test.go View File

@ -64,22 +64,22 @@ func TestStateProposerSelection0(t *testing.T) {
startTestRound(cs1, height, round) startTestRound(cs1, height, round)
// wait for new round so proposer is set
// Wait for new round so proposer is set.
<-newRoundCh <-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() prop := cs1.GetRoundState().Validators.GetProposer()
if !bytes.Equal(prop.Address, cs1.privValidator.GetAddress()) { if !bytes.Equal(prop.Address, cs1.privValidator.GetAddress()) {
t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address) t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address)
} }
// wait for complete proposal
// Wait for complete proposal.
<-proposalCh <-proposalCh
rs := cs1.GetRoundState() rs := cs1.GetRoundState()
signAddVotes(cs1, types.VoteTypePrecommit, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:]...) 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 <-newRoundCh
prop = cs1.GetRoundState().Validators.GetProposer() prop = cs1.GetRoundState().Validators.GetProposer()


+ 1
- 0
evidence/pool_test.go View File

@ -27,6 +27,7 @@ func initializeValidatorState(valAddr []byte, height int64) dbm.DB {
LastBlockHeight: 0, LastBlockHeight: 0,
LastBlockTime: time.Now(), LastBlockTime: time.Now(),
Validators: valSet, Validators: valSet,
NextValidators: valSet.CopyIncrementAccum(1),
LastHeightValidatorsChanged: 1, LastHeightValidatorsChanged: 1,
ConsensusParams: types.ConsensusParams{ ConsensusParams: types.ConsensusParams{
EvidenceParams: types.EvidenceParams{ EvidenceParams: types.EvidenceParams{


+ 3
- 4
rpc/core/status.go View File

@ -109,7 +109,7 @@ func validatorAtHeight(h int64) *types.Validator {
privValAddress := pubKey.Address() 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 { if lastBlockHeight == h {
for _, val := range vals { for _, val := range vals {
if bytes.Equal(val.Address, privValAddress) { 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 { if lastBlockHeight > h {
vals, err := sm.LoadValidators(stateDB, h) vals, err := sm.LoadValidators(stateDB, h)
if err != nil { if err != nil {
// should not happen
return nil
return nil // should not happen
} }
_, val := vals.GetByAddress(privValAddress) _, val := vals.GetByAddress(privValAddress)
return val return val


+ 22
- 22
state/execution.go View File

@ -80,18 +80,18 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b
fail.Fail() // XXX fail.Fail() // XXX
// save the results before we commit
// Save the results before we commit.
saveABCIResponses(blockExec.db, block.Height, abciResponses) saveABCIResponses(blockExec.db, block.Height, abciResponses)
fail.Fail() // XXX 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) state, err = updateState(state, blockID, block.Header, abciResponses)
if err != nil { if err != nil {
return state, fmt.Errorf("Commit failed for application: %v", err) 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) appHash, err := blockExec.Commit(block)
if err != nil { if err != nil {
return state, fmt.Errorf("Commit failed for application: %v", err) 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 fail.Fail() // XXX
// update the app hash and save the state
// Update the app hash and save the state.
state.AppHash = appHash state.AppHash = appHash
SaveState(blockExec.db, state) SaveState(blockExec.db, state)
fail.Fail() // XXX 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 // NOTE: if we crash between Commit and Save, events wont be fired during replay
fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses) fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses)
@ -164,7 +164,7 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus,
txIndex := 0 txIndex := 0
abciResponses := NewABCIResponses(block) abciResponses := NewABCIResponses(block)
// Execute transactions and get hash
// Execute transactions and get hash.
proxyCb := func(req *abci.Request, res *abci.Response) { proxyCb := func(req *abci.Request, res *abci.Response) {
switch r := res.Value.(type) { switch r := res.Value.(type) {
case *abci.Response_DeliverTx: case *abci.Response_DeliverTx:
@ -186,7 +186,7 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus,
signVals, byzVals := getBeginBlockValidatorInfo(block, lastValSet, stateDB) signVals, byzVals := getBeginBlockValidatorInfo(block, lastValSet, stateDB)
// Begin block
// Begin block.
_, err := proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{ _, err := proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{
Hash: block.Hash(), Hash: block.Hash(),
Header: types.TM2PB.Header(block.Header), Header: types.TM2PB.Header(block.Header),
@ -198,7 +198,7 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus,
return nil, err return nil, err
} }
// Run txs of block
// Run txs of block.
for _, tx := range block.Txs { for _, tx := range block.Txs {
proxyAppConn.DeliverTxAsync(tx) proxyAppConn.DeliverTxAsync(tx)
if err := proxyAppConn.Error(); err != nil { 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}) abciResponses.EndBlock, err = proxyAppConn.EndBlockSync(abci.RequestEndBlock{block.Height})
if err != nil { if err != nil {
logger.Error("Error in proxyAppConn.EndBlock", "err", err) 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, func updateState(state State, blockID types.BlockID, header *types.Header,
abciResponses *ABCIResponses) (State, error) { 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 lastHeightValsChanged := state.LastHeightValidatorsChanged
if len(abciResponses.EndBlock.ValidatorUpdates) > 0 { if len(abciResponses.EndBlock.ValidatorUpdates) > 0 {
err := updateValidators(nextValSet, abciResponses.EndBlock.ValidatorUpdates)
err := updateValidators(nValSet, abciResponses.EndBlock.ValidatorUpdates)
if err != nil { if err != nil {
return state, fmt.Errorf("Error changing validator set: %v", err) 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 nextParams := state.ConsensusParams
lastHeightParamsChanged := state.LastHeightConsensusParamsChanged lastHeightParamsChanged := state.LastHeightConsensusParamsChanged
if abciResponses.EndBlock.ConsensusParamUpdates != nil { if abciResponses.EndBlock.ConsensusParamUpdates != nil {
@ -336,7 +335,7 @@ func updateState(state State, blockID types.BlockID, header *types.Header,
if err != nil { if err != nil {
return state, fmt.Errorf("Error updating consensus params: %v", err) 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 lastHeightParamsChanged = header.Height + 1
} }
@ -348,7 +347,8 @@ func updateState(state State, blockID types.BlockID, header *types.Header,
LastBlockTotalTx: state.LastBlockTotalTx + header.NumTxs, LastBlockTotalTx: state.LastBlockTotalTx + header.NumTxs,
LastBlockID: blockID, LastBlockID: blockID,
LastBlockTime: header.Time, LastBlockTime: header.Time,
Validators: nextValSet,
NextValidators: nValSet,
Validators: state.NextValidators.Copy(),
LastValidators: state.Validators.Copy(), LastValidators: state.Validators.Copy(),
LastHeightValidatorsChanged: lastHeightValsChanged, LastHeightValidatorsChanged: lastHeightValsChanged,
ConsensusParams: nextParams, ConsensusParams: nextParams,


+ 8
- 9
state/state.go View File

@ -24,7 +24,7 @@ var (
// Instead, use state.Copy() or state.NextState(...). // Instead, use state.Copy() or state.NextState(...).
// NOTE: not goroutine-safe. // NOTE: not goroutine-safe.
type State struct { type State struct {
// Immutable
// immutable
ChainID string ChainID string
// LastBlockHeight=0 at genesis (ie. block(H=0) does not exist) // 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. // so we can query for historical validator sets.
// Note that if s.LastBlockHeight causes a valset change, // Note that if s.LastBlockHeight causes a valset change,
// we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 // we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1
NextValidators *types.ValidatorSet
Validators *types.ValidatorSet Validators *types.ValidatorSet
LastValidators *types.ValidatorSet LastValidators *types.ValidatorSet
LastHeightValidatorsChanged int64 LastHeightValidatorsChanged int64
@ -50,7 +51,7 @@ type State struct {
// Merkle root of the results from executing prev block // Merkle root of the results from executing prev block
LastResultsHash []byte LastResultsHash []byte
// The latest AppHash we've received from calling abci.Commit()
// the latest AppHash we've received from calling abci.Commit()
AppHash []byte AppHash []byte
} }
@ -64,6 +65,7 @@ func (state State) Copy() State {
LastBlockID: state.LastBlockID, LastBlockID: state.LastBlockID,
LastBlockTime: state.LastBlockTime, LastBlockTime: state.LastBlockTime,
NextValidators: state.NextValidators.Copy(),
Validators: state.Validators.Copy(), Validators: state.Validators.Copy(),
LastValidators: state.LastValidators.Copy(), LastValidators: state.LastValidators.Copy(),
LastHeightValidatorsChanged: state.LastHeightValidatorsChanged, LastHeightValidatorsChanged: state.LastHeightValidatorsChanged,
@ -93,24 +95,20 @@ func (state State) IsEmpty() bool {
return state.Validators == nil // XXX can't compare to Empty 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 // Create a block from the latest state
// MakeBlock builds a block with the given txs and commit from the current 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) { 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) block := types.MakeBlock(height, txs, commit)
// fill header with state data
// Fill header with state data.
block.ChainID = state.ChainID block.ChainID = state.ChainID
block.TotalTxs = state.LastBlockTotalTx + block.NumTxs block.TotalTxs = state.LastBlockTotalTx + block.NumTxs
block.LastBlockID = state.LastBlockID block.LastBlockID = state.LastBlockID
block.ValidatorsHash = state.Validators.Hash() block.ValidatorsHash = state.Validators.Hash()
block.NextValidatorsHash = state.NextValidators.Hash()
block.AppHash = state.AppHash block.AppHash = state.AppHash
block.ConsensusHash = state.ConsensusParams.Hash() block.ConsensusHash = state.ConsensusParams.Hash()
block.LastResultsHash = state.LastResultsHash block.LastResultsHash = state.LastResultsHash
@ -175,6 +173,7 @@ func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) {
LastBlockID: types.BlockID{}, LastBlockID: types.BlockID{},
LastBlockTime: genDoc.GenesisTime, LastBlockTime: genDoc.GenesisTime,
NextValidators: types.NewValidatorSet(validators).CopyIncrementAccum(1),
Validators: types.NewValidatorSet(validators), Validators: types.NewValidatorSet(validators),
LastValidators: types.NewValidatorSet(nil), LastValidators: types.NewValidatorSet(nil),
LastHeightValidatorsChanged: 1, LastHeightValidatorsChanged: 1,


+ 78
- 79
state/state_test.go View File

@ -16,7 +16,7 @@ import (
"github.com/tendermint/tendermint/types" "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) { func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, State) {
config := cfg.ResetTestRoot("state_") config := cfg.ResetTestRoot("state_")
dbType := dbm.DBBackendType(config.DBBackend) dbType := dbm.DBBackendType(config.DBBackend)
@ -72,7 +72,7 @@ func TestABCIResponsesSaveLoad1(t *testing.T) {
state.LastBlockHeight++ state.LastBlockHeight++
// build mock responses
// Build mock responses.
block := makeBlock(state, 2) block := makeBlock(state, 2)
abciResponses := NewABCIResponses(block) abciResponses := NewABCIResponses(block)
abciResponses.DeliverTx[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Tags: nil} abciResponses.DeliverTx[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Tags: nil}
@ -89,7 +89,7 @@ func TestABCIResponsesSaveLoad1(t *testing.T) {
loadedABCIResponses, abciResponses)) loadedABCIResponses, abciResponses))
} }
// TestResultsSaveLoad tests saving and loading abci results.
// TestResultsSaveLoad tests saving and loading ABCI results.
func TestABCIResponsesSaveLoad2(t *testing.T) { func TestABCIResponsesSaveLoad2(t *testing.T) {
tearDown, stateDB, _ := setupTestCase(t) tearDown, stateDB, _ := setupTestCase(t)
defer tearDown(t) defer tearDown(t)
@ -97,8 +97,8 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
cases := [...]struct { 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 added []*abci.ResponseDeliverTx
expected types.ABCIResults 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 { for i := range cases {
h := int64(i + 1) h := int64(i + 1)
res, err := LoadABCIResponses(stateDB, h) res, err := LoadABCIResponses(stateDB, h)
assert.Error(err, "%d: %#v", i, res) assert.Error(err, "%d: %#v", i, res)
} }
// add all cases
// Add all cases.
for i, tc := range cases { for i, tc := range cases {
h := int64(i + 1) // last block height, one below what we save h := int64(i + 1) // last block height, one below what we save
responses := &ABCIResponses{ responses := &ABCIResponses{
@ -149,7 +149,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
saveABCIResponses(stateDB, h, responses) saveABCIResponses(stateDB, h, responses)
} }
// query all before, should return expected value
// Query all before, should return expected value.
for i, tc := range cases { for i, tc := range cases {
h := int64(i + 1) h := int64(i + 1)
res, err := LoadABCIResponses(stateDB, h) res, err := LoadABCIResponses(stateDB, h)
@ -165,34 +165,30 @@ func TestValidatorSimpleSaveLoad(t *testing.T) {
// nolint: vetshadow // nolint: vetshadow
assert := assert.New(t) assert := assert.New(t)
// can't load anything for height 0
// Can't load anything for height 0.
v, err := LoadValidators(stateDB, 0) v, err := LoadValidators(stateDB, 0)
assert.IsType(ErrNoValSetForHeight{}, err, "expected err at height 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) v, err = LoadValidators(stateDB, 1)
assert.Nil(err, "expected no err at height 1") assert.Nil(err, "expected no err at height 1")
assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match") 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++ state.LastBlockHeight++
nextHeight := state.LastBlockHeight + 1 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.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.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. // TestValidatorChangesSaveLoad tests saving and loading a validator set with changes.
@ -200,38 +196,37 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
tearDown, stateDB, state := setupTestCase(t) tearDown, stateDB, state := setupTestCase(t)
defer tearDown(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} changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20}
N := len(changeHeights) 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 highestHeight := changeHeights[N-1] + 5
changeIndex := 0 changeIndex := 0
_, val := state.Validators.GetByIndex(0) _, val := state.Validators.GetByIndex(0)
power := val.VotingPower power := val.VotingPower
var err error var err error
for i := int64(1); i < highestHeight; i++ { 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] { if changeIndex < len(changeHeights) && i == changeHeights[changeIndex] {
changeIndex++ changeIndex++
power++ power++
} }
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, i, power)
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, power)
state, err = updateState(state, blockID, header, responses) state, err = updateState(state, blockID, header, responses)
assert.Nil(t, err) assert.Nil(t, err)
nextHeight := state.LastBlockHeight + 1 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) testCases := make([]int64, highestHeight)
changeIndex = 0 changeIndex = 0
power = val.VotingPower power = val.VotingPower
for i := int64(1); i < highestHeight+1; i++ { 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 { if changeIndex < len(changeHeights) && i == changeHeights[changeIndex]+1 {
changeIndex++ changeIndex++
power++ power++
@ -240,7 +235,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
} }
for i, power := range testCases { 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.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()) assert.Equal(t, v.Size(), 1, "validator set size is greater than 1: %d", v.Size())
_, val := v.GetByIndex(0) _, val := v.GetByIndex(0)
@ -255,25 +250,41 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
func TestManyValidatorChangesSaveLoad(t *testing.T) { func TestManyValidatorChangesSaveLoad(t *testing.T) {
const valSetSize = 7 const valSetSize = 7
tearDown, stateDB, state := setupTestCase(t) tearDown, stateDB, state := setupTestCase(t)
require.Equal(t, int64(0), state.LastBlockHeight)
state.Validators = genValSet(valSetSize) state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementAccum(1)
SaveState(stateDB, state) SaveState(stateDB, state)
defer tearDown(t) 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 var err error
state, err = updateState(state, blockID, header, responses) state, err = updateState(state, blockID, header, responses)
require.Nil(t, err) require.Nil(t, err)
nextHeight := state.LastBlockHeight + 1 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.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) assert.NotNil(t, val)
if index < 0 { if index < 0 {
t.Fatal("expected to find newly added validator") t.Fatal("expected to find newly added validator")
@ -294,12 +305,12 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
tearDown, stateDB, state := setupTestCase(t) tearDown, stateDB, state := setupTestCase(t)
defer tearDown(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} changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20}
N := len(changeHeights) 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 := make([]types.ConsensusParams, N+1)
params[0] = state.ConsensusParams params[0] = state.ConsensusParams
for i := 1; i < N+1; i++ { for i := 1; i < N+1; i++ {
@ -307,20 +318,19 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
params[i].BlockSize.MaxBytes += i 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 highestHeight := changeHeights[N-1] + 5
changeIndex := 0 changeIndex := 0
cp := params[changeIndex] cp := params[changeIndex]
var err error var err error
for i := int64(1); i < highestHeight; i++ { 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] { if changeIndex < len(changeHeights) && i == changeHeights[changeIndex] {
changeIndex++ changeIndex++
cp = params[changeIndex] cp = params[changeIndex]
} }
header, blockID, responses := makeHeaderPartsResponsesParams(state, i, cp)
header, blockID, responses := makeHeaderPartsResponsesParams(state, cp)
state, err = updateState(state, blockID, header, responses) state, err = updateState(state, blockID, header, responses)
require.Nil(t, err) require.Nil(t, err)
@ -328,13 +338,13 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
saveConsensusParamsInfo(stateDB, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams) 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) testCases := make([]paramsChangeTestCase, highestHeight)
changeIndex = 0 changeIndex = 0
cp = params[changeIndex] cp = params[changeIndex]
for i := int64(1); i < highestHeight+1; i++ { 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 { if changeIndex < len(changeHeights) && i == changeHeights[changeIndex]+1 {
changeIndex++ changeIndex++
cp = params[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{ abciResponses := &ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, 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()) { if !bytes.Equal(pubkey.Bytes(), val.PubKey.Bytes()) {
abciResponses.EndBlock = &abci.ResponseEndBlock{ abciResponses.EndBlock = &abci.ResponseEndBlock{
ValidatorUpdates: []abci.Validator{ ValidatorUpdates: []abci.Validator{
@ -441,16 +451,16 @@ func makeHeaderPartsResponsesValPubKeyChange(state State, height int64,
return block.Header, types.BlockID{block.Hash(), types.PartSetHeader{}}, abciResponses 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{ abciResponses := &ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, 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 { if val.VotingPower != power {
abciResponses.EndBlock = &abci.ResponseEndBlock{ abciResponses.EndBlock = &abci.ResponseEndBlock{
ValidatorUpdates: []abci.Validator{ ValidatorUpdates: []abci.Validator{
@ -462,10 +472,10 @@ func makeHeaderPartsResponsesValPowerChange(state State, height int64,
return block.Header, types.BlockID{block.Hash(), types.PartSetHeader{}}, abciResponses 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{ abciResponses := &ABCIResponses{
EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(&params)}, EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(&params)},
} }
@ -476,14 +486,3 @@ type paramsChangeTestCase struct {
height int64 height int64
params types.ConsensusParams 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
}

+ 8
- 1
state/store.go View File

@ -86,7 +86,14 @@ func SaveState(db dbm.DB, state State) {
func saveState(db dbm.DB, state State, key []byte) { func saveState(db dbm.DB, state State, key []byte) {
nextHeight := state.LastBlockHeight + 1 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) saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
db.SetSync(stateKey, state.Bytes()) db.SetSync(stateKey, state.Bytes())
} }


+ 8
- 4
state/validation.go View File

@ -13,12 +13,12 @@ import (
// Validate block // Validate block
func validateBlock(stateDB dbm.DB, state State, block *types.Block) error { func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// validate internal consistency
// Validate internal consistency.
if err := block.ValidateBasic(); err != nil { if err := block.ValidateBasic(); err != nil {
return err return err
} }
// validate basic info
// Validate basic info.
if block.ChainID != state.ChainID { if block.ChainID != state.ChainID {
return fmt.Errorf("Wrong Block.Header.ChainID. Expected %v, got %v", state.ChainID, block.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) { if !block.LastBlockID.Equals(state.LastBlockID) {
return fmt.Errorf("Wrong Block.Header.LastBlockID. Expected %v, got %v", state.LastBlockID, block.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) 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) { if !bytes.Equal(block.AppHash, state.AppHash) {
return fmt.Errorf("Wrong Block.Header.AppHash. Expected %X, got %v", state.AppHash, block.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()) { if !bytes.Equal(block.ValidatorsHash, state.Validators.Hash()) {
return fmt.Errorf("Wrong Block.Header.ValidatorsHash. Expected %X, got %v", state.Validators.Hash(), block.ValidatorsHash) 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. // Validate block LastCommit.
if block.Height == 1 { 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. // TODO: Each check requires loading an old validator set.
// We should cap the amount of evidence per block // We should cap the amount of evidence per block
// to prevent potential proposer DoS. // to prevent potential proposer DoS.


+ 21
- 17
types/block.go View File

@ -196,10 +196,11 @@ type Header struct {
DataHash cmn.HexBytes `json:"data_hash"` // transactions DataHash cmn.HexBytes `json:"data_hash"` // transactions
// hashes from the app output from the prev block // hashes from the app output from the prev block
ValidatorsHash cmn.HexBytes `json:"validators_hash"` // validators for the current 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
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
// consensus info // consensus info
EvidenceHash cmn.HexBytes `json:"evidence_hash"` // evidence included in the block EvidenceHash cmn.HexBytes `json:"evidence_hash"` // evidence included in the block
@ -214,19 +215,20 @@ func (h *Header) Hash() cmn.HexBytes {
return nil return nil
} }
return merkle.SimpleHashFromMap(map[string]merkle.Hasher{ return merkle.SimpleHashFromMap(map[string]merkle.Hasher{
"ChainID": aminoHasher(h.ChainID),
"Height": aminoHasher(h.Height),
"Time": aminoHasher(h.Time),
"NumTxs": aminoHasher(h.NumTxs),
"TotalTxs": aminoHasher(h.TotalTxs),
"LastBlockID": aminoHasher(h.LastBlockID),
"LastCommit": aminoHasher(h.LastCommitHash),
"Data": aminoHasher(h.DataHash),
"Validators": aminoHasher(h.ValidatorsHash),
"App": aminoHasher(h.AppHash),
"Consensus": aminoHasher(h.ConsensusHash),
"Results": aminoHasher(h.LastResultsHash),
"Evidence": aminoHasher(h.EvidenceHash),
"ChainID": aminoHasher(h.ChainID),
"Height": aminoHasher(h.Height),
"Time": aminoHasher(h.Time),
"NumTxs": aminoHasher(h.NumTxs),
"TotalTxs": aminoHasher(h.TotalTxs),
"LastBlockID": aminoHasher(h.LastBlockID),
"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),
"Evidence": aminoHasher(h.EvidenceHash),
}) })
} }
@ -245,6 +247,7 @@ func (h *Header) StringIndented(indent string) string {
%s LastCommit: %v %s LastCommit: %v
%s Data: %v %s Data: %v
%s Validators: %v %s Validators: %v
%s NextValidators: %v
%s App: %v %s App: %v
%s Consensus: %v %s Consensus: %v
%s Results: %v %s Results: %v
@ -259,6 +262,7 @@ func (h *Header) StringIndented(indent string) string {
indent, h.LastCommitHash, indent, h.LastCommitHash,
indent, h.DataHash, indent, h.DataHash,
indent, h.ValidatorsHash, indent, h.ValidatorsHash,
indent, h.NextValidatorsHash,
indent, h.AppHash, indent, h.AppHash,
indent, h.ConsensusHash, indent, h.ConsensusHash,
indent, h.LastResultsHash, indent, h.LastResultsHash,


+ 9
- 2
types/validator_set.go View File

@ -46,7 +46,14 @@ func NewValidatorSet(vals []*Validator) *ValidatorSet {
return vs 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) { func (valSet *ValidatorSet) IncrementAccum(times int) {
// Add VotingPower * times to each validator and order into heap. // Add VotingPower * times to each validator and order into heap.
validatorsHeap := cmn.NewHeap() validatorsHeap := cmn.NewHeap()
@ -387,7 +394,7 @@ func (valSet *ValidatorSet) StringIndented(indent string) string {
%s}`, %s}`,
indent, valSet.GetProposer().String(), indent, valSet.GetProposer().String(),
indent, indent,
indent, strings.Join(valStrings, "\n"+indent+" "),
indent, strings.Join(valStrings, "\n"+indent+" "),
indent) indent)
} }


Loading…
Cancel
Save