diff --git a/consensus/common_test.go b/consensus/common_test.go index d7c090aa2..297b842e9 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -262,7 +262,6 @@ func randConsensusNet(nValidators int) []*ConsensusState { thisConfig := tendermint_test.ResetConfig(Fmt("consensus_reactor_test_%d", i)) EnsureDir(thisConfig.GetString("cs_wal_dir"), 0700) // dir for wal css[i] = newConsensusStateWithConfig(thisConfig, state, privVals[i], counter.NewCounterApplication(true)) - css[i].SetPrivValidatorIndex(i) } return css } diff --git a/consensus/state.go b/consensus/state.go index 185ad658e..4a70f5b92 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -226,8 +226,8 @@ type ConsensusState struct { blockStore *bc.BlockStore mempool *mempl.Mempool - privValidator PrivValidator - privValidatorIndex int // TODO: update if validator set changes + privValidator PrivValidator // for signing votes + privValidatorIndex int // cached index; updated if validators added/removed to validator set mtx sync.Mutex RoundState @@ -320,13 +320,7 @@ func (cs *ConsensusState) SetPrivValidator(priv PrivValidator) { cs.mtx.Lock() defer cs.mtx.Unlock() cs.privValidator = priv -} - -// Caches the index of our privValidator in the validator set to use when voting -func (cs *ConsensusState) SetPrivValidatorIndex(index int) { - cs.mtx.Lock() - defer cs.mtx.Unlock() - cs.privValidatorIndex = index + cs.setPrivValidatorIndex() } func (cs *ConsensusState) LoadCommit(height int) *types.Commit { @@ -585,10 +579,24 @@ func (cs *ConsensusState) updateToState(state *sm.State) { cs.state = state + if cs.state.ValidatorAddedOrRemoved() { + cs.setPrivValidatorIndex() + } + // Finally, broadcast RoundState cs.newStep() } +func (cs *ConsensusState) setPrivValidatorIndex() { + // TODO: just return -1 for not found + valIdx, val := cs.state.Validators.GetByAddress(cs.privValidator.GetAddress()) + if val == nil { + cs.privValidatorIndex = -1 + } else { + cs.privValidatorIndex = valIdx + } +} + func (cs *ConsensusState) newStep() { rs := cs.RoundStateEvent() cs.wal.Save(rs) diff --git a/node/node.go b/node/node.go index 8dbf2fac9..dcde7faca 100644 --- a/node/node.go +++ b/node/node.go @@ -103,13 +103,6 @@ func NewNode(config cfg.Config, privValidator *types.PrivValidator, clientCreato consensusState := consensus.NewConsensusState(config, state.Copy(), proxyApp.Consensus(), blockStore, mempool) if privValidator != nil { consensusState.SetPrivValidator(privValidator) - // TODO: just return -1 for not found - valIdx, val := state.Validators.GetByAddress(privValidator.GetAddress()) - if val == nil { - consensusState.SetPrivValidatorIndex(-1) - } else { - consensusState.SetPrivValidatorIndex(valIdx) - } } consensusReactor := consensus.NewConsensusReactor(consensusState, fastSync) diff --git a/state/execution.go b/state/execution.go index daf32af9d..6822b3aa2 100644 --- a/state/execution.go +++ b/state/execution.go @@ -43,7 +43,8 @@ func (s *State) ExecBlock(eventCache types.Fireable, proxyAppConn proxy.AppConnC } // update the validator set - if err := updateValidators(nextValSet, changedValidators); err != nil { + s.valAddedOrRemoved, err = updateValidators(nextValSet, changedValidators) + if err != nil { log.Warn("Error changing validator set", "error", err) // TODO: err or carry on? } @@ -131,20 +132,22 @@ func execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn proxy.AppConnCo return changedValidators, nil } -func updateValidators(validators *types.ValidatorSet, changedValidators []*tmsp.Validator) error { +func updateValidators(validators *types.ValidatorSet, changedValidators []*tmsp.Validator) (bool, error) { // TODO: prevent change of 1/3+ at once + var addedOrRemoved bool + for _, v := range changedValidators { pubkey, err := crypto.PubKeyFromBytes(v.PubKey) // NOTE: expects go-wire encoded pubkey if err != nil { - return err + return false, err } address := pubkey.Address() power := int64(v.Power) // mind the overflow from uint64 if power < 0 { - return errors.New(Fmt("Power (%d) overflows int64", v.Power)) + return false, errors.New(Fmt("Power (%d) overflows int64", v.Power)) } _, val := validators.GetByAddress(address) @@ -152,24 +155,26 @@ func updateValidators(validators *types.ValidatorSet, changedValidators []*tmsp. // add val added := validators.Add(types.NewValidator(pubkey, power)) if !added { - return errors.New(Fmt("Failed to add new validator %X with voting power %d", address, power)) + return false, errors.New(Fmt("Failed to add new validator %X with voting power %d", address, power)) } + addedOrRemoved = true } else if v.Power == 0 { // remove val _, removed := validators.Remove(address) if !removed { - return errors.New(Fmt("Failed to remove validator %X)")) + return false, errors.New(Fmt("Failed to remove validator %X)")) } + addedOrRemoved = true } else { // update val val.VotingPower = power updated := validators.Update(val) if !updated { - return errors.New(Fmt("Failed to update validator %X with voting power %d", address, power)) + return false, errors.New(Fmt("Failed to update validator %X with voting power %d", address, power)) } } } - return nil + return addedOrRemoved, nil } // return a bit array of validators that signed the last commit diff --git a/state/state.go b/state/state.go index 969df9ff8..980f27986 100644 --- a/state/state.go +++ b/state/state.go @@ -40,6 +40,8 @@ type State struct { // it's stale after ExecBlock and before Commit Stale bool AppHash []byte + + valAddedOrRemoved bool // true if a validator was added or removed } func LoadState(db dbm.DB) *State { @@ -105,6 +107,10 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ s.Stale = true } +func (s *State) ValidatorAddedOrRemoved() bool { + return s.valAddedOrRemoved +} + func (s *State) GetValidators() (*types.ValidatorSet, *types.ValidatorSet) { return s.LastValidators, s.Validators } diff --git a/types/block.go b/types/block.go index 5b145a19b..0a46b2f61 100644 --- a/types/block.go +++ b/types/block.go @@ -155,8 +155,8 @@ type Header struct { NumTxs int `json:"num_txs"` LastBlockID BlockID `json:"last_block_id"` LastCommitHash []byte `json:"last_commit_hash"` // commit from validators from the last block - ValidatorsHash []byte `json:"validators_hash"` // validators for the current block DataHash []byte `json:"data_hash"` // transactions + ValidatorsHash []byte `json:"validators_hash"` // validators for the current block AppHash []byte `json:"app_hash"` // state after txs from the previous block }