Initial version of new API

This commit is contained in:
Zarko Milosevic
2018-12-11 13:08:29 +01:00
parent f799122787
commit 79ddf9d473
2 changed files with 129 additions and 6 deletions
+67
View File
@@ -343,6 +343,73 @@ func validateValidatorUpdates(abciUpdates []abci.ValidatorUpdate,
return nil
}
func NextValidators(currentSet *types.ValidatorSet, updates []*types.Validator) (*types.ValidatorSet, error) {
nValSet := currentSet.Copy()
// update proposer priority
nValSet.UpdateProposerPriority()
for _, valUpdate := range updates {
// should already have been checked
if valUpdate.VotingPower < 0 {
return nil, fmt.Errorf("Voting power can't be negative %v", valUpdate)
}
address := valUpdate.Address
_, val := nValSet.GetByAddress(address)
// valUpdate.VotingPower is ensured to be non-negative in validation method
if valUpdate.VotingPower == 0 { // remove val
_, removed := nValSet.Remove(address)
if !removed {
return nil, fmt.Errorf("Failed to remove validator %X", address)
}
} else if val == nil { // add val
// make sure we do not exceed MaxTotalVotingPower by adding this validator:
totalVotingPower := currentSet.TotalVotingPower()
updatedVotingPower := valUpdate.VotingPower + totalVotingPower
overflow := updatedVotingPower > types.MaxTotalVotingPower || updatedVotingPower < 0
if overflow {
return nil, fmt.Errorf(
"Failed to add new validator %v. Adding it would exceed max allowed total voting power %v",
valUpdate,
types.MaxTotalVotingPower)
}
// TODO: issue #1558 update spec according to the following:
// Set ProposerPriority to -C*totalVotingPower (with C ~= 1.125) to make sure validators can't
// unbond/rebond to reset their (potentially previously negative) ProposerPriority to zero.
//
// Contract: totalVotingPower < MaxTotalVotingPower to ensure ProposerPriority does
// not exceed the bounds of int64.
//
// Compute ProposerPriority = -1.125*totalVotingPower == -(totalVotingPower + (totalVotingPower >> 3)).
valUpdate.ProposerPriority = -(totalVotingPower + (totalVotingPower >> 3))
added := nValSet.Add(valUpdate)
if !added {
return nil, fmt.Errorf("Failed to add new validator %v", valUpdate)
}
} else { // update val
// make sure we do not exceed MaxTotalVotingPower by updating this validator:
totalVotingPower := nValSet.TotalVotingPower()
curVotingPower := val.VotingPower
updatedVotingPower := totalVotingPower - curVotingPower + valUpdate.VotingPower
overflow := updatedVotingPower > types.MaxTotalVotingPower || updatedVotingPower < 0
if overflow {
return nil, fmt.Errorf(
"Failed to update existing validator %v. Updating it would exceed max allowed total voting power %v",
valUpdate,
types.MaxTotalVotingPower)
}
updated := nValSet.Update(valUpdate)
if !updated {
return nil, fmt.Errorf("Failed to update validator %X to %v", address, valUpdate)
}
}
}
return nValSet, 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.
+62 -6
View File
@@ -36,7 +36,9 @@ type ValidatorSet struct {
Proposer *Validator `json:"proposer"`
// cached (unexported)
totalVotingPower int64
totalVotingPower int64
initProposerPriorities []int64
round int
}
// NewValidatorSet initializes a ValidatorSet by copying over the
@@ -44,13 +46,22 @@ type ValidatorSet struct {
// the new ValidatorSet will have an empty list of Validators.
func NewValidatorSet(valz []*Validator) *ValidatorSet {
validators := make([]*Validator, len(valz))
propPriorities := make([]int64, len(valz))
for i, val := range valz {
validators[i] = val.Copy()
}
sort.Sort(ValidatorsByAddress(validators))
vals := &ValidatorSet{
Validators: validators,
for i, val := range validators {
propPriorities[i] = val.ProposerPriority
}
vals := &ValidatorSet{
Validators: validators,
initProposerPriorities: propPriorities,
round: 0,
}
if len(valz) > 0 {
vals.IncrementProposerPriority(1)
}
@@ -70,6 +81,29 @@ func (vals *ValidatorSet) CopyIncrementProposerPriority(times int) *ValidatorSet
return copy
}
// FindProposer computes the proposer of the given round for the validator set.
// The function for a given validator set and round number always return the same validator
// as a proposer, i.e., it is purely functional.
func (vals *ValidatorSet) FindProposer(round int) *Validator {
var proposer *Validator
if round < vals.round {
initialValSet := vals.Copy()
initialValSet.round = 0
for i, val := range initialValSet.Validators {
val.ProposerPriority = initialValSet.initProposerPriorities[i]
}
for i := initialValSet.round; i <= round; i++ {
proposer = vals.updateProposerPriority()
}
} else {
for i := vals.round; i <= round; i++ {
proposer = vals.updateProposerPriority()
}
vals.round = round
}
return proposer
}
// IncrementProposerPriority increments ProposerPriority of each validator and updates the
// proposer. Panics if validator set is empty.
// `times` must be positive.
@@ -93,6 +127,26 @@ func (vals *ValidatorSet) IncrementProposerPriority(times int) {
vals.Proposer = proposer
}
func (vals *ValidatorSet) UpdateProposerPriority() *Validator {
for _, val := range vals.Validators {
// Check for overflow for sum.
val.ProposerPriority = safeAddClip(val.ProposerPriority, val.VotingPower)
}
validatorsHeap := cmn.NewHeap()
// just update the heap
for _, val := range vals.Validators {
validatorsHeap.PushComparable(val, proposerPriorityComparable{val})
}
// Decrement the validator with most ProposerPriority:
mostest := validatorsHeap.Peek().(*Validator)
// mind underflow
mostest.ProposerPriority = safeSubClip(mostest.ProposerPriority, vals.TotalVotingPower())
return mostest
}
func (vals *ValidatorSet) incrementProposerPriority(subAvg bool) *Validator {
for _, val := range vals.Validators {
// Check for overflow for sum.
@@ -255,7 +309,8 @@ func (vals *ValidatorSet) Add(val *Validator) (added bool) {
vals.Validators = append(vals.Validators, val)
// Invalidate cache
vals.Proposer = nil
vals.totalVotingPower = 0
//vals.totalVotingPower = 0
vals.totalVotingPower = vals.totalVotingPower + val.VotingPower
return true
} else if bytes.Equal(vals.Validators[idx].Address, val.Address) {
return false
@@ -267,7 +322,8 @@ func (vals *ValidatorSet) Add(val *Validator) (added bool) {
vals.Validators = newValidators
// Invalidate cache
vals.Proposer = nil
vals.totalVotingPower = 0
//vals.totalVotingPower = 0
vals.totalVotingPower = vals.totalVotingPower + val.VotingPower
return true
}
}
@@ -291,7 +347,7 @@ func (vals *ValidatorSet) Update(val *Validator) (updated bool) {
vals.Validators[index] = val.Copy()
// Invalidate cache
vals.Proposer = nil
vals.totalVotingPower = 0
vals.totalVotingPower = vals.totalVotingPower - sameVal.VotingPower + val.VotingPower
return true
}