mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-19 06:31:57 +00:00
Initial version of new API
This commit is contained in:
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user