diff --git a/docs/app-development.rst b/docs/app-development.rst index cbb39fa34..18477eda7 100644 --- a/docs/app-development.rst +++ b/docs/app-development.rst @@ -409,11 +409,10 @@ to update the validator set. To add a new validator or update an existing one, simply include them in the list returned in the EndBlock response. To remove one, include it in the list with a ``power`` equal to ``0``. Tendermint core will take care of updating the validator set. Note the change in voting power -must be strictly less than 1/3 per block. Otherwise it will be impossible for a -light client to prove the transition externally. See the `light client docs +must be strictly less than 1/3 per block if you want a light client to be able +to prove the transition externally. See the `light client docs `__ -for details on how it tracks validators. Tendermint core will fail with an -error if the change in voting power is more or equal than 1/3. +for details on how it tracks validators. .. container:: toggle diff --git a/state/execution.go b/state/execution.go index 8d54840a6..9dde52174 100644 --- a/state/execution.go +++ b/state/execution.go @@ -1,7 +1,6 @@ package state import ( - "errors" "fmt" fail "github.com/ebuchman/fail-test" @@ -238,18 +237,10 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus, return abciResponses, nil } +// If more or equal than 1/3 of total voting power changed in one block, then +// a light client could never prove the transition externally. See +// ./lite/doc.go for details on how a light client tracks validators. func updateValidators(currentSet *types.ValidatorSet, updates []abci.Validator) error { - // If more or equal than 1/3 of total voting power changed in one block, then - // a light client could never prove the transition externally. See - // ./lite/doc.go for details on how a light client tracks validators. - vp23, err := changeInVotingPowerMoreOrEqualToOneThird(currentSet, updates) - if err != nil { - return err - } - if vp23 { - return errors.New("the change in voting power must be strictly less than 1/3") - } - for _, v := range updates { pubkey, err := crypto.PubKeyFromBytes(v.PubKey) // NOTE: expects go-wire encoded pubkey if err != nil { @@ -288,42 +279,6 @@ func updateValidators(currentSet *types.ValidatorSet, updates []abci.Validator) return nil } -func changeInVotingPowerMoreOrEqualToOneThird(currentSet *types.ValidatorSet, updates []abci.Validator) (bool, error) { - threshold := currentSet.TotalVotingPower() * 1 / 3 - acc := int64(0) - - for _, v := range updates { - pubkey, err := crypto.PubKeyFromBytes(v.PubKey) // NOTE: expects go-wire encoded pubkey - if err != nil { - return false, err - } - - address := pubkey.Address() - power := int64(v.Power) - // mind the overflow from int64 - if power < 0 { - return false, fmt.Errorf("Power (%d) overflows int64", v.Power) - } - - _, val := currentSet.GetByAddress(address) - if val == nil { - acc += power - } else { - np := val.VotingPower - power - if np < 0 { - np = -np - } - acc += np - } - - if acc >= threshold { - return true, nil - } - } - - return false, nil -} - // updateState returns a new State updated according to the header and responses. func updateState(s State, blockID types.BlockID, header *types.Header, abciResponses *ABCIResponses) (State, error) { diff --git a/state/state_test.go b/state/state_test.go index 76b713aee..02c94253e 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -375,73 +375,6 @@ func makeParams(blockBytes, blockTx, blockGas, txBytes, } } -func TestLessThanOneThirdOfVotingPowerPerBlockEnforced(t *testing.T) { - testCases := []struct { - initialValSetSize int - shouldErr bool - valUpdatesFn func(vals *types.ValidatorSet) []abci.Validator - }{ - ///////////// 1 val (vp: 10) => less than 3 is ok //////////////////////// - // adding 1 validator => 10 - 0: {1, false, func(vals *types.ValidatorSet) []abci.Validator { - return []abci.Validator{ - {PubKey: pk(), Power: 2}, - } - }}, - 1: {1, true, func(vals *types.ValidatorSet) []abci.Validator { - return []abci.Validator{ - {PubKey: pk(), Power: 3}, - } - }}, - 2: {1, true, func(vals *types.ValidatorSet) []abci.Validator { - return []abci.Validator{ - {PubKey: pk(), Power: 100}, - } - }}, - - ///////////// 3 val (vp: 30) => less than 10 is ok //////////////////////// - // adding and removing validator => 20 - 3: {3, true, func(vals *types.ValidatorSet) []abci.Validator { - _, firstVal := vals.GetByIndex(0) - return []abci.Validator{ - {PubKey: firstVal.PubKey.Bytes(), Power: 0}, - {PubKey: pk(), Power: 10}, - } - }}, - // adding 1 validator => 10 - 4: {3, true, func(vals *types.ValidatorSet) []abci.Validator { - return []abci.Validator{ - {PubKey: pk(), Power: 10}, - } - }}, - // adding 2 validators => 8 - 5: {3, false, func(vals *types.ValidatorSet) []abci.Validator { - return []abci.Validator{ - {PubKey: pk(), Power: 4}, - {PubKey: pk(), Power: 4}, - } - }}, - } - - for i, tc := range testCases { - tearDown, stateDB, state := setupTestCase(t) - state.Validators = genValSet(tc.initialValSetSize) - SaveState(stateDB, state) - height := state.LastBlockHeight + 1 - block := makeBlock(state, height) - abciResponses := &ABCIResponses{ - EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: tc.valUpdatesFn(state.Validators)}, - } - state, err := updateState(state, types.BlockID{block.Hash(), types.PartSetHeader{}}, block.Header, abciResponses) - if tc.shouldErr { - assert.Error(t, err, "#%d", i) - } else { - assert.NoError(t, err, "#%d", i) - } - tearDown(t) - } -} - func pk() []byte { return crypto.GenPrivKeyEd25519().PubKey().Bytes() }