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 }