package pubsub
import (
"fmt"
"math"
"net"
"time"
"github.com/libp2p/go-libp2p/core/peer"
)
type PeerScoreThresholds struct {
SkipAtomicValidation bool
GossipThreshold float64
PublishThreshold float64
GraylistThreshold float64
AcceptPXThreshold float64
OpportunisticGraftThreshold float64
}
func (p *PeerScoreThresholds ) validate () error {
if !p .SkipAtomicValidation || p .PublishThreshold != 0 || p .GossipThreshold != 0 || p .GraylistThreshold != 0 {
if p .GossipThreshold > 0 || isInvalidNumber (p .GossipThreshold ) {
return fmt .Errorf ("invalid gossip threshold; it must be <= 0 and a valid number" )
}
if p .PublishThreshold > 0 || p .PublishThreshold > p .GossipThreshold || isInvalidNumber (p .PublishThreshold ) {
return fmt .Errorf ("invalid publish threshold; it must be <= 0 and <= gossip threshold and a valid number" )
}
if p .GraylistThreshold > 0 || p .GraylistThreshold > p .PublishThreshold || isInvalidNumber (p .GraylistThreshold ) {
return fmt .Errorf ("invalid graylist threshold; it must be <= 0 and <= publish threshold and a valid number" )
}
}
if !p .SkipAtomicValidation || p .AcceptPXThreshold != 0 {
if p .AcceptPXThreshold < 0 || isInvalidNumber (p .AcceptPXThreshold ) {
return fmt .Errorf ("invalid accept PX threshold; it must be >= 0 and a valid number" )
}
}
if !p .SkipAtomicValidation || p .OpportunisticGraftThreshold != 0 {
if p .OpportunisticGraftThreshold < 0 || isInvalidNumber (p .OpportunisticGraftThreshold ) {
return fmt .Errorf ("invalid opportunistic grafting threshold; it must be >= 0 and a valid number" )
}
}
return nil
}
type PeerScoreParams struct {
SkipAtomicValidation bool
Topics map [string ]*TopicScoreParams
TopicScoreCap float64
AppSpecificScore func (p peer .ID ) float64
AppSpecificWeight float64
IPColocationFactorWeight float64
IPColocationFactorThreshold int
IPColocationFactorWhitelist []*net .IPNet
BehaviourPenaltyWeight, BehaviourPenaltyThreshold, BehaviourPenaltyDecay float64
DecayInterval time .Duration
DecayToZero float64
RetainScore time .Duration
SeenMsgTTL time .Duration
}
type TopicScoreParams struct {
SkipAtomicValidation bool
TopicWeight float64
TimeInMeshWeight float64
TimeInMeshQuantum time .Duration
TimeInMeshCap float64
FirstMessageDeliveriesWeight, FirstMessageDeliveriesDecay float64
FirstMessageDeliveriesCap float64
MeshMessageDeliveriesWeight, MeshMessageDeliveriesDecay float64
MeshMessageDeliveriesCap, MeshMessageDeliveriesThreshold float64
MeshMessageDeliveriesWindow, MeshMessageDeliveriesActivation time .Duration
MeshFailurePenaltyWeight, MeshFailurePenaltyDecay float64
InvalidMessageDeliveriesWeight, InvalidMessageDeliveriesDecay float64
}
func (p *PeerScoreParams ) validate () error {
for topic , params := range p .Topics {
err := params .validate ()
if err != nil {
return fmt .Errorf ("invalid score parameters for topic %s: %w" , topic , err )
}
}
if !p .SkipAtomicValidation || p .TopicScoreCap != 0 {
if p .TopicScoreCap < 0 || isInvalidNumber (p .TopicScoreCap ) {
return fmt .Errorf ("invalid topic score cap; must be positive (or 0 for no cap) and a valid number" )
}
}
if p .AppSpecificScore == nil {
if p .SkipAtomicValidation {
p .AppSpecificScore = func (p peer .ID ) float64 {
return 0
}
} else {
return fmt .Errorf ("missing application specific score function" )
}
}
if !p .SkipAtomicValidation || p .IPColocationFactorWeight != 0 {
if p .IPColocationFactorWeight > 0 || isInvalidNumber (p .IPColocationFactorWeight ) {
return fmt .Errorf ("invalid IPColocationFactorWeight; must be negative (or 0 to disable) and a valid number" )
}
if p .IPColocationFactorWeight != 0 && p .IPColocationFactorThreshold < 1 {
return fmt .Errorf ("invalid IPColocationFactorThreshold; must be at least 1" )
}
}
if !p .SkipAtomicValidation || p .BehaviourPenaltyWeight != 0 || p .BehaviourPenaltyThreshold != 0 {
if p .BehaviourPenaltyWeight > 0 || isInvalidNumber (p .BehaviourPenaltyWeight ) {
return fmt .Errorf ("invalid BehaviourPenaltyWeight; must be negative (or 0 to disable) and a valid number" )
}
if p .BehaviourPenaltyWeight != 0 && (p .BehaviourPenaltyDecay <= 0 || p .BehaviourPenaltyDecay >= 1 || isInvalidNumber (p .BehaviourPenaltyDecay )) {
return fmt .Errorf ("invalid BehaviourPenaltyDecay; must be between 0 and 1" )
}
if p .BehaviourPenaltyThreshold < 0 || isInvalidNumber (p .BehaviourPenaltyThreshold ) {
return fmt .Errorf ("invalid BehaviourPenaltyThreshold; must be >= 0 and a valid number" )
}
}
if !p .SkipAtomicValidation || p .DecayInterval != 0 || p .DecayToZero != 0 {
if p .DecayInterval < time .Second {
return fmt .Errorf ("invalid DecayInterval; must be at least 1s" )
}
if p .DecayToZero <= 0 || p .DecayToZero >= 1 || isInvalidNumber (p .DecayToZero ) {
return fmt .Errorf ("invalid DecayToZero; must be between 0 and 1" )
}
}
return nil
}
func (p *TopicScoreParams ) validate () error {
if p .TopicWeight < 0 || isInvalidNumber (p .TopicWeight ) {
return fmt .Errorf ("invalid topic weight; must be >= 0 and a valid number" )
}
if err := p .validateTimeInMeshParams (); err != nil {
return err
}
if err := p .validateMessageDeliveryParams (); err != nil {
return err
}
if err := p .validateMeshMessageDeliveryParams (); err != nil {
return err
}
if err := p .validateMessageFailurePenaltyParams (); err != nil {
return err
}
if err := p .validateInvalidMessageDeliveryParams (); err != nil {
return err
}
return nil
}
func (p *TopicScoreParams ) validateTimeInMeshParams () error {
if p .SkipAtomicValidation {
if p .TimeInMeshWeight == 0 && p .TimeInMeshQuantum == 0 && p .TimeInMeshCap == 0 {
return nil
}
}
if p .TimeInMeshQuantum == 0 {
return fmt .Errorf ("invalid TimeInMeshQuantum; must be non zero" )
}
if p .TimeInMeshWeight < 0 || isInvalidNumber (p .TimeInMeshWeight ) {
return fmt .Errorf ("invalid TimeInMeshWeight; must be positive (or 0 to disable) and a valid number" )
}
if p .TimeInMeshWeight != 0 && p .TimeInMeshQuantum <= 0 {
return fmt .Errorf ("invalid TimeInMeshQuantum; must be positive" )
}
if p .TimeInMeshWeight != 0 && (p .TimeInMeshCap <= 0 || isInvalidNumber (p .TimeInMeshCap )) {
return fmt .Errorf ("invalid TimeInMeshCap; must be positive and a valid number" )
}
return nil
}
func (p *TopicScoreParams ) validateMessageDeliveryParams () error {
if p .SkipAtomicValidation {
if p .FirstMessageDeliveriesWeight == 0 && p .FirstMessageDeliveriesCap == 0 && p .FirstMessageDeliveriesDecay == 0 {
return nil
}
}
if p .FirstMessageDeliveriesWeight < 0 || isInvalidNumber (p .FirstMessageDeliveriesWeight ) {
return fmt .Errorf ("invallid FirstMessageDeliveriesWeight; must be positive (or 0 to disable) and a valid number" )
}
if p .FirstMessageDeliveriesWeight != 0 && (p .FirstMessageDeliveriesDecay <= 0 || p .FirstMessageDeliveriesDecay >= 1 || isInvalidNumber (p .FirstMessageDeliveriesDecay )) {
return fmt .Errorf ("invalid FirstMessageDeliveriesDecay; must be between 0 and 1" )
}
if p .FirstMessageDeliveriesWeight != 0 && (p .FirstMessageDeliveriesCap <= 0 || isInvalidNumber (p .FirstMessageDeliveriesCap )) {
return fmt .Errorf ("invalid FirstMessageDeliveriesCap; must be positive and a valid number" )
}
return nil
}
func (p *TopicScoreParams ) validateMeshMessageDeliveryParams () error {
if p .SkipAtomicValidation {
if p .MeshMessageDeliveriesWeight == 0 &&
p .MeshMessageDeliveriesCap == 0 &&
p .MeshMessageDeliveriesDecay == 0 &&
p .MeshMessageDeliveriesThreshold == 0 &&
p .MeshMessageDeliveriesWindow == 0 &&
p .MeshMessageDeliveriesActivation == 0 {
return nil
}
}
if p .MeshMessageDeliveriesWeight > 0 || isInvalidNumber (p .MeshMessageDeliveriesWeight ) {
return fmt .Errorf ("invalid MeshMessageDeliveriesWeight; must be negative (or 0 to disable) and a valid number" )
}
if p .MeshMessageDeliveriesWeight != 0 && (p .MeshMessageDeliveriesDecay <= 0 || p .MeshMessageDeliveriesDecay >= 1 || isInvalidNumber (p .MeshMessageDeliveriesDecay )) {
return fmt .Errorf ("invalid MeshMessageDeliveriesDecay; must be between 0 and 1" )
}
if p .MeshMessageDeliveriesWeight != 0 && (p .MeshMessageDeliveriesCap <= 0 || isInvalidNumber (p .MeshMessageDeliveriesCap )) {
return fmt .Errorf ("invalid MeshMessageDeliveriesCap; must be positive and a valid number" )
}
if p .MeshMessageDeliveriesWeight != 0 && (p .MeshMessageDeliveriesThreshold <= 0 || isInvalidNumber (p .MeshMessageDeliveriesThreshold )) {
return fmt .Errorf ("invalid MeshMessageDeliveriesThreshold; must be positive and a valid number" )
}
if p .MeshMessageDeliveriesWindow < 0 {
return fmt .Errorf ("invalid MeshMessageDeliveriesWindow; must be non-negative" )
}
if p .MeshMessageDeliveriesWeight != 0 && p .MeshMessageDeliveriesActivation < time .Second {
return fmt .Errorf ("invalid MeshMessageDeliveriesActivation; must be at least 1s" )
}
return nil
}
func (p *TopicScoreParams ) validateMessageFailurePenaltyParams () error {
if p .SkipAtomicValidation {
if p .MeshFailurePenaltyDecay == 0 && p .MeshFailurePenaltyWeight == 0 {
return nil
}
}
if p .MeshFailurePenaltyWeight > 0 || isInvalidNumber (p .MeshFailurePenaltyWeight ) {
return fmt .Errorf ("invalid MeshFailurePenaltyWeight; must be negative (or 0 to disable) and a valid number" )
}
if p .MeshFailurePenaltyWeight != 0 && (isInvalidNumber (p .MeshFailurePenaltyDecay ) || p .MeshFailurePenaltyDecay <= 0 || p .MeshFailurePenaltyDecay >= 1 ) {
return fmt .Errorf ("invalid MeshFailurePenaltyDecay; must be between 0 and 1" )
}
return nil
}
func (p *TopicScoreParams ) validateInvalidMessageDeliveryParams () error {
if p .SkipAtomicValidation {
if p .InvalidMessageDeliveriesDecay == 0 && p .InvalidMessageDeliveriesWeight == 0 {
return nil
}
}
if p .InvalidMessageDeliveriesWeight > 0 || isInvalidNumber (p .InvalidMessageDeliveriesWeight ) {
return fmt .Errorf ("invalid InvalidMessageDeliveriesWeight; must be negative (or 0 to disable) and a valid number" )
}
if p .InvalidMessageDeliveriesDecay <= 0 || p .InvalidMessageDeliveriesDecay >= 1 || isInvalidNumber (p .InvalidMessageDeliveriesDecay ) {
return fmt .Errorf ("invalid InvalidMessageDeliveriesDecay; must be between 0 and 1" )
}
return nil
}
const (
DefaultDecayInterval = time .Second
DefaultDecayToZero = 0.01
)
func ScoreParameterDecay (decay time .Duration ) float64 {
return ScoreParameterDecayWithBase (decay , DefaultDecayInterval , DefaultDecayToZero )
}
func ScoreParameterDecayWithBase (decay time .Duration , base time .Duration , decayToZero float64 ) float64 {
ticks := float64 (decay / base )
return math .Pow (decayToZero , 1 /ticks )
}
func isInvalidNumber(num float64 ) bool {
return math .IsNaN (num ) || math .IsInf (num , 0 )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .