544 lines
16 KiB
Go
544 lines
16 KiB
Go
package types
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/tendermint/tendermint/crypto/merkle"
|
|
cmn "github.com/tendermint/tendermint/libs/common"
|
|
)
|
|
|
|
// 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.
|
|
// On the other hand, the .AccumPower of each validator and
|
|
// the designated .GetProposer() of a set changes every round,
|
|
// upon calling .IncrementAccum().
|
|
// 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
|
|
}
|
|
|
|
func NewValidatorSet(valz []*Validator) *ValidatorSet {
|
|
if valz != nil && len(valz) == 0 {
|
|
panic("validator set initialization slice cannot be an empty slice (but it can be nil)")
|
|
}
|
|
validators := make([]*Validator, len(valz))
|
|
for i, val := range valz {
|
|
validators[i] = val.Copy()
|
|
}
|
|
sort.Sort(ValidatorsByAddress(validators))
|
|
vals := &ValidatorSet{
|
|
Validators: validators,
|
|
}
|
|
if len(valz) > 0 {
|
|
vals.IncrementAccum(1)
|
|
}
|
|
|
|
return vals
|
|
}
|
|
|
|
// Nil or empty validator sets are invalid.
|
|
func (vals *ValidatorSet) IsNilOrEmpty() bool {
|
|
return vals == nil || len(vals.Validators) == 0
|
|
}
|
|
|
|
// Increment Accum and update the proposer on a copy, and return it.
|
|
func (vals *ValidatorSet) CopyIncrementAccum(times int) *ValidatorSet {
|
|
copy := vals.Copy()
|
|
copy.IncrementAccum(times)
|
|
return copy
|
|
}
|
|
|
|
// IncrementAccum increments accum of each validator and updates the
|
|
// proposer. Panics if validator set is empty.
|
|
func (vals *ValidatorSet) IncrementAccum(times int) {
|
|
|
|
// Add VotingPower * times to each validator and order into heap.
|
|
validatorsHeap := cmn.NewHeap()
|
|
for _, val := range vals.Validators {
|
|
// Check for overflow both multiplication and sum.
|
|
val.Accum = safeAddClip(val.Accum, safeMulClip(val.VotingPower, int64(times)))
|
|
validatorsHeap.PushComparable(val, accumComparable{val})
|
|
}
|
|
|
|
// Decrement the validator with most accum times times.
|
|
for i := 0; i < times; i++ {
|
|
mostest := validatorsHeap.Peek().(*Validator)
|
|
// mind underflow
|
|
mostest.Accum = safeSubClip(mostest.Accum, vals.TotalVotingPower())
|
|
|
|
if i == times-1 {
|
|
vals.Proposer = mostest
|
|
} else {
|
|
validatorsHeap.Update(mostest, accumComparable{mostest})
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy each validator into a new ValidatorSet
|
|
func (vals *ValidatorSet) Copy() *ValidatorSet {
|
|
validators := make([]*Validator, len(vals.Validators))
|
|
for i, val := range vals.Validators {
|
|
// NOTE: must copy, since IncrementAccum updates in place.
|
|
validators[i] = val.Copy()
|
|
}
|
|
return &ValidatorSet{
|
|
Validators: 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)
|
|
}
|
|
|
|
// TotalVotingPower returns the sum of the voting powers of all validators.
|
|
func (vals *ValidatorSet) TotalVotingPower() int64 {
|
|
if vals.totalVotingPower == 0 {
|
|
for _, val := range vals.Validators {
|
|
// mind overflow
|
|
vals.totalVotingPower = safeAddClip(vals.totalVotingPower, val.VotingPower)
|
|
}
|
|
}
|
|
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.CompareAccum(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
|
|
}
|
|
hashers := make([]merkle.Hasher, len(vals.Validators))
|
|
for i, val := range vals.Validators {
|
|
hashers[i] = val
|
|
}
|
|
return merkle.SimpleHashFromHashers(hashers)
|
|
}
|
|
|
|
// Add adds val to the validator set and returns true. It returns false if val
|
|
// is already in the set.
|
|
func (vals *ValidatorSet) Add(val *Validator) (added bool) {
|
|
val = val.Copy()
|
|
idx := sort.Search(len(vals.Validators), func(i int) bool {
|
|
return bytes.Compare(val.Address, vals.Validators[i].Address) <= 0
|
|
})
|
|
if idx >= len(vals.Validators) {
|
|
vals.Validators = append(vals.Validators, val)
|
|
// Invalidate cache
|
|
vals.Proposer = nil
|
|
vals.totalVotingPower = 0
|
|
return true
|
|
} else if bytes.Equal(vals.Validators[idx].Address, val.Address) {
|
|
return false
|
|
} else {
|
|
newValidators := make([]*Validator, len(vals.Validators)+1)
|
|
copy(newValidators[:idx], vals.Validators[:idx])
|
|
newValidators[idx] = val
|
|
copy(newValidators[idx+1:], vals.Validators[idx:])
|
|
vals.Validators = newValidators
|
|
// Invalidate cache
|
|
vals.Proposer = nil
|
|
vals.totalVotingPower = 0
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Update updates val and returns true. It returns false if val is not present
|
|
// in the set.
|
|
func (vals *ValidatorSet) Update(val *Validator) (updated bool) {
|
|
index, sameVal := vals.GetByAddress(val.Address)
|
|
if sameVal == nil {
|
|
return false
|
|
}
|
|
vals.Validators[index] = val.Copy()
|
|
// Invalidate cache
|
|
vals.Proposer = nil
|
|
vals.totalVotingPower = 0
|
|
return true
|
|
}
|
|
|
|
// Remove deletes the validator with address. It returns the validator removed
|
|
// and true. If returns nil and false if validator is not present in the set.
|
|
func (vals *ValidatorSet) Remove(address []byte) (val *Validator, removed bool) {
|
|
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 nil, false
|
|
}
|
|
removedVal := vals.Validators[idx]
|
|
newValidators := vals.Validators[:idx]
|
|
if idx+1 < len(vals.Validators) {
|
|
newValidators = append(newValidators, vals.Validators[idx+1:]...)
|
|
}
|
|
vals.Validators = newValidators
|
|
// Invalidate cache
|
|
vals.Proposer = nil
|
|
vals.totalVotingPower = 0
|
|
return removedVal, true
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 vals.Size() != len(commit.Precommits) {
|
|
return fmt.Errorf("Invalid commit -- wrong set size: %v vs %v", vals.Size(), len(commit.Precommits))
|
|
}
|
|
if height != commit.Height() {
|
|
return fmt.Errorf("Invalid commit -- wrong height: %v vs %v", 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)
|
|
round := commit.Round()
|
|
|
|
for idx, precommit := range commit.Precommits {
|
|
if precommit == nil {
|
|
continue // OK, some precommits can be missing.
|
|
}
|
|
if precommit.Height != height {
|
|
return fmt.Errorf("Invalid commit -- wrong height: want %v got %v", height, precommit.Height)
|
|
}
|
|
if precommit.Round != round {
|
|
return fmt.Errorf("Invalid commit -- wrong round: want %v got %v", round, precommit.Round)
|
|
}
|
|
if precommit.Type != VoteTypePrecommit {
|
|
return fmt.Errorf("Invalid commit -- not precommit @ index %v", idx)
|
|
}
|
|
_, val := vals.GetByIndex(idx)
|
|
// Validate signature.
|
|
precommitSignBytes := precommit.SignBytes(chainID)
|
|
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 fmt.Errorf("Invalid commit -- insufficient voting power: got %v, needed %v",
|
|
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 cmn.NewError("Blocks don't match - %d vs %d", round, precommit.Round)
|
|
}
|
|
if precommit.Round != round {
|
|
return cmn.NewError("Invalid commit -- wrong round: %v vs %v", round, precommit.Round)
|
|
}
|
|
if precommit.Type != VoteTypePrecommit {
|
|
return cmn.NewError("Invalid commit -- not precommit @ index %v", idx)
|
|
}
|
|
// See if this validator is in oldVals.
|
|
idx, val := oldVals.GetByAddress(precommit.ValidatorAddress)
|
|
if val == nil || seen[idx] {
|
|
continue // missing or double vote...
|
|
}
|
|
seen[idx] = true
|
|
|
|
// Validate signature.
|
|
precommitSignBytes := precommit.SignBytes(chainID)
|
|
if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) {
|
|
return cmn.NewError("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 cmn.NewError("Invalid commit -- insufficient old voting power: got %v, needed %v",
|
|
oldVotingPower, (oldVals.TotalVotingPower()*2/3 + 1))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (vals *ValidatorSet) String() string {
|
|
return vals.StringIndented("")
|
|
}
|
|
|
|
// String
|
|
func (vals *ValidatorSet) StringIndented(indent string) string {
|
|
if vals == nil {
|
|
return "nil-ValidatorSet"
|
|
}
|
|
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
|
|
}
|
|
|
|
//-------------------------------------
|
|
// Use with Heap for sorting validators by accum
|
|
|
|
type accumComparable struct {
|
|
*Validator
|
|
}
|
|
|
|
// We want to find the validator with the greatest accum.
|
|
func (ac accumComparable) Less(o interface{}) bool {
|
|
other := o.(accumComparable).Validator
|
|
larger := ac.CompareAccum(other)
|
|
return bytes.Equal(larger.Address, ac.Address)
|
|
}
|
|
|
|
//----------------------------------------
|
|
// 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 multiplication and addition/subtraction
|
|
|
|
func safeMul(a, b int64) (int64, bool) {
|
|
if a == 0 || b == 0 {
|
|
return 0, false
|
|
}
|
|
if a == 1 {
|
|
return b, false
|
|
}
|
|
if b == 1 {
|
|
return a, false
|
|
}
|
|
if a == math.MinInt64 || b == math.MinInt64 {
|
|
return -1, true
|
|
}
|
|
c := a * b
|
|
return c, c/b != a
|
|
}
|
|
|
|
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 safeMulClip(a, b int64) int64 {
|
|
c, overflow := safeMul(a, b)
|
|
if overflow {
|
|
if (a < 0 || b < 0) && !(a < 0 && b < 0) {
|
|
return math.MinInt64
|
|
}
|
|
return math.MaxInt64
|
|
}
|
|
return c
|
|
}
|
|
|
|
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
|
|
}
|