func (o *Oracle) CalcEligibility(ctx context.Context, layer types.LayerID, round uint32, committeeSize int,
	id types.NodeID, vrfSig []byte) (uint16, error) {
	n, p, vrfFrac, done, err := o.prepareEligibilityCheck(ctx, layer, round, committeeSize, id, vrfSig)
	if done {
		return 0, err
	}
	defer func() {
		if msg := recover(); msg != nil {
			o.With().Error("panic in calc eligibility",
				layer, layer.GetEpoch(), log.Uint32("round_id", round),
				log.String("msg", fmt.Sprint(msg)),
				log.Int("committee_size", committeeSize),
				log.Int("n", n),
				log.String("p", fmt.Sprintf("%g", p.Float())),
				log.String("vrf_frac", fmt.Sprintf("%g", vrfFrac.Float())),
			)
			o.Panic("%s", msg)
		}
	}()
	o.With().Info("params",
		layer, layer.GetEpoch(), log.Uint32("round_id", round),
		log.Int("committee_size", committeeSize),
		log.Int("n", n),
		log.String("p", fmt.Sprintf("%g", p.Float())),
		log.String("vrf_frac", fmt.Sprintf("%g", vrfFrac.Float())),
	)
	for x := 0; x < n; x++ {
		if fixed.BinCDF(n, p, x).GreaterThan(vrfFrac) {
			return uint16(x), nil
		}
	}
	return uint16(n), nil
}
CalcEligibility calculates the number of eligibilities of ID on the given Layer where msg is the VRF message, sig is
the role proof and assuming commSize as the expected committee size.
package eligibility
import (
	"context"
	"errors"
	"fmt"
	"sync"
	lru "github.com/hashicorp/golang-lru"
	"github.com/spacemeshos/fixed"
	"github.com/spacemeshos/go-spacemesh/common/types"
	eCfg "github.com/spacemeshos/go-spacemesh/hare/eligibility/config"
	"github.com/spacemeshos/go-spacemesh/log"
)
const (
	vrfMsgCacheSize  = 20          
	activesCacheSize = 5           
	maxSupportedN    = 1073741824  
)
type valueProvider interface {
	Value(context.Context, types.EpochID) (uint32, error)
}
type signer interface {
	Sign(msg []byte) []byte
}
type addGet interface {
	Add(key, value interface{}) (evicted bool)
	Get(key interface{}) (value interface{}, ok bool)
}
type atxProvider interface {
	GetMinerWeightsInEpochFromView(targetEpoch types.EpochID, blocks map[types.BlockID]struct{}) (map[string]uint64, error)
	GetEpochAtxs(types.EpochID) ([]types.ATXID, error)
	GetAtxHeader(types.ATXID) (*types.ActivationTxHeader, error)
}
type meshProvider interface {
	LayerContextuallyValidBlocks(context.Context, types.LayerID) (map[types.BlockID]struct{}, error)
}
type verifierFunc = func(pub, msg, sig []byte) bool
type Oracle struct {
	lock           sync.Mutex
	beacon         valueProvider
	atxdb          atxProvider
	meshdb         meshProvider
	vrfSigner      signer
	vrfVerifier    verifierFunc
	layersPerEpoch uint32
	vrfMsgCache    addGet
	activesCache   addGet
	cfg            eCfg.Config
	log.Log
}
func safeLayerRange(
	targetLayer types.LayerID,
	safetyParam,
	layersPerEpoch,
	epochOffset uint32,
) (safeLayerStart, safeLayerEnd types.LayerID) {
	if targetLayer.Uint32() <= safetyParam {
		return types.GetEffectiveGenesis(), types.GetEffectiveGenesis()
	}
	safeLayer := targetLayer.Sub(safetyParam)
	safeEpoch := safeLayer.GetEpoch()
	safeLayerStart = safeEpoch.FirstLayer()
	safeLayerEnd = safeLayerStart.Add(epochOffset)
	if safeLayer.Before(safeLayerEnd) {
		if safeLayerStart.Uint32() <= layersPerEpoch {
			return types.GetEffectiveGenesis(), types.GetEffectiveGenesis()
		}
		safeLayerStart = safeLayerStart.Sub(layersPerEpoch)
		safeLayerEnd = safeLayerEnd.Sub(layersPerEpoch)
	}
	if !safeLayerStart.After(types.GetEffectiveGenesis()) {
		return types.GetEffectiveGenesis(), types.GetEffectiveGenesis()
	}
	return
}
func New(
	beacon valueProvider,
	atxdb atxProvider,
	meshdb meshProvider,
	vrfVerifier verifierFunc,
	vrfSigner signer,
	layersPerEpoch uint32,
	cfg eCfg.Config,
	logger log.Log) *Oracle {
	vmc, err := lru.New(vrfMsgCacheSize)
	if err != nil {
		logger.With().Panic("could not create lru cache", log.Err(err))
	}
	ac, err := lru.New(activesCacheSize)
	if err != nil {
		logger.With().Panic("could not create lru cache", log.Err(err))
	}
	return &Oracle{
		beacon:         beacon,
		atxdb:          atxdb,
		meshdb:         meshdb,
		vrfVerifier:    vrfVerifier,
		vrfSigner:      vrfSigner,
		layersPerEpoch: layersPerEpoch,
		vrfMsgCache:    vmc,
		activesCache:   ac,
		cfg:            cfg,
		Log:            logger,
	}
}
func (o *Oracle) GetEpochBeacon(ctx context.Context, epoch types.EpochID) bool {
	_, err := o.beacon.Value(ctx, epoch)
	return err == nil
}
type vrfMessage struct {
	Beacon uint32
	Round  uint32
	Layer  types.LayerID
}
func buildKey(l types.LayerID, r uint32) [2]uint64 {
	return [2]uint64{uint64(l.Uint32()), uint64(r)}
}
func (o *Oracle) buildVRFMessage(ctx context.Context, layer types.LayerID, round uint32) ([]byte, error) {
	key := buildKey(layer, round)
	o.lock.Lock()
	defer o.lock.Unlock()
	if val, exist := o.vrfMsgCache.Get(key); exist {
		return val.([]byte), nil
	}
	v, err := o.beacon.Value(ctx, layer.GetEpoch())
	if err != nil {
		o.WithContext(ctx).With().Error("could not get hare beacon value for epoch",
			log.Err(err),
			layer,
			layer.GetEpoch(),
			log.Uint32("round", round))
		return nil, fmt.Errorf("get beacon: %w", err)
	}
	msg := vrfMessage{Beacon: v, Round: round, Layer: layer}
	buf, err := types.InterfaceToBytes(&msg)
	if err != nil {
		o.WithContext(ctx).With().Panic("failed to encode", log.Err(err))
	}
	o.vrfMsgCache.Add(key, buf)
	return buf, nil
}
func (o *Oracle) totalWeight(ctx context.Context, layer types.LayerID) (uint64, error) {
	actives, err := o.actives(ctx, layer)
	if err != nil {
		o.WithContext(ctx).With().Error("totalWeight erred while calling actives func", log.Err(err), layer)
		return 0, err
	}
	var totalWeight uint64
	for _, w := range actives {
		totalWeight += w
	}
	return totalWeight, nil
}
func (o *Oracle) minerWeight(ctx context.Context, layer types.LayerID, id types.NodeID) (uint64, error) {
	actives, err := o.actives(ctx, layer)
	if err != nil {
		o.With().Error("minerWeight erred while calling actives func", log.Err(err), layer)
		return 0, err
	}
	w, ok := actives[id.Key]
	if !ok {
		o.With().Debug("miner is not active in specified layer",
			log.Int("active_set_size", len(actives)),
			log.String("actives", fmt.Sprintf("%v", actives)),
			layer, log.String("id.Key", id.Key),
		)
		return 0, errors.New("miner is not active in specified layer")
	}
	return w, nil
}
func calcVrfFrac(vrfSig []byte) fixed.Fixed {
	return fixed.FracFromBytes(vrfSig[:8])
}
func (o *Oracle) prepareEligibilityCheck(ctx context.Context, layer types.LayerID, round uint32, committeeSize int,
	id types.NodeID, vrfSig []byte) (n int, p, vrfFrac fixed.Fixed, done bool, err error) {
	logger := o.WithContext(ctx).WithFields(
		layer,
		id,
		log.Uint32("round", round),
		log.Int("committee_size", committeeSize))
	if committeeSize < 1 {
		logger.Error("committee size must be positive (received %d)", committeeSize)
		return 0, fixed.Fixed{}, fixed.Fixed{}, true, nil
	}
	msg, err := o.buildVRFMessage(ctx, layer, round)
	if err != nil {
		logger.Error("eligibility: could not build vrf message")
		return 0, fixed.Fixed{}, fixed.Fixed{}, true, err
	}
	if !o.vrfVerifier(id.VRFPublicKey, msg, vrfSig) {
		logger.With().Info("eligibility: a node did not pass vrf signature verification",
			id,
			layer)
		return 0, fixed.Fixed{}, fixed.Fixed{}, true, nil
	}
	totalWeight, err := o.totalWeight(ctx, layer)
	if err != nil {
		return 0, fixed.Fixed{}, fixed.Fixed{}, true, err
	}
	if totalWeight == 0 {
		logger.Warning("eligibility: total weight is zero")
		return 0, fixed.Fixed{}, fixed.Fixed{}, true, errors.New("total weight is zero")
	}
	minerWeight, err := o.minerWeight(ctx, layer, id)
	if err != nil {
		return 0, fixed.Fixed{}, fixed.Fixed{}, true, err
	}
	logger.With().Info("preparing eligibility check",
		log.Uint64("miner_weight", minerWeight),
		log.Uint64("total_weight", totalWeight),
	)
	n = int(minerWeight)
	if uint64(n) != minerWeight {
		logger.Panic(fmt.Sprintf("minerWeight overflows int (%d)", minerWeight))
	}
	if committeeSize > int(totalWeight) {
		logger.With().Warning("committee size is greater than total weight",
			log.Int("committee_size", committeeSize),
			log.Uint64("total_weight", totalWeight),
		)
		totalWeight *= uint64(committeeSize)
		n *= committeeSize
	}
	p = fixed.DivUint64(uint64(committeeSize), totalWeight)
	if minerWeight > maxSupportedN {
		return 0, fixed.Fixed{}, fixed.Fixed{}, false,
			fmt.Errorf("miner weight exceeds supported maximum (id: %v, weight: %d, max: %d",
				id, minerWeight, maxSupportedN)
	}
	return n, p, calcVrfFrac(vrfSig), false, nil
}
func (o *Oracle) Validate(ctx context.Context, layer types.LayerID, round uint32, committeeSize int, id types.NodeID, sig []byte, eligibilityCount uint16) (bool, error) {
	n, p, vrfFrac, done, err := o.prepareEligibilityCheck(ctx, layer, round, committeeSize, id, sig)
	if done || err != nil {
		return false, err
	}
	defer func() {
		if msg := recover(); msg != nil {
			o.WithContext(ctx).With().Error("panic in validate",
				log.String("msg", fmt.Sprint(msg)),
				log.Int("n", n),
				log.String("p", p.String()),
				log.String("vrf_frac", vrfFrac.String()),
			)
			o.Panic("%s", msg)
		}
	}()
	x := int(eligibilityCount)
	if !fixed.BinCDF(n, p, x-1).GreaterThan(vrfFrac) && vrfFrac.LessThan(fixed.BinCDF(n, p, x)) {
		return true, nil
	}
	o.WithContext(ctx).With().Warning("eligibility: node did not pass vrf eligibility threshold",
		layer,
		log.Uint32("round", round),
		log.Int("committee_size", committeeSize),
		id,
		log.Uint64("eligibility_count", uint64(eligibilityCount)),
		log.Int("n", n),
		log.String("p", p.String()),
		log.String("vrf_frac", vrfFrac.String()),
		log.Int("x", x),
	)
	return false, nil
}