tendermint/state/validator.go

158 lines
4.0 KiB
Go
Raw Normal View History

package state
import (
"io"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
)
// Holds state for a Validator at a given height+round.
// Meant to be discarded every round of the consensus protocol.
// TODO consider moving this to another common types package.
type Validator struct {
Account
2014-10-04 19:16:49 -07:00
BondHeight uint32 // TODO: is this needed?
VotingPower uint64
Accum int64
}
// Used to persist the state of ConsensusStateControl.
func ReadValidator(r io.Reader, n *int64, err *error) *Validator {
return &Validator{
2014-10-04 19:16:49 -07:00
Account: ReadAccount(r, n, err),
BondHeight: ReadUInt32(r, n, err),
VotingPower: ReadUInt64(r, n, err),
Accum: ReadInt64(r, n, err),
}
}
// Creates a new copy of the validator so we can mutate accum.
func (v *Validator) Copy() *Validator {
return &Validator{
Account: v.Account,
BondHeight: v.BondHeight,
VotingPower: v.VotingPower,
Accum: v.Accum,
}
}
// Used to persist the state of ConsensusStateControl.
func (v *Validator) WriteTo(w io.Writer) (n int64, err error) {
2014-10-04 19:16:49 -07:00
WriteBinary(w, v.Account, &n, &err)
WriteUInt32(w, v.BondHeight, &n, &err)
WriteUInt64(w, v.VotingPower, &n, &err)
WriteInt64(w, v.Accum, &n, &err)
return
}
//-----------------------------------------------------------------------------
// Not goroutine-safe.
type ValidatorSet struct {
2014-09-14 15:37:32 -07:00
validators map[uint64]*Validator
indexToId map[uint32]uint64 // bitarray index to validator id
idToIndex map[uint64]uint32 // validator id to bitarray index
totalVotingPower uint64
}
func NewValidatorSet(validators map[uint64]*Validator) *ValidatorSet {
if validators == nil {
validators = make(map[uint64]*Validator)
}
2014-09-14 15:37:32 -07:00
ids := []uint64{}
indexToId := map[uint32]uint64{}
idToIndex := map[uint64]uint32{}
totalVotingPower := uint64(0)
for id, val := range validators {
ids = append(ids, id)
totalVotingPower += val.VotingPower
}
UInt64Slice(ids).Sort()
for i, id := range ids {
indexToId[uint32(i)] = id
idToIndex[id] = uint32(i)
}
return &ValidatorSet{
2014-09-14 15:37:32 -07:00
validators: validators,
indexToId: indexToId,
idToIndex: idToIndex,
totalVotingPower: totalVotingPower,
}
}
2014-09-14 15:37:32 -07:00
func (vset *ValidatorSet) IncrementAccum() {
totalDelta := int64(0)
2014-09-14 15:37:32 -07:00
for _, validator := range vset.validators {
validator.Accum += int64(validator.VotingPower)
totalDelta += int64(validator.VotingPower)
}
2014-09-14 15:37:32 -07:00
proposer := vset.GetProposer()
proposer.Accum -= totalDelta
// NOTE: sum(v) here should be zero.
if true {
totalAccum := int64(0)
2014-09-14 15:37:32 -07:00
for _, validator := range vset.validators {
totalAccum += validator.Accum
}
if totalAccum != 0 {
Panicf("Total Accum of validators did not equal 0. Got: ", totalAccum)
}
}
}
2014-09-14 15:37:32 -07:00
func (vset *ValidatorSet) Copy() *ValidatorSet {
validators := map[uint64]*Validator{}
for id, val := range vset.validators {
validators[id] = val.Copy()
}
return &ValidatorSet{
2014-09-14 15:37:32 -07:00
validators: validators,
indexToId: vset.indexToId,
idToIndex: vset.idToIndex,
totalVotingPower: vset.totalVotingPower,
}
}
2014-09-14 15:37:32 -07:00
func (vset *ValidatorSet) GetById(id uint64) *Validator {
return vset.validators[id]
}
func (vset *ValidatorSet) GetIndexById(id uint64) (uint32, bool) {
index, ok := vset.idToIndex[id]
return index, ok
}
func (vset *ValidatorSet) GetIdByIndex(index uint32) (uint64, bool) {
id, ok := vset.indexToId[index]
return id, ok
}
2014-09-14 15:37:32 -07:00
func (vset *ValidatorSet) Map() map[uint64]*Validator {
return vset.validators
}
2014-09-14 15:37:32 -07:00
func (vset *ValidatorSet) Size() uint {
return uint(len(vset.validators))
}
2014-09-14 15:37:32 -07:00
func (vset *ValidatorSet) TotalVotingPower() uint64 {
return vset.totalVotingPower
}
// TODO: cache proposer. invalidate upon increment.
2014-09-14 15:37:32 -07:00
func (vset *ValidatorSet) GetProposer() (proposer *Validator) {
highestAccum := int64(0)
2014-09-14 15:37:32 -07:00
for _, validator := range vset.validators {
if validator.Accum > highestAccum {
highestAccum = validator.Accum
proposer = validator
} else if validator.Accum == highestAccum {
if validator.Id < proposer.Id { // Seniority
proposer = validator
}
}
}
return
}