mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 14:52:17 +00:00
change voting power change, not number of vals
This commit is contained in:
parent
cf0b5d3715
commit
0093f9877a
@ -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++ {
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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…
x
Reference in New Issue
Block a user