|
|
- package state
-
- import (
- "fmt"
- "io"
- "sort"
- "strings"
-
- . "github.com/tendermint/tendermint/binary"
- "github.com/tendermint/tendermint/merkle"
- )
-
- //-------------------------------------
- // Implements sort for sorting validators by id.
-
- type ValidatorSlice []*Validator
-
- func (vs ValidatorSlice) Len() int {
- return len(vs)
- }
-
- func (vs ValidatorSlice) Less(i, j int) bool {
- return vs[i].Id < vs[j].Id
- }
-
- func (vs ValidatorSlice) Swap(i, j int) {
- it := vs[i]
- vs[i] = vs[j]
- vs[j] = it
- }
-
- //-------------------------------------
-
- // Not goroutine-safe.
- // TODO: consider validator Accum overflow?
- // TODO: replace validators []*Validator with github.com/jaekwon/go-ibbs?
- // NOTE: all get/set to validators should copy the value for safety.
- type ValidatorSet struct {
- validators []*Validator
-
- // cache
- 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(ValidatorSlice(validators))
- return &ValidatorSet{
- validators: validators,
- }
- }
-
- func ReadValidatorSet(r io.Reader, n *int64, err *error) *ValidatorSet {
- size := ReadUVarInt(r, n, err)
- validators := []*Validator{}
- for i := uint(0); i < size; i++ {
- validator := ReadValidator(r, n, err)
- validators = append(validators, validator)
- }
- sort.Sort(ValidatorSlice(validators))
- return NewValidatorSet(validators)
- }
-
- func (vset *ValidatorSet) WriteTo(w io.Writer) (n int64, err error) {
- WriteUVarInt(w, uint(len(vset.validators)), &n, &err)
- vset.Iterate(func(index uint, val *Validator) bool {
- WriteBinary(w, val, &n, &err)
- return false
- })
- return
- }
-
- func (vset *ValidatorSet) IncrementAccum() {
- // Decrement from previous proposer
- oldProposer := vset.Proposer()
- oldProposer.Accum -= int64(vset.TotalVotingPower())
- vset.Update(oldProposer)
- var newProposer *Validator
- // Increment accum and find new proposer
- // NOTE: updates validators in place.
- for _, val := range vset.validators {
- val.Accum += int64(val.VotingPower)
- newProposer = newProposer.CompareAccum(val)
- }
- vset.proposer = newProposer
- }
-
- func (vset *ValidatorSet) Copy() *ValidatorSet {
- validators := make([]*Validator, len(vset.validators))
- for i, val := range vset.validators {
- // NOTE: must copy, since IncrementAccum updates in place.
- validators[i] = val.Copy()
- }
- return &ValidatorSet{
- validators: validators,
- proposer: vset.proposer,
- totalVotingPower: vset.totalVotingPower,
- }
- }
-
- func (vset *ValidatorSet) HasId(id uint64) bool {
- idx := sort.Search(len(vset.validators), func(i int) bool {
- return id <= vset.validators[i].Id
- })
- return idx != len(vset.validators) && vset.validators[idx].Id == id
- }
-
- func (vset *ValidatorSet) GetById(id uint64) (index uint, val *Validator) {
- idx := sort.Search(len(vset.validators), func(i int) bool {
- return id <= vset.validators[i].Id
- })
- if idx != len(vset.validators) && vset.validators[idx].Id == id {
- return uint(idx), vset.validators[idx].Copy()
- } else {
- return 0, nil
- }
- }
-
- func (vset *ValidatorSet) GetByIndex(index uint) (id uint64, val *Validator) {
- val = vset.validators[index]
- return val.Id, val.Copy()
- }
-
- func (vset *ValidatorSet) Size() uint {
- return uint(len(vset.validators))
- }
-
- func (vset *ValidatorSet) TotalVotingPower() uint64 {
- if vset.totalVotingPower == 0 {
- for _, val := range vset.validators {
- vset.totalVotingPower += val.VotingPower
- }
- }
- return vset.totalVotingPower
- }
-
- func (vset *ValidatorSet) Proposer() (proposer *Validator) {
- if vset.proposer == nil {
- for _, val := range vset.validators {
- vset.proposer = vset.proposer.CompareAccum(val)
- }
- }
- return vset.proposer.Copy()
- }
-
- func (vset *ValidatorSet) Hash() []byte {
- if len(vset.validators) == 0 {
- return nil
- }
- hashables := make([]merkle.Hashable, len(vset.validators))
- for i, val := range vset.validators {
- hashables[i] = val
- }
- return merkle.HashFromHashables(hashables)
- }
-
- func (vset *ValidatorSet) Add(val *Validator) (added bool) {
- val = val.Copy()
- idx := sort.Search(len(vset.validators), func(i int) bool {
- return val.Id <= vset.validators[i].Id
- })
- if idx == len(vset.validators) {
- vset.validators = append(vset.validators, val)
- // Invalidate cache
- vset.proposer = nil
- vset.totalVotingPower = 0
- return true
- } else if vset.validators[idx].Id == val.Id {
- return false
- } else {
- newValidators := append(vset.validators[:idx], val)
- newValidators = append(newValidators, vset.validators[idx:]...)
- vset.validators = newValidators
- // Invalidate cache
- vset.proposer = nil
- vset.totalVotingPower = 0
- return true
- }
- }
-
- func (vset *ValidatorSet) Update(val *Validator) (updated bool) {
- index, sameVal := vset.GetById(val.Id)
- if sameVal == nil {
- return false
- } else {
- vset.validators[index] = val.Copy()
- // Invalidate cache
- vset.proposer = nil
- vset.totalVotingPower = 0
- return true
- }
- }
-
- func (vset *ValidatorSet) Remove(id uint64) (val *Validator, removed bool) {
- idx := sort.Search(len(vset.validators), func(i int) bool {
- return id <= vset.validators[i].Id
- })
- if idx == len(vset.validators) || vset.validators[idx].Id != id {
- return nil, false
- } else {
- removedVal := vset.validators[idx]
- newValidators := vset.validators[:idx]
- if idx+1 < len(vset.validators) {
- newValidators = append(newValidators, vset.validators[idx+1:]...)
- }
- vset.validators = newValidators
- // Invalidate cache
- vset.proposer = nil
- vset.totalVotingPower = 0
- return removedVal, true
- }
- }
-
- func (vset *ValidatorSet) Iterate(fn func(index uint, val *Validator) bool) {
- for i, val := range vset.validators {
- stop := fn(uint(i), val.Copy())
- if stop {
- break
- }
- }
- }
-
- func (vset *ValidatorSet) String() string {
- return vset.StringWithIndent("")
- }
-
- func (vset *ValidatorSet) StringWithIndent(indent string) string {
- valStrings := []string{}
- vset.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, vset.Proposer().String(),
- indent,
- indent, strings.Join(valStrings, "\n"+indent+" "),
- indent)
-
- }
|