diff --git a/Makefile b/Makefile index 079c58f90..0f7578c0b 100644 --- a/Makefile +++ b/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) ######################################## diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 498a857b9..70af588a0 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -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) diff --git a/consensus/replay.go b/consensus/replay.go index f681828cf..751730613 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -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) diff --git a/consensus/state.go b/consensus/state.go index 5d6842a81..a12345d75 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -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 diff --git a/consensus/state_test.go b/consensus/state_test.go index d0def6309..ece70dd5d 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -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() diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 019076234..4b3e35816 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -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{ diff --git a/rpc/core/status.go b/rpc/core/status.go index 2c54d0a94..5738685b6 100644 --- a/rpc/core/status.go +++ b/rpc/core/status.go @@ -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 diff --git a/state/execution.go b/state/execution.go index 0d6ee81bf..3dbd3bf27 100644 --- a/state/execution.go +++ b/state/execution.go @@ -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, diff --git a/state/state.go b/state/state.go index 3bc08dae3..0891f8370 100644 --- a/state/state.go +++ b/state/state.go @@ -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, diff --git a/state/state_test.go b/state/state_test.go index 30a87fb05..ae70cc106 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -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 -} diff --git a/state/store.go b/state/store.go index 798932541..040bc9fdd 100644 --- a/state/store.go +++ b/state/store.go @@ -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()) } diff --git a/state/validation.go b/state/validation.go index 84a4cc824..76c1a1ec5 100644 --- a/state/validation.go +++ b/state/validation.go @@ -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. diff --git a/types/block.go b/types/block.go index 6adc0c4c4..e72b5fc7b 100644 --- a/types/block.go +++ b/types/block.go @@ -196,10 +196,11 @@ type Header struct { DataHash cmn.HexBytes `json:"data_hash"` // transactions // 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 EvidenceHash cmn.HexBytes `json:"evidence_hash"` // evidence included in the block @@ -214,19 +215,20 @@ func (h *Header) Hash() cmn.HexBytes { return nil } 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 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, diff --git a/types/validator_set.go b/types/validator_set.go index f2fac2929..8f085090e 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -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() @@ -387,7 +394,7 @@ func (valSet *ValidatorSet) StringIndented(indent string) string { %s}`, indent, valSet.GetProposer().String(), indent, - indent, strings.Join(valStrings, "\n"+indent+" "), + indent, strings.Join(valStrings, "\n"+indent+" "), indent) }