You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

296 lines
8.5 KiB

package state
import (
"bytes"
"errors"
"fmt"
"sort"
"strings"
"github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/merkle"
"github.com/tendermint/tendermint/types"
)
// 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 .Proposer() 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.
// TODO: consider validator Accum overflow
// TODO: replace validators []*Validator with github.com/jaekwon/go-ibbs?
type ValidatorSet struct {
Validators []*Validator // NOTE: persisted via reflect, must be exported.
// cached (unexported)
proposer *Validator
totalVotingPower uint64
}
func NewValidatorSet(vals []*Validator) *ValidatorSet {
validators := make([]*Validator, len(vals))
for i, val := range vals {
validators[i] = val.Copy()
}
sort.Sort(ValidatorsByAddress(validators))
return &ValidatorSet{
Validators: validators,
}
}
// TODO: mind the overflow when times and votingPower shares too large.
func (valSet *ValidatorSet) IncrementAccum(times uint) {
// Add VotingPower * times to each validator and order into heap.
validatorsHeap := NewHeap()
for _, val := range valSet.Validators {
val.Accum += int64(val.VotingPower) * int64(times) // TODO: mind overflow
validatorsHeap.Push(val, accumComparable(val.Accum))
}
// Decrement the validator with most accum, times times.
for i := uint(0); i < times; i++ {
mostest := validatorsHeap.Peek().(*Validator)
if i == times-1 {
valSet.proposer = mostest
}
mostest.Accum -= int64(valSet.TotalVotingPower())
validatorsHeap.Update(mostest, accumComparable(mostest.Accum))
}
}
func (valSet *ValidatorSet) Copy() *ValidatorSet {
validators := make([]*Validator, len(valSet.Validators))
for i, val := range valSet.Validators {
// NOTE: must copy, since IncrementAccum updates in place.
validators[i] = val.Copy()
}
return &ValidatorSet{
Validators: validators,
proposer: valSet.proposer,
totalVotingPower: valSet.totalVotingPower,
}
}
func (valSet *ValidatorSet) HasAddress(address []byte) bool {
idx := sort.Search(len(valSet.Validators), func(i int) bool {
return bytes.Compare(address, valSet.Validators[i].Address) <= 0
})
return idx != len(valSet.Validators) && bytes.Compare(valSet.Validators[idx].Address, address) == 0
}
func (valSet *ValidatorSet) GetByAddress(address []byte) (index uint, val *Validator) {
idx := sort.Search(len(valSet.Validators), func(i int) bool {
return bytes.Compare(address, valSet.Validators[i].Address) <= 0
})
if idx != len(valSet.Validators) && bytes.Compare(valSet.Validators[idx].Address, address) == 0 {
return uint(idx), valSet.Validators[idx].Copy()
} else {
return 0, nil
}
}
func (valSet *ValidatorSet) GetByIndex(index uint) (address []byte, val *Validator) {
val = valSet.Validators[index]
return val.Address, val.Copy()
}
func (valSet *ValidatorSet) Size() uint {
return uint(len(valSet.Validators))
}
func (valSet *ValidatorSet) TotalVotingPower() uint64 {
if valSet.totalVotingPower == 0 {
for _, val := range valSet.Validators {
valSet.totalVotingPower += val.VotingPower
}
}
return valSet.totalVotingPower
}
func (valSet *ValidatorSet) Proposer() (proposer *Validator) {
if valSet.proposer == nil {
for _, val := range valSet.Validators {
valSet.proposer = valSet.proposer.CompareAccum(val)
}
}
return valSet.proposer.Copy()
}
func (valSet *ValidatorSet) Hash() []byte {
if len(valSet.Validators) == 0 {
return nil
}
hashables := make([]merkle.Hashable, len(valSet.Validators))
for i, val := range valSet.Validators {
hashables[i] = val
}
return merkle.SimpleHashFromHashables(hashables)
}
func (valSet *ValidatorSet) Add(val *Validator) (added bool) {
val = val.Copy()
idx := sort.Search(len(valSet.Validators), func(i int) bool {
return bytes.Compare(val.Address, valSet.Validators[i].Address) <= 0
})
if idx == len(valSet.Validators) {
valSet.Validators = append(valSet.Validators, val)
// Invalidate cache
valSet.proposer = nil
valSet.totalVotingPower = 0
return true
} else if bytes.Compare(valSet.Validators[idx].Address, val.Address) == 0 {
return false
} else {
newValidators := make([]*Validator, len(valSet.Validators)+1)
copy(newValidators[:idx], valSet.Validators[:idx])
newValidators[idx] = val
copy(newValidators[idx+1:], valSet.Validators[idx:])
valSet.Validators = newValidators
// Invalidate cache
valSet.proposer = nil
valSet.totalVotingPower = 0
return true
}
}
func (valSet *ValidatorSet) Update(val *Validator) (updated bool) {
index, sameVal := valSet.GetByAddress(val.Address)
if sameVal == nil {
return false
} else {
valSet.Validators[index] = val.Copy()
// Invalidate cache
valSet.proposer = nil
valSet.totalVotingPower = 0
return true
}
}
func (valSet *ValidatorSet) Remove(address []byte) (val *Validator, removed bool) {
idx := sort.Search(len(valSet.Validators), func(i int) bool {
return bytes.Compare(address, valSet.Validators[i].Address) <= 0
})
if idx == len(valSet.Validators) || bytes.Compare(valSet.Validators[idx].Address, address) != 0 {
return nil, false
} else {
removedVal := valSet.Validators[idx]
newValidators := valSet.Validators[:idx]
if idx+1 < len(valSet.Validators) {
newValidators = append(newValidators, valSet.Validators[idx+1:]...)
}
valSet.Validators = newValidators
// Invalidate cache
valSet.proposer = nil
valSet.totalVotingPower = 0
return removedVal, true
}
}
func (valSet *ValidatorSet) Iterate(fn func(index uint, val *Validator) bool) {
for i, val := range valSet.Validators {
stop := fn(uint(i), val.Copy())
if stop {
break
}
}
}
// Verify that +2/3 of the set had signed the given signBytes
func (valSet *ValidatorSet) VerifyValidation(chainID string, hash []byte, parts types.PartSetHeader, height uint, v *types.Validation) error {
if valSet.Size() != uint(len(v.Commits)) {
return errors.New(Fmt("Invalid validation -- wrong set size: %v vs %v",
valSet.Size(), len(v.Commits)))
}
talliedVotingPower := uint64(0)
seenValidators := map[string]struct{}{}
for idx, commit := range v.Commits {
// may be zero, in which case skip.
if commit.Signature.IsZero() {
continue
}
_, val := valSet.GetByIndex(uint(idx))
commitSignBytes := account.SignBytes(chainID, &types.Vote{
Height: height, Round: commit.Round, Type: types.VoteTypeCommit,
BlockHash: hash,
BlockParts: parts,
})
// Validate
if _, seen := seenValidators[string(val.Address)]; seen {
return fmt.Errorf("Duplicate validator for commit %v for Validation %v", commit, v)
}
if !val.PubKey.VerifyBytes(commitSignBytes, commit.Signature) {
return fmt.Errorf("Invalid signature for commit %v for Validation %v", commit, v)
}
// Tally
seenValidators[string(val.Address)] = struct{}{}
talliedVotingPower += val.VotingPower
}
if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
return nil
} else {
return fmt.Errorf("insufficient voting power %v, needed %v",
talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1))
}
}
func (valSet *ValidatorSet) String() string {
return valSet.StringIndented("")
}
func (valSet *ValidatorSet) StringIndented(indent string) string {
valStrings := []string{}
valSet.Iterate(func(index uint, val *Validator) bool {
valStrings = append(valStrings, val.String())
return false
})
return fmt.Sprintf(`ValidatorSet{
%s Proposer: %v
%s Validators:
%s %v
%s}`,
indent, valSet.Proposer().String(),
indent,
indent, strings.Join(valStrings, "\n"+indent+" "),
indent)
}
//-------------------------------------
// Implements sort for sorting validators by address.
type ValidatorsByAddress []*Validator
func (vs ValidatorsByAddress) Len() int {
return len(vs)
}
func (vs ValidatorsByAddress) Less(i, j int) bool {
return bytes.Compare(vs[i].Address, vs[j].Address) == -1
}
func (vs ValidatorsByAddress) Swap(i, j int) {
it := vs[i]
vs[i] = vs[j]
vs[j] = it
}
//-------------------------------------
// Use with Heap for sorting validators by accum
type accumComparable uint64
// We want to find the validator with the greatest accum.
func (ac accumComparable) Less(o interface{}) bool {
return uint64(ac) < uint64(o.(accumComparable))
}