Browse Source

Merge pull request #1201 from tendermint/1022-do-not-enforce-1/3-val-changes

do not enforce 1/3 validator power change
pull/1209/head
Ethan Buchman 6 years ago
committed by GitHub
parent
commit
f1c8489270
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 6 additions and 119 deletions
  1. +3
    -4
      docs/app-development.rst
  2. +3
    -48
      state/execution.go
  3. +0
    -67
      state/state_test.go

+ 3
- 4
docs/app-development.rst View File

@ -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
<https://godoc.org/github.com/tendermint/tendermint/lite#hdr-How_We_Track_Validators>`__
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


+ 3
- 48
state/execution.go View File

@ -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) {


+ 0
- 67
state/state_test.go View File

@ -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()
}


Loading…
Cancel
Save