|
package types
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"math/big"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/tendermint/tendermint/crypto/merkle"
|
|
tmmath "github.com/tendermint/tendermint/libs/math"
|
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
)
|
|
|
|
const (
|
|
// MaxTotalVotingPower - the maximum allowed total voting power.
|
|
// It needs to be sufficiently small to, in all cases:
|
|
// 1. prevent clipping in incrementProposerPriority()
|
|
// 2. let (diff+diffMax-1) not overflow in IncrementProposerPriority()
|
|
// (Proof of 1 is tricky, left to the reader).
|
|
// It could be higher, but this is sufficiently large for our purposes,
|
|
// and leaves room for defensive purposes.
|
|
MaxTotalVotingPower = int64(math.MaxInt64) / 8
|
|
|
|
// PriorityWindowSizeFactor - is a constant that when multiplied with the
|
|
// total voting power gives the maximum allowed distance between validator
|
|
// priorities.
|
|
PriorityWindowSizeFactor = 2
|
|
)
|
|
|
|
// ErrTotalVotingPowerOverflow is returned if the total voting power of the
|
|
// resulting validator set exceeds MaxTotalVotingPower.
|
|
var ErrTotalVotingPowerOverflow = fmt.Errorf("total voting power of resulting valset exceeds max %d",
|
|
MaxTotalVotingPower)
|
|
|
|
// ValidatorSet represent a set of *Validator at a given height.
|
|
//
|
|
// The validators can be fetched by address or index.
|
|
// The index is in order of .VotingPower, so the indices are fixed for all
|
|
// rounds of a given blockchain height - ie. the validators are sorted by their
|
|
// voting power (descending). Secondary index - .Address (ascending).
|
|
//
|
|
// On the other hand, the .ProposerPriority of each validator and the
|
|
// designated .GetProposer() of a set changes every round, upon calling
|
|
// .IncrementProposerPriority().
|
|
//
|
|
// NOTE: Not goroutine-safe.
|
|
// NOTE: All get/set to validators should copy the value for safety.
|
|
type ValidatorSet struct {
|
|
// NOTE: persisted via reflect, must be exported.
|
|
Validators []*Validator `json:"validators"`
|
|
Proposer *Validator `json:"proposer"`
|
|
|
|
// cached (unexported)
|
|
totalVotingPower int64
|
|
}
|
|
|
|
// NewValidatorSet initializes a ValidatorSet by copying over the values from
|
|
// `valz`, a list of Validators. If valz is nil or empty, the new ValidatorSet
|
|
// will have an empty list of Validators.
|
|
//
|
|
// The addresses of validators in `valz` must be unique otherwise the function
|
|
// panics.
|
|
//
|
|
// Note the validator set size has an implied limit equal to that of the
|
|
// MaxVotesCount - commits by a validator set larger than this will fail
|
|
// validation.
|
|
func NewValidatorSet(valz []*Validator) *ValidatorSet {
|
|
vals := &ValidatorSet{}
|
|
err := vals.updateWithChangeSet(valz, false)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Cannot create validator set: %v", err))
|
|
}
|
|
if len(valz) > 0 {
|
|
vals.IncrementProposerPriority(1)
|
|
}
|
|
return vals
|
|
}
|
|
|
|
func (vals *ValidatorSet) ValidateBasic() error {
|
|
if vals.IsNilOrEmpty() {
|
|
return errors.New("validator set is nil or empty")
|
|
}
|
|
|
|
for idx, val := range vals.Validators {
|
|
if err := val.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("invalid validator #%d: %w", idx, err)
|
|
}
|
|
}
|
|
|
|
if err := vals.Proposer.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("proposer failed validate basic, error: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsNilOrEmpty returns true if validator set is nil or empty.
|
|
func (vals *ValidatorSet) IsNilOrEmpty() bool {
|
|
return vals == nil || len(vals.Validators) == 0
|
|
}
|
|
|
|
// CopyIncrementProposerPriority increments ProposerPriority and updates the
|
|
// proposer on a copy, and returns it.
|
|
func (vals *ValidatorSet) CopyIncrementProposerPriority(times int32) *ValidatorSet {
|
|
copy := vals.Copy()
|
|
copy.IncrementProposerPriority(times)
|
|
return copy
|
|
}
|
|
|
|
// IncrementProposerPriority increments ProposerPriority of each validator and
|
|
// updates the proposer. Panics if validator set is empty.
|
|
// `times` must be positive.
|
|
func (vals *ValidatorSet) IncrementProposerPriority(times int32) {
|
|
if vals.IsNilOrEmpty() {
|
|
panic("empty validator set")
|
|
}
|
|
if times <= 0 {
|
|
panic("Cannot call IncrementProposerPriority with non-positive times")
|
|
}
|
|
|
|
// Cap the difference between priorities to be proportional to 2*totalPower by
|
|
// re-normalizing priorities, i.e., rescale all priorities by multiplying with:
|
|
// 2*totalVotingPower/(maxPriority - minPriority)
|
|
diffMax := PriorityWindowSizeFactor * vals.TotalVotingPower()
|
|
vals.RescalePriorities(diffMax)
|
|
vals.shiftByAvgProposerPriority()
|
|
|
|
var proposer *Validator
|
|
// Call IncrementProposerPriority(1) times times.
|
|
for i := int32(0); i < times; i++ {
|
|
proposer = vals.incrementProposerPriority()
|
|
}
|
|
|
|
vals.Proposer = proposer
|
|
}
|
|
|
|
// RescalePriorities rescales the priorities such that the distance between the
|
|
// maximum and minimum is smaller than `diffMax`. Panics if validator set is
|
|
// empty.
|
|
func (vals *ValidatorSet) RescalePriorities(diffMax int64) {
|
|
if vals.IsNilOrEmpty() {
|
|
panic("empty validator set")
|
|
}
|
|
// NOTE: This check is merely a sanity check which could be
|
|
// removed if all tests would init. voting power appropriately;
|
|
// i.e. diffMax should always be > 0
|
|
if diffMax <= 0 {
|
|
return
|
|
}
|
|
|
|
// Calculating ceil(diff/diffMax):
|
|
// Re-normalization is performed by dividing by an integer for simplicity.
|
|
// NOTE: This may make debugging priority issues easier as well.
|
|
diff := computeMaxMinPriorityDiff(vals)
|
|
ratio := (diff + diffMax - 1) / diffMax
|
|
if diff > diffMax {
|
|
for _, val := range vals.Validators {
|
|
val.ProposerPriority /= ratio
|
|
}
|
|
}
|
|
}
|
|
|
|
func (vals *ValidatorSet) incrementProposerPriority() *Validator {
|
|
for _, val := range vals.Validators {
|
|
// Check for overflow for sum.
|
|
newPrio := safeAddClip(val.ProposerPriority, val.VotingPower)
|
|
val.ProposerPriority = newPrio
|
|
}
|
|
// Decrement the validator with most ProposerPriority.
|
|
mostest := vals.getValWithMostPriority()
|
|
// Mind the underflow.
|
|
mostest.ProposerPriority = safeSubClip(mostest.ProposerPriority, vals.TotalVotingPower())
|
|
|
|
return mostest
|
|
}
|
|
|
|
// Should not be called on an empty validator set.
|
|
func (vals *ValidatorSet) computeAvgProposerPriority() int64 {
|
|
n := int64(len(vals.Validators))
|
|
sum := big.NewInt(0)
|
|
for _, val := range vals.Validators {
|
|
sum.Add(sum, big.NewInt(val.ProposerPriority))
|
|
}
|
|
avg := sum.Div(sum, big.NewInt(n))
|
|
if avg.IsInt64() {
|
|
return avg.Int64()
|
|
}
|
|
|
|
// This should never happen: each val.ProposerPriority is in bounds of int64.
|
|
panic(fmt.Sprintf("Cannot represent avg ProposerPriority as an int64 %v", avg))
|
|
}
|
|
|
|
// Compute the difference between the max and min ProposerPriority of that set.
|
|
func computeMaxMinPriorityDiff(vals *ValidatorSet) int64 {
|
|
if vals.IsNilOrEmpty() {
|
|
panic("empty validator set")
|
|
}
|
|
max := int64(math.MinInt64)
|
|
min := int64(math.MaxInt64)
|
|
for _, v := range vals.Validators {
|
|
if v.ProposerPriority < min {
|
|
min = v.ProposerPriority
|
|
}
|
|
if v.ProposerPriority > max {
|
|
max = v.ProposerPriority
|
|
}
|
|
}
|
|
diff := max - min
|
|
if diff < 0 {
|
|
return -1 * diff
|
|
}
|
|
return diff
|
|
}
|
|
|
|
func (vals *ValidatorSet) getValWithMostPriority() *Validator {
|
|
var res *Validator
|
|
for _, val := range vals.Validators {
|
|
res = res.CompareProposerPriority(val)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (vals *ValidatorSet) shiftByAvgProposerPriority() {
|
|
if vals.IsNilOrEmpty() {
|
|
panic("empty validator set")
|
|
}
|
|
avgProposerPriority := vals.computeAvgProposerPriority()
|
|
for _, val := range vals.Validators {
|
|
val.ProposerPriority = safeSubClip(val.ProposerPriority, avgProposerPriority)
|
|
}
|
|
}
|
|
|
|
// Makes a copy of the validator list.
|
|
func validatorListCopy(valsList []*Validator) []*Validator {
|
|
if valsList == nil {
|
|
return nil
|
|
}
|
|
valsCopy := make([]*Validator, len(valsList))
|
|
for i, val := range valsList {
|
|
valsCopy[i] = val.Copy()
|
|
}
|
|
return valsCopy
|
|
}
|
|
|
|
// Copy each validator into a new ValidatorSet.
|
|
func (vals *ValidatorSet) Copy() *ValidatorSet {
|
|
return &ValidatorSet{
|
|
Validators: validatorListCopy(vals.Validators),
|
|
Proposer: vals.Proposer,
|
|
totalVotingPower: vals.totalVotingPower,
|
|
}
|
|
}
|
|
|
|
// HasAddress returns true if address given is in the validator set, false -
|
|
// otherwise.
|
|
func (vals *ValidatorSet) HasAddress(address []byte) bool {
|
|
for _, val := range vals.Validators {
|
|
if bytes.Equal(val.Address, address) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetByAddress returns an index of the validator with address and validator
|
|
// itself (copy) if found. Otherwise, -1 and nil are returned.
|
|
func (vals *ValidatorSet) GetByAddress(address []byte) (index int32, val *Validator) {
|
|
for idx, val := range vals.Validators {
|
|
if bytes.Equal(val.Address, address) {
|
|
return int32(idx), val.Copy()
|
|
}
|
|
}
|
|
return -1, nil
|
|
}
|
|
|
|
// GetByIndex returns the validator's address and validator itself (copy) by
|
|
// index.
|
|
// It returns nil values if index is less than 0 or greater or equal to
|
|
// len(ValidatorSet.Validators).
|
|
func (vals *ValidatorSet) GetByIndex(index int32) (address []byte, val *Validator) {
|
|
if index < 0 || int(index) >= len(vals.Validators) {
|
|
return nil, nil
|
|
}
|
|
val = vals.Validators[index]
|
|
return val.Address, val.Copy()
|
|
}
|
|
|
|
// Size returns the length of the validator set.
|
|
func (vals *ValidatorSet) Size() int {
|
|
return len(vals.Validators)
|
|
}
|
|
|
|
// Forces recalculation of the set's total voting power.
|
|
// Panics if total voting power is bigger than MaxTotalVotingPower.
|
|
func (vals *ValidatorSet) updateTotalVotingPower() {
|
|
sum := int64(0)
|
|
for _, val := range vals.Validators {
|
|
// mind overflow
|
|
sum = safeAddClip(sum, val.VotingPower)
|
|
if sum > MaxTotalVotingPower {
|
|
panic(fmt.Sprintf(
|
|
"Total voting power should be guarded to not exceed %v; got: %v",
|
|
MaxTotalVotingPower,
|
|
sum))
|
|
}
|
|
}
|
|
|
|
vals.totalVotingPower = sum
|
|
}
|
|
|
|
// TotalVotingPower returns the sum of the voting powers of all validators.
|
|
// It recomputes the total voting power if required.
|
|
func (vals *ValidatorSet) TotalVotingPower() int64 {
|
|
if vals.totalVotingPower == 0 {
|
|
vals.updateTotalVotingPower()
|
|
}
|
|
return vals.totalVotingPower
|
|
}
|
|
|
|
// GetProposer returns the current proposer. If the validator set is empty, nil
|
|
// is returned.
|
|
func (vals *ValidatorSet) GetProposer() (proposer *Validator) {
|
|
if len(vals.Validators) == 0 {
|
|
return nil
|
|
}
|
|
if vals.Proposer == nil {
|
|
vals.Proposer = vals.findProposer()
|
|
}
|
|
return vals.Proposer.Copy()
|
|
}
|
|
|
|
func (vals *ValidatorSet) findProposer() *Validator {
|
|
var proposer *Validator
|
|
for _, val := range vals.Validators {
|
|
if proposer == nil || !bytes.Equal(val.Address, proposer.Address) {
|
|
proposer = proposer.CompareProposerPriority(val)
|
|
}
|
|
}
|
|
return proposer
|
|
}
|
|
|
|
// Hash returns the Merkle root hash build using validators (as leaves) in the
|
|
// set.
|
|
func (vals *ValidatorSet) Hash() []byte {
|
|
bzs := make([][]byte, len(vals.Validators))
|
|
for i, val := range vals.Validators {
|
|
bzs[i] = val.Bytes()
|
|
}
|
|
return merkle.HashFromByteSlices(bzs)
|
|
}
|
|
|
|
// Iterate will run the given function over the set.
|
|
func (vals *ValidatorSet) Iterate(fn func(index int, val *Validator) bool) {
|
|
for i, val := range vals.Validators {
|
|
stop := fn(i, val.Copy())
|
|
if stop {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Checks changes against duplicates, splits the changes in updates and
|
|
// removals, sorts them by address.
|
|
//
|
|
// Returns:
|
|
// updates, removals - the sorted lists of updates and removals
|
|
// err - non-nil if duplicate entries or entries with negative voting power are seen
|
|
//
|
|
// No changes are made to 'origChanges'.
|
|
func processChanges(origChanges []*Validator) (updates, removals []*Validator, err error) {
|
|
// Make a deep copy of the changes and sort by address.
|
|
changes := validatorListCopy(origChanges)
|
|
sort.Sort(ValidatorsByAddress(changes))
|
|
|
|
removals = make([]*Validator, 0, len(changes))
|
|
updates = make([]*Validator, 0, len(changes))
|
|
var prevAddr Address
|
|
|
|
// Scan changes by address and append valid validators to updates or removals lists.
|
|
for _, valUpdate := range changes {
|
|
if bytes.Equal(valUpdate.Address, prevAddr) {
|
|
err = fmt.Errorf("duplicate entry %v in %v", valUpdate, changes)
|
|
return nil, nil, err
|
|
}
|
|
|
|
switch {
|
|
case valUpdate.VotingPower < 0:
|
|
err = fmt.Errorf("voting power can't be negative: %d", valUpdate.VotingPower)
|
|
return nil, nil, err
|
|
case valUpdate.VotingPower > MaxTotalVotingPower:
|
|
err = fmt.Errorf("to prevent clipping/overflow, voting power can't be higher than %d, got %d",
|
|
MaxTotalVotingPower, valUpdate.VotingPower)
|
|
return nil, nil, err
|
|
case valUpdate.VotingPower == 0:
|
|
removals = append(removals, valUpdate)
|
|
default:
|
|
updates = append(updates, valUpdate)
|
|
}
|
|
|
|
prevAddr = valUpdate.Address
|
|
}
|
|
|
|
return updates, removals, err
|
|
}
|
|
|
|
// verifyUpdates verifies a list of updates against a validator set, making sure the allowed
|
|
// total voting power would not be exceeded if these updates would be applied to the set.
|
|
//
|
|
// Inputs:
|
|
// updates - a list of proper validator changes, i.e. they have been verified by processChanges for duplicates
|
|
// and invalid values.
|
|
// vals - the original validator set. Note that vals is NOT modified by this function.
|
|
// removedPower - the total voting power that will be removed after the updates are verified and applied.
|
|
//
|
|
// Returns:
|
|
// tvpAfterUpdatesBeforeRemovals - the new total voting power if these updates would be applied without the removals.
|
|
// Note that this will be < 2 * MaxTotalVotingPower in case high power validators are removed and
|
|
// validators are added/ updated with high power values.
|
|
//
|
|
// err - non-nil if the maximum allowed total voting power would be exceeded
|
|
func verifyUpdates(
|
|
updates []*Validator,
|
|
vals *ValidatorSet,
|
|
removedPower int64,
|
|
) (tvpAfterUpdatesBeforeRemovals int64, err error) {
|
|
|
|
delta := func(update *Validator, vals *ValidatorSet) int64 {
|
|
_, val := vals.GetByAddress(update.Address)
|
|
if val != nil {
|
|
return update.VotingPower - val.VotingPower
|
|
}
|
|
return update.VotingPower
|
|
}
|
|
|
|
updatesCopy := validatorListCopy(updates)
|
|
sort.Slice(updatesCopy, func(i, j int) bool {
|
|
return delta(updatesCopy[i], vals) < delta(updatesCopy[j], vals)
|
|
})
|
|
|
|
tvpAfterRemovals := vals.TotalVotingPower() - removedPower
|
|
for _, upd := range updatesCopy {
|
|
tvpAfterRemovals += delta(upd, vals)
|
|
if tvpAfterRemovals > MaxTotalVotingPower {
|
|
return 0, ErrTotalVotingPowerOverflow
|
|
}
|
|
}
|
|
return tvpAfterRemovals + removedPower, nil
|
|
}
|
|
|
|
func numNewValidators(updates []*Validator, vals *ValidatorSet) int {
|
|
numNewValidators := 0
|
|
for _, valUpdate := range updates {
|
|
if !vals.HasAddress(valUpdate.Address) {
|
|
numNewValidators++
|
|
}
|
|
}
|
|
return numNewValidators
|
|
}
|
|
|
|
// computeNewPriorities computes the proposer priority for the validators not present in the set based on
|
|
// 'updatedTotalVotingPower'.
|
|
// Leaves unchanged the priorities of validators that are changed.
|
|
//
|
|
// 'updates' parameter must be a list of unique validators to be added or updated.
|
|
//
|
|
// 'updatedTotalVotingPower' is the total voting power of a set where all updates would be applied but
|
|
// not the removals. It must be < 2*MaxTotalVotingPower and may be close to this limit if close to
|
|
// MaxTotalVotingPower will be removed. This is still safe from overflow since MaxTotalVotingPower is maxInt64/8.
|
|
//
|
|
// No changes are made to the validator set 'vals'.
|
|
func computeNewPriorities(updates []*Validator, vals *ValidatorSet, updatedTotalVotingPower int64) {
|
|
for _, valUpdate := range updates {
|
|
address := valUpdate.Address
|
|
_, val := vals.GetByAddress(address)
|
|
if val == nil {
|
|
// add val
|
|
// Set ProposerPriority to -C*totalVotingPower (with C ~= 1.125) to make sure validators can't
|
|
// un-bond and then re-bond to reset their (potentially previously negative) ProposerPriority to zero.
|
|
//
|
|
// Contract: updatedVotingPower < 2 * MaxTotalVotingPower to ensure ProposerPriority does
|
|
// not exceed the bounds of int64.
|
|
//
|
|
// Compute ProposerPriority = -1.125*totalVotingPower == -(updatedVotingPower + (updatedVotingPower >> 3)).
|
|
valUpdate.ProposerPriority = -(updatedTotalVotingPower + (updatedTotalVotingPower >> 3))
|
|
} else {
|
|
valUpdate.ProposerPriority = val.ProposerPriority
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Merges the vals' validator list with the updates list.
|
|
// When two elements with same address are seen, the one from updates is selected.
|
|
// Expects updates to be a list of updates sorted by address with no duplicates or errors,
|
|
// must have been validated with verifyUpdates() and priorities computed with computeNewPriorities().
|
|
func (vals *ValidatorSet) applyUpdates(updates []*Validator) {
|
|
existing := vals.Validators
|
|
sort.Sort(ValidatorsByAddress(existing))
|
|
|
|
merged := make([]*Validator, len(existing)+len(updates))
|
|
i := 0
|
|
|
|
for len(existing) > 0 && len(updates) > 0 {
|
|
if bytes.Compare(existing[0].Address, updates[0].Address) < 0 { // unchanged validator
|
|
merged[i] = existing[0]
|
|
existing = existing[1:]
|
|
} else {
|
|
// Apply add or update.
|
|
merged[i] = updates[0]
|
|
if bytes.Equal(existing[0].Address, updates[0].Address) {
|
|
// Validator is present in both, advance existing.
|
|
existing = existing[1:]
|
|
}
|
|
updates = updates[1:]
|
|
}
|
|
i++
|
|
}
|
|
|
|
// Add the elements which are left.
|
|
for j := 0; j < len(existing); j++ {
|
|
merged[i] = existing[j]
|
|
i++
|
|
}
|
|
// OR add updates which are left.
|
|
for j := 0; j < len(updates); j++ {
|
|
merged[i] = updates[j]
|
|
i++
|
|
}
|
|
|
|
vals.Validators = merged[:i]
|
|
}
|
|
|
|
// Checks that the validators to be removed are part of the validator set.
|
|
// No changes are made to the validator set 'vals'.
|
|
func verifyRemovals(deletes []*Validator, vals *ValidatorSet) (votingPower int64, err error) {
|
|
removedVotingPower := int64(0)
|
|
for _, valUpdate := range deletes {
|
|
address := valUpdate.Address
|
|
_, val := vals.GetByAddress(address)
|
|
if val == nil {
|
|
return removedVotingPower, fmt.Errorf("failed to find validator %X to remove", address)
|
|
}
|
|
removedVotingPower += val.VotingPower
|
|
}
|
|
if len(deletes) > len(vals.Validators) {
|
|
panic("more deletes than validators")
|
|
}
|
|
return removedVotingPower, nil
|
|
}
|
|
|
|
// Removes the validators specified in 'deletes' from validator set 'vals'.
|
|
// Should not fail as verification has been done before.
|
|
// Expects vals to be sorted by address (done by applyUpdates).
|
|
func (vals *ValidatorSet) applyRemovals(deletes []*Validator) {
|
|
existing := vals.Validators
|
|
|
|
merged := make([]*Validator, len(existing)-len(deletes))
|
|
i := 0
|
|
|
|
// Loop over deletes until we removed all of them.
|
|
for len(deletes) > 0 {
|
|
if bytes.Equal(existing[0].Address, deletes[0].Address) {
|
|
deletes = deletes[1:]
|
|
} else { // Leave it in the resulting slice.
|
|
merged[i] = existing[0]
|
|
i++
|
|
}
|
|
existing = existing[1:]
|
|
}
|
|
|
|
// Add the elements which are left.
|
|
for j := 0; j < len(existing); j++ {
|
|
merged[i] = existing[j]
|
|
i++
|
|
}
|
|
|
|
vals.Validators = merged[:i]
|
|
}
|
|
|
|
// Main function used by UpdateWithChangeSet() and NewValidatorSet().
|
|
// If 'allowDeletes' is false then delete operations (identified by validators with voting power 0)
|
|
// are not allowed and will trigger an error if present in 'changes'.
|
|
// The 'allowDeletes' flag is set to false by NewValidatorSet() and to true by UpdateWithChangeSet().
|
|
func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes bool) error {
|
|
if len(changes) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Check for duplicates within changes, split in 'updates' and 'deletes' lists (sorted).
|
|
updates, deletes, err := processChanges(changes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !allowDeletes && len(deletes) != 0 {
|
|
return fmt.Errorf("cannot process validators with voting power 0: %v", deletes)
|
|
}
|
|
|
|
// Check that the resulting set will not be empty.
|
|
if numNewValidators(updates, vals) == 0 && len(vals.Validators) == len(deletes) {
|
|
return errors.New("applying the validator changes would result in empty set")
|
|
}
|
|
|
|
// Verify that applying the 'deletes' against 'vals' will not result in error.
|
|
// Get the voting power that is going to be removed.
|
|
removedVotingPower, err := verifyRemovals(deletes, vals)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Verify that applying the 'updates' against 'vals' will not result in error.
|
|
// Get the updated total voting power before removal. Note that this is < 2 * MaxTotalVotingPower
|
|
tvpAfterUpdatesBeforeRemovals, err := verifyUpdates(updates, vals, removedVotingPower)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Compute the priorities for updates.
|
|
computeNewPriorities(updates, vals, tvpAfterUpdatesBeforeRemovals)
|
|
|
|
// Apply updates and removals.
|
|
vals.applyUpdates(updates)
|
|
vals.applyRemovals(deletes)
|
|
|
|
vals.updateTotalVotingPower() // will panic if total voting power > MaxTotalVotingPower
|
|
|
|
// Scale and center.
|
|
vals.RescalePriorities(PriorityWindowSizeFactor * vals.TotalVotingPower())
|
|
vals.shiftByAvgProposerPriority()
|
|
|
|
sort.Sort(ValidatorsByVotingPower(vals.Validators))
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateWithChangeSet attempts to update the validator set with 'changes'.
|
|
// It performs the following steps:
|
|
// - validates the changes making sure there are no duplicates and splits them in updates and deletes
|
|
// - verifies that applying the changes will not result in errors
|
|
// - computes the total voting power BEFORE removals to ensure that in the next steps the priorities
|
|
// across old and newly added validators are fair
|
|
// - computes the priorities of new validators against the final set
|
|
// - applies the updates against the validator set
|
|
// - applies the removals against the validator set
|
|
// - performs scaling and centering of priority values
|
|
// If an error is detected during verification steps, it is returned and the validator set
|
|
// is not changed.
|
|
func (vals *ValidatorSet) UpdateWithChangeSet(changes []*Validator) error {
|
|
return vals.updateWithChangeSet(changes, true)
|
|
}
|
|
|
|
// VerifyCommit verifies +2/3 of the set had signed the given commit.
|
|
//
|
|
// It checks all the signatures! While it's safe to exit as soon as we have
|
|
// 2/3+ signatures, doing so would impact incentivization logic in the ABCI
|
|
// application that depends on the LastCommitInfo sent in BeginBlock, which
|
|
// includes which validators signed. For instance, Gaia incentivizes proposers
|
|
// with a bonus for including more than +2/3 of the signatures.
|
|
func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID,
|
|
height int64, commit *Commit) error {
|
|
if commit == nil {
|
|
return errors.New("nil commit")
|
|
}
|
|
|
|
if vals.Size() != len(commit.Signatures) {
|
|
return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures))
|
|
}
|
|
|
|
// Validate Height and BlockID.
|
|
if height != commit.Height {
|
|
return NewErrInvalidCommitHeight(height, commit.Height)
|
|
}
|
|
if !blockID.Equals(commit.BlockID) {
|
|
return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v",
|
|
blockID, commit.BlockID)
|
|
}
|
|
|
|
talliedVotingPower := int64(0)
|
|
votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
|
|
for idx, commitSig := range commit.Signatures {
|
|
if commitSig.Absent() {
|
|
continue // OK, some signatures can be absent.
|
|
}
|
|
|
|
// The vals and commit have a 1-to-1 correspondance.
|
|
// This means we don't need the validator address or to do any lookup.
|
|
val := vals.Validators[idx]
|
|
|
|
// Validate signature.
|
|
voteSignBytes := commit.VoteSignBytes(chainID, int32(idx))
|
|
if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) {
|
|
return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature)
|
|
}
|
|
// Good!
|
|
if commitSig.ForBlock() {
|
|
talliedVotingPower += val.VotingPower
|
|
}
|
|
// else {
|
|
// It's OK. We include stray signatures (~votes for nil) to measure
|
|
// validator availability.
|
|
// }
|
|
}
|
|
|
|
if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed {
|
|
return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LIGHT CLIENT VERIFICATION METHODS
|
|
|
|
// VerifyCommitLight verifies +2/3 of the set had signed the given commit.
|
|
//
|
|
// This method is primarily used by the light client and does not check all the
|
|
// signatures.
|
|
func (vals *ValidatorSet) VerifyCommitLight(chainID string, blockID BlockID,
|
|
height int64, commit *Commit) error {
|
|
if commit == nil {
|
|
return errors.New("nil commit")
|
|
}
|
|
|
|
if vals.Size() != len(commit.Signatures) {
|
|
return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures))
|
|
}
|
|
|
|
// Validate Height and BlockID.
|
|
if height != commit.Height {
|
|
return NewErrInvalidCommitHeight(height, commit.Height)
|
|
}
|
|
if !blockID.Equals(commit.BlockID) {
|
|
return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v",
|
|
blockID, commit.BlockID)
|
|
}
|
|
|
|
talliedVotingPower := int64(0)
|
|
votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
|
|
for idx, commitSig := range commit.Signatures {
|
|
// No need to verify absent or nil votes.
|
|
if !commitSig.ForBlock() {
|
|
continue
|
|
}
|
|
|
|
// The vals and commit have a 1-to-1 correspondance.
|
|
// This means we don't need the validator address or to do any lookup.
|
|
val := vals.Validators[idx]
|
|
|
|
// Validate signature.
|
|
voteSignBytes := commit.VoteSignBytes(chainID, int32(idx))
|
|
if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) {
|
|
return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature)
|
|
}
|
|
|
|
talliedVotingPower += val.VotingPower
|
|
|
|
// return as soon as +2/3 of the signatures are verified
|
|
if talliedVotingPower > votingPowerNeeded {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded}
|
|
}
|
|
|
|
// VerifyCommitLightTrusting verifies that trustLevel of the validator set signed
|
|
// this commit.
|
|
//
|
|
// NOTE the given validators do not necessarily correspond to the validator set
|
|
// for this commit, but there may be some intersection.
|
|
//
|
|
// This method is primarily used by the light client and does not check all the
|
|
// signatures.
|
|
func (vals *ValidatorSet) VerifyCommitLightTrusting(chainID string, commit *Commit, trustLevel tmmath.Fraction) error {
|
|
// sanity checks
|
|
if trustLevel.Denominator == 0 {
|
|
return errors.New("trustLevel has zero Denominator")
|
|
}
|
|
if commit == nil {
|
|
return errors.New("nil commit")
|
|
}
|
|
|
|
var (
|
|
talliedVotingPower int64
|
|
seenVals = make(map[int32]int, len(commit.Signatures)) // validator index -> commit index
|
|
)
|
|
|
|
// Safely calculate voting power needed.
|
|
totalVotingPowerMulByNumerator, overflow := safeMul(vals.TotalVotingPower(), int64(trustLevel.Numerator))
|
|
if overflow {
|
|
return errors.New("int64 overflow while calculating voting power needed. please provide smaller trustLevel numerator")
|
|
}
|
|
votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator)
|
|
|
|
for idx, commitSig := range commit.Signatures {
|
|
// No need to verify absent or nil votes.
|
|
if !commitSig.ForBlock() {
|
|
continue
|
|
}
|
|
|
|
// We don't know the validators that committed this block, so we have to
|
|
// check for each vote if its validator is already known.
|
|
valIdx, val := vals.GetByAddress(commitSig.ValidatorAddress)
|
|
|
|
if val != nil {
|
|
// check for double vote of validator on the same commit
|
|
if firstIndex, ok := seenVals[valIdx]; ok {
|
|
secondIndex := idx
|
|
return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex)
|
|
}
|
|
seenVals[valIdx] = idx
|
|
|
|
// Validate signature.
|
|
voteSignBytes := commit.VoteSignBytes(chainID, int32(idx))
|
|
if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) {
|
|
return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature)
|
|
}
|
|
|
|
talliedVotingPower += val.VotingPower
|
|
|
|
if talliedVotingPower > votingPowerNeeded {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded}
|
|
}
|
|
|
|
// findPreviousProposer reverses the compare proposer priority function to find the validator
|
|
// with the lowest proposer priority which would have been the previous proposer.
|
|
//
|
|
// Is used when recreating a validator set from an existing array of validators.
|
|
func (vals *ValidatorSet) findPreviousProposer() *Validator {
|
|
var previousProposer *Validator
|
|
for _, val := range vals.Validators {
|
|
if previousProposer == nil {
|
|
previousProposer = val
|
|
continue
|
|
}
|
|
if previousProposer == previousProposer.CompareProposerPriority(val) {
|
|
previousProposer = val
|
|
}
|
|
}
|
|
return previousProposer
|
|
}
|
|
|
|
//-----------------
|
|
|
|
// IsErrNotEnoughVotingPowerSigned returns true if err is
|
|
// ErrNotEnoughVotingPowerSigned.
|
|
func IsErrNotEnoughVotingPowerSigned(err error) bool {
|
|
return errors.As(err, &ErrNotEnoughVotingPowerSigned{})
|
|
}
|
|
|
|
// ErrNotEnoughVotingPowerSigned is returned when not enough validators signed
|
|
// a commit.
|
|
type ErrNotEnoughVotingPowerSigned struct {
|
|
Got int64
|
|
Needed int64
|
|
}
|
|
|
|
func (e ErrNotEnoughVotingPowerSigned) Error() string {
|
|
return fmt.Sprintf("invalid commit -- insufficient voting power: got %d, needed more than %d", e.Got, e.Needed)
|
|
}
|
|
|
|
//----------------
|
|
|
|
// String returns a string representation of ValidatorSet.
|
|
//
|
|
// See StringIndented.
|
|
func (vals *ValidatorSet) String() string {
|
|
return vals.StringIndented("")
|
|
}
|
|
|
|
// StringIndented returns an intended String.
|
|
//
|
|
// See Validator#String.
|
|
func (vals *ValidatorSet) StringIndented(indent string) string {
|
|
if vals == nil {
|
|
return "nil-ValidatorSet"
|
|
}
|
|
var valStrings []string
|
|
vals.Iterate(func(index int, val *Validator) bool {
|
|
valStrings = append(valStrings, val.String())
|
|
return false
|
|
})
|
|
return fmt.Sprintf(`ValidatorSet{
|
|
%s Proposer: %v
|
|
%s Validators:
|
|
%s %v
|
|
%s}`,
|
|
indent, vals.GetProposer().String(),
|
|
indent,
|
|
indent, strings.Join(valStrings, "\n"+indent+" "),
|
|
indent)
|
|
|
|
}
|
|
|
|
//-------------------------------------
|
|
|
|
// ValidatorsByVotingPower implements sort.Interface for []*Validator based on
|
|
// the VotingPower and Address fields.
|
|
type ValidatorsByVotingPower []*Validator
|
|
|
|
func (valz ValidatorsByVotingPower) Len() int { return len(valz) }
|
|
|
|
func (valz ValidatorsByVotingPower) Less(i, j int) bool {
|
|
if valz[i].VotingPower == valz[j].VotingPower {
|
|
return bytes.Compare(valz[i].Address, valz[j].Address) == -1
|
|
}
|
|
return valz[i].VotingPower > valz[j].VotingPower
|
|
}
|
|
|
|
func (valz ValidatorsByVotingPower) Swap(i, j int) {
|
|
valz[i], valz[j] = valz[j], valz[i]
|
|
}
|
|
|
|
// ValidatorsByAddress implements sort.Interface for []*Validator based on
|
|
// the Address field.
|
|
type ValidatorsByAddress []*Validator
|
|
|
|
func (valz ValidatorsByAddress) Len() int { return len(valz) }
|
|
|
|
func (valz ValidatorsByAddress) Less(i, j int) bool {
|
|
return bytes.Compare(valz[i].Address, valz[j].Address) == -1
|
|
}
|
|
|
|
func (valz ValidatorsByAddress) Swap(i, j int) {
|
|
valz[i], valz[j] = valz[j], valz[i]
|
|
}
|
|
|
|
// ToProto converts ValidatorSet to protobuf
|
|
func (vals *ValidatorSet) ToProto() (*tmproto.ValidatorSet, error) {
|
|
if vals.IsNilOrEmpty() {
|
|
return &tmproto.ValidatorSet{}, nil // validator set should never be nil
|
|
}
|
|
|
|
vp := new(tmproto.ValidatorSet)
|
|
valsProto := make([]*tmproto.Validator, len(vals.Validators))
|
|
for i := 0; i < len(vals.Validators); i++ {
|
|
valp, err := vals.Validators[i].ToProto()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
valsProto[i] = valp
|
|
}
|
|
vp.Validators = valsProto
|
|
|
|
valProposer, err := vals.Proposer.ToProto()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("toProto: validatorSet proposer error: %w", err)
|
|
}
|
|
vp.Proposer = valProposer
|
|
|
|
vp.TotalVotingPower = vals.totalVotingPower
|
|
|
|
return vp, nil
|
|
}
|
|
|
|
// ValidatorSetFromProto sets a protobuf ValidatorSet to the given pointer.
|
|
// It returns an error if any of the validators from the set or the proposer
|
|
// is invalid
|
|
func ValidatorSetFromProto(vp *tmproto.ValidatorSet) (*ValidatorSet, error) {
|
|
if vp == nil {
|
|
return nil, errors.New("nil validator set") // validator set should never be nil, bigger issues are at play if empty
|
|
}
|
|
vals := new(ValidatorSet)
|
|
|
|
valsProto := make([]*Validator, len(vp.Validators))
|
|
for i := 0; i < len(vp.Validators); i++ {
|
|
v, err := ValidatorFromProto(vp.Validators[i])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
valsProto[i] = v
|
|
}
|
|
vals.Validators = valsProto
|
|
|
|
p, err := ValidatorFromProto(vp.GetProposer())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fromProto: validatorSet proposer error: %w", err)
|
|
}
|
|
|
|
vals.Proposer = p
|
|
|
|
vals.totalVotingPower = vp.GetTotalVotingPower()
|
|
|
|
return vals, vals.ValidateBasic()
|
|
}
|
|
|
|
// ValidatorSetFromExistingValidators takes an existing array of validators and rebuilds
|
|
// the exact same validator set that corresponds to it without changing the proposer priority or power
|
|
// if any of the validators fail validate basic then an empty set is returned.
|
|
func ValidatorSetFromExistingValidators(valz []*Validator) (*ValidatorSet, error) {
|
|
for _, val := range valz {
|
|
err := val.ValidateBasic()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("can't create validator set: %w", err)
|
|
}
|
|
}
|
|
vals := &ValidatorSet{
|
|
Validators: valz,
|
|
}
|
|
vals.Proposer = vals.findPreviousProposer()
|
|
vals.updateTotalVotingPower()
|
|
sort.Sort(ValidatorsByVotingPower(vals.Validators))
|
|
return vals, nil
|
|
}
|
|
|
|
//----------------------------------------
|
|
|
|
// RandValidatorSet returns a randomized validator set (size: +numValidators+),
|
|
// where each validator has a voting power of +votingPower+.
|
|
//
|
|
// EXPOSED FOR TESTING.
|
|
func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []PrivValidator) {
|
|
var (
|
|
valz = make([]*Validator, numValidators)
|
|
privValidators = make([]PrivValidator, numValidators)
|
|
)
|
|
|
|
for i := 0; i < numValidators; i++ {
|
|
val, privValidator := RandValidator(false, votingPower)
|
|
valz[i] = val
|
|
privValidators[i] = privValidator
|
|
}
|
|
|
|
sort.Sort(PrivValidatorsByAddress(privValidators))
|
|
|
|
return NewValidatorSet(valz), privValidators
|
|
}
|
|
|
|
// safe addition/subtraction/multiplication
|
|
|
|
func safeAdd(a, b int64) (int64, bool) {
|
|
if b > 0 && a > math.MaxInt64-b {
|
|
return -1, true
|
|
} else if b < 0 && a < math.MinInt64-b {
|
|
return -1, true
|
|
}
|
|
return a + b, false
|
|
}
|
|
|
|
func safeSub(a, b int64) (int64, bool) {
|
|
if b > 0 && a < math.MinInt64+b {
|
|
return -1, true
|
|
} else if b < 0 && a > math.MaxInt64+b {
|
|
return -1, true
|
|
}
|
|
return a - b, false
|
|
}
|
|
|
|
func safeAddClip(a, b int64) int64 {
|
|
c, overflow := safeAdd(a, b)
|
|
if overflow {
|
|
if b < 0 {
|
|
return math.MinInt64
|
|
}
|
|
return math.MaxInt64
|
|
}
|
|
return c
|
|
}
|
|
|
|
func safeSubClip(a, b int64) int64 {
|
|
c, overflow := safeSub(a, b)
|
|
if overflow {
|
|
if b > 0 {
|
|
return math.MinInt64
|
|
}
|
|
return math.MaxInt64
|
|
}
|
|
return c
|
|
}
|
|
|
|
func safeMul(a, b int64) (int64, bool) {
|
|
if a == 0 || b == 0 {
|
|
return 0, false
|
|
}
|
|
|
|
absOfB := b
|
|
if b < 0 {
|
|
absOfB = -b
|
|
}
|
|
|
|
absOfA := a
|
|
if a < 0 {
|
|
absOfA = -a
|
|
}
|
|
|
|
if absOfA > math.MaxInt64/absOfB {
|
|
return 0, true
|
|
}
|
|
|
|
return a * b, false
|
|
}
|