package types import ( "bytes" "fmt" "math" "math/big" "sort" "strings" "github.com/pkg/errors" "github.com/tendermint/tendermint/crypto/merkle" ) // 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. // PriorityWindowSizeFactor - is a constant that when multiplied with the total voting power gives // the maximum allowed distance between validator priorities. const ( MaxTotalVotingPower = int64(math.MaxInt64) / 8 PriorityWindowSizeFactor = 2 ) // 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 .Address, so the indices are fixed // for all rounds of a given blockchain height - ie. the validators // are sorted by their address. // 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: %s", err)) } if len(valz) > 0 { vals.IncrementProposerPriority(1) } return vals } // Nil or empty validator sets are invalid. func (vals *ValidatorSet) IsNilOrEmpty() bool { return vals == nil || len(vals.Validators) == 0 } // Increment ProposerPriority and update the proposer on a copy, and return it. func (vals *ValidatorSet) CopyIncrementProposerPriority(times int) *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 int) { 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 := 0; i < times; i++ { proposer = vals.incrementProposerPriority() } vals.Proposer = proposer } 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 } else { 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 { idx := sort.Search(len(vals.Validators), func(i int) bool { return bytes.Compare(address, vals.Validators[i].Address) <= 0 }) return idx < len(vals.Validators) && bytes.Equal(vals.Validators[idx].Address, address) } // GetByAddress returns an index of the validator with address and validator // itself if found. Otherwise, -1 and nil are returned. func (vals *ValidatorSet) GetByAddress(address []byte) (index int, val *Validator) { idx := sort.Search(len(vals.Validators), func(i int) bool { return bytes.Compare(address, vals.Validators[i].Address) <= 0 }) if idx < len(vals.Validators) && bytes.Equal(vals.Validators[idx].Address, address) { return idx, vals.Validators[idx].Copy() } return -1, nil } // GetByIndex returns the validator's address and validator itself 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 int) (address []byte, val *Validator) { if index < 0 || 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) } // Force recalculation of the set's total voting power. 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.SimpleHashFromByteSlices(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 } if valUpdate.VotingPower < 0 { err = fmt.Errorf("voting power can't be negative: %v", valUpdate) return nil, nil, err } if valUpdate.VotingPower > MaxTotalVotingPower { err = fmt.Errorf("to prevent clipping/ overflow, voting power can't be higher than %v: %v ", MaxTotalVotingPower, valUpdate) return nil, nil, err } if valUpdate.VotingPower == 0 { removals = append(removals, valUpdate) } else { updates = append(updates, valUpdate) } prevAddr = valUpdate.Address } return updates, removals, err } // 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. // // Returns: // updatedTotalVotingPower - the new total voting power if these updates would be applied // numNewValidators - number of new validators // err - non-nil if the maximum allowed total voting power would be exceeded // // 'updates' should be a list of proper validator changes, i.e. they have been verified // by processChanges for duplicates and invalid values. // No changes are made to the validator set 'vals'. func verifyUpdates( updates []*Validator, vals *ValidatorSet, ) (updatedTotalVotingPower int64, numNewValidators int, err error) { updatedTotalVotingPower = vals.TotalVotingPower() for _, valUpdate := range updates { address := valUpdate.Address _, val := vals.GetByAddress(address) if val == nil { // New validator, add its voting power the the total. updatedTotalVotingPower += valUpdate.VotingPower numNewValidators++ } else { // Updated validator, add the difference in power to the total. updatedTotalVotingPower += valUpdate.VotingPower - val.VotingPower } } overflow := updatedTotalVotingPower > MaxTotalVotingPower if overflow { err = fmt.Errorf( "failed to add/update validator, total voting power would exceed the max allowed %v", MaxTotalVotingPower) return 0, 0, err } return updatedTotalVotingPower, numNewValidators, nil } // 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. // 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 < 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 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) error { for _, valUpdate := range deletes { address := valUpdate.Address _, val := vals.GetByAddress(address) if val == nil { return fmt.Errorf("failed to find validator %X to remove", address) } } if len(deletes) > len(vals.Validators) { panic("more deletes than validators") } return nil } // Removes the validators specified in 'deletes' from validator set 'vals'. // Should not fail as verification has been done before. 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) } // Verify that applying the 'deletes' against 'vals' will not result in error. if err := verifyRemovals(deletes, vals); err != nil { return err } // Verify that applying the 'updates' against 'vals' will not result in error. updatedTotalVotingPower, numNewValidators, err := verifyUpdates(updates, vals) if err != nil { return err } // Check that the resulting set will not be empty. if numNewValidators == 0 && len(vals.Validators) == len(deletes) { return errors.New("applying the validator changes would result in empty set") } // Compute the priorities for updates. computeNewPriorities(updates, vals, updatedTotalVotingPower) // Apply updates and removals. vals.applyUpdates(updates) vals.applyRemovals(deletes) vals.updateTotalVotingPower() // Scale and center. vals.RescalePriorities(PriorityWindowSizeFactor * vals.TotalVotingPower()) vals.shiftByAvgProposerPriority() 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) } // Verify that +2/3 of the set had signed the given signBytes. func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height int64, commit *Commit) error { if err := commit.ValidateBasic(); err != nil { return err } if vals.Size() != len(commit.Precommits) { return NewErrInvalidCommitPrecommits(vals.Size(), len(commit.Precommits)) } 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) for idx, precommit := range commit.Precommits { if precommit == nil { continue // OK, some precommits can be missing. } _, val := vals.GetByIndex(idx) // Validate signature. precommitSignBytes := commit.VoteSignBytes(chainID, idx) if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) { return fmt.Errorf("invalid commit -- invalid signature: %v", precommit) } // Good precommit! if blockID.Equals(precommit.BlockID) { talliedVotingPower += val.VotingPower } // else { // It's OK that the BlockID doesn't match. We include stray // precommits to measure validator availability. // } } if talliedVotingPower > vals.TotalVotingPower()*2/3 { return nil } return errTooMuchChange{talliedVotingPower, vals.TotalVotingPower()*2/3 + 1} } // VerifyFutureCommit will check to see if the set would be valid with a different // validator set. // // vals is the old validator set that we know. Over 2/3 of the power in old // signed this block. // // In Tendermint, 1/3 of the voting power can halt or fork the chain, but 1/3 // can't make arbitrary state transitions. You still need > 2/3 Byzantine to // make arbitrary state transitions. // // To preserve this property in the light client, we also require > 2/3 of the // old vals to sign the future commit at H, that way we preserve the property // that if they weren't being truthful about the validator set at H (block hash // -> vals hash) or about the app state (block hash -> app hash) we can slash // > 2/3. Otherwise, the lite client isn't providing the same security // guarantees. // // Even if we added a slashing condition that if you sign a block header with // the wrong validator set, then we would only need > 1/3 of signatures from // the old vals on the new commit, it wouldn't be sufficient because the new // vals can be arbitrary and commit some arbitrary app hash. // // newSet is the validator set that signed this block. Only votes from new are // sufficient for 2/3 majority in the new set as well, for it to be a valid // commit. // // NOTE: This doesn't check whether the commit is a future commit, because the // current height isn't part of the ValidatorSet. Caller must check that the // commit height is greater than the height for this validator set. func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID string, blockID BlockID, height int64, commit *Commit) error { oldVals := vals // Commit must be a valid commit for newSet. err := newSet.VerifyCommit(chainID, blockID, height, commit) if err != nil { return err } // Check old voting power. oldVotingPower := int64(0) seen := map[int]bool{} round := commit.Round() for idx, precommit := range commit.Precommits { if precommit == nil { continue } if precommit.Height != height { return errors.Errorf("blocks don't match - %d vs %d", round, precommit.Round) } if precommit.Round != round { return errors.Errorf("invalid commit -- wrong round: %v vs %v", round, precommit.Round) } if precommit.Type != PrecommitType { return errors.Errorf("invalid commit -- not precommit @ index %v", idx) } // See if this validator is in oldVals. oldIdx, val := oldVals.GetByAddress(precommit.ValidatorAddress) if val == nil || seen[oldIdx] { continue // missing or double vote... } seen[oldIdx] = true // Validate signature. precommitSignBytes := commit.VoteSignBytes(chainID, idx) if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) { return errors.Errorf("invalid commit -- invalid signature: %v", precommit) } // Good precommit! if blockID.Equals(precommit.BlockID) { oldVotingPower += val.VotingPower } // else { // It's OK that the BlockID doesn't match. We include stray // precommits to measure validator availability. // } } if oldVotingPower <= oldVals.TotalVotingPower()*2/3 { return errTooMuchChange{oldVotingPower, oldVals.TotalVotingPower()*2/3 + 1} } return nil } //----------------- // ErrTooMuchChange func IsErrTooMuchChange(err error) bool { _, ok := errors.Cause(err).(errTooMuchChange) return ok } type errTooMuchChange struct { got int64 needed int64 } func (e errTooMuchChange) Error() string { return fmt.Sprintf("Invalid commit -- insufficient old voting power: got %v, needed %v", e.got, e.needed) } //---------------- func (vals *ValidatorSet) String() string { return vals.StringIndented("") } // 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) } //------------------------------------- // Implements sort for sorting validators by address. // Sort validators by address. 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) { it := valz[i] valz[i] = valz[j] valz[j] = it } //---------------------------------------- // for testing // RandValidatorSet returns a randomized validator set, useful for testing. // NOTE: PrivValidator are in order. // UNSTABLE func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []PrivValidator) { 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 } vals := NewValidatorSet(valz) sort.Sort(PrivValidatorsByAddress(privValidators)) return vals, privValidators } /////////////////////////////////////////////////////////////////////////////// // safe addition/subtraction 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 }