Fantom-foundation/go-lachesis

View on GitHub
inter/pos/validators.go

Summary

Maintainability
A
0 mins
Test Coverage
package pos

import (
    "io"
    "math/big"
    "sort"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/rlp"

    "github.com/Fantom-foundation/go-lachesis/inter/idx"
)

type (
    cache struct {
        indexes    map[idx.StakerID]idx.Validator
        stakes     []Stake
        ids        []idx.StakerID
        totalStake Stake
    }
    // Validators group of an epoch with stakes.
    // Optimized for BFT algorithm calculations.
    // Read-only.
    Validators struct {
        values map[idx.StakerID]Stake
        cache  cache
    }

    // ValidatorsBuilder is a helper to create Validators object
    ValidatorsBuilder map[idx.StakerID]Stake

    // GenesisValidator is helper structure to define genesis validators
    GenesisValidator struct {
        ID      idx.StakerID
        Address common.Address
        Stake   *big.Int
    }

    // GValidators defines genesis validators
    GValidators []GenesisValidator
)

var (
    // EmptyValidators is empty validators group
    EmptyValidators = NewBuilder().Build()
)

// NewBuilder creates new mutable ValidatorsBuilder
func NewBuilder() ValidatorsBuilder {
    return ValidatorsBuilder{}
}

// Set appends item to ValidatorsBuilder object
func (vv ValidatorsBuilder) Set(id idx.StakerID, stake Stake) {
    if stake == 0 {
        delete(vv, id)
    } else {
        vv[id] = stake
    }
}

// Build new read-only Validators object
func (vv ValidatorsBuilder) Build() *Validators {
    return newValidators(vv)
}

// EqualStakeValidators builds new read-only Validators object with equal stakes (for tests)
func EqualStakeValidators(ids []idx.StakerID, stake Stake) *Validators {
    builder := NewBuilder()
    for _, id := range ids {
        builder.Set(id, stake)
    }
    return builder.Build()
}

// ArrayToValidators builds new read-only Validators object from array
func ArrayToValidators(ids []idx.StakerID, stakes []Stake) *Validators {
    builder := NewBuilder()
    for i, id := range ids {
        builder.Set(id, stakes[i])
    }
    return builder.Build()
}

// newValidators builds new read-only Validators object
func newValidators(values ValidatorsBuilder) *Validators {
    valuesCopy := make(ValidatorsBuilder)
    for id, s := range values {
        valuesCopy.Set(id, s)
    }

    vv := &Validators{
        values: valuesCopy,
    }
    vv.cache = vv.calcCaches()
    return vv
}

// Len returns count of validators in Validators objects
func (vv *Validators) Len() int {
    return len(vv.values)
}

// calcCaches calculates internal caches for validators
func (vv *Validators) calcCaches() cache {
    cache := cache{
        indexes: make(map[idx.StakerID]idx.Validator),
        stakes:  make([]Stake, vv.Len()),
        ids:     make([]idx.StakerID, vv.Len()),
    }

    for i, v := range vv.sortedArray() {
        cache.indexes[v.ID] = idx.Validator(i)
        cache.stakes[i] = v.Stake
        cache.ids[i] = v.ID
        cache.totalStake += v.Stake
    }

    return cache
}

// Get returns stake for validator by ID
func (vv *Validators) Get(id idx.StakerID) Stake {
    return vv.values[id]
}

// GetIdx returns index (offset) of validator in the group
func (vv *Validators) GetIdx(id idx.StakerID) idx.Validator {
    return vv.cache.indexes[id]
}

// GetStakeByIdx returns stake for validator by index
func (vv *Validators) GetStakeByIdx(i idx.Validator) Stake {
    return vv.cache.stakes[i]
}

// Exists returns boolean true if address exists in Validators object
func (vv *Validators) Exists(id idx.StakerID) bool {
    _, ok := vv.values[id]
    return ok
}

// IDs returns not sorted ids.
func (vv *Validators) IDs() []idx.StakerID {
    return vv.cache.ids
}

// SortedIDs returns deterministically sorted ids.
// The order is the same as for Idxs().
func (vv *Validators) SortedIDs() []idx.StakerID {
    return vv.cache.ids
}

// SortedStakes returns deterministically sorted stakes.
// The order is the same as for Idxs().
func (vv *Validators) SortedStakes() []Stake {
    return vv.cache.stakes
}

// Idxs gets deterministic total order of validators.
func (vv *Validators) Idxs() map[idx.StakerID]idx.Validator {
    return vv.cache.indexes
}

// sortedArray is sorted by stake and ID
func (vv *Validators) sortedArray() validators {
    array := make(validators, 0, len(vv.values))
    for id, s := range vv.values {
        array = append(array, validator{
            ID:    id,
            Stake: s,
        })
    }
    sort.Sort(array)
    return array
}

// Copy constructs a copy.
func (vv *Validators) Copy() *Validators {
    return newValidators(vv.values)
}

// Builder returns a mutable copy of content
func (vv *Validators) Builder() ValidatorsBuilder {
    return vv.Copy().values
}

// Quorum limit of validators.
func (vv *Validators) Quorum() Stake {
    return vv.TotalStake()*2/3 + 1
}

// TotalStake of validators.
func (vv *Validators) TotalStake() (sum Stake) {
    return vv.cache.totalStake
}

// EncodeRLP is for RLP serialization.
func (vv *Validators) EncodeRLP(w io.Writer) error {
    return rlp.Encode(w, vv.sortedArray())
}

// DecodeRLP is for RLP deserialization.
func (vv *Validators) DecodeRLP(s *rlp.Stream) error {
    var arr []validator
    if err := s.Decode(&arr); err != nil {
        return err
    }

    builder := NewBuilder()
    for _, w := range arr {
        builder.Set(w.ID, w.Stake)
    }
    *vv = *builder.Build()

    return nil
}

// Validators converts GValidators to Validators
func (gv GValidators) Validators() *Validators {
    builder := NewBuilder()
    for _, validator := range gv {
        builder.Set(validator.ID, BalanceToStake(validator.Stake))
    }
    return builder.Build()
}

// TotalStake returns sum of stakes
func (gv GValidators) TotalStake() *big.Int {
    totalStake := new(big.Int)
    for _, validator := range gv {
        totalStake.Add(totalStake, validator.Stake)
    }
    return totalStake
}

// Map converts GValidators to map
func (gv GValidators) Map() map[idx.StakerID]GenesisValidator {
    validators := map[idx.StakerID]GenesisValidator{}
    for _, validator := range gv {
        validators[validator.ID] = validator
    }
    return validators
}

// Addresses returns not sorted genesis addresses
func (gv GValidators) Addresses() []common.Address {
    res := make([]common.Address, 0, len(gv))
    for _, v := range gv {
        res = append(res, v.Address)
    }
    return res
}