- 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 {
- if len(vals.Validators) == 0 {
- return nil
- }
- 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 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.VerifyBytes(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 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.VerifyBytes(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 check
- if trustLevel.Denominator == 0 {
- return errors.New("trustLevel has zero Denominator")
- }
-
- 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(), trustLevel.Numerator)
- if overflow {
- return errors.New("int64 overflow while calculating voting power needed. please provide smaller trustLevel numerator")
- }
- votingPowerNeeded := totalVotingPowerMulByNumerator / 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.VerifyBytes(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}
- }
-
- //-----------------
-
- // 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)
- }
-
- //----------------
-
- func (vals *ValidatorSet) String() string {
- return vals.StringIndented("")
- }
-
- // StringIndented returns an intended string representation of ValidatorSet.
- 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 == nil {
- return nil, errors.New("nil validator set") // validator set should never be nil
- }
- if err := vals.ValidateBasic(); err != nil {
- return nil, fmt.Errorf("validator set failed basic: %w", err)
- }
- 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, 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
- }
|