Browse Source

change voting power change, not number of vals

pull/1004/head
Anton Kaliaev 7 years ago
parent
commit
0093f9877a
No known key found for this signature in database GPG Key ID: 7B6881D965918214
5 changed files with 63 additions and 28 deletions
  1. +1
    -1
      consensus/common_test.go
  2. +3
    -3
      consensus/reactor_test.go
  3. +5
    -4
      docs/app-development.rst
  4. +44
    -11
      state/execution.go
  5. +10
    -9
      state/state_test.go

+ 1
- 1
consensus/common_test.go View File

@ -347,7 +347,7 @@ func consensusLogger() log.Logger {
}
func randConsensusNet(nValidators int, testName string, tickerFunc func() TimeoutTicker, appFunc func() abci.Application, configOpts ...func(*cfg.Config)) []*ConsensusState {
genDoc, privVals := randGenesisDoc(nValidators, false, 10)
genDoc, privVals := randGenesisDoc(nValidators, false, 30)
css := make([]*ConsensusState, nValidators)
logger := consensusLogger()
for i := 0; i < nValidators; i++ {


+ 3
- 3
consensus/reactor_test.go View File

@ -180,7 +180,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
}
updateValidatorTx = dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 100)
updateValidatorTx = dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 26)
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
@ -194,8 +194,8 @@ func TestReactorVotingPowerChange(t *testing.T) {
}
func TestReactorValidatorSetChanges(t *testing.T) {
nPeers := 9
nVals := 6
nPeers := 7
nVals := 4
css := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentDummy)
logger := log.TestingLogger()


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

@ -408,11 +408,12 @@ Additionally, the response may contain a list of validators, which can be used
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 you can not update more than
1/3 of validators in one block because this will make it impossible for a light
client to prove the transition externally. See the `light client docs
will take care of updating the validator set. Note the change in voting power
must be strictly less than 1/3 because otherwise it will be impossible for a
light client 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.
for details on how it tracks validators. Tendermint core will report an error
if that is the case.
.. container:: toggle


+ 44
- 11
state/execution.go View File

@ -122,18 +122,15 @@ func execBlockOnProxyApp(txEventPublisher types.TxEventPublisher, proxyAppConn p
}
func updateValidators(currentSet *types.ValidatorSet, updates []*abci.Validator) error {
// ## prevent update of 1/3+ at once
//
// If more than 1/3 validators 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.
maxUpdates := currentSet.Size() / 3
if maxUpdates == 0 { // if current set size is less than 3
maxUpdates = 1
// 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 len(updates) > maxUpdates {
return errors.New("Can not update more than 1/3 of validators at once")
if vp23 {
return errors.New("the change in voting power must be strictly less than 1/3")
}
for _, v := range updates {
@ -174,6 +171,42 @@ 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
}
// return a bit array of validators that signed the last commit
// NOTE: assumes commits have already been authenticated
/* function is currently unused


+ 10
- 9
state/state_test.go View File

@ -134,7 +134,7 @@ func TestValidatorSimpleSaveLoad(t *testing.T) {
// TestValidatorChangesSaveLoad tests saving and loading a validator set with
// changes.
func TestValidatorChangesSaveLoad(t *testing.T) {
const valSetSize = 6
const valSetSize = 7
tearDown, _, state := setupTestCase(t)
state.Validators = genValSet(valSetSize)
state.Save()
@ -171,16 +171,14 @@ func genValSet(size int) *types.ValidatorSet {
// with changes.
func TestConsensusParamsChangesSaveLoad(t *testing.T) {
tearDown, _, state := setupTestCase(t)
const valSetSize = 20
state.Validators = genValSet(valSetSize)
state.Save()
defer tearDown(t)
// change vals at these heights
changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20}
N := len(changeHeights)
// create list of new vals
// 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++ {
@ -247,18 +245,21 @@ func makeParams(blockBytes, blockTx, blockGas, txBytes,
}
}
func TestLessThanOneThirdOfValidatorUpdatesEnforced(t *testing.T) {
func TestLessThanOneThirdOfVotingPowerPerBlockEnforced(t *testing.T) {
tearDown, _, state := setupTestCase(t)
defer tearDown(t)
height := state.LastBlockHeight + 1
block := makeBlock(state, height)
abciResponses := &ABCIResponses{
Height: height,
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []*abci.Validator{{PubKey: []byte("a"), Power: 10}}},
Height: height,
// 1 val (vp: 10) => less than 3 is ok
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []*abci.Validator{
{PubKey: crypto.GenPrivKeyEd25519().PubKey().Bytes(), Power: 3},
}},
}
err := state.SetBlockAndValidators(block.Header, types.PartSetHeader{}, abciResponses)
assert.NotNil(t, err, "expected err when trying to update more than 1/3 of validators")
assert.Error(t, err)
}
func TestApplyUpdates(t *testing.T) {


Loading…
Cancel
Save