Enables non-atomic validation for peer scoring parameters (#499)

* decouples topic scoring parameters

* adds skiping atomic validation for topic parameters

* cleans up

* adds skip atomic validation to peer score threshold

* adds skip atomic validation for peer parameters

* adds test for non-atomic validation

* adds tests for peer score

* adds tests for peer score thresholds

* refactors tests
This commit is contained in:
Yahya Hassanzadeh 2022-09-07 22:20:23 -07:00 committed by GitHub
parent 4f56e8f0a7
commit 8866ca88a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 769 additions and 231 deletions

View File

@ -10,7 +10,10 @@ import (
)
type PeerScoreThresholds struct {
// GossipThreshold is the score threshold below which gossip propagation is supressed;
// whether it is allowed to just set some params and not all of them.
SkipAtomicValidation bool
// GossipThreshold is the score threshold below which gossip propagation is suppressed;
// should be negative.
GossipThreshold float64
@ -18,8 +21,8 @@ type PeerScoreThresholds struct {
// publishing (also applies to fanout and floodsub peers); should be negative and <= GossipThreshold.
PublishThreshold float64
// GraylistThreshold is the score threshold below which message processing is supressed altogether,
// implementing an effective graylist according to peer score; should be negative and <= PublisThreshold.
// GraylistThreshold is the score threshold below which message processing is suppressed altogether,
// implementing an effective gray list according to peer score; should be negative and <= PublishThreshold.
GraylistThreshold float64
// AcceptPXThreshold is the score threshold below which PX will be ignored; this should be positive
@ -32,25 +35,38 @@ type PeerScoreThresholds struct {
}
func (p *PeerScoreThresholds) validate() error {
if p.GossipThreshold > 0 || isInvalidNumber(p.GossipThreshold) {
return fmt.Errorf("invalid gossip threshold; it must be <= 0 and a valid number")
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.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.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.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.AcceptPXThreshold < 0 || isInvalidNumber(p.AcceptPXThreshold) {
return fmt.Errorf("invalid accept PX threshold; it must be >= 0 and a valid number")
}
if p.OpportunisticGraftThreshold < 0 || isInvalidNumber(p.OpportunisticGraftThreshold) {
return fmt.Errorf("invalid opportunistic grafting 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 {
// whether it is allowed to just set some params and not all of them.
SkipAtomicValidation bool
// Score parameters per topic.
Topics map[string]*TopicScoreParams
@ -99,12 +115,15 @@ type PeerScoreParams struct {
}
type TopicScoreParams struct {
// whether it is allowed to just set some params and not all of them.
SkipAtomicValidation bool
// The weight of the topic.
TopicWeight float64
// P1: time in the mesh
// This is the time the peer has ben grafted in the mesh.
// The value of of the parameter is the time/TimeInMeshQuantum, capped by TimeInMeshCap
// This is the time the peer has been grafted in the mesh.
// The value of the parameter is the time/TimeInMeshQuantum, capped by TimeInMeshCap.
// The weight of the parameter MUST be positive (or zero to disable).
TimeInMeshWeight float64
TimeInMeshQuantum time.Duration
@ -124,7 +143,7 @@ type TopicScoreParams struct {
// when validation succeeds.
// This window accounts for the minimum time before a hostile mesh peer trying to game the score
// could replay back a valid message we just sent them.
// It effectively tracks first and near-first deliveries, ie a message seen from a mesh peer
// It effectively tracks first and near-first deliveries, i.e., a message seen from a mesh peer
// before we have forwarded it to them.
// The parameter has an associated counter, decaying with MeshMessageDeliveriesDecay.
// If the counter exceeds the threshold, its value is 0.
@ -159,41 +178,55 @@ func (p *PeerScoreParams) validate() error {
}
}
// check that the topic score is 0 or something positive
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.SkipAtomicValidation || p.TopicScoreCap != 0 {
// check that the topic score is 0 or something positive
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")
}
}
// check that we have an app specific score; the weight can be anything (but expected positive)
if p.AppSpecificScore == nil {
return fmt.Errorf("missing application specific score function")
if p.SkipAtomicValidation {
p.AppSpecificScore = func(p peer.ID) float64 {
return 0
}
} else {
return fmt.Errorf("missing application specific score function")
}
}
// check the IP colocation factor
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.IPColocationFactorWeight != 0 {
// check the IP collocation factor
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")
}
}
// check the behaviour penalty
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.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")
}
}
// check the decay parameters
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")
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")
}
}
// no need to check the score retention; a value of 0 means that we don't retain scores
@ -207,6 +240,43 @@ func (p *TopicScoreParams) validate() error {
}
// check P1
if err := p.validateTimeInMeshParams(); err != nil {
return err
}
// check P2
if err := p.validateMessageDeliveryParams(); err != nil {
return err
}
// check P3
if err := p.validateMeshMessageDeliveryParams(); err != nil {
return err
}
// check P3b
if err := p.validateMessageFailurePenaltyParams(); err != nil {
return err
}
// check P4
if err := p.validateInvalidMessageDeliveryParams(); err != nil {
return err
}
return nil
}
func (p *TopicScoreParams) validateTimeInMeshParams() error {
if p.SkipAtomicValidation {
// in non-atomic mode, parameters at their zero values are dismissed from validation.
if p.TimeInMeshWeight == 0 && p.TimeInMeshQuantum == 0 && p.TimeInMeshCap == 0 {
return nil
}
}
// either atomic validation mode, or some parameters have been set a value,
// hence, proceed with normal validation of all related parameters in this context.
if p.TimeInMeshQuantum == 0 {
return fmt.Errorf("invalid TimeInMeshQuantum; must be non zero")
}
@ -220,7 +290,20 @@ func (p *TopicScoreParams) validate() error {
return fmt.Errorf("invalid TimeInMeshCap; must be positive and a valid number")
}
// check P2
return nil
}
func (p *TopicScoreParams) validateMessageDeliveryParams() error {
if p.SkipAtomicValidation {
// in non-atomic mode, parameters at their zero values are dismissed from validation.
if p.FirstMessageDeliveriesWeight == 0 && p.FirstMessageDeliveriesCap == 0 && p.FirstMessageDeliveriesDecay == 0 {
return nil
}
}
// either atomic validation mode, or some parameters have been set a value,
// hence, proceed with normal validation of all related parameters in this context.
if p.FirstMessageDeliveriesWeight < 0 || isInvalidNumber(p.FirstMessageDeliveriesWeight) {
return fmt.Errorf("invallid FirstMessageDeliveriesWeight; must be positive (or 0 to disable) and a valid number")
}
@ -231,7 +314,25 @@ func (p *TopicScoreParams) validate() error {
return fmt.Errorf("invalid FirstMessageDeliveriesCap; must be positive and a valid number")
}
// check P3
return nil
}
func (p *TopicScoreParams) validateMeshMessageDeliveryParams() error {
if p.SkipAtomicValidation {
// in non-atomic mode, parameters at their zero values are dismissed from validation.
if p.MeshMessageDeliveriesWeight == 0 &&
p.MeshMessageDeliveriesCap == 0 &&
p.MeshMessageDeliveriesDecay == 0 &&
p.MeshMessageDeliveriesThreshold == 0 &&
p.MeshMessageDeliveriesWindow == 0 &&
p.MeshMessageDeliveriesActivation == 0 {
return nil
}
}
// either atomic validation mode, or some parameters have been set a value,
// hence, proceed with normal validation of all related parameters in this context.
if p.MeshMessageDeliveriesWeight > 0 || isInvalidNumber(p.MeshMessageDeliveriesWeight) {
return fmt.Errorf("invalid MeshMessageDeliveriesWeight; must be negative (or 0 to disable) and a valid number")
}
@ -251,7 +352,20 @@ func (p *TopicScoreParams) validate() error {
return fmt.Errorf("invalid MeshMessageDeliveriesActivation; must be at least 1s")
}
// check P3b
return nil
}
func (p *TopicScoreParams) validateMessageFailurePenaltyParams() error {
if p.SkipAtomicValidation {
// in selective mode, parameters at their zero values are dismissed from validation.
if p.MeshFailurePenaltyDecay == 0 && p.MeshFailurePenaltyWeight == 0 {
return nil
}
}
// either atomic validation mode, or some parameters have been set a value,
// hence, proceed with normal validation of all related parameters in this context.
if p.MeshFailurePenaltyWeight > 0 || isInvalidNumber(p.MeshFailurePenaltyWeight) {
return fmt.Errorf("invalid MeshFailurePenaltyWeight; must be negative (or 0 to disable) and a valid number")
}
@ -259,7 +373,20 @@ func (p *TopicScoreParams) validate() error {
return fmt.Errorf("invalid MeshFailurePenaltyDecay; must be between 0 and 1")
}
// check P4
return nil
}
func (p *TopicScoreParams) validateInvalidMessageDeliveryParams() error {
if p.SkipAtomicValidation {
// in selective mode, parameters at their zero values are dismissed from validation.
if p.InvalidMessageDeliveriesDecay == 0 && p.InvalidMessageDeliveriesWeight == 0 {
return nil
}
}
// either atomic validation mode, or some parameters have been set a value,
// hence, proceed with normal validation of all related parameters in this context.
if p.InvalidMessageDeliveriesWeight > 0 || isInvalidNumber(p.InvalidMessageDeliveriesWeight) {
return fmt.Errorf("invalid InvalidMessageDeliveriesWeight; must be negative (or 0 to disable) and a valid number")
}
@ -281,7 +408,7 @@ func ScoreParameterDecay(decay time.Duration) float64 {
return ScoreParameterDecayWithBase(decay, DefaultDecayInterval, DefaultDecayToZero)
}
// ScoreParameterDecay computes the decay factor for a parameter using base as the DecayInterval
// ScoreParameterDecayWithBase computes the decay factor for a parameter using base as the DecayInterval
func ScoreParameterDecayWithBase(decay time.Duration, base time.Duration, decayToZero float64) float64 {
// the decay is linear, so after n ticks the value is factor^n
// so factor^n = decayToZero => factor = decayToZero^(1/n)

View File

@ -8,121 +8,317 @@ import (
"github.com/libp2p/go-libp2p/core/peer"
)
func TestPeerScoreThresholdsValidation(t *testing.T) {
if (&PeerScoreThresholds{GossipThreshold: 1}).validate() == nil {
func TestPeerScoreThreshold_AtomicValidation(t *testing.T) {
testPeerScoreThresholdsValidation(t, false)
}
func TestPeerScoreThreshold_SkipAtomicValidation(t *testing.T) {
testPeerScoreThresholdsValidation(t, true)
}
func testPeerScoreThresholdsValidation(t *testing.T, skipAtomicValidation bool) {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
GossipThreshold: 1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreThresholds{PublishThreshold: 1}).validate() == nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
PublishThreshold: 1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreThresholds{GossipThreshold: -1, PublishThreshold: 0}).validate() == nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
GossipThreshold: -1,
PublishThreshold: 0,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreThresholds{GossipThreshold: -1, PublishThreshold: -2, GraylistThreshold: 0}).validate() == nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
GossipThreshold: -1,
PublishThreshold: -2,
GraylistThreshold: 0,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreThresholds{AcceptPXThreshold: -1}).validate() == nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
AcceptPXThreshold: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreThresholds{OpportunisticGraftThreshold: -1}).validate() == nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
OpportunisticGraftThreshold: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreThresholds{GossipThreshold: -1, PublishThreshold: -2, GraylistThreshold: -3, AcceptPXThreshold: 1, OpportunisticGraftThreshold: 2}).validate() != nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
GossipThreshold: -1,
PublishThreshold: -2,
GraylistThreshold: -3,
AcceptPXThreshold: 1,
OpportunisticGraftThreshold: 2}).validate() != nil {
t.Fatal("expected validation success")
}
if (&PeerScoreThresholds{GossipThreshold: math.Inf(-1), PublishThreshold: -2, GraylistThreshold: -3, AcceptPXThreshold: 1, OpportunisticGraftThreshold: 2}).validate() == nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
GossipThreshold: math.Inf(-1),
PublishThreshold: -2,
GraylistThreshold: -3,
AcceptPXThreshold: 1,
OpportunisticGraftThreshold: 2,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreThresholds{GossipThreshold: -1, PublishThreshold: math.Inf(-1), GraylistThreshold: -3, AcceptPXThreshold: 1, OpportunisticGraftThreshold: 2}).validate() == nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
GossipThreshold: -1,
PublishThreshold: math.Inf(-1),
GraylistThreshold: -3,
AcceptPXThreshold: 1,
OpportunisticGraftThreshold: 2,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreThresholds{GossipThreshold: -1, PublishThreshold: -2, GraylistThreshold: math.Inf(-1), AcceptPXThreshold: 1, OpportunisticGraftThreshold: 2}).validate() == nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
GossipThreshold: -1,
PublishThreshold: -2,
GraylistThreshold: math.Inf(-1),
AcceptPXThreshold: 1,
OpportunisticGraftThreshold: 2,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreThresholds{GossipThreshold: -1, PublishThreshold: -2, GraylistThreshold: -3, AcceptPXThreshold: math.NaN(), OpportunisticGraftThreshold: 2}).validate() == nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
GossipThreshold: -1,
PublishThreshold: -2,
GraylistThreshold: -3,
AcceptPXThreshold: math.NaN(),
OpportunisticGraftThreshold: 2,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreThresholds{GossipThreshold: -1, PublishThreshold: -2, GraylistThreshold: -3, AcceptPXThreshold: 1, OpportunisticGraftThreshold: math.Inf(0)}).validate() == nil {
if (&PeerScoreThresholds{
SkipAtomicValidation: skipAtomicValidation,
GossipThreshold: -1,
PublishThreshold: -2,
GraylistThreshold: -3,
AcceptPXThreshold: 1,
OpportunisticGraftThreshold: math.Inf(0),
}).validate() == nil {
t.Fatal("expected validation error")
}
}
func TestTopicScoreParamsValidation(t *testing.T) {
if (&TopicScoreParams{}).validate() == nil {
func TestTopicScoreParamsValidation_InvalidParams_AtomicValidation(t *testing.T) {
testTopicScoreParamsValidationWithInvalidParameters(t, false)
}
func TestTopicScoreParamsValidation_InvalidParams_SkipAtomicValidation(t *testing.T) {
testTopicScoreParamsValidationWithInvalidParameters(t, true)
}
func testTopicScoreParamsValidationWithInvalidParameters(t *testing.T, skipAtomicValidation bool) {
if skipAtomicValidation {
if (&TopicScoreParams{
SkipAtomicValidation: true}).validate() != nil {
t.Fatal("expected validation success")
}
} else {
if (&TopicScoreParams{}).validate() == nil {
t.Fatal("expected validation failure")
}
}
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TopicWeight: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TopicWeight: -1}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshWeight: -1,
TimeInMeshQuantum: time.Second,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshWeight: 1,
TimeInMeshQuantum: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshWeight: 1,
TimeInMeshQuantum: time.Second,
TimeInMeshCap: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshWeight: -1, TimeInMeshQuantum: time.Second}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
FirstMessageDeliveriesWeight: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshWeight: 1, TimeInMeshQuantum: -1}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshWeight: 1, TimeInMeshQuantum: time.Second, TimeInMeshCap: -1}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: 2,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: .5,
FirstMessageDeliveriesCap: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, FirstMessageDeliveriesWeight: -1}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
MeshMessageDeliveriesWeight: 1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, FirstMessageDeliveriesWeight: 1, FirstMessageDeliveriesDecay: -1}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, FirstMessageDeliveriesWeight: 1, FirstMessageDeliveriesDecay: 2}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: 2}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, FirstMessageDeliveriesWeight: 1, FirstMessageDeliveriesDecay: .5, FirstMessageDeliveriesCap: -1}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: .5,
MeshMessageDeliveriesCap: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: .5,
MeshMessageDeliveriesCap: 5,
MeshMessageDeliveriesThreshold: -3,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: .5,
MeshMessageDeliveriesCap: 5,
MeshMessageDeliveriesThreshold: 3,
MeshMessageDeliveriesWindow: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: .5,
MeshMessageDeliveriesCap: 5,
MeshMessageDeliveriesThreshold: 3,
MeshMessageDeliveriesWindow: time.Millisecond,
MeshMessageDeliveriesActivation: time.Millisecond}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshMessageDeliveriesWeight: 1}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
MeshFailurePenaltyWeight: 1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshMessageDeliveriesWeight: -1, MeshMessageDeliveriesDecay: -1}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
MeshFailurePenaltyWeight: -1,
MeshFailurePenaltyDecay: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshMessageDeliveriesWeight: -1, MeshMessageDeliveriesDecay: 2}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshMessageDeliveriesWeight: -1, MeshMessageDeliveriesDecay: .5, MeshMessageDeliveriesCap: -1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshMessageDeliveriesWeight: -1, MeshMessageDeliveriesDecay: .5, MeshMessageDeliveriesCap: 5, MeshMessageDeliveriesThreshold: -3}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshMessageDeliveriesWeight: -1, MeshMessageDeliveriesDecay: .5, MeshMessageDeliveriesCap: 5, MeshMessageDeliveriesThreshold: 3, MeshMessageDeliveriesWindow: -1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshMessageDeliveriesWeight: -1, MeshMessageDeliveriesDecay: .5, MeshMessageDeliveriesCap: 5, MeshMessageDeliveriesThreshold: 3, MeshMessageDeliveriesWindow: time.Millisecond, MeshMessageDeliveriesActivation: time.Millisecond}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
MeshFailurePenaltyWeight: -1,
MeshFailurePenaltyDecay: 2,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshFailurePenaltyWeight: 1}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
InvalidMessageDeliveriesWeight: 1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshFailurePenaltyWeight: -1, MeshFailurePenaltyDecay: -1}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
InvalidMessageDeliveriesWeight: -1,
InvalidMessageDeliveriesDecay: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshFailurePenaltyWeight: -1, MeshFailurePenaltyDecay: 2}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, InvalidMessageDeliveriesWeight: 1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, InvalidMessageDeliveriesWeight: -1, InvalidMessageDeliveriesDecay: -1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&TopicScoreParams{TimeInMeshQuantum: time.Second, InvalidMessageDeliveriesWeight: -1, InvalidMessageDeliveriesDecay: 2}).validate() == nil {
if (&TopicScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TimeInMeshQuantum: time.Second,
InvalidMessageDeliveriesWeight: -1,
InvalidMessageDeliveriesDecay: 2,
}).validate() == nil {
t.Fatal("expected validation error")
}
}
func TestTopicScoreParamsValidation_ValidParams_AtomicValidation(t *testing.T) {
// Don't use these params in production!
if (&TopicScoreParams{
SkipAtomicValidation: false,
TopicWeight: 1,
TimeInMeshWeight: 0.01,
TimeInMeshQuantum: time.Second,
@ -145,148 +341,177 @@ func TestTopicScoreParamsValidation(t *testing.T) {
}
}
func TestPeerScoreParamsValidation(t *testing.T) {
func TestTopicScoreParamsValidation_NonAtomicValidation(t *testing.T) {
// Don't use these params in production!
// In non-atomic (selective) validation mode, the subset of parameters passes
// validation if the individual parameters values pass validation.
p := &TopicScoreParams{}
setTopicParamAndValidate(t, p, func(params *TopicScoreParams) {
params.SkipAtomicValidation = true
})
// including topic weight.
setTopicParamAndValidate(t, p, func(params *TopicScoreParams) {
params.TopicWeight = 1
})
// including time in mesh parameters.
setTopicParamAndValidate(t, p, func(params *TopicScoreParams) {
params.TimeInMeshWeight = 0.01
params.TimeInMeshQuantum = time.Second
params.TimeInMeshCap = 10
})
// including first message delivery parameters.
setTopicParamAndValidate(t, p, func(params *TopicScoreParams) {
params.FirstMessageDeliveriesWeight = 1
params.FirstMessageDeliveriesDecay = 0.5
params.FirstMessageDeliveriesCap = 10
})
// including mesh message delivery parameters.
setTopicParamAndValidate(t, p, func(params *TopicScoreParams) {
params.MeshMessageDeliveriesWeight = -1
params.MeshMessageDeliveriesDecay = 0.5
params.MeshMessageDeliveriesCap = 10
params.MeshMessageDeliveriesThreshold = 5
params.MeshMessageDeliveriesWindow = time.Millisecond
params.MeshMessageDeliveriesActivation = time.Second
})
// including mesh failure penalty parameters.
setTopicParamAndValidate(t, p, func(params *TopicScoreParams) {
params.MeshFailurePenaltyWeight = -1
params.MeshFailurePenaltyDecay = 0.5
})
// including invalid message delivery parameters.
setTopicParamAndValidate(t, p, func(params *TopicScoreParams) {
params.InvalidMessageDeliveriesWeight = -1
params.InvalidMessageDeliveriesDecay = 0.5
})
}
func TestPeerScoreParamsValidation_InvalidParams_AtomicValidation(t *testing.T) {
testPeerScoreParamsValidationWithInvalidParams(t, false)
}
func TestPeerScoreParamsValidation_InvalidParams_SkipAtomicValidation(t *testing.T) {
testPeerScoreParamsValidationWithInvalidParams(t, true)
}
func testPeerScoreParamsValidationWithInvalidParams(t *testing.T, skipAtomicValidation bool) {
appScore := func(peer.ID) float64 { return 0 }
if (&PeerScoreParams{TopicScoreCap: -1, AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 0.01}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{TopicScoreCap: 1, DecayInterval: time.Second, DecayToZero: 0.01}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{TopicScoreCap: 1, AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 0.01, IPColocationFactorWeight: 1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{TopicScoreCap: 1, AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 0.01, IPColocationFactorWeight: -1, IPColocationFactorThreshold: -1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{TopicScoreCap: 1, AppSpecificScore: appScore, DecayInterval: time.Millisecond, DecayToZero: 0.01, IPColocationFactorWeight: -1, IPColocationFactorThreshold: 1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{TopicScoreCap: 1, AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: -1, IPColocationFactorWeight: -1, IPColocationFactorThreshold: 1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{TopicScoreCap: 1, AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 2, IPColocationFactorWeight: -1, IPColocationFactorThreshold: 1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 0.01, BehaviourPenaltyWeight: 1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 0.01, BehaviourPenaltyWeight: -1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 0.01, BehaviourPenaltyWeight: -1, BehaviourPenaltyDecay: 2}).validate() == nil {
t.Fatal("expected validation error")
}
// don't use these params in production!
if (&PeerScoreParams{
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
BehaviourPenaltyWeight: -1,
BehaviourPenaltyDecay: 0.999,
}).validate() != nil {
t.Fatal("expected validation success")
}
if (&PeerScoreParams{
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
BehaviourPenaltyWeight: -1,
BehaviourPenaltyDecay: 0.999,
}).validate() != nil {
t.Fatal("expected validation success")
}
if (&PeerScoreParams{
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
Topics: map[string]*TopicScoreParams{
"test": &TopicScoreParams{
TopicWeight: 1,
TimeInMeshWeight: 0.01,
TimeInMeshQuantum: time.Second,
TimeInMeshCap: 10,
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: 0.5,
FirstMessageDeliveriesCap: 10,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: 0.5,
MeshMessageDeliveriesCap: 10,
MeshMessageDeliveriesThreshold: 5,
MeshMessageDeliveriesWindow: time.Millisecond,
MeshMessageDeliveriesActivation: time.Second,
MeshFailurePenaltyWeight: -1,
MeshFailurePenaltyDecay: 0.5,
InvalidMessageDeliveriesWeight: -1,
InvalidMessageDeliveriesDecay: 0.5,
},
},
}).validate() != nil {
t.Fatal("expected validation success")
}
// don't use these params in production!
if (&PeerScoreParams{
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
Topics: map[string]*TopicScoreParams{
"test": &TopicScoreParams{
TopicWeight: -1,
TimeInMeshWeight: 0.01,
TimeInMeshQuantum: time.Second,
TimeInMeshCap: 10,
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: 0.5,
FirstMessageDeliveriesCap: 10,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: 0.5,
MeshMessageDeliveriesCap: 10,
MeshMessageDeliveriesThreshold: 5,
MeshMessageDeliveriesWindow: time.Millisecond,
MeshMessageDeliveriesActivation: time.Second,
MeshFailurePenaltyWeight: -1,
MeshFailurePenaltyDecay: 0.5,
InvalidMessageDeliveriesWeight: -1,
InvalidMessageDeliveriesDecay: 0.5,
},
},
SkipAtomicValidation: skipAtomicValidation,
TopicScoreCap: -1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
}).validate() == nil {
t.Fatal("expected validation failure")
t.Fatal("expected validation error")
}
if skipAtomicValidation {
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TopicScoreCap: 1,
DecayInterval: time.Second,
DecayToZero: 0.01,
}).validate() != nil {
t.Fatal("expected validation success")
}
} else {
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TopicScoreCap: 1,
DecayInterval: time.Second,
DecayToZero: 0.01,
}).validate() == nil {
t.Fatal("expected validation error")
}
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
IPColocationFactorWeight: 1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: -1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Millisecond,
DecayToZero: 0.01,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: -1,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 2,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
BehaviourPenaltyWeight: 1}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
BehaviourPenaltyWeight: -1,
}).validate() == nil {
t.Fatal("expected validation error")
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
BehaviourPenaltyWeight: -1,
BehaviourPenaltyDecay: 2,
}).validate() == nil {
t.Fatal("expected validation error")
}
// Checks the topic parameters for invalid values such as infinite and
// NaN numbers.
// Don't use these params in production!
if (&PeerScoreParams{
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: math.Inf(0),
IPColocationFactorWeight: math.Inf(-1),
IPColocationFactorThreshold: 1,
BehaviourPenaltyWeight: math.Inf(0),
BehaviourPenaltyDecay: math.NaN(),
}).validate() == nil {
t.Fatal("expected validation failure")
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
@ -294,7 +519,7 @@ func TestPeerScoreParamsValidation(t *testing.T) {
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
Topics: map[string]*TopicScoreParams{
"test": &TopicScoreParams{
"test": {
TopicWeight: math.Inf(0),
TimeInMeshWeight: math.NaN(),
TimeInMeshQuantum: time.Second,
@ -318,6 +543,178 @@ func TestPeerScoreParamsValidation(t *testing.T) {
t.Fatal("expected validation failure")
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: math.Inf(0),
IPColocationFactorWeight: math.Inf(-1),
IPColocationFactorThreshold: 1,
BehaviourPenaltyWeight: math.Inf(0),
BehaviourPenaltyDecay: math.NaN(),
}).validate() == nil {
t.Fatal("expected validation failure")
}
if (&PeerScoreParams{
SkipAtomicValidation: skipAtomicValidation,
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
Topics: map[string]*TopicScoreParams{
"test": {
TopicWeight: -1,
TimeInMeshWeight: 0.01,
TimeInMeshQuantum: time.Second,
TimeInMeshCap: 10,
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: 0.5,
FirstMessageDeliveriesCap: 10,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: 0.5,
MeshMessageDeliveriesCap: 10,
MeshMessageDeliveriesThreshold: 5,
MeshMessageDeliveriesWindow: time.Millisecond,
MeshMessageDeliveriesActivation: time.Second,
MeshFailurePenaltyWeight: -1,
MeshFailurePenaltyDecay: 0.5,
InvalidMessageDeliveriesWeight: -1,
InvalidMessageDeliveriesDecay: 0.5,
},
},
}).validate() == nil {
t.Fatal("expected validation failure")
}
}
func TestPeerScoreParamsValidation_ValidParams_AtomicValidation(t *testing.T) {
appScore := func(peer.ID) float64 { return 0 }
// don't use these params in production!
if (&PeerScoreParams{
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
BehaviourPenaltyWeight: -1,
BehaviourPenaltyDecay: 0.999,
}).validate() != nil {
t.Fatal("expected validation success")
}
if (&PeerScoreParams{
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
BehaviourPenaltyWeight: -1,
BehaviourPenaltyDecay: 0.999,
}).validate() != nil {
t.Fatal("expected validation success")
}
if (&PeerScoreParams{
TopicScoreCap: 1,
AppSpecificScore: appScore,
DecayInterval: time.Second,
DecayToZero: 0.01,
IPColocationFactorWeight: -1,
IPColocationFactorThreshold: 1,
Topics: map[string]*TopicScoreParams{
"test": {
TopicWeight: 1,
TimeInMeshWeight: 0.01,
TimeInMeshQuantum: time.Second,
TimeInMeshCap: 10,
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: 0.5,
FirstMessageDeliveriesCap: 10,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: 0.5,
MeshMessageDeliveriesCap: 10,
MeshMessageDeliveriesThreshold: 5,
MeshMessageDeliveriesWindow: time.Millisecond,
MeshMessageDeliveriesActivation: time.Second,
MeshFailurePenaltyWeight: -1,
MeshFailurePenaltyDecay: 0.5,
InvalidMessageDeliveriesWeight: -1,
InvalidMessageDeliveriesDecay: 0.5,
},
},
}).validate() != nil {
t.Fatal("expected validation success")
}
}
func TestPeerScoreParamsValidation_ValidParams_SkipAtomicValidation(t *testing.T) {
appScore := func(peer.ID) float64 { return 0 }
// don't use these params in production!
p := &PeerScoreParams{}
setParamAndValidate(t, p, func(params *PeerScoreParams) {
params.SkipAtomicValidation = true
})
setParamAndValidate(t, p, func(params *PeerScoreParams) {
params.AppSpecificScore = appScore
})
setParamAndValidate(t, p, func(params *PeerScoreParams) {
params.DecayInterval = time.Second
params.DecayToZero = 0.01
})
setParamAndValidate(t, p, func(params *PeerScoreParams) {
params.IPColocationFactorWeight = -1
params.IPColocationFactorThreshold = 1
})
setParamAndValidate(t, p, func(params *PeerScoreParams) {
params.BehaviourPenaltyWeight = -1
params.BehaviourPenaltyDecay = 0.999
})
p = &PeerScoreParams{SkipAtomicValidation: true, AppSpecificScore: appScore}
setParamAndValidate(t, p, func(params *PeerScoreParams) {
params.TopicScoreCap = 1
})
setParamAndValidate(t, p, func(params *PeerScoreParams) {
params.DecayInterval = time.Second
params.DecayToZero = 0.01
})
setParamAndValidate(t, p, func(params *PeerScoreParams) {
params.IPColocationFactorWeight = -1
params.IPColocationFactorThreshold = 1
})
setParamAndValidate(t, p, func(params *PeerScoreParams) {
params.BehaviourPenaltyWeight = -1
params.BehaviourPenaltyDecay = 0.999
})
setParamAndValidate(t, p, func(params *PeerScoreParams) {
params.Topics = map[string]*TopicScoreParams{
"test": {
TopicWeight: 1,
TimeInMeshWeight: 0.01,
TimeInMeshQuantum: time.Second,
TimeInMeshCap: 10,
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: 0.5,
FirstMessageDeliveriesCap: 10,
MeshMessageDeliveriesWeight: -1,
MeshMessageDeliveriesDecay: 0.5,
MeshMessageDeliveriesCap: 10,
MeshMessageDeliveriesThreshold: 5,
MeshMessageDeliveriesWindow: time.Millisecond,
MeshMessageDeliveriesActivation: time.Second,
MeshFailurePenaltyWeight: -1,
MeshFailurePenaltyDecay: 0.5,
InvalidMessageDeliveriesWeight: -1,
InvalidMessageDeliveriesDecay: 0.5,
},
}
})
}
func TestScoreParameterDecay(t *testing.T) {
@ -326,3 +723,17 @@ func TestScoreParameterDecay(t *testing.T) {
t.Fatalf("expected .9987216039048303, got %f", decay1hr)
}
}
func setParamAndValidate(t *testing.T, params *PeerScoreParams, set func(*PeerScoreParams)) {
set(params)
if err := params.validate(); err != nil {
t.Fatalf("expected validation success, got: %s", err)
}
}
func setTopicParamAndValidate(t *testing.T, params *TopicScoreParams, set func(topic *TopicScoreParams)) {
set(params)
if err := params.validate(); err != nil {
t.Fatalf("expected validation success, got: %s", err)
}
}