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 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{ 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) { 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 { 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) } 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{ validators: validators, indexToId: indexToId, idToIndex: idToIndex, totalVotingPower: totalVotingPower, } } func (vset *ValidatorSet) IncrementAccum() { totalDelta := int64(0) for _, validator := range vset.validators { validator.Accum += int64(validator.VotingPower) totalDelta += int64(validator.VotingPower) } proposer := vset.GetProposer() proposer.Accum -= totalDelta // NOTE: sum(v) here should be zero. if true { totalAccum := int64(0) for _, validator := range vset.validators { totalAccum += validator.Accum } if totalAccum != 0 { Panicf("Total Accum of validators did not equal 0. Got: ", totalAccum) } } } func (vset *ValidatorSet) Copy() *ValidatorSet { validators := map[uint64]*Validator{} for id, val := range vset.validators { validators[id] = val.Copy() } return &ValidatorSet{ validators: validators, indexToId: vset.indexToId, idToIndex: vset.idToIndex, totalVotingPower: vset.totalVotingPower, } } 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 } func (vset *ValidatorSet) Map() map[uint64]*Validator { return vset.validators } func (vset *ValidatorSet) Size() uint { return uint(len(vset.validators)) } func (vset *ValidatorSet) TotalVotingPower() uint64 { return vset.totalVotingPower } // TODO: cache proposer. invalidate upon increment. func (vset *ValidatorSet) GetProposer() (proposer *Validator) { highestAccum := int64(0) 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 }