From c93e47ada12c85c8d783c845e1862424c3ff1661 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 19 Oct 2022 13:31:43 -0700 Subject: [PATCH 01/12] Update go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 231497f1..1e7d882a 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/libp2p/go-libp2p-pubsub +module github.com/yhassanzadeh13/go-libp2p-pubsub go 1.17 From 19b4efd116aa692055dfa7458ac0962b3374bb4d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 28 Oct 2022 10:10:20 -0700 Subject: [PATCH 02/12] Refactor GossipSub Construction (#1) * 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 * chore: Update .github/workflows/stale.yml [skip ci] * adds with gossipsub tracker Co-authored-by: libp2p-mgmt-read-write[bot] <104492852+libp2p-mgmt-read-write[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 26 ++ gossipsub.go | 24 +- score_params.go | 219 ++++++++++--- score_params_test.go | 621 ++++++++++++++++++++++++++++++------ 4 files changed, 733 insertions(+), 157 deletions(-) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..6f6d895d --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,26 @@ +name: Close and mark stale issue + +on: + schedule: + - cron: '0 0 * * *' + +jobs: + stale: + + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'Oops, seems like we needed more information for this issue, please comment with more details or this issue will be closed in 7 days.' + close-issue-message: 'This issue was closed because it is missing author input.' + stale-issue-label: 'kind/stale' + any-of-labels: 'need/author-input' + exempt-issue-labels: 'need/triage,need/community-input,need/maintainer-input,need/maintainers-input,need/analysis,status/blocked,status/in-progress,status/ready,status/deferred,status/inactive' + days-before-issue-stale: 6 + days-before-issue-close: 7 + enable-statistics: true diff --git a/gossipsub.go b/gossipsub.go index 713bc623..3e604111 100644 --- a/gossipsub.go +++ b/gossipsub.go @@ -204,10 +204,22 @@ type GossipSubParams struct { IWantFollowupTime time.Duration } -// NewGossipSub returns a new PubSub object using GossipSubRouter as the router. +// NewGossipSub returns a new PubSub object using the default GossipSubRouter as the router. func NewGossipSub(ctx context.Context, h host.Host, opts ...Option) (*PubSub, error) { + rt := DefaultGossipSubRouter(h) + opts = append(opts, WithRawTracer(rt.tagTracer)) + return NewGossipSubWithRouter(ctx, h, rt, opts...) +} + +// NewGossipSubWithRouter returns a new PubSub object using the given router. +func NewGossipSubWithRouter(ctx context.Context, h host.Host, rt PubSubRouter, opts ...Option) (*PubSub, error) { + return NewPubSub(ctx, h, rt, opts...) +} + +// DefaultGossipSubRouter returns a new GossipSubRouter with default parameters. +func DefaultGossipSubRouter(h host.Host) *GossipSubRouter { params := DefaultGossipSubParams() - rt := &GossipSubRouter{ + return &GossipSubRouter{ peers: make(map[peer.ID]protocol.ID), mesh: make(map[string]map[peer.ID]struct{}), fanout: make(map[string]map[peer.ID]struct{}), @@ -225,10 +237,6 @@ func NewGossipSub(ctx context.Context, h host.Host, opts ...Option) (*PubSub, er tagTracer: newTagTracer(h.ConnManager()), params: params, } - - // hook the tag tracer - opts = append(opts, WithRawTracer(rt.tagTracer)) - return NewPubSub(ctx, h, rt, opts...) } // DefaultGossipSubParams returns the default gossip sub parameters @@ -1909,6 +1917,10 @@ func (gs *GossipSubRouter) getPeers(topic string, count int, filter func(peer.ID return peers } +func (gs *GossipSubRouter) WithTagTracerPubsubOption() Option { + return WithRawTracer(gs.tagTracer) +} + func peerListToMap(peers []peer.ID) map[peer.ID]struct{} { pmap := make(map[peer.ID]struct{}) for _, p := range peers { diff --git a/score_params.go b/score_params.go index 827f0456..99c9526d 100644 --- a/score_params.go +++ b/score_params.go @@ -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.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.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.AcceptPXThreshold < 0 || isInvalidNumber(p.AcceptPXThreshold) { - return fmt.Errorf("invalid accept PX threshold; it must be >= 0 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.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) diff --git a/score_params_test.go b/score_params_test.go index e91a6832..89f216e1 100644 --- a/score_params_test.go +++ b/score_params_test.go @@ -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 { - t.Fatal("expected validation error") +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{TopicWeight: -1}).validate() == nil { + if (&TopicScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + TopicWeight: -1, + }).validate() == nil { t.Fatal("expected validation error") } - if (&TopicScoreParams{TimeInMeshWeight: -1, TimeInMeshQuantum: time.Second}).validate() == nil { + if (&TopicScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + TimeInMeshWeight: -1, + TimeInMeshQuantum: time.Second, + }).validate() == nil { t.Fatal("expected validation error") } - if (&TopicScoreParams{TimeInMeshWeight: 1, TimeInMeshQuantum: -1}).validate() == nil { + if (&TopicScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + TimeInMeshWeight: 1, + TimeInMeshQuantum: -1, + }).validate() == nil { t.Fatal("expected validation error") } - if (&TopicScoreParams{TimeInMeshWeight: 1, TimeInMeshQuantum: time.Second, TimeInMeshCap: -1}).validate() == nil { + if (&TopicScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + TimeInMeshWeight: 1, + TimeInMeshQuantum: time.Second, + TimeInMeshCap: -1, + }).validate() == nil { t.Fatal("expected validation error") } - if (&TopicScoreParams{TimeInMeshQuantum: time.Second, FirstMessageDeliveriesWeight: -1}).validate() == nil { + if (&TopicScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + TimeInMeshQuantum: time.Second, + FirstMessageDeliveriesWeight: -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, + FirstMessageDeliveriesWeight: 1, + FirstMessageDeliveriesDecay: -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, + FirstMessageDeliveriesWeight: 1, + FirstMessageDeliveriesDecay: 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, + FirstMessageDeliveriesWeight: 1, + FirstMessageDeliveriesDecay: .5, + FirstMessageDeliveriesCap: -1, + }).validate() == nil { t.Fatal("expected validation error") } - if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshMessageDeliveriesWeight: 1}).validate() == nil { + if (&TopicScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + TimeInMeshQuantum: time.Second, + MeshMessageDeliveriesWeight: 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, + MeshMessageDeliveriesWeight: -1, + MeshMessageDeliveriesDecay: -1, + }).validate() == nil { t.Fatal("expected validation error") } - if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshMessageDeliveriesWeight: -1, MeshMessageDeliveriesDecay: 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, MeshMessageDeliveriesWeight: -1, MeshMessageDeliveriesDecay: .5, MeshMessageDeliveriesCap: -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{TimeInMeshQuantum: time.Second, MeshMessageDeliveriesWeight: -1, MeshMessageDeliveriesDecay: .5, MeshMessageDeliveriesCap: 5, MeshMessageDeliveriesThreshold: -3}).validate() == nil { + if (&TopicScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + 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 { + 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{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, + 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, MeshFailurePenaltyWeight: 1}).validate() == nil { + if (&TopicScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + TimeInMeshQuantum: time.Second, + MeshFailurePenaltyWeight: 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, + MeshFailurePenaltyWeight: -1, + MeshFailurePenaltyDecay: -1, + }).validate() == nil { t.Fatal("expected validation error") } - if (&TopicScoreParams{TimeInMeshQuantum: time.Second, MeshFailurePenaltyWeight: -1, MeshFailurePenaltyDecay: 2}).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, InvalidMessageDeliveriesWeight: 1}).validate() == nil { + if (&TopicScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + TimeInMeshQuantum: time.Second, + InvalidMessageDeliveriesWeight: 1, + }).validate() == nil { t.Fatal("expected validation error") } - if (&TopicScoreParams{TimeInMeshQuantum: time.Second, InvalidMessageDeliveriesWeight: -1, InvalidMessageDeliveriesDecay: -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, 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,67 +341,223 @@ 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 { + if (&PeerScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + 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 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{TopicScoreCap: 1, AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 0.01, IPColocationFactorWeight: 1}).validate() == nil { + + 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{TopicScoreCap: 1, AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 0.01, IPColocationFactorWeight: -1, IPColocationFactorThreshold: -1}).validate() == nil { + 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{TopicScoreCap: 1, AppSpecificScore: appScore, DecayInterval: time.Millisecond, DecayToZero: 0.01, IPColocationFactorWeight: -1, IPColocationFactorThreshold: 1}).validate() == nil { + 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{TopicScoreCap: 1, AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: -1, IPColocationFactorWeight: -1, IPColocationFactorThreshold: 1}).validate() == nil { + 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{TopicScoreCap: 1, AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 2, IPColocationFactorWeight: -1, IPColocationFactorThreshold: 1}).validate() == nil { + 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{AppSpecificScore: appScore, DecayInterval: time.Second, DecayToZero: 0.01, BehaviourPenaltyWeight: 1}).validate() == nil { + if (&PeerScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + 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 { + if (&PeerScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + 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 { + if (&PeerScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + 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! + // Checks the topic parameters for invalid values such as infinite and + // NaN numbers. if (&PeerScoreParams{ + SkipAtomicValidation: skipAtomicValidation, + 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") + Topics: map[string]*TopicScoreParams{ + "test": { + TopicWeight: math.Inf(0), + TimeInMeshWeight: math.NaN(), + TimeInMeshQuantum: time.Second, + TimeInMeshCap: 10, + FirstMessageDeliveriesWeight: math.Inf(1), + FirstMessageDeliveriesDecay: 0.5, + FirstMessageDeliveriesCap: 10, + MeshMessageDeliveriesWeight: math.Inf(-1), + MeshMessageDeliveriesDecay: math.NaN(), + MeshMessageDeliveriesCap: math.Inf(0), + MeshMessageDeliveriesThreshold: 5, + MeshMessageDeliveriesWindow: time.Millisecond, + MeshMessageDeliveriesActivation: time.Second, + MeshFailurePenaltyWeight: -1, + MeshFailurePenaltyDecay: math.NaN(), + InvalidMessageDeliveriesWeight: math.Inf(0), + InvalidMessageDeliveriesDecay: math.NaN(), + }, + }, + }).validate() == nil { + t.Fatal("expected validation failure") } if (&PeerScoreParams{ - TopicScoreCap: 1, + SkipAtomicValidation: skipAtomicValidation, AppSpecificScore: appScore, DecayInterval: time.Second, - DecayToZero: 0.01, - IPColocationFactorWeight: -1, + DecayToZero: math.Inf(0), + IPColocationFactorWeight: math.Inf(-1), IPColocationFactorThreshold: 1, - BehaviourPenaltyWeight: -1, - BehaviourPenaltyDecay: 0.999, - }).validate() != nil { - t.Fatal("expected validation success") + 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, @@ -213,8 +565,8 @@ func TestPeerScoreParamsValidation(t *testing.T) { IPColocationFactorWeight: -1, IPColocationFactorThreshold: 1, Topics: map[string]*TopicScoreParams{ - "test": &TopicScoreParams{ - TopicWeight: 1, + "test": { + TopicWeight: -1, TimeInMeshWeight: 0.01, TimeInMeshQuantum: time.Second, TimeInMeshCap: 10, @@ -233,11 +585,40 @@ func TestPeerScoreParamsValidation(t *testing.T) { 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") } - // don't use these params in production! if (&PeerScoreParams{ TopicScoreCap: 1, AppSpecificScore: appScore, @@ -246,8 +627,8 @@ func TestPeerScoreParamsValidation(t *testing.T) { IPColocationFactorWeight: -1, IPColocationFactorThreshold: 1, Topics: map[string]*TopicScoreParams{ - "test": &TopicScoreParams{ - TopicWeight: -1, + "test": { + TopicWeight: 1, TimeInMeshWeight: 0.01, TimeInMeshQuantum: time.Second, TimeInMeshCap: 10, @@ -266,58 +647,74 @@ func TestPeerScoreParamsValidation(t *testing.T) { InvalidMessageDeliveriesDecay: 0.5, }, }, - }).validate() == nil { - t.Fatal("expected validation failure") + }).validate() != nil { + t.Fatal("expected validation success") } +} - // Checks the topic parameters for invalid values such as infinite and - // NaN numbers. +func TestPeerScoreParamsValidation_ValidParams_SkipAtomicValidation(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: math.Inf(0), - IPColocationFactorWeight: math.Inf(-1), - IPColocationFactorThreshold: 1, - BehaviourPenaltyWeight: math.Inf(0), - BehaviourPenaltyDecay: math.NaN(), - }).validate() == nil { - t.Fatal("expected validation failure") - } + // 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 + }) - if (&PeerScoreParams{ - TopicScoreCap: 1, - AppSpecificScore: appScore, - DecayInterval: time.Second, - DecayToZero: 0.01, - IPColocationFactorWeight: -1, - IPColocationFactorThreshold: 1, - Topics: map[string]*TopicScoreParams{ - "test": &TopicScoreParams{ - TopicWeight: math.Inf(0), - TimeInMeshWeight: math.NaN(), + 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: math.Inf(1), + FirstMessageDeliveriesWeight: 1, FirstMessageDeliveriesDecay: 0.5, FirstMessageDeliveriesCap: 10, - MeshMessageDeliveriesWeight: math.Inf(-1), - MeshMessageDeliveriesDecay: math.NaN(), - MeshMessageDeliveriesCap: math.Inf(0), + MeshMessageDeliveriesWeight: -1, + MeshMessageDeliveriesDecay: 0.5, + MeshMessageDeliveriesCap: 10, MeshMessageDeliveriesThreshold: 5, MeshMessageDeliveriesWindow: time.Millisecond, MeshMessageDeliveriesActivation: time.Second, MeshFailurePenaltyWeight: -1, - MeshFailurePenaltyDecay: math.NaN(), - InvalidMessageDeliveriesWeight: math.Inf(0), - InvalidMessageDeliveriesDecay: math.NaN(), + MeshFailurePenaltyDecay: 0.5, + InvalidMessageDeliveriesWeight: -1, + InvalidMessageDeliveriesDecay: 0.5, }, - }, - }).validate() == nil { - t.Fatal("expected validation failure") - } - + } + }) } 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) + } +} From 7c7c05a7487fc2e06d5330817347ed0751e945e2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 7 Nov 2022 17:52:14 -0800 Subject: [PATCH 03/12] decouples options --- discovery_test.go | 2 +- gossip_tracer.go | 50 ++++++------- gossipsub.go | 148 +++++++++++++++++++++++++------------- gossipsub_connmgr_test.go | 4 +- gossipsub_feat.go | 12 +--- gossipsub_feat_test.go | 4 +- gossipsub_matchfn_test.go | 8 +-- gossipsub_test.go | 146 +++++++++++++++++++++++-------------- peer_gater.go | 58 +++++++-------- score.go | 85 +++++++++++----------- score_test.go | 4 +- tag_tracer.go | 58 +++++++-------- topic.go | 6 +- trace_test.go | 4 +- 14 files changed, 334 insertions(+), 255 deletions(-) diff --git a/discovery_test.go b/discovery_test.go index 8d5d4bd4..2a38285b 100644 --- a/discovery_test.go +++ b/discovery_test.go @@ -244,7 +244,7 @@ func TestGossipSubDiscoveryAfterBootstrap(t *testing.T) { s = server2 } disc := &mockDiscoveryClient{h, s} - ps := getGossipsub(ctx, h, WithDiscovery(disc, WithDiscoveryOpts(discOpts...))) + ps := getGossipSub(ctx, h, WithDiscovery(disc, WithDiscoveryOpts(discOpts...))) psubs[i] = ps topicHandlers[i], _ = ps.Join(topic) } diff --git a/gossip_tracer.go b/gossip_tracer.go index 7435cf74..10bf1f62 100644 --- a/gossip_tracer.go +++ b/gossip_tracer.go @@ -9,10 +9,10 @@ import ( "github.com/libp2p/go-libp2p/core/protocol" ) -// gossipTracer is an internal tracer that tracks IWANT requests in order to penalize +// GossipTracer is an internal tracer that tracks IWANT requests in order to penalize // peers who don't follow up on IWANT requests after an IHAVE advertisement. // The tracking of promises is probabilistic to avoid using too much memory. -type gossipTracer struct { +type GossipTracer struct { sync.Mutex idGen *msgIDGenerator @@ -27,15 +27,15 @@ type gossipTracer struct { peerPromises map[peer.ID]map[string]struct{} } -func newGossipTracer() *gossipTracer { - return &gossipTracer{ +func newGossipTracer() *GossipTracer { + return &GossipTracer{ idGen: newMsgIdGenerator(), promises: make(map[string]map[peer.ID]time.Time), peerPromises: make(map[peer.ID]map[string]struct{}), } } -func (gt *gossipTracer) Start(gs *GossipSubRouter) { +func (gt *GossipTracer) Start(gs *GossipSubRouter) { if gt == nil { return } @@ -45,7 +45,7 @@ func (gt *gossipTracer) Start(gs *GossipSubRouter) { } // track a promise to deliver a message from a list of msgIDs we are requesting -func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs []string) { +func (gt *GossipTracer) AddPromise(p peer.ID, msgIDs []string) { if gt == nil { return } @@ -76,7 +76,7 @@ func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs []string) { // returns the number of broken promises for each peer who didn't follow up // on an IWANT request. -func (gt *gossipTracer) GetBrokenPromises() map[peer.ID]int { +func (gt *GossipTracer) GetBrokenPromises() map[peer.ID]int { if gt == nil { return nil } @@ -114,9 +114,9 @@ func (gt *gossipTracer) GetBrokenPromises() map[peer.ID]int { return res } -var _ RawTracer = (*gossipTracer)(nil) +var _ RawTracer = (*GossipTracer)(nil) -func (gt *gossipTracer) fulfillPromise(msg *Message) { +func (gt *GossipTracer) fulfillPromise(msg *Message) { mid := gt.idGen.ID(msg) gt.Lock() @@ -140,12 +140,12 @@ func (gt *gossipTracer) fulfillPromise(msg *Message) { } } -func (gt *gossipTracer) DeliverMessage(msg *Message) { +func (gt *GossipTracer) DeliverMessage(msg *Message) { // someone delivered a message, fulfill promises for it gt.fulfillPromise(msg) } -func (gt *gossipTracer) RejectMessage(msg *Message, reason string) { +func (gt *GossipTracer) RejectMessage(msg *Message, reason string) { // A message got rejected, so we can fulfill promises and let the score penalty apply // from invalid message delivery. // We do take exception and apply promise penalty regardless in the following cases, where @@ -160,26 +160,26 @@ func (gt *gossipTracer) RejectMessage(msg *Message, reason string) { gt.fulfillPromise(msg) } -func (gt *gossipTracer) ValidateMessage(msg *Message) { +func (gt *GossipTracer) ValidateMessage(msg *Message) { // we consider the promise fulfilled as soon as the message begins validation // if it was a case of signature issue it would have been rejected immediately // without triggering the Validate trace gt.fulfillPromise(msg) } -func (gt *gossipTracer) AddPeer(p peer.ID, proto protocol.ID) {} -func (gt *gossipTracer) RemovePeer(p peer.ID) {} -func (gt *gossipTracer) Join(topic string) {} -func (gt *gossipTracer) Leave(topic string) {} -func (gt *gossipTracer) Graft(p peer.ID, topic string) {} -func (gt *gossipTracer) Prune(p peer.ID, topic string) {} -func (gt *gossipTracer) DuplicateMessage(msg *Message) {} -func (gt *gossipTracer) RecvRPC(rpc *RPC) {} -func (gt *gossipTracer) SendRPC(rpc *RPC, p peer.ID) {} -func (gt *gossipTracer) DropRPC(rpc *RPC, p peer.ID) {} -func (gt *gossipTracer) UndeliverableMessage(msg *Message) {} - -func (gt *gossipTracer) ThrottlePeer(p peer.ID) { +func (gt *GossipTracer) AddPeer(p peer.ID, proto protocol.ID) {} +func (gt *GossipTracer) RemovePeer(p peer.ID) {} +func (gt *GossipTracer) Join(topic string) {} +func (gt *GossipTracer) Leave(topic string) {} +func (gt *GossipTracer) Graft(p peer.ID, topic string) {} +func (gt *GossipTracer) Prune(p peer.ID, topic string) {} +func (gt *GossipTracer) DuplicateMessage(msg *Message) {} +func (gt *GossipTracer) RecvRPC(rpc *RPC) {} +func (gt *GossipTracer) SendRPC(rpc *RPC, p peer.ID) {} +func (gt *GossipTracer) DropRPC(rpc *RPC, p peer.ID) {} +func (gt *GossipTracer) UndeliverableMessage(msg *Message) {} + +func (gt *GossipTracer) ThrottlePeer(p peer.ID) { gt.Lock() defer gt.Unlock() diff --git a/gossipsub.go b/gossipsub.go index 3e604111..0f847446 100644 --- a/gossipsub.go +++ b/gossipsub.go @@ -206,7 +206,10 @@ type GossipSubParams struct { // NewGossipSub returns a new PubSub object using the default GossipSubRouter as the router. func NewGossipSub(ctx context.Context, h host.Host, opts ...Option) (*PubSub, error) { - rt := DefaultGossipSubRouter(h) + rt, err := DefaultGossipSubRouter(h) + if err != nil { + return nil, fmt.Errorf("failed to create default gossipsub router: %w", err) + } opts = append(opts, WithRawTracer(rt.tagTracer)) return NewGossipSubWithRouter(ctx, h, rt, opts...) } @@ -216,10 +219,12 @@ func NewGossipSubWithRouter(ctx context.Context, h host.Host, rt PubSubRouter, o return NewPubSub(ctx, h, rt, opts...) } +type GossipSubRouterOption func(*GossipSubRouter) error + // DefaultGossipSubRouter returns a new GossipSubRouter with default parameters. -func DefaultGossipSubRouter(h host.Host) *GossipSubRouter { +func DefaultGossipSubRouter(h host.Host, opts ...GossipSubRouterOption) (*GossipSubRouter, error) { params := DefaultGossipSubParams() - return &GossipSubRouter{ + rt := &GossipSubRouter{ peers: make(map[peer.ID]protocol.ID), mesh: make(map[string]map[peer.ID]struct{}), fanout: make(map[string]map[peer.ID]struct{}), @@ -237,6 +242,14 @@ func DefaultGossipSubRouter(h host.Host) *GossipSubRouter { tagTracer: newTagTracer(h.ConnManager()), params: params, } + + for _, opt := range opts { + if err := opt(rt); err != nil { + return nil, fmt.Errorf("failed to apply gossipsub router option: %w", err) + } + } + + return rt, nil } // DefaultGossipSubParams returns the default gossip sub parameters @@ -277,7 +290,7 @@ func DefaultGossipSubParams() GossipSubParams { // WithPeerScore is a gossipsub router option that enables peer scoring. func WithPeerScore(params *PeerScoreParams, thresholds *PeerScoreThresholds) Option { return func(ps *PubSub) error { - gs, ok := ps.rt.(*GossipSubRouter) + gs, ok := ps.rt.(GossipPubSubRouter) if !ok { return fmt.Errorf("pubsub router is not gossipsub") } @@ -294,21 +307,17 @@ func WithPeerScore(params *PeerScoreParams, thresholds *PeerScoreThresholds) Opt return err } - gs.score = newPeerScore(params) - gs.gossipThreshold = thresholds.GossipThreshold - gs.publishThreshold = thresholds.PublishThreshold - gs.graylistThreshold = thresholds.GraylistThreshold - gs.acceptPXThreshold = thresholds.AcceptPXThreshold - gs.opportunisticGraftThreshold = thresholds.OpportunisticGraftThreshold + gs.SetPeerScore(newPeerScore(params)) + gs.SetPeerScoreThresholds(thresholds) - gs.gossipTracer = newGossipTracer() + gs.SetGossipTracer(newGossipTracer()) // hook the tracer if ps.tracer != nil { - ps.tracer.raw = append(ps.tracer.raw, gs.score, gs.gossipTracer) + ps.tracer.raw = append(ps.tracer.raw, gs.GetPeerScore(), gs.GetGossipTracer()) } else { ps.tracer = &pubsubTracer{ - raw: []RawTracer{gs.score, gs.gossipTracer}, + raw: []RawTracer{gs.GetPeerScore(), gs.GetGossipTracer()}, pid: ps.host.ID(), idGen: ps.idGen, } @@ -321,15 +330,9 @@ func WithPeerScore(params *PeerScoreParams, thresholds *PeerScoreThresholds) Opt // WithFloodPublish is a gossipsub router option that enables flood publishing. // When this is enabled, published messages are forwarded to all peers with score >= // to publishThreshold -func WithFloodPublish(floodPublish bool) Option { - return func(ps *PubSub) error { - gs, ok := ps.rt.(*GossipSubRouter) - if !ok { - return fmt.Errorf("pubsub router is not gossipsub") - } - +func WithFloodPublish(floodPublish bool) GossipSubRouterOption { + return func(gs *GossipSubRouter) error { gs.floodPublish = floodPublish - return nil } } @@ -337,15 +340,9 @@ func WithFloodPublish(floodPublish bool) Option { // WithPeerExchange is a gossipsub router option that enables Peer eXchange on PRUNE. // This should generally be enabled in bootstrappers and well connected/trusted nodes // used for bootstrapping. -func WithPeerExchange(doPX bool) Option { - return func(ps *PubSub) error { - gs, ok := ps.rt.(*GossipSubRouter) - if !ok { - return fmt.Errorf("pubsub router is not gossipsub") - } - +func WithPeerExchange(doPX bool) GossipSubRouterOption { + return func(gs *GossipSubRouter) error { gs.doPX = doPX - return nil } } @@ -357,7 +354,7 @@ func WithPeerExchange(doPX bool) Option { // symmetrically configured at both ends. func WithDirectPeers(pis []peer.AddrInfo) Option { return func(ps *PubSub) error { - gs, ok := ps.rt.(*GossipSubRouter) + gs, ok := ps.rt.(GossipPubSubRouter) if !ok { return fmt.Errorf("pubsub router is not gossipsub") } @@ -368,10 +365,10 @@ func WithDirectPeers(pis []peer.AddrInfo) Option { ps.host.Peerstore().AddAddrs(pi.ID, pi.Addrs, peerstore.PermanentAddrTTL) } - gs.direct = direct + gs.SetDirectPeers(direct) - if gs.tagTracer != nil { - gs.tagTracer.direct = direct + if gs.GetTagTracer() != nil { + gs.GetTagTracer().direct = direct } return nil @@ -382,12 +379,8 @@ func WithDirectPeers(pis []peer.AddrInfo) Option { // heartbeat ticks between attempting to reconnect direct peers that are not // currently connected. A "tick" is based on the heartbeat interval, which is // 1s by default. The default value for direct connect ticks is 300. -func WithDirectConnectTicks(t uint64) Option { - return func(ps *PubSub) error { - gs, ok := ps.rt.(*GossipSubRouter) - if !ok { - return fmt.Errorf("pubsub router is not gossipsub") - } +func WithDirectConnectTicks(t uint64) GossipSubRouterOption { + return func(gs *GossipSubRouter) error { gs.params.DirectConnectTicks = t return nil } @@ -395,12 +388,8 @@ func WithDirectConnectTicks(t uint64) Option { // WithGossipSubParams is a gossip sub router option that allows a custom // config to be set when instantiating the gossipsub router. -func WithGossipSubParams(cfg GossipSubParams) Option { - return func(ps *PubSub) error { - gs, ok := ps.rt.(*GossipSubRouter) - if !ok { - return fmt.Errorf("pubsub router is not gossipsub") - } +func WithGossipSubParams(cfg GossipSubParams) GossipSubRouterOption { + return func(gs *GossipSubRouter) error { // Overwrite current config and associated variables in the router. gs.params = cfg gs.connect = make(chan connectInfo, cfg.MaxPendingConnections) @@ -410,6 +399,25 @@ func WithGossipSubParams(cfg GossipSubParams) Option { } } +type GossipPubSubRouter interface { + PubSubRouter + + SetPeerScore(*PeerScore) + GetPeerScore() *PeerScore + + SetPeerScoreThresholds(*PeerScoreThresholds) + + SetGossipTracer(*GossipTracer) + GetGossipTracer() *GossipTracer + + GetTagTracer() *TagTracer + + SetDirectPeers(map[peer.ID]struct{}) + + SetPeerGater(*PeerGater) + GetPeerGater() *PeerGater +} + // GossipSubRouter is a router that implements the gossipsub protocol. // For each topic we have joined, we maintain an overlay through which // messages flow; this is the mesh map. @@ -437,10 +445,10 @@ type GossipSubRouter struct { mcache *MessageCache tracer *pubsubTracer - score *peerScore - gossipTracer *gossipTracer - tagTracer *tagTracer - gate *peerGater + score *PeerScore + gossipTracer *GossipTracer + tagTracer *TagTracer + gate *PeerGater // config for gossipsub parameters params GossipSubParams @@ -476,6 +484,8 @@ type GossipSubRouter struct { heartbeatTicks uint64 } +var _ GossipPubSubRouter = (*GossipSubRouter)(nil) + type connectInfo struct { p peer.ID spr *record.Envelope @@ -1921,6 +1931,46 @@ func (gs *GossipSubRouter) WithTagTracerPubsubOption() Option { return WithRawTracer(gs.tagTracer) } +func (gs *GossipSubRouter) SetPeerScore(score *PeerScore) { + gs.score = score +} + +func (gs *GossipSubRouter) GetPeerScore() *PeerScore { + return gs.score +} + +func (gs *GossipSubRouter) SetPeerScoreThresholds(thresholds *PeerScoreThresholds) { + gs.publishThreshold = thresholds.PublishThreshold + gs.graylistThreshold = thresholds.GraylistThreshold + gs.acceptPXThreshold = thresholds.AcceptPXThreshold + gs.gossipThreshold = thresholds.GossipThreshold + gs.opportunisticGraftThreshold = thresholds.OpportunisticGraftThreshold +} + +func (gs *GossipSubRouter) SetGossipTracer(tracer *GossipTracer) { + gs.gossipTracer = tracer +} + +func (gs *GossipSubRouter) GetGossipTracer() *GossipTracer { + return gs.gossipTracer +} + +func (gs *GossipSubRouter) GetTagTracer() *TagTracer { + return gs.tagTracer +} + +func (gs *GossipSubRouter) SetDirectPeers(direct map[peer.ID]struct{}) { + gs.direct = direct +} + +func (gs *GossipSubRouter) SetPeerGater(gater *PeerGater) { + gs.gate = gater +} + +func (gs *GossipSubRouter) GetPeerGater() *PeerGater { + return gs.gate +} + func peerListToMap(peers []peer.ID) map[peer.ID]struct{} { pmap := make(map[peer.ID]struct{}) for _, p := range peers { diff --git a/gossipsub_connmgr_test.go b/gossipsub_connmgr_test.go index 0a97312c..9502b941 100644 --- a/gossipsub_connmgr_test.go +++ b/gossipsub_connmgr_test.go @@ -79,8 +79,8 @@ func TestGossipsubConnTagMessageDeliveries(t *testing.T) { // use flood publishing, so non-mesh peers will still be delivering messages // to everyone - psubs := getGossipsubs(ctx, honestHosts, - WithFloodPublish(true)) + routers := getGossipSubRouters(honestHosts, WithFloodPublish(true)) + psubs := getGossipSubsWithRouters(ctx, honestHosts, routers) // sybil squatters to be connected later sybilHosts := getNetHosts(t, ctx, nSquatter) diff --git a/gossipsub_feat.go b/gossipsub_feat.go index d5750af3..83a3b9d9 100644 --- a/gossipsub_feat.go +++ b/gossipsub_feat.go @@ -1,8 +1,6 @@ package pubsub import ( - "fmt" - "github.com/libp2p/go-libp2p/core/protocol" ) @@ -37,16 +35,10 @@ func GossipSubDefaultFeatures(feat GossipSubFeature, proto protocol.ID) bool { // WithGossipSubProtocols is a gossipsub router option that configures a custom protocol list // and feature test function -func WithGossipSubProtocols(protos []protocol.ID, feature GossipSubFeatureTest) Option { - return func(ps *PubSub) error { - gs, ok := ps.rt.(*GossipSubRouter) - if !ok { - return fmt.Errorf("pubsub router is not gossipsub") - } - +func WithGossipSubProtocols(protos []protocol.ID, feature GossipSubFeatureTest) GossipSubRouterOption { + return func(gs *GossipSubRouter) error { gs.protos = protos gs.feature = feature - return nil } } diff --git a/gossipsub_feat_test.go b/gossipsub_feat_test.go index 712f16df..0643d9e9 100644 --- a/gossipsub_feat_test.go +++ b/gossipsub_feat_test.go @@ -43,8 +43,8 @@ func TestGossipSubCustomProtocols(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() hosts := getNetHosts(t, ctx, 3) - - gsubs := getGossipsubs(ctx, hosts[:2], WithGossipSubProtocols(protos, features)) + routers := getGossipSubRouters(hosts[:2], WithGossipSubProtocols(protos, features)) + gsubs := getGossipSubsWithRouters(ctx, hosts[:2], routers) fsub := getPubsub(ctx, hosts[2]) psubs := append(gsubs, fsub) diff --git a/gossipsub_matchfn_test.go b/gossipsub_matchfn_test.go index 516fdf5f..a5f5d8a8 100644 --- a/gossipsub_matchfn_test.go +++ b/gossipsub_matchfn_test.go @@ -19,10 +19,10 @@ func TestGossipSubMatchingFn(t *testing.T) { h := getNetHosts(t, ctx, 4) psubs := []*PubSub{ - getGossipsub(ctx, h[0], WithProtocolMatchFn(protocolNameMatch), WithGossipSubProtocols([]protocol.ID{customsubA100, GossipSubID_v11}, GossipSubDefaultFeatures)), - getGossipsub(ctx, h[1], WithProtocolMatchFn(protocolNameMatch), WithGossipSubProtocols([]protocol.ID{customsubA101Beta}, GossipSubDefaultFeatures)), - getGossipsub(ctx, h[2], WithProtocolMatchFn(protocolNameMatch), WithGossipSubProtocols([]protocol.ID{GossipSubID_v11}, GossipSubDefaultFeatures)), - getGossipsub(ctx, h[3], WithProtocolMatchFn(protocolNameMatch), WithGossipSubProtocols([]protocol.ID{customsubB100}, GossipSubDefaultFeatures)), + getGossipSubWithRouter(ctx, h[0], getGossipSubRouter(h[0], WithGossipSubProtocols([]protocol.ID{customsubA100, GossipSubID_v11}, GossipSubDefaultFeatures)), WithProtocolMatchFn(protocolNameMatch)), + getGossipSubWithRouter(ctx, h[1], getGossipSubRouter(h[1], WithGossipSubProtocols([]protocol.ID{customsubA101Beta}, GossipSubDefaultFeatures)), WithProtocolMatchFn(protocolNameMatch)), + getGossipSubWithRouter(ctx, h[2], getGossipSubRouter(h[2], WithGossipSubProtocols([]protocol.ID{GossipSubID_v11}, GossipSubDefaultFeatures)), WithProtocolMatchFn(protocolNameMatch)), + getGossipSubWithRouter(ctx, h[3], getGossipSubRouter(h[3], WithGossipSubProtocols([]protocol.ID{customsubB100}, GossipSubDefaultFeatures)), WithProtocolMatchFn(protocolNameMatch)), } connect(t, h[0], h[1]) diff --git a/gossipsub_test.go b/gossipsub_test.go index ed7859a1..951f1484 100644 --- a/gossipsub_test.go +++ b/gossipsub_test.go @@ -25,7 +25,7 @@ import ( "github.com/libp2p/go-msgio/protoio" ) -func getGossipsub(ctx context.Context, h host.Host, opts ...Option) *PubSub { +func getGossipSub(ctx context.Context, h host.Host, opts ...Option) *PubSub { ps, err := NewGossipSub(ctx, h, opts...) if err != nil { panic(err) @@ -33,20 +33,57 @@ func getGossipsub(ctx context.Context, h host.Host, opts ...Option) *PubSub { return ps } -func getGossipsubs(ctx context.Context, hs []host.Host, opts ...Option) []*PubSub { +func getGossipSubs(ctx context.Context, hs []host.Host, opts ...Option) []*PubSub { var psubs []*PubSub for _, h := range hs { - psubs = append(psubs, getGossipsub(ctx, h, opts...)) + psubs = append(psubs, getGossipSub(ctx, h, opts...)) } return psubs } +func getGossipSubsWithRouters(ctx context.Context, hs []host.Host, routers []GossipPubSubRouter, opts ...Option) []*PubSub { + var psubs []*PubSub + if len(hs) != len(routers) { + panic("hosts and routers must have the same length") + } + + for i, h := range hs { + psubs = append(psubs, getGossipSubWithRouter(ctx, h, routers[i], opts...)) + } + + return psubs +} + +func getGossipSubWithRouter(ctx context.Context, hs host.Host, router GossipPubSubRouter, opts ...Option) *PubSub { + ps, err := NewGossipSubWithRouter(ctx, hs, router, opts...) + if err != nil { + panic(err) + } + return ps +} + +func getGossipSubRouter(h host.Host, opts ...GossipSubRouterOption) *GossipSubRouter { + ps, err := DefaultGossipSubRouter(h, opts...) + if err != nil { + panic(err) + } + return ps +} + +func getGossipSubRouters(hs []host.Host, opts ...GossipSubRouterOption) []GossipPubSubRouter { + var routers []GossipPubSubRouter + for _, h := range hs { + routers = append(routers, getGossipSubRouter(h, opts...)) + } + return routers +} + func TestSparseGossipsub(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -87,7 +124,7 @@ func TestDenseGossipsub(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -128,7 +165,7 @@ func TestGossipsubFanout(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs[1:] { @@ -197,7 +234,7 @@ func TestGossipsubFanoutMaintenance(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs[1:] { @@ -282,7 +319,7 @@ func TestGossipsubFanoutExpiry(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 10) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs[1:] { @@ -341,7 +378,7 @@ func TestGossipsubGossip(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -389,7 +426,7 @@ func TestGossipsubGossipPiggyback(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -457,7 +494,7 @@ func TestGossipsubGossipPropagation(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) hosts1 := hosts[:GossipSubD+1] hosts2 := append(hosts[GossipSubD+1:], hosts[0]) @@ -538,7 +575,7 @@ func TestGossipsubPrune(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -594,7 +631,9 @@ func TestGossipsubPruneBackoffTime(t *testing.T) { params.HeartbeatInitialDelay = time.Millisecond * 10 params.HeartbeatInterval = time.Millisecond * 100 - psubs := getGossipsubs(ctx, hosts, WithGossipSubParams(params), WithPeerScore( + routers := getGossipSubRouters(hosts, WithGossipSubParams(params)) + + psubs := getGossipSubsWithRouters(ctx, hosts, routers, WithPeerScore( &PeerScoreParams{ AppSpecificScore: func(p peer.ID) float64 { if p == hosts[0].ID() { @@ -685,7 +724,7 @@ func TestGossipsubGraft(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) sparseConnect(t, hosts) @@ -730,7 +769,7 @@ func TestGossipsubRemovePeer(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -779,7 +818,7 @@ func TestGossipsubGraftPruneRetry(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 10) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) denseConnect(t, hosts) var topics []string @@ -829,7 +868,7 @@ func TestGossipsubControlPiggyback(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 10) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) denseConnect(t, hosts) for _, ps := range psubs { @@ -911,7 +950,7 @@ func TestMixedGossipsub(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 30) - gsubs := getGossipsubs(ctx, hosts[:20]) + gsubs := getGossipSubs(ctx, hosts[:20]) fsubs := getPubsubs(ctx, hosts[20:]) psubs := append(gsubs, fsubs...) @@ -955,7 +994,7 @@ func TestGossipsubMultihops(t *testing.T) { hosts := getNetHosts(t, ctx, 6) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) connect(t, hosts[0], hosts[1]) connect(t, hosts[1], hosts[2]) @@ -997,7 +1036,7 @@ func TestGossipsubTreeTopology(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 10) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) connect(t, hosts[0], hosts[1]) connect(t, hosts[1], hosts[2]) @@ -1061,7 +1100,8 @@ func TestGossipsubStarTopology(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts, WithPeerExchange(true), WithFloodPublish(true)) + routers := getGossipSubRouters(hosts, WithPeerExchange(true), WithFloodPublish(true)) + psubs := getGossipSubsWithRouters(ctx, hosts, routers) // configure the center of the star with a very low D psubs[0].eval <- func() { @@ -1145,7 +1185,8 @@ func TestGossipsubStarTopologyWithSignedPeerRecords(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts, WithPeerExchange(true), WithFloodPublish(true)) + routers := getGossipSubRouters(hosts, WithPeerExchange(true), WithFloodPublish(true)) + psubs := getGossipSubsWithRouters(ctx, hosts, routers) // configure the center of the star with a very low D psubs[0].eval <- func() { @@ -1224,9 +1265,9 @@ func TestGossipsubDirectPeers(t *testing.T) { h := getNetHosts(t, ctx, 3) psubs := []*PubSub{ - getGossipsub(ctx, h[0], WithDirectConnectTicks(2)), - getGossipsub(ctx, h[1], WithDirectPeers([]peer.AddrInfo{{ID: h[2].ID(), Addrs: h[2].Addrs()}}), WithDirectConnectTicks(2)), - getGossipsub(ctx, h[2], WithDirectPeers([]peer.AddrInfo{{ID: h[1].ID(), Addrs: h[1].Addrs()}}), WithDirectConnectTicks(2)), + getGossipSubWithRouter(ctx, h[0], getGossipSubRouter(h[0], WithDirectConnectTicks(2))), + getGossipSubWithRouter(ctx, h[1], getGossipSubRouter(h[1], WithDirectConnectTicks(2)), WithDirectPeers([]peer.AddrInfo{{ID: h[2].ID(), Addrs: h[2].Addrs()}})), + getGossipSubWithRouter(ctx, h[2], getGossipSubRouter(h[2], WithDirectConnectTicks(2)), WithDirectPeers([]peer.AddrInfo{{ID: h[1].ID(), Addrs: h[1].Addrs()}})), } connect(t, h[0], h[1]) @@ -1288,13 +1329,13 @@ func TestGossipSubPeerFilter(t *testing.T) { h := getNetHosts(t, ctx, 3) psubs := []*PubSub{ - getGossipsub(ctx, h[0], WithPeerFilter(func(pid peer.ID, topic string) bool { + getGossipSub(ctx, h[0], WithPeerFilter(func(pid peer.ID, topic string) bool { return pid == h[1].ID() })), - getGossipsub(ctx, h[1], WithPeerFilter(func(pid peer.ID, topic string) bool { + getGossipSub(ctx, h[1], WithPeerFilter(func(pid peer.ID, topic string) bool { return pid == h[0].ID() })), - getGossipsub(ctx, h[2]), + getGossipSub(ctx, h[2]), } connect(t, h[0], h[1]) @@ -1330,9 +1371,9 @@ func TestGossipsubDirectPeersFanout(t *testing.T) { h := getNetHosts(t, ctx, 3) psubs := []*PubSub{ - getGossipsub(ctx, h[0]), - getGossipsub(ctx, h[1], WithDirectPeers([]peer.AddrInfo{{ID: h[2].ID(), Addrs: h[2].Addrs()}})), - getGossipsub(ctx, h[2], WithDirectPeers([]peer.AddrInfo{{ID: h[1].ID(), Addrs: h[1].Addrs()}})), + getGossipSub(ctx, h[0]), + getGossipSub(ctx, h[1], WithDirectPeers([]peer.AddrInfo{{ID: h[2].ID(), Addrs: h[2].Addrs()}})), + getGossipSub(ctx, h[2], WithDirectPeers([]peer.AddrInfo{{ID: h[1].ID(), Addrs: h[1].Addrs()}})), } connect(t, h[0], h[1]) @@ -1416,7 +1457,8 @@ func TestGossipsubFloodPublish(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts, WithFloodPublish(true)) + routers := getGossipSubRouters(hosts, WithFloodPublish(true)) + psubs := getGossipSubsWithRouters(ctx, hosts, routers) // build the star for i := 1; i < 20; i++ { @@ -1451,7 +1493,7 @@ func TestGossipsubEnoughPeers(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) for _, ps := range psubs { _, err := ps.Subscribe("test") @@ -1500,8 +1542,8 @@ func TestGossipsubCustomParams(t *testing.T) { wantedMaxPendingConns := 23 params.MaxPendingConnections = wantedMaxPendingConns hosts := getNetHosts(t, ctx, 1) - psubs := getGossipsubs(ctx, hosts, - WithGossipSubParams(params)) + routers := getGossipSubRouters(hosts, WithGossipSubParams(params)) + psubs := getGossipSubsWithRouters(ctx, hosts, routers) if len(psubs) != 1 { t.Fatalf("incorrect number of pusbub objects received: wanted %d but got %d", 1, len(psubs)) @@ -1529,7 +1571,7 @@ func TestGossipsubNegativeScore(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts, + psubs := getGossipSubs(ctx, hosts, WithPeerScore( &PeerScoreParams{ AppSpecificScore: func(p peer.ID) float64 { @@ -1613,7 +1655,7 @@ func TestGossipsubScoreValidatorEx(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 3) - psubs := getGossipsubs(ctx, hosts, + psubs := getGossipSubs(ctx, hosts, WithPeerScore( &PeerScoreParams{ AppSpecificScore: func(p peer.ID) float64 { return 0 }, @@ -1702,7 +1744,7 @@ func TestGossipsubPiggybackControl(t *testing.T) { h := bhost.NewBlankHost(swarmt.GenSwarm(t)) defer h.Close() - ps := getGossipsub(ctx, h) + ps := getGossipSub(ctx, h) blah := peer.ID("bogotr0n") @@ -1750,7 +1792,7 @@ func TestGossipsubMultipleGraftTopics(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 2) - psubs := getGossipsubs(ctx, hosts) + psubs := getGossipSubs(ctx, hosts) sparseConnect(t, hosts) time.Sleep(time.Second * 1) @@ -1819,8 +1861,8 @@ func TestGossipsubOpportunisticGrafting(t *testing.T) { hosts := getNetHosts(t, ctx, 50) // pubsubs for the first 10 hosts - psubs := getGossipsubs(ctx, hosts[:10], - WithFloodPublish(true), + routers := getGossipSubRouters(hosts[:10], WithFloodPublish(true)) + psubs := getGossipSubsWithRouters(ctx, hosts[:10], routers, WithPeerScore( &PeerScoreParams{ AppSpecificScore: func(peer.ID) float64 { return 0 }, @@ -1920,8 +1962,8 @@ func TestGossipSubLeaveTopic(t *testing.T) { h := getNetHosts(t, ctx, 2) psubs := []*PubSub{ - getGossipsub(ctx, h[0]), - getGossipsub(ctx, h[1]), + getGossipSub(ctx, h[0]), + getGossipSub(ctx, h[1]), } connect(t, h[0], h[1]) @@ -1991,9 +2033,9 @@ func TestGossipSubJoinTopic(t *testing.T) { h := getNetHosts(t, ctx, 3) psubs := []*PubSub{ - getGossipsub(ctx, h[0]), - getGossipsub(ctx, h[1]), - getGossipsub(ctx, h[2]), + getGossipSub(ctx, h[0]), + getGossipSub(ctx, h[1]), + getGossipSub(ctx, h[2]), } connect(t, h[0], h[1]) @@ -2072,9 +2114,9 @@ func TestGossipsubPeerScoreInspect(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 2) - inspector := &mockPeerScoreInspector{} - psub1 := getGossipsub(ctx, hosts[0], + router1 := getGossipSubRouter(hosts[0], WithPeerScoreInspect(inspector.inspect, time.Second)) + psub1 := getGossipSubWithRouter(ctx, hosts[0], router1, WithPeerScore( &PeerScoreParams{ Topics: map[string]*TopicScoreParams{ @@ -2097,8 +2139,8 @@ func TestGossipsubPeerScoreInspect(t *testing.T) { PublishThreshold: -10, GraylistThreshold: -1000, }), - WithPeerScoreInspect(inspector.inspect, time.Second)) - psub2 := getGossipsub(ctx, hosts[1]) + ) + psub2 := getGossipSub(ctx, hosts[1]) psubs := []*PubSub{psub1, psub2} connect(t, hosts[0], hosts[1]) @@ -2133,7 +2175,7 @@ func TestGossipsubPeerScoreResetTopicParams(t *testing.T) { hosts := getNetHosts(t, ctx, 1) - ps := getGossipsub(ctx, hosts[0], + ps := getGossipSub(ctx, hosts[0], WithPeerScore( &PeerScoreParams{ Topics: map[string]*TopicScoreParams{ @@ -2199,7 +2241,7 @@ func TestGossipsubRPCFragmentation(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 2) - ps := getGossipsub(ctx, hosts[0]) + ps := getGossipSub(ctx, hosts[0]) // make a fake peer that requests everything through IWANT gossip iwe := iwantEverything{h: hosts[1]} diff --git a/peer_gater.go b/peer_gater.go index 4e26bc26..af778539 100644 --- a/peer_gater.go +++ b/peer_gater.go @@ -116,7 +116,7 @@ func DefaultPeerGaterParams() *PeerGaterParams { } // the gater object. -type peerGater struct { +type PeerGater struct { sync.Mutex host host.Host @@ -163,7 +163,7 @@ type peerGaterStats struct { // interval. func WithPeerGater(params *PeerGaterParams) Option { return func(ps *PubSub) error { - gs, ok := ps.rt.(*GossipSubRouter) + gs, ok := ps.rt.(GossipPubSubRouter) if !ok { return fmt.Errorf("pubsub router is not gossipsub") } @@ -173,14 +173,14 @@ func WithPeerGater(params *PeerGaterParams) Option { return err } - gs.gate = newPeerGater(ps.ctx, ps.host, params) + gs.SetPeerGater(newPeerGater(ps.ctx, ps.host, params)) // hook the tracer if ps.tracer != nil { - ps.tracer.raw = append(ps.tracer.raw, gs.gate) + ps.tracer.raw = append(ps.tracer.raw, gs.GetPeerGater()) } else { ps.tracer = &pubsubTracer{ - raw: []RawTracer{gs.gate}, + raw: []RawTracer{gs.GetPeerGater()}, pid: ps.host.ID(), idGen: ps.idGen, } @@ -190,8 +190,8 @@ func WithPeerGater(params *PeerGaterParams) Option { } } -func newPeerGater(ctx context.Context, host host.Host, params *PeerGaterParams) *peerGater { - pg := &peerGater{ +func newPeerGater(ctx context.Context, host host.Host, params *PeerGaterParams) *PeerGater { + pg := &PeerGater{ params: params, peerStats: make(map[peer.ID]*peerGaterStats), ipStats: make(map[string]*peerGaterStats), @@ -201,7 +201,7 @@ func newPeerGater(ctx context.Context, host host.Host, params *PeerGaterParams) return pg } -func (pg *peerGater) background(ctx context.Context) { +func (pg *PeerGater) background(ctx context.Context) { tick := time.NewTicker(pg.params.DecayInterval) defer tick.Stop() @@ -216,7 +216,7 @@ func (pg *peerGater) background(ctx context.Context) { } } -func (pg *peerGater) decayStats() { +func (pg *PeerGater) decayStats() { pg.Lock() defer pg.Unlock() @@ -258,7 +258,7 @@ func (pg *peerGater) decayStats() { } } -func (pg *peerGater) getPeerStats(p peer.ID) *peerGaterStats { +func (pg *PeerGater) getPeerStats(p peer.ID) *peerGaterStats { st, ok := pg.peerStats[p] if !ok { st = pg.getIPStats(p) @@ -267,7 +267,7 @@ func (pg *peerGater) getPeerStats(p peer.ID) *peerGaterStats { return st } -func (pg *peerGater) getIPStats(p peer.ID) *peerGaterStats { +func (pg *PeerGater) getIPStats(p peer.ID) *peerGaterStats { ip := pg.getPeerIP(p) st, ok := pg.ipStats[ip] if !ok { @@ -277,7 +277,7 @@ func (pg *peerGater) getIPStats(p peer.ID) *peerGaterStats { return st } -func (pg *peerGater) getPeerIP(p peer.ID) string { +func (pg *PeerGater) getPeerIP(p peer.ID) string { if pg.getIP != nil { return pg.getIP(p) } @@ -317,7 +317,7 @@ func (pg *peerGater) getPeerIP(p peer.ID) string { } // router interface -func (pg *peerGater) AcceptFrom(p peer.ID) AcceptStatus { +func (pg *PeerGater) AcceptFrom(p peer.ID) AcceptStatus { if pg == nil { return AcceptAll } @@ -363,10 +363,10 @@ func (pg *peerGater) AcceptFrom(p peer.ID) AcceptStatus { } // -- RawTracer interface methods -var _ RawTracer = (*peerGater)(nil) +var _ RawTracer = (*PeerGater)(nil) // tracer interface -func (pg *peerGater) AddPeer(p peer.ID, proto protocol.ID) { +func (pg *PeerGater) AddPeer(p peer.ID, proto protocol.ID) { pg.Lock() defer pg.Unlock() @@ -374,7 +374,7 @@ func (pg *peerGater) AddPeer(p peer.ID, proto protocol.ID) { st.connected++ } -func (pg *peerGater) RemovePeer(p peer.ID) { +func (pg *PeerGater) RemovePeer(p peer.ID) { pg.Lock() defer pg.Unlock() @@ -385,19 +385,19 @@ func (pg *peerGater) RemovePeer(p peer.ID) { delete(pg.peerStats, p) } -func (pg *peerGater) Join(topic string) {} -func (pg *peerGater) Leave(topic string) {} -func (pg *peerGater) Graft(p peer.ID, topic string) {} -func (pg *peerGater) Prune(p peer.ID, topic string) {} +func (pg *PeerGater) Join(topic string) {} +func (pg *PeerGater) Leave(topic string) {} +func (pg *PeerGater) Graft(p peer.ID, topic string) {} +func (pg *PeerGater) Prune(p peer.ID, topic string) {} -func (pg *peerGater) ValidateMessage(msg *Message) { +func (pg *PeerGater) ValidateMessage(msg *Message) { pg.Lock() defer pg.Unlock() pg.validate++ } -func (pg *peerGater) DeliverMessage(msg *Message) { +func (pg *PeerGater) DeliverMessage(msg *Message) { pg.Lock() defer pg.Unlock() @@ -413,7 +413,7 @@ func (pg *peerGater) DeliverMessage(msg *Message) { st.deliver += weight } -func (pg *peerGater) RejectMessage(msg *Message, reason string) { +func (pg *PeerGater) RejectMessage(msg *Message, reason string) { pg.Lock() defer pg.Unlock() @@ -434,7 +434,7 @@ func (pg *peerGater) RejectMessage(msg *Message, reason string) { } } -func (pg *peerGater) DuplicateMessage(msg *Message) { +func (pg *PeerGater) DuplicateMessage(msg *Message) { pg.Lock() defer pg.Unlock() @@ -442,12 +442,12 @@ func (pg *peerGater) DuplicateMessage(msg *Message) { st.duplicate++ } -func (pg *peerGater) ThrottlePeer(p peer.ID) {} +func (pg *PeerGater) ThrottlePeer(p peer.ID) {} -func (pg *peerGater) RecvRPC(rpc *RPC) {} +func (pg *PeerGater) RecvRPC(rpc *RPC) {} -func (pg *peerGater) SendRPC(rpc *RPC, p peer.ID) {} +func (pg *PeerGater) SendRPC(rpc *RPC, p peer.ID) {} -func (pg *peerGater) DropRPC(rpc *RPC, p peer.ID) {} +func (pg *PeerGater) DropRPC(rpc *RPC, p peer.ID) {} -func (pg *peerGater) UndeliverableMessage(msg *Message) {} +func (pg *PeerGater) UndeliverableMessage(msg *Message) {} diff --git a/score.go b/score.go index ec6c8213..da0d70c0 100644 --- a/score.go +++ b/score.go @@ -61,7 +61,7 @@ type topicStats struct { invalidMessageDeliveries float64 } -type peerScore struct { +type PeerScore struct { sync.Mutex // the score parameters @@ -85,7 +85,7 @@ type peerScore struct { inspectPeriod time.Duration } -var _ RawTracer = (*peerScore)(nil) +var _ RawTracer = (*PeerScore)(nil) type messageDeliveries struct { seenMsgTTL time.Duration @@ -149,13 +149,8 @@ type TopicScoreSnapshot struct { // components for debugging peer scoring. // // This option must be passed _after_ the WithPeerScore option. -func WithPeerScoreInspect(inspect interface{}, period time.Duration) Option { - return func(ps *PubSub) error { - gs, ok := ps.rt.(*GossipSubRouter) - if !ok { - return fmt.Errorf("pubsub router is not gossipsub") - } - +func WithPeerScoreInspect(inspect interface{}, period time.Duration) GossipSubRouterOption { + return func(gs *GossipSubRouter) error { if gs.score == nil { return fmt.Errorf("peer scoring is not enabled") } @@ -180,12 +175,12 @@ func WithPeerScoreInspect(inspect interface{}, period time.Duration) Option { } // implementation -func newPeerScore(params *PeerScoreParams) *peerScore { +func newPeerScore(params *PeerScoreParams) *PeerScore { seenMsgTTL := params.SeenMsgTTL if seenMsgTTL == 0 { seenMsgTTL = TimeCacheDuration } - return &peerScore{ + return &PeerScore{ params: params, peerStats: make(map[peer.ID]*peerStats), peerIPs: make(map[string]map[peer.ID]struct{}), @@ -198,7 +193,7 @@ func newPeerScore(params *PeerScoreParams) *peerScore { // If the topic previously had parameters and the parameters are lowering delivery caps, // then the score counters are recapped appropriately. // Note: assumes that the topic score parameters have already been validated -func (ps *peerScore) SetTopicScoreParams(topic string, p *TopicScoreParams) error { +func (ps *PeerScore) SetTopicScoreParams(topic string, p *TopicScoreParams) error { ps.Lock() defer ps.Unlock() @@ -241,7 +236,7 @@ func (ps *peerScore) SetTopicScoreParams(topic string, p *TopicScoreParams) erro } // router interface -func (ps *peerScore) Start(gs *GossipSubRouter) { +func (ps *PeerScore) Start(gs *GossipSubRouter) { if ps == nil { return } @@ -251,7 +246,7 @@ func (ps *peerScore) Start(gs *GossipSubRouter) { go ps.background(gs.p.ctx) } -func (ps *peerScore) Score(p peer.ID) float64 { +func (ps *PeerScore) Score(p peer.ID) float64 { if ps == nil { return 0 } @@ -262,7 +257,7 @@ func (ps *peerScore) Score(p peer.ID) float64 { return ps.score(p) } -func (ps *peerScore) score(p peer.ID) float64 { +func (ps *PeerScore) score(p peer.ID) float64 { pstats, ok := ps.peerStats[p] if !ok { return 0 @@ -341,7 +336,7 @@ func (ps *peerScore) score(p peer.ID) float64 { return score } -func (ps *peerScore) ipColocationFactor(p peer.ID) float64 { +func (ps *PeerScore) ipColocationFactor(p peer.ID) float64 { pstats, ok := ps.peerStats[p] if !ok { return 0 @@ -388,7 +383,7 @@ loop: } // behavioural pattern penalties -func (ps *peerScore) AddPenalty(p peer.ID, count int) { +func (ps *PeerScore) AddPenalty(p peer.ID, count int) { if ps == nil { return } @@ -405,7 +400,7 @@ func (ps *peerScore) AddPenalty(p peer.ID, count int) { } // periodic maintenance -func (ps *peerScore) background(ctx context.Context) { +func (ps *PeerScore) background(ctx context.Context) { refreshScores := time.NewTicker(ps.params.DecayInterval) defer refreshScores.Stop() @@ -445,7 +440,7 @@ func (ps *peerScore) background(ctx context.Context) { } // inspectScores dumps all tracked scores into the inspect function. -func (ps *peerScore) inspectScores() { +func (ps *PeerScore) inspectScores() { if ps.inspect != nil { ps.inspectScoresSimple() } @@ -454,7 +449,7 @@ func (ps *peerScore) inspectScores() { } } -func (ps *peerScore) inspectScoresSimple() { +func (ps *PeerScore) inspectScoresSimple() { ps.Lock() scores := make(map[peer.ID]float64, len(ps.peerStats)) for p := range ps.peerStats { @@ -469,7 +464,7 @@ func (ps *peerScore) inspectScoresSimple() { go ps.inspect(scores) } -func (ps *peerScore) inspectScoresExtended() { +func (ps *PeerScore) inspectScoresExtended() { ps.Lock() scores := make(map[peer.ID]*PeerScoreSnapshot, len(ps.peerStats)) for p, pstats := range ps.peerStats { @@ -501,7 +496,7 @@ func (ps *peerScore) inspectScoresExtended() { // refreshScores decays scores, and purges score records for disconnected peers, // once their expiry has elapsed. -func (ps *peerScore) refreshScores() { +func (ps *PeerScore) refreshScores() { ps.Lock() defer ps.Unlock() @@ -565,7 +560,7 @@ func (ps *peerScore) refreshScores() { } // refreshIPs refreshes IPs we know of peers we're tracking. -func (ps *peerScore) refreshIPs() { +func (ps *PeerScore) refreshIPs() { ps.Lock() defer ps.Unlock() @@ -584,7 +579,7 @@ func (ps *peerScore) refreshIPs() { } } -func (ps *peerScore) gcDeliveryRecords() { +func (ps *PeerScore) gcDeliveryRecords() { ps.Lock() defer ps.Unlock() @@ -592,7 +587,7 @@ func (ps *peerScore) gcDeliveryRecords() { } // tracer interface -func (ps *peerScore) AddPeer(p peer.ID, proto protocol.ID) { +func (ps *PeerScore) AddPeer(p peer.ID, proto protocol.ID) { ps.Lock() defer ps.Unlock() @@ -608,7 +603,7 @@ func (ps *peerScore) AddPeer(p peer.ID, proto protocol.ID) { pstats.ips = ips } -func (ps *peerScore) RemovePeer(p peer.ID) { +func (ps *PeerScore) RemovePeer(p peer.ID) { ps.Lock() defer ps.Unlock() @@ -643,10 +638,10 @@ func (ps *peerScore) RemovePeer(p peer.ID) { pstats.expire = time.Now().Add(ps.params.RetainScore) } -func (ps *peerScore) Join(topic string) {} -func (ps *peerScore) Leave(topic string) {} +func (ps *PeerScore) Join(topic string) {} +func (ps *PeerScore) Leave(topic string) {} -func (ps *peerScore) Graft(p peer.ID, topic string) { +func (ps *PeerScore) Graft(p peer.ID, topic string) { ps.Lock() defer ps.Unlock() @@ -666,7 +661,7 @@ func (ps *peerScore) Graft(p peer.ID, topic string) { tstats.meshMessageDeliveriesActive = false } -func (ps *peerScore) Prune(p peer.ID, topic string) { +func (ps *PeerScore) Prune(p peer.ID, topic string) { ps.Lock() defer ps.Unlock() @@ -690,7 +685,7 @@ func (ps *peerScore) Prune(p peer.ID, topic string) { tstats.inMesh = false } -func (ps *peerScore) ValidateMessage(msg *Message) { +func (ps *PeerScore) ValidateMessage(msg *Message) { ps.Lock() defer ps.Unlock() @@ -699,7 +694,7 @@ func (ps *peerScore) ValidateMessage(msg *Message) { _ = ps.deliveries.getRecord(ps.idGen.ID(msg)) } -func (ps *peerScore) DeliverMessage(msg *Message) { +func (ps *PeerScore) DeliverMessage(msg *Message) { ps.Lock() defer ps.Unlock() @@ -725,7 +720,7 @@ func (ps *peerScore) DeliverMessage(msg *Message) { } } -func (ps *peerScore) RejectMessage(msg *Message, reason string) { +func (ps *PeerScore) RejectMessage(msg *Message, reason string) { ps.Lock() defer ps.Unlock() @@ -792,7 +787,7 @@ func (ps *peerScore) RejectMessage(msg *Message, reason string) { drec.peers = nil } -func (ps *peerScore) DuplicateMessage(msg *Message) { +func (ps *PeerScore) DuplicateMessage(msg *Message) { ps.Lock() defer ps.Unlock() @@ -826,15 +821,15 @@ func (ps *peerScore) DuplicateMessage(msg *Message) { } } -func (ps *peerScore) ThrottlePeer(p peer.ID) {} +func (ps *PeerScore) ThrottlePeer(p peer.ID) {} -func (ps *peerScore) RecvRPC(rpc *RPC) {} +func (ps *PeerScore) RecvRPC(rpc *RPC) {} -func (ps *peerScore) SendRPC(rpc *RPC, p peer.ID) {} +func (ps *PeerScore) SendRPC(rpc *RPC, p peer.ID) {} -func (ps *peerScore) DropRPC(rpc *RPC, p peer.ID) {} +func (ps *PeerScore) DropRPC(rpc *RPC, p peer.ID) {} -func (ps *peerScore) UndeliverableMessage(msg *Message) {} +func (ps *PeerScore) UndeliverableMessage(msg *Message) {} // message delivery records func (d *messageDeliveries) getRecord(id string) *deliveryRecord { @@ -898,7 +893,7 @@ func (pstats *peerStats) getTopicStats(topic string, params *PeerScoreParams) (* // markInvalidMessageDelivery increments the "invalid message deliveries" // counter for all scored topics the message is published in. -func (ps *peerScore) markInvalidMessageDelivery(p peer.ID, msg *Message) { +func (ps *PeerScore) markInvalidMessageDelivery(p peer.ID, msg *Message) { pstats, ok := ps.peerStats[p] if !ok { return @@ -916,7 +911,7 @@ func (ps *peerScore) markInvalidMessageDelivery(p peer.ID, msg *Message) { // markFirstMessageDelivery increments the "first message deliveries" counter // for all scored topics the message is published in, as well as the "mesh // message deliveries" counter, if the peer is in the mesh for the topic. -func (ps *peerScore) markFirstMessageDelivery(p peer.ID, msg *Message) { +func (ps *PeerScore) markFirstMessageDelivery(p peer.ID, msg *Message) { pstats, ok := ps.peerStats[p] if !ok { return @@ -948,7 +943,7 @@ func (ps *peerScore) markFirstMessageDelivery(p peer.ID, msg *Message) { // markDuplicateMessageDelivery increments the "mesh message deliveries" counter // for messages we've seen before, as long the message was received within the // P3 window. -func (ps *peerScore) markDuplicateMessageDelivery(p peer.ID, msg *Message, validated time.Time) { +func (ps *PeerScore) markDuplicateMessageDelivery(p peer.ID, msg *Message, validated time.Time) { pstats, ok := ps.peerStats[p] if !ok { return @@ -981,7 +976,7 @@ func (ps *peerScore) markDuplicateMessageDelivery(p peer.ID, msg *Message, valid } // getIPs gets the current IPs for a peer. -func (ps *peerScore) getIPs(p peer.ID) []string { +func (ps *PeerScore) getIPs(p peer.ID) []string { // in unit tests this can be nil if ps.host == nil { return nil @@ -1025,7 +1020,7 @@ func (ps *peerScore) getIPs(p peer.ID) []string { // setIPs adds tracking for the new IPs in the list, and removes tracking from // the obsolete IPs. -func (ps *peerScore) setIPs(p peer.ID, newips, oldips []string) { +func (ps *PeerScore) setIPs(p peer.ID, newips, oldips []string) { addNewIPs: // add the new IPs to the tracking for _, ip := range newips { @@ -1066,7 +1061,7 @@ removeOldIPs: } // removeIPs removes an IP list from the tracking list for a peer. -func (ps *peerScore) removeIPs(p peer.ID, ips []string) { +func (ps *PeerScore) removeIPs(p peer.ID, ips []string) { for _, ip := range ips { peers, ok := ps.peerIPs[ip] if !ok { diff --git a/score_test.go b/score_test.go index 6a6c6890..85979c02 100644 --- a/score_test.go +++ b/score_test.go @@ -811,7 +811,7 @@ func TestScoreBehaviourPenalty(t *testing.T) { peerA := peer.ID("A") - var ps *peerScore + var ps *PeerScore // first check AddPenalty on a nil peerScore ps.AddPenalty(peerA, 1) @@ -1069,7 +1069,7 @@ func withinVariance(score float64, expected float64, variance float64) bool { } // hack to set IPs for a peer without having to spin up real hosts with shared IPs -func setIPsForPeer(t *testing.T, ps *peerScore, p peer.ID, ips ...string) { +func setIPsForPeer(t *testing.T, ps *PeerScore, p peer.ID, ips ...string) { t.Helper() ps.setIPs(p, ips, []string{}) pstats, ok := ps.peerStats[p] diff --git a/tag_tracer.go b/tag_tracer.go index d813c188..305b6637 100644 --- a/tag_tracer.go +++ b/tag_tracer.go @@ -30,7 +30,7 @@ var ( GossipSubConnTagMessageDeliveryCap = 15 ) -// tagTracer is an internal tracer that applies connection manager tags to peer +// TagTracer is an internal tracer that applies connection manager tags to peer // connections based on their behavior. // // We tag a peer's connections for the following reasons: @@ -41,7 +41,7 @@ var ( // first. // The delivery tags have a maximum value, GossipSubConnTagMessageDeliveryCap, and they decay at // a rate of GossipSubConnTagDecayAmount / GossipSubConnTagDecayInterval. -type tagTracer struct { +type TagTracer struct { sync.RWMutex cmgr connmgr.ConnManager @@ -55,12 +55,12 @@ type tagTracer struct { nearFirst map[string]map[peer.ID]struct{} } -func newTagTracer(cmgr connmgr.ConnManager) *tagTracer { +func newTagTracer(cmgr connmgr.ConnManager) *TagTracer { decayer, ok := connmgr.SupportsDecay(cmgr) if !ok { log.Debugf("connection manager does not support decaying tags, delivery tags will not be applied") } - return &tagTracer{ + return &TagTracer{ cmgr: cmgr, idGen: newMsgIdGenerator(), decayer: decayer, @@ -69,7 +69,7 @@ func newTagTracer(cmgr connmgr.ConnManager) *tagTracer { } } -func (t *tagTracer) Start(gs *GossipSubRouter) { +func (t *TagTracer) Start(gs *GossipSubRouter) { if t == nil { return } @@ -78,7 +78,7 @@ func (t *tagTracer) Start(gs *GossipSubRouter) { t.direct = gs.direct } -func (t *tagTracer) tagPeerIfDirect(p peer.ID) { +func (t *TagTracer) tagPeerIfDirect(p peer.ID) { if t.direct == nil { return } @@ -90,12 +90,12 @@ func (t *tagTracer) tagPeerIfDirect(p peer.ID) { } } -func (t *tagTracer) tagMeshPeer(p peer.ID, topic string) { +func (t *TagTracer) tagMeshPeer(p peer.ID, topic string) { tag := topicTag(topic) t.cmgr.Protect(p, tag) } -func (t *tagTracer) untagMeshPeer(p peer.ID, topic string) { +func (t *TagTracer) untagMeshPeer(p peer.ID, topic string) { tag := topicTag(topic) t.cmgr.Unprotect(p, tag) } @@ -104,7 +104,7 @@ func topicTag(topic string) string { return fmt.Sprintf("pubsub:%s", topic) } -func (t *tagTracer) addDeliveryTag(topic string) { +func (t *TagTracer) addDeliveryTag(topic string) { if t.decayer == nil { return } @@ -125,7 +125,7 @@ func (t *tagTracer) addDeliveryTag(topic string) { t.decaying[topic] = tag } -func (t *tagTracer) removeDeliveryTag(topic string) { +func (t *TagTracer) removeDeliveryTag(topic string) { t.Lock() defer t.Unlock() tag, ok := t.decaying[topic] @@ -139,7 +139,7 @@ func (t *tagTracer) removeDeliveryTag(topic string) { delete(t.decaying, topic) } -func (t *tagTracer) bumpDeliveryTag(p peer.ID, topic string) error { +func (t *TagTracer) bumpDeliveryTag(p peer.ID, topic string) error { t.RLock() defer t.RUnlock() @@ -150,7 +150,7 @@ func (t *tagTracer) bumpDeliveryTag(p peer.ID, topic string) error { return tag.Bump(p, GossipSubConnTagBumpMessageDelivery) } -func (t *tagTracer) bumpTagsForMessage(p peer.ID, msg *Message) { +func (t *TagTracer) bumpTagsForMessage(p peer.ID, msg *Message) { topic := msg.GetTopic() err := t.bumpDeliveryTag(p, topic) if err != nil { @@ -159,7 +159,7 @@ func (t *tagTracer) bumpTagsForMessage(p peer.ID, msg *Message) { } // nearFirstPeers returns the peers who delivered the message while it was still validating -func (t *tagTracer) nearFirstPeers(msg *Message) []peer.ID { +func (t *TagTracer) nearFirstPeers(msg *Message) []peer.ID { t.Lock() defer t.Unlock() peersMap, ok := t.nearFirst[t.idGen.ID(msg)] @@ -174,17 +174,17 @@ func (t *tagTracer) nearFirstPeers(msg *Message) []peer.ID { } // -- RawTracer interface methods -var _ RawTracer = (*tagTracer)(nil) +var _ RawTracer = (*TagTracer)(nil) -func (t *tagTracer) AddPeer(p peer.ID, proto protocol.ID) { +func (t *TagTracer) AddPeer(p peer.ID, proto protocol.ID) { t.tagPeerIfDirect(p) } -func (t *tagTracer) Join(topic string) { +func (t *TagTracer) Join(topic string) { t.addDeliveryTag(topic) } -func (t *tagTracer) DeliverMessage(msg *Message) { +func (t *TagTracer) DeliverMessage(msg *Message) { nearFirst := t.nearFirstPeers(msg) t.bumpTagsForMessage(msg.ReceivedFrom, msg) @@ -198,19 +198,19 @@ func (t *tagTracer) DeliverMessage(msg *Message) { t.Unlock() } -func (t *tagTracer) Leave(topic string) { +func (t *TagTracer) Leave(topic string) { t.removeDeliveryTag(topic) } -func (t *tagTracer) Graft(p peer.ID, topic string) { +func (t *TagTracer) Graft(p peer.ID, topic string) { t.tagMeshPeer(p, topic) } -func (t *tagTracer) Prune(p peer.ID, topic string) { +func (t *TagTracer) Prune(p peer.ID, topic string) { t.untagMeshPeer(p, topic) } -func (t *tagTracer) ValidateMessage(msg *Message) { +func (t *TagTracer) ValidateMessage(msg *Message) { t.Lock() defer t.Unlock() @@ -222,7 +222,7 @@ func (t *tagTracer) ValidateMessage(msg *Message) { t.nearFirst[id] = make(map[peer.ID]struct{}) } -func (t *tagTracer) DuplicateMessage(msg *Message) { +func (t *TagTracer) DuplicateMessage(msg *Message) { t.Lock() defer t.Unlock() @@ -234,7 +234,7 @@ func (t *tagTracer) DuplicateMessage(msg *Message) { peers[msg.ReceivedFrom] = struct{}{} } -func (t *tagTracer) RejectMessage(msg *Message, reason string) { +func (t *TagTracer) RejectMessage(msg *Message, reason string) { t.Lock() defer t.Unlock() @@ -251,9 +251,9 @@ func (t *tagTracer) RejectMessage(msg *Message, reason string) { } } -func (t *tagTracer) RemovePeer(peer.ID) {} -func (t *tagTracer) ThrottlePeer(p peer.ID) {} -func (t *tagTracer) RecvRPC(rpc *RPC) {} -func (t *tagTracer) SendRPC(rpc *RPC, p peer.ID) {} -func (t *tagTracer) DropRPC(rpc *RPC, p peer.ID) {} -func (t *tagTracer) UndeliverableMessage(msg *Message) {} +func (t *TagTracer) RemovePeer(peer.ID) {} +func (t *TagTracer) ThrottlePeer(p peer.ID) {} +func (t *TagTracer) RecvRPC(rpc *RPC) {} +func (t *TagTracer) SendRPC(rpc *RPC, p peer.ID) {} +func (t *TagTracer) DropRPC(rpc *RPC, p peer.ID) {} +func (t *TagTracer) UndeliverableMessage(msg *Message) {} diff --git a/topic.go b/topic.go index c08b081b..de866383 100644 --- a/topic.go +++ b/topic.go @@ -56,18 +56,18 @@ func (t *Topic) SetScoreParams(p *TopicScoreParams) error { result := make(chan error, 1) update := func() { - gs, ok := t.p.rt.(*GossipSubRouter) + gs, ok := t.p.rt.(GossipPubSubRouter) if !ok { result <- fmt.Errorf("pubsub router is not gossipsub") return } - if gs.score == nil { + if gs.GetPeerScore() == nil { result <- fmt.Errorf("peer scoring is not enabled in router") return } - err := gs.score.SetTopicScoreParams(t.topic, p) + err := gs.GetPeerScore().SetTopicScoreParams(t.topic, p) result <- err } diff --git a/trace_test.go b/trace_test.go index fb8cb56d..5a16d55f 100644 --- a/trace_test.go +++ b/trace_test.go @@ -28,10 +28,10 @@ func testWithTracer(t *testing.T, tracer EventTracer) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipsubs(ctx, hosts, + routers := getGossipSubRouters(hosts, WithPeerExchange(true)) + psubs := getGossipSubsWithRouters(ctx, hosts, routers, WithEventTracer(tracer), // to bootstrap from star topology - WithPeerExchange(true), // to exercise the score paths in the tracer WithPeerScore( &PeerScoreParams{ From 5f71a48c6960268139ef20f00a4b215dd887f40b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 7 Nov 2022 18:17:40 -0800 Subject: [PATCH 04/12] fixes conflict --- gossipsub.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gossipsub.go b/gossipsub.go index da332ea2..fc496bef 100644 --- a/gossipsub.go +++ b/gossipsub.go @@ -224,7 +224,7 @@ type GossipSubRouterOption func(*GossipSubRouter) error // DefaultGossipSubRouter returns a new GossipSubRouter with default parameters. func DefaultGossipSubRouter(h host.Host, opts ...GossipSubRouterOption) (*GossipSubRouter, error) { params := DefaultGossipSubParams() - return &GossipSubRouter{ + rt := &GossipSubRouter{ peers: make(map[peer.ID]protocol.ID), mesh: make(map[string]map[peer.ID]struct{}), fanout: make(map[string]map[peer.ID]struct{}), From 1c1c946647d70137b7b19c66900371ddfd1dc8df Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 7 Nov 2022 18:18:14 -0800 Subject: [PATCH 05/12] reverts back module --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 1e7d882a..231497f1 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/yhassanzadeh13/go-libp2p-pubsub +module github.com/libp2p/go-libp2p-pubsub go 1.17 From ab8365c644405b15ce52c444c9c34b1e6b3ea821 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 8 Nov 2022 15:44:02 -0800 Subject: [PATCH 06/12] fixes peer score helper --- gossipsub.go | 2 +- gossipsub_test.go | 4 ++-- score.go | 19 ++++++++++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/gossipsub.go b/gossipsub.go index fc496bef..b0441952 100644 --- a/gossipsub.go +++ b/gossipsub.go @@ -1936,10 +1936,10 @@ func (gs *GossipSubRouter) GetPeerScore() *PeerScore { } func (gs *GossipSubRouter) SetPeerScoreThresholds(thresholds *PeerScoreThresholds) { + gs.gossipThreshold = thresholds.GossipThreshold gs.publishThreshold = thresholds.PublishThreshold gs.graylistThreshold = thresholds.GraylistThreshold gs.acceptPXThreshold = thresholds.AcceptPXThreshold - gs.gossipThreshold = thresholds.GossipThreshold gs.opportunisticGraftThreshold = thresholds.OpportunisticGraftThreshold } diff --git a/gossipsub_test.go b/gossipsub_test.go index 951f1484..d7544ca8 100644 --- a/gossipsub_test.go +++ b/gossipsub_test.go @@ -2115,8 +2115,7 @@ func TestGossipsubPeerScoreInspect(t *testing.T) { hosts := getNetHosts(t, ctx, 2) inspector := &mockPeerScoreInspector{} - router1 := getGossipSubRouter(hosts[0], WithPeerScoreInspect(inspector.inspect, time.Second)) - psub1 := getGossipSubWithRouter(ctx, hosts[0], router1, + psub1 := getGossipSub(ctx, hosts[0], WithPeerScore( &PeerScoreParams{ Topics: map[string]*TopicScoreParams{ @@ -2139,6 +2138,7 @@ func TestGossipsubPeerScoreInspect(t *testing.T) { PublishThreshold: -10, GraylistThreshold: -1000, }), + WithPeerScoreInspect(inspector.inspect, time.Second), ) psub2 := getGossipSub(ctx, hosts[1]) psubs := []*PubSub{psub1, psub2} diff --git a/score.go b/score.go index da0d70c0..eb84e262 100644 --- a/score.go +++ b/score.go @@ -149,26 +149,31 @@ type TopicScoreSnapshot struct { // components for debugging peer scoring. // // This option must be passed _after_ the WithPeerScore option. -func WithPeerScoreInspect(inspect interface{}, period time.Duration) GossipSubRouterOption { - return func(gs *GossipSubRouter) error { - if gs.score == nil { +func WithPeerScoreInspect(inspect interface{}, period time.Duration) Option { + return func(ps *PubSub) error { + gs, ok := ps.rt.(GossipPubSubRouter) + if !ok { + return fmt.Errorf("pubsub router is not gossipsub") + } + + if gs.GetPeerScore() == nil { return fmt.Errorf("peer scoring is not enabled") } - if gs.score.inspect != nil || gs.score.inspectEx != nil { + if gs.GetPeerScore().inspect != nil || gs.GetPeerScore().inspectEx != nil { return fmt.Errorf("duplicate peer score inspector") } switch i := inspect.(type) { case PeerScoreInspectFn: - gs.score.inspect = i + gs.GetPeerScore().inspect = i case ExtendedPeerScoreInspectFn: - gs.score.inspectEx = i + gs.GetPeerScore().inspectEx = i default: return fmt.Errorf("unknown peer score insector type: %v", inspect) } - gs.score.inspectPeriod = period + gs.GetPeerScore().inspectPeriod = period return nil } From 5e7a6828a45318e7ea584f39ad1744b289f5ddef Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 9 Nov 2022 16:01:48 -0800 Subject: [PATCH 07/12] Adds send control message to gossipsub router (#2) --- go.mod | 3 ++- go.sum | 2 ++ gossipsub.go | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 1e7d882a..7ea7fa4a 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/ipfs/go-log v1.0.5 github.com/libp2p/go-libp2p v0.22.0 + github.com/libp2p/go-libp2p-pubsub v0.8.1 github.com/libp2p/go-libp2p-testing v0.12.0 github.com/libp2p/go-msgio v0.2.0 github.com/multiformats/go-multiaddr v0.6.0 @@ -36,6 +37,7 @@ require ( github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/klauspost/compress v1.15.1 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect + github.com/kr/pretty v0.2.1 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect github.com/libp2p/go-netroute v0.2.0 // indirect @@ -86,7 +88,6 @@ require ( golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect golang.org/x/tools v0.1.12 // indirect google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.1.7 // indirect diff --git a/go.sum b/go.sum index 84d10209..cc1baca1 100644 --- a/go.sum +++ b/go.sum @@ -317,6 +317,8 @@ github.com/libp2p/go-libp2p v0.22.0 h1:2Tce0kHOp5zASFKJbNzRElvh0iZwdtG5uZheNW8ch github.com/libp2p/go-libp2p v0.22.0/go.mod h1:UDolmweypBSjQb2f7xutPnwZ/fxioLbMBxSjRksxxU4= github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= github.com/libp2p/go-libp2p-core v0.19.0/go.mod h1:AkA+FUKQfYt1FLNef5fOPlo/naAWjKy/RCjkcPjqzYg= +github.com/libp2p/go-libp2p-pubsub v0.8.1 h1:hSw09NauFUaA0FLgQPBJp6QOy0a2n+HSkb8IeOx8OnY= +github.com/libp2p/go-libp2p-pubsub v0.8.1/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= github.com/libp2p/go-libp2p-testing v0.11.0/go.mod h1:qG4sF27dfKFoK9KlVzK2y52LQKhp0VEmLjV5aDqr1Hg= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= diff --git a/gossipsub.go b/gossipsub.go index 3e604111..02655b6b 100644 --- a/gossipsub.go +++ b/gossipsub.go @@ -1805,6 +1805,12 @@ func (gs *GossipSubRouter) pushControl(p peer.ID, ctl *pb.ControlMessage) { } } +// SendControl dispatches the given set of control messages to the given peer. +func (gs *GossipSubRouter) SendControl(p peer.ID, ctl *pb.ControlMessage) { + out := rpcWithControl(nil, ctl.Ihave, ctl.Iwant, ctl.Graft, ctl.Prune) + gs.sendRPC(p, out) +} + func (gs *GossipSubRouter) piggybackControl(p peer.ID, out *RPC, ctl *pb.ControlMessage) { // check control message for staleness first var tograft []*pb.ControlGraft From b897e22c3c7cfae3849916ce3a55eb8a2cf23859 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 9 Nov 2022 16:34:01 -0800 Subject: [PATCH 08/12] adjusts libp2p version (#3) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7ea7fa4a..fbcdac26 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/ipfs/go-log v1.0.5 github.com/libp2p/go-libp2p v0.22.0 - github.com/libp2p/go-libp2p-pubsub v0.8.1 + github.com/libp2p/go-libp2p-pubsub v0.8.1-0.20220908052023-8866ca88a105 github.com/libp2p/go-libp2p-testing v0.12.0 github.com/libp2p/go-msgio v0.2.0 github.com/multiformats/go-multiaddr v0.6.0 diff --git a/go.sum b/go.sum index cc1baca1..e72143d4 100644 --- a/go.sum +++ b/go.sum @@ -317,8 +317,8 @@ github.com/libp2p/go-libp2p v0.22.0 h1:2Tce0kHOp5zASFKJbNzRElvh0iZwdtG5uZheNW8ch github.com/libp2p/go-libp2p v0.22.0/go.mod h1:UDolmweypBSjQb2f7xutPnwZ/fxioLbMBxSjRksxxU4= github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= github.com/libp2p/go-libp2p-core v0.19.0/go.mod h1:AkA+FUKQfYt1FLNef5fOPlo/naAWjKy/RCjkcPjqzYg= -github.com/libp2p/go-libp2p-pubsub v0.8.1 h1:hSw09NauFUaA0FLgQPBJp6QOy0a2n+HSkb8IeOx8OnY= -github.com/libp2p/go-libp2p-pubsub v0.8.1/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= +github.com/libp2p/go-libp2p-pubsub v0.8.1-0.20220908052023-8866ca88a105 h1:EAAgUl0EnSk4Z/ct1xsHTYSy9JYDdcTazrC6phSdlIY= +github.com/libp2p/go-libp2p-pubsub v0.8.1-0.20220908052023-8866ca88a105/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= github.com/libp2p/go-libp2p-testing v0.11.0/go.mod h1:qG4sF27dfKFoK9KlVzK2y52LQKhp0VEmLjV5aDqr1Hg= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= From 60457b3ef6d574f603c2c0b3ebfaf45c54714949 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 10 Nov 2022 10:11:55 -0800 Subject: [PATCH 09/12] Update go.mod (#4) --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a0aa79cd..fbcdac26 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/libp2p/go-libp2p-pubsub +module github.com/yhassanzadeh13/go-libp2p-pubsub go 1.17 From 3d47fa10689c517490af52f3c3361de6254d847f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 24 Nov 2022 10:22:48 -0800 Subject: [PATCH 10/12] Create ci.yml (#5) --- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..d72a1851 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Build + run: make build + + + unittest: + name: Unit Tests + strategy: + fail-fast: false + matrix: + go-version: + - 1.16 + runs-on: ubuntu-latest + steps: + - name: Check Go Version + uses: actions/setup-go@v2 + with: + go-version: ${{matrix.go-version}} + - name: Checkout repo + uses: actions/checkout@v2 + - name: Run tests + run: make test From a94ddf380c08dc7bce89f436eb95e6d62047bfe9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 24 Nov 2022 10:30:19 -0800 Subject: [PATCH 11/12] Create Makefile (#7) --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..c4ead52a --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +test: + GO111MODULE=on go test ./... +build: + go build -v ./... + From 26f8d34de42dcfe0281e393973d5a60ee0108f47 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 24 Nov 2022 10:32:11 -0800 Subject: [PATCH 12/12] Revert "Merge branch 'yahya/gossipsub-router-interface'" (#6) This reverts commit 1c91995b7fbce0e4b9c5990c5bfda0d555267182. --- discovery_test.go | 2 +- gossip_tracer.go | 50 ++++++------- gossipsub.go | 154 +++++++++++++------------------------- gossipsub_connmgr_test.go | 4 +- gossipsub_feat.go | 12 ++- gossipsub_feat_test.go | 4 +- gossipsub_matchfn_test.go | 8 +- gossipsub_test.go | 146 +++++++++++++----------------------- peer_gater.go | 58 +++++++------- score.go | 88 +++++++++++----------- score_test.go | 4 +- tag_tracer.go | 58 +++++++------- topic.go | 6 +- trace_test.go | 4 +- 14 files changed, 255 insertions(+), 343 deletions(-) diff --git a/discovery_test.go b/discovery_test.go index 2a38285b..8d5d4bd4 100644 --- a/discovery_test.go +++ b/discovery_test.go @@ -244,7 +244,7 @@ func TestGossipSubDiscoveryAfterBootstrap(t *testing.T) { s = server2 } disc := &mockDiscoveryClient{h, s} - ps := getGossipSub(ctx, h, WithDiscovery(disc, WithDiscoveryOpts(discOpts...))) + ps := getGossipsub(ctx, h, WithDiscovery(disc, WithDiscoveryOpts(discOpts...))) psubs[i] = ps topicHandlers[i], _ = ps.Join(topic) } diff --git a/gossip_tracer.go b/gossip_tracer.go index 10bf1f62..7435cf74 100644 --- a/gossip_tracer.go +++ b/gossip_tracer.go @@ -9,10 +9,10 @@ import ( "github.com/libp2p/go-libp2p/core/protocol" ) -// GossipTracer is an internal tracer that tracks IWANT requests in order to penalize +// gossipTracer is an internal tracer that tracks IWANT requests in order to penalize // peers who don't follow up on IWANT requests after an IHAVE advertisement. // The tracking of promises is probabilistic to avoid using too much memory. -type GossipTracer struct { +type gossipTracer struct { sync.Mutex idGen *msgIDGenerator @@ -27,15 +27,15 @@ type GossipTracer struct { peerPromises map[peer.ID]map[string]struct{} } -func newGossipTracer() *GossipTracer { - return &GossipTracer{ +func newGossipTracer() *gossipTracer { + return &gossipTracer{ idGen: newMsgIdGenerator(), promises: make(map[string]map[peer.ID]time.Time), peerPromises: make(map[peer.ID]map[string]struct{}), } } -func (gt *GossipTracer) Start(gs *GossipSubRouter) { +func (gt *gossipTracer) Start(gs *GossipSubRouter) { if gt == nil { return } @@ -45,7 +45,7 @@ func (gt *GossipTracer) Start(gs *GossipSubRouter) { } // track a promise to deliver a message from a list of msgIDs we are requesting -func (gt *GossipTracer) AddPromise(p peer.ID, msgIDs []string) { +func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs []string) { if gt == nil { return } @@ -76,7 +76,7 @@ func (gt *GossipTracer) AddPromise(p peer.ID, msgIDs []string) { // returns the number of broken promises for each peer who didn't follow up // on an IWANT request. -func (gt *GossipTracer) GetBrokenPromises() map[peer.ID]int { +func (gt *gossipTracer) GetBrokenPromises() map[peer.ID]int { if gt == nil { return nil } @@ -114,9 +114,9 @@ func (gt *GossipTracer) GetBrokenPromises() map[peer.ID]int { return res } -var _ RawTracer = (*GossipTracer)(nil) +var _ RawTracer = (*gossipTracer)(nil) -func (gt *GossipTracer) fulfillPromise(msg *Message) { +func (gt *gossipTracer) fulfillPromise(msg *Message) { mid := gt.idGen.ID(msg) gt.Lock() @@ -140,12 +140,12 @@ func (gt *GossipTracer) fulfillPromise(msg *Message) { } } -func (gt *GossipTracer) DeliverMessage(msg *Message) { +func (gt *gossipTracer) DeliverMessage(msg *Message) { // someone delivered a message, fulfill promises for it gt.fulfillPromise(msg) } -func (gt *GossipTracer) RejectMessage(msg *Message, reason string) { +func (gt *gossipTracer) RejectMessage(msg *Message, reason string) { // A message got rejected, so we can fulfill promises and let the score penalty apply // from invalid message delivery. // We do take exception and apply promise penalty regardless in the following cases, where @@ -160,26 +160,26 @@ func (gt *GossipTracer) RejectMessage(msg *Message, reason string) { gt.fulfillPromise(msg) } -func (gt *GossipTracer) ValidateMessage(msg *Message) { +func (gt *gossipTracer) ValidateMessage(msg *Message) { // we consider the promise fulfilled as soon as the message begins validation // if it was a case of signature issue it would have been rejected immediately // without triggering the Validate trace gt.fulfillPromise(msg) } -func (gt *GossipTracer) AddPeer(p peer.ID, proto protocol.ID) {} -func (gt *GossipTracer) RemovePeer(p peer.ID) {} -func (gt *GossipTracer) Join(topic string) {} -func (gt *GossipTracer) Leave(topic string) {} -func (gt *GossipTracer) Graft(p peer.ID, topic string) {} -func (gt *GossipTracer) Prune(p peer.ID, topic string) {} -func (gt *GossipTracer) DuplicateMessage(msg *Message) {} -func (gt *GossipTracer) RecvRPC(rpc *RPC) {} -func (gt *GossipTracer) SendRPC(rpc *RPC, p peer.ID) {} -func (gt *GossipTracer) DropRPC(rpc *RPC, p peer.ID) {} -func (gt *GossipTracer) UndeliverableMessage(msg *Message) {} - -func (gt *GossipTracer) ThrottlePeer(p peer.ID) { +func (gt *gossipTracer) AddPeer(p peer.ID, proto protocol.ID) {} +func (gt *gossipTracer) RemovePeer(p peer.ID) {} +func (gt *gossipTracer) Join(topic string) {} +func (gt *gossipTracer) Leave(topic string) {} +func (gt *gossipTracer) Graft(p peer.ID, topic string) {} +func (gt *gossipTracer) Prune(p peer.ID, topic string) {} +func (gt *gossipTracer) DuplicateMessage(msg *Message) {} +func (gt *gossipTracer) RecvRPC(rpc *RPC) {} +func (gt *gossipTracer) SendRPC(rpc *RPC, p peer.ID) {} +func (gt *gossipTracer) DropRPC(rpc *RPC, p peer.ID) {} +func (gt *gossipTracer) UndeliverableMessage(msg *Message) {} + +func (gt *gossipTracer) ThrottlePeer(p peer.ID) { gt.Lock() defer gt.Unlock() diff --git a/gossipsub.go b/gossipsub.go index 61271845..02655b6b 100644 --- a/gossipsub.go +++ b/gossipsub.go @@ -206,10 +206,7 @@ type GossipSubParams struct { // NewGossipSub returns a new PubSub object using the default GossipSubRouter as the router. func NewGossipSub(ctx context.Context, h host.Host, opts ...Option) (*PubSub, error) { - rt, err := DefaultGossipSubRouter(h) - if err != nil { - return nil, fmt.Errorf("failed to create default gossipsub router: %w", err) - } + rt := DefaultGossipSubRouter(h) opts = append(opts, WithRawTracer(rt.tagTracer)) return NewGossipSubWithRouter(ctx, h, rt, opts...) } @@ -219,12 +216,10 @@ func NewGossipSubWithRouter(ctx context.Context, h host.Host, rt PubSubRouter, o return NewPubSub(ctx, h, rt, opts...) } -type GossipSubRouterOption func(*GossipSubRouter) error - // DefaultGossipSubRouter returns a new GossipSubRouter with default parameters. -func DefaultGossipSubRouter(h host.Host, opts ...GossipSubRouterOption) (*GossipSubRouter, error) { +func DefaultGossipSubRouter(h host.Host) *GossipSubRouter { params := DefaultGossipSubParams() - rt := &GossipSubRouter{ + return &GossipSubRouter{ peers: make(map[peer.ID]protocol.ID), mesh: make(map[string]map[peer.ID]struct{}), fanout: make(map[string]map[peer.ID]struct{}), @@ -242,14 +237,6 @@ func DefaultGossipSubRouter(h host.Host, opts ...GossipSubRouterOption) (*Gossip tagTracer: newTagTracer(h.ConnManager()), params: params, } - - for _, opt := range opts { - if err := opt(rt); err != nil { - return nil, fmt.Errorf("failed to apply gossipsub router option: %w", err) - } - } - - return rt, nil } // DefaultGossipSubParams returns the default gossip sub parameters @@ -290,7 +277,7 @@ func DefaultGossipSubParams() GossipSubParams { // WithPeerScore is a gossipsub router option that enables peer scoring. func WithPeerScore(params *PeerScoreParams, thresholds *PeerScoreThresholds) Option { return func(ps *PubSub) error { - gs, ok := ps.rt.(GossipPubSubRouter) + gs, ok := ps.rt.(*GossipSubRouter) if !ok { return fmt.Errorf("pubsub router is not gossipsub") } @@ -307,17 +294,21 @@ func WithPeerScore(params *PeerScoreParams, thresholds *PeerScoreThresholds) Opt return err } - gs.SetPeerScore(newPeerScore(params)) - gs.SetPeerScoreThresholds(thresholds) + gs.score = newPeerScore(params) + gs.gossipThreshold = thresholds.GossipThreshold + gs.publishThreshold = thresholds.PublishThreshold + gs.graylistThreshold = thresholds.GraylistThreshold + gs.acceptPXThreshold = thresholds.AcceptPXThreshold + gs.opportunisticGraftThreshold = thresholds.OpportunisticGraftThreshold - gs.SetGossipTracer(newGossipTracer()) + gs.gossipTracer = newGossipTracer() // hook the tracer if ps.tracer != nil { - ps.tracer.raw = append(ps.tracer.raw, gs.GetPeerScore(), gs.GetGossipTracer()) + ps.tracer.raw = append(ps.tracer.raw, gs.score, gs.gossipTracer) } else { ps.tracer = &pubsubTracer{ - raw: []RawTracer{gs.GetPeerScore(), gs.GetGossipTracer()}, + raw: []RawTracer{gs.score, gs.gossipTracer}, pid: ps.host.ID(), idGen: ps.idGen, } @@ -330,9 +321,15 @@ func WithPeerScore(params *PeerScoreParams, thresholds *PeerScoreThresholds) Opt // WithFloodPublish is a gossipsub router option that enables flood publishing. // When this is enabled, published messages are forwarded to all peers with score >= // to publishThreshold -func WithFloodPublish(floodPublish bool) GossipSubRouterOption { - return func(gs *GossipSubRouter) error { +func WithFloodPublish(floodPublish bool) Option { + return func(ps *PubSub) error { + gs, ok := ps.rt.(*GossipSubRouter) + if !ok { + return fmt.Errorf("pubsub router is not gossipsub") + } + gs.floodPublish = floodPublish + return nil } } @@ -340,9 +337,15 @@ func WithFloodPublish(floodPublish bool) GossipSubRouterOption { // WithPeerExchange is a gossipsub router option that enables Peer eXchange on PRUNE. // This should generally be enabled in bootstrappers and well connected/trusted nodes // used for bootstrapping. -func WithPeerExchange(doPX bool) GossipSubRouterOption { - return func(gs *GossipSubRouter) error { +func WithPeerExchange(doPX bool) Option { + return func(ps *PubSub) error { + gs, ok := ps.rt.(*GossipSubRouter) + if !ok { + return fmt.Errorf("pubsub router is not gossipsub") + } + gs.doPX = doPX + return nil } } @@ -354,7 +357,7 @@ func WithPeerExchange(doPX bool) GossipSubRouterOption { // symmetrically configured at both ends. func WithDirectPeers(pis []peer.AddrInfo) Option { return func(ps *PubSub) error { - gs, ok := ps.rt.(GossipPubSubRouter) + gs, ok := ps.rt.(*GossipSubRouter) if !ok { return fmt.Errorf("pubsub router is not gossipsub") } @@ -365,10 +368,10 @@ func WithDirectPeers(pis []peer.AddrInfo) Option { ps.host.Peerstore().AddAddrs(pi.ID, pi.Addrs, peerstore.PermanentAddrTTL) } - gs.SetDirectPeers(direct) + gs.direct = direct - if gs.GetTagTracer() != nil { - gs.GetTagTracer().direct = direct + if gs.tagTracer != nil { + gs.tagTracer.direct = direct } return nil @@ -379,8 +382,12 @@ func WithDirectPeers(pis []peer.AddrInfo) Option { // heartbeat ticks between attempting to reconnect direct peers that are not // currently connected. A "tick" is based on the heartbeat interval, which is // 1s by default. The default value for direct connect ticks is 300. -func WithDirectConnectTicks(t uint64) GossipSubRouterOption { - return func(gs *GossipSubRouter) error { +func WithDirectConnectTicks(t uint64) Option { + return func(ps *PubSub) error { + gs, ok := ps.rt.(*GossipSubRouter) + if !ok { + return fmt.Errorf("pubsub router is not gossipsub") + } gs.params.DirectConnectTicks = t return nil } @@ -388,8 +395,12 @@ func WithDirectConnectTicks(t uint64) GossipSubRouterOption { // WithGossipSubParams is a gossip sub router option that allows a custom // config to be set when instantiating the gossipsub router. -func WithGossipSubParams(cfg GossipSubParams) GossipSubRouterOption { - return func(gs *GossipSubRouter) error { +func WithGossipSubParams(cfg GossipSubParams) Option { + return func(ps *PubSub) error { + gs, ok := ps.rt.(*GossipSubRouter) + if !ok { + return fmt.Errorf("pubsub router is not gossipsub") + } // Overwrite current config and associated variables in the router. gs.params = cfg gs.connect = make(chan connectInfo, cfg.MaxPendingConnections) @@ -399,25 +410,6 @@ func WithGossipSubParams(cfg GossipSubParams) GossipSubRouterOption { } } -type GossipPubSubRouter interface { - PubSubRouter - - SetPeerScore(*PeerScore) - GetPeerScore() *PeerScore - - SetPeerScoreThresholds(*PeerScoreThresholds) - - SetGossipTracer(*GossipTracer) - GetGossipTracer() *GossipTracer - - GetTagTracer() *TagTracer - - SetDirectPeers(map[peer.ID]struct{}) - - SetPeerGater(*PeerGater) - GetPeerGater() *PeerGater -} - // GossipSubRouter is a router that implements the gossipsub protocol. // For each topic we have joined, we maintain an overlay through which // messages flow; this is the mesh map. @@ -445,10 +437,10 @@ type GossipSubRouter struct { mcache *MessageCache tracer *pubsubTracer - score *PeerScore - gossipTracer *GossipTracer - tagTracer *TagTracer - gate *PeerGater + score *peerScore + gossipTracer *gossipTracer + tagTracer *tagTracer + gate *peerGater // config for gossipsub parameters params GossipSubParams @@ -484,8 +476,6 @@ type GossipSubRouter struct { heartbeatTicks uint64 } -var _ GossipPubSubRouter = (*GossipSubRouter)(nil) - type connectInfo struct { p peer.ID spr *record.Envelope @@ -1933,51 +1923,7 @@ func (gs *GossipSubRouter) getPeers(topic string, count int, filter func(peer.ID return peers } -func (gs *GossipSubRouter) SetPeerScore(score *PeerScore) { - gs.score = score -} - -func (gs *GossipSubRouter) GetPeerScore() *PeerScore { - return gs.score -} - -func (gs *GossipSubRouter) SetPeerScoreThresholds(thresholds *PeerScoreThresholds) { - gs.gossipThreshold = thresholds.GossipThreshold - gs.publishThreshold = thresholds.PublishThreshold - gs.graylistThreshold = thresholds.GraylistThreshold - gs.acceptPXThreshold = thresholds.AcceptPXThreshold - gs.opportunisticGraftThreshold = thresholds.OpportunisticGraftThreshold -} - -func (gs *GossipSubRouter) SetGossipTracer(tracer *GossipTracer) { - gs.gossipTracer = tracer -} - -func (gs *GossipSubRouter) GetGossipTracer() *GossipTracer { - return gs.gossipTracer -} - -func (gs *GossipSubRouter) GetTagTracer() *TagTracer { - return gs.tagTracer -} - -func (gs *GossipSubRouter) SetDirectPeers(direct map[peer.ID]struct{}) { - gs.direct = direct -} - -func (gs *GossipSubRouter) SetPeerGater(gater *PeerGater) { - gs.gate = gater -} - -func (gs *GossipSubRouter) GetPeerGater() *PeerGater { - return gs.gate -} - -// WithDefaultTagTracer returns the tag tracer of the GossipSubRouter as a PubSub option. -// This is useful for cases where the GossipSubRouter is instantiated externally, and is -// injected into the GossipSub constructor as a dependency. This allows the tag tracer to be -// also injected into the GossipSub constructor as a PubSub option dependency. -func (gs *GossipSubRouter) WithDefaultTagTracer() Option { +func (gs *GossipSubRouter) WithTagTracerPubsubOption() Option { return WithRawTracer(gs.tagTracer) } diff --git a/gossipsub_connmgr_test.go b/gossipsub_connmgr_test.go index 9502b941..0a97312c 100644 --- a/gossipsub_connmgr_test.go +++ b/gossipsub_connmgr_test.go @@ -79,8 +79,8 @@ func TestGossipsubConnTagMessageDeliveries(t *testing.T) { // use flood publishing, so non-mesh peers will still be delivering messages // to everyone - routers := getGossipSubRouters(honestHosts, WithFloodPublish(true)) - psubs := getGossipSubsWithRouters(ctx, honestHosts, routers) + psubs := getGossipsubs(ctx, honestHosts, + WithFloodPublish(true)) // sybil squatters to be connected later sybilHosts := getNetHosts(t, ctx, nSquatter) diff --git a/gossipsub_feat.go b/gossipsub_feat.go index 83a3b9d9..d5750af3 100644 --- a/gossipsub_feat.go +++ b/gossipsub_feat.go @@ -1,6 +1,8 @@ package pubsub import ( + "fmt" + "github.com/libp2p/go-libp2p/core/protocol" ) @@ -35,10 +37,16 @@ func GossipSubDefaultFeatures(feat GossipSubFeature, proto protocol.ID) bool { // WithGossipSubProtocols is a gossipsub router option that configures a custom protocol list // and feature test function -func WithGossipSubProtocols(protos []protocol.ID, feature GossipSubFeatureTest) GossipSubRouterOption { - return func(gs *GossipSubRouter) error { +func WithGossipSubProtocols(protos []protocol.ID, feature GossipSubFeatureTest) Option { + return func(ps *PubSub) error { + gs, ok := ps.rt.(*GossipSubRouter) + if !ok { + return fmt.Errorf("pubsub router is not gossipsub") + } + gs.protos = protos gs.feature = feature + return nil } } diff --git a/gossipsub_feat_test.go b/gossipsub_feat_test.go index 0643d9e9..712f16df 100644 --- a/gossipsub_feat_test.go +++ b/gossipsub_feat_test.go @@ -43,8 +43,8 @@ func TestGossipSubCustomProtocols(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() hosts := getNetHosts(t, ctx, 3) - routers := getGossipSubRouters(hosts[:2], WithGossipSubProtocols(protos, features)) - gsubs := getGossipSubsWithRouters(ctx, hosts[:2], routers) + + gsubs := getGossipsubs(ctx, hosts[:2], WithGossipSubProtocols(protos, features)) fsub := getPubsub(ctx, hosts[2]) psubs := append(gsubs, fsub) diff --git a/gossipsub_matchfn_test.go b/gossipsub_matchfn_test.go index a5f5d8a8..516fdf5f 100644 --- a/gossipsub_matchfn_test.go +++ b/gossipsub_matchfn_test.go @@ -19,10 +19,10 @@ func TestGossipSubMatchingFn(t *testing.T) { h := getNetHosts(t, ctx, 4) psubs := []*PubSub{ - getGossipSubWithRouter(ctx, h[0], getGossipSubRouter(h[0], WithGossipSubProtocols([]protocol.ID{customsubA100, GossipSubID_v11}, GossipSubDefaultFeatures)), WithProtocolMatchFn(protocolNameMatch)), - getGossipSubWithRouter(ctx, h[1], getGossipSubRouter(h[1], WithGossipSubProtocols([]protocol.ID{customsubA101Beta}, GossipSubDefaultFeatures)), WithProtocolMatchFn(protocolNameMatch)), - getGossipSubWithRouter(ctx, h[2], getGossipSubRouter(h[2], WithGossipSubProtocols([]protocol.ID{GossipSubID_v11}, GossipSubDefaultFeatures)), WithProtocolMatchFn(protocolNameMatch)), - getGossipSubWithRouter(ctx, h[3], getGossipSubRouter(h[3], WithGossipSubProtocols([]protocol.ID{customsubB100}, GossipSubDefaultFeatures)), WithProtocolMatchFn(protocolNameMatch)), + getGossipsub(ctx, h[0], WithProtocolMatchFn(protocolNameMatch), WithGossipSubProtocols([]protocol.ID{customsubA100, GossipSubID_v11}, GossipSubDefaultFeatures)), + getGossipsub(ctx, h[1], WithProtocolMatchFn(protocolNameMatch), WithGossipSubProtocols([]protocol.ID{customsubA101Beta}, GossipSubDefaultFeatures)), + getGossipsub(ctx, h[2], WithProtocolMatchFn(protocolNameMatch), WithGossipSubProtocols([]protocol.ID{GossipSubID_v11}, GossipSubDefaultFeatures)), + getGossipsub(ctx, h[3], WithProtocolMatchFn(protocolNameMatch), WithGossipSubProtocols([]protocol.ID{customsubB100}, GossipSubDefaultFeatures)), } connect(t, h[0], h[1]) diff --git a/gossipsub_test.go b/gossipsub_test.go index d7544ca8..ed7859a1 100644 --- a/gossipsub_test.go +++ b/gossipsub_test.go @@ -25,7 +25,7 @@ import ( "github.com/libp2p/go-msgio/protoio" ) -func getGossipSub(ctx context.Context, h host.Host, opts ...Option) *PubSub { +func getGossipsub(ctx context.Context, h host.Host, opts ...Option) *PubSub { ps, err := NewGossipSub(ctx, h, opts...) if err != nil { panic(err) @@ -33,57 +33,20 @@ func getGossipSub(ctx context.Context, h host.Host, opts ...Option) *PubSub { return ps } -func getGossipSubs(ctx context.Context, hs []host.Host, opts ...Option) []*PubSub { +func getGossipsubs(ctx context.Context, hs []host.Host, opts ...Option) []*PubSub { var psubs []*PubSub for _, h := range hs { - psubs = append(psubs, getGossipSub(ctx, h, opts...)) + psubs = append(psubs, getGossipsub(ctx, h, opts...)) } return psubs } -func getGossipSubsWithRouters(ctx context.Context, hs []host.Host, routers []GossipPubSubRouter, opts ...Option) []*PubSub { - var psubs []*PubSub - if len(hs) != len(routers) { - panic("hosts and routers must have the same length") - } - - for i, h := range hs { - psubs = append(psubs, getGossipSubWithRouter(ctx, h, routers[i], opts...)) - } - - return psubs -} - -func getGossipSubWithRouter(ctx context.Context, hs host.Host, router GossipPubSubRouter, opts ...Option) *PubSub { - ps, err := NewGossipSubWithRouter(ctx, hs, router, opts...) - if err != nil { - panic(err) - } - return ps -} - -func getGossipSubRouter(h host.Host, opts ...GossipSubRouterOption) *GossipSubRouter { - ps, err := DefaultGossipSubRouter(h, opts...) - if err != nil { - panic(err) - } - return ps -} - -func getGossipSubRouters(hs []host.Host, opts ...GossipSubRouterOption) []GossipPubSubRouter { - var routers []GossipPubSubRouter - for _, h := range hs { - routers = append(routers, getGossipSubRouter(h, opts...)) - } - return routers -} - func TestSparseGossipsub(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -124,7 +87,7 @@ func TestDenseGossipsub(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -165,7 +128,7 @@ func TestGossipsubFanout(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs[1:] { @@ -234,7 +197,7 @@ func TestGossipsubFanoutMaintenance(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs[1:] { @@ -319,7 +282,7 @@ func TestGossipsubFanoutExpiry(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 10) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs[1:] { @@ -378,7 +341,7 @@ func TestGossipsubGossip(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -426,7 +389,7 @@ func TestGossipsubGossipPiggyback(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -494,7 +457,7 @@ func TestGossipsubGossipPropagation(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) hosts1 := hosts[:GossipSubD+1] hosts2 := append(hosts[GossipSubD+1:], hosts[0]) @@ -575,7 +538,7 @@ func TestGossipsubPrune(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -631,9 +594,7 @@ func TestGossipsubPruneBackoffTime(t *testing.T) { params.HeartbeatInitialDelay = time.Millisecond * 10 params.HeartbeatInterval = time.Millisecond * 100 - routers := getGossipSubRouters(hosts, WithGossipSubParams(params)) - - psubs := getGossipSubsWithRouters(ctx, hosts, routers, WithPeerScore( + psubs := getGossipsubs(ctx, hosts, WithGossipSubParams(params), WithPeerScore( &PeerScoreParams{ AppSpecificScore: func(p peer.ID) float64 { if p == hosts[0].ID() { @@ -724,7 +685,7 @@ func TestGossipsubGraft(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) sparseConnect(t, hosts) @@ -769,7 +730,7 @@ func TestGossipsubRemovePeer(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) var msgs []*Subscription for _, ps := range psubs { @@ -818,7 +779,7 @@ func TestGossipsubGraftPruneRetry(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 10) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) denseConnect(t, hosts) var topics []string @@ -868,7 +829,7 @@ func TestGossipsubControlPiggyback(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 10) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) denseConnect(t, hosts) for _, ps := range psubs { @@ -950,7 +911,7 @@ func TestMixedGossipsub(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 30) - gsubs := getGossipSubs(ctx, hosts[:20]) + gsubs := getGossipsubs(ctx, hosts[:20]) fsubs := getPubsubs(ctx, hosts[20:]) psubs := append(gsubs, fsubs...) @@ -994,7 +955,7 @@ func TestGossipsubMultihops(t *testing.T) { hosts := getNetHosts(t, ctx, 6) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) connect(t, hosts[0], hosts[1]) connect(t, hosts[1], hosts[2]) @@ -1036,7 +997,7 @@ func TestGossipsubTreeTopology(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 10) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) connect(t, hosts[0], hosts[1]) connect(t, hosts[1], hosts[2]) @@ -1100,8 +1061,7 @@ func TestGossipsubStarTopology(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - routers := getGossipSubRouters(hosts, WithPeerExchange(true), WithFloodPublish(true)) - psubs := getGossipSubsWithRouters(ctx, hosts, routers) + psubs := getGossipsubs(ctx, hosts, WithPeerExchange(true), WithFloodPublish(true)) // configure the center of the star with a very low D psubs[0].eval <- func() { @@ -1185,8 +1145,7 @@ func TestGossipsubStarTopologyWithSignedPeerRecords(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - routers := getGossipSubRouters(hosts, WithPeerExchange(true), WithFloodPublish(true)) - psubs := getGossipSubsWithRouters(ctx, hosts, routers) + psubs := getGossipsubs(ctx, hosts, WithPeerExchange(true), WithFloodPublish(true)) // configure the center of the star with a very low D psubs[0].eval <- func() { @@ -1265,9 +1224,9 @@ func TestGossipsubDirectPeers(t *testing.T) { h := getNetHosts(t, ctx, 3) psubs := []*PubSub{ - getGossipSubWithRouter(ctx, h[0], getGossipSubRouter(h[0], WithDirectConnectTicks(2))), - getGossipSubWithRouter(ctx, h[1], getGossipSubRouter(h[1], WithDirectConnectTicks(2)), WithDirectPeers([]peer.AddrInfo{{ID: h[2].ID(), Addrs: h[2].Addrs()}})), - getGossipSubWithRouter(ctx, h[2], getGossipSubRouter(h[2], WithDirectConnectTicks(2)), WithDirectPeers([]peer.AddrInfo{{ID: h[1].ID(), Addrs: h[1].Addrs()}})), + getGossipsub(ctx, h[0], WithDirectConnectTicks(2)), + getGossipsub(ctx, h[1], WithDirectPeers([]peer.AddrInfo{{ID: h[2].ID(), Addrs: h[2].Addrs()}}), WithDirectConnectTicks(2)), + getGossipsub(ctx, h[2], WithDirectPeers([]peer.AddrInfo{{ID: h[1].ID(), Addrs: h[1].Addrs()}}), WithDirectConnectTicks(2)), } connect(t, h[0], h[1]) @@ -1329,13 +1288,13 @@ func TestGossipSubPeerFilter(t *testing.T) { h := getNetHosts(t, ctx, 3) psubs := []*PubSub{ - getGossipSub(ctx, h[0], WithPeerFilter(func(pid peer.ID, topic string) bool { + getGossipsub(ctx, h[0], WithPeerFilter(func(pid peer.ID, topic string) bool { return pid == h[1].ID() })), - getGossipSub(ctx, h[1], WithPeerFilter(func(pid peer.ID, topic string) bool { + getGossipsub(ctx, h[1], WithPeerFilter(func(pid peer.ID, topic string) bool { return pid == h[0].ID() })), - getGossipSub(ctx, h[2]), + getGossipsub(ctx, h[2]), } connect(t, h[0], h[1]) @@ -1371,9 +1330,9 @@ func TestGossipsubDirectPeersFanout(t *testing.T) { h := getNetHosts(t, ctx, 3) psubs := []*PubSub{ - getGossipSub(ctx, h[0]), - getGossipSub(ctx, h[1], WithDirectPeers([]peer.AddrInfo{{ID: h[2].ID(), Addrs: h[2].Addrs()}})), - getGossipSub(ctx, h[2], WithDirectPeers([]peer.AddrInfo{{ID: h[1].ID(), Addrs: h[1].Addrs()}})), + getGossipsub(ctx, h[0]), + getGossipsub(ctx, h[1], WithDirectPeers([]peer.AddrInfo{{ID: h[2].ID(), Addrs: h[2].Addrs()}})), + getGossipsub(ctx, h[2], WithDirectPeers([]peer.AddrInfo{{ID: h[1].ID(), Addrs: h[1].Addrs()}})), } connect(t, h[0], h[1]) @@ -1457,8 +1416,7 @@ func TestGossipsubFloodPublish(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - routers := getGossipSubRouters(hosts, WithFloodPublish(true)) - psubs := getGossipSubsWithRouters(ctx, hosts, routers) + psubs := getGossipsubs(ctx, hosts, WithFloodPublish(true)) // build the star for i := 1; i < 20; i++ { @@ -1493,7 +1451,7 @@ func TestGossipsubEnoughPeers(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) for _, ps := range psubs { _, err := ps.Subscribe("test") @@ -1542,8 +1500,8 @@ func TestGossipsubCustomParams(t *testing.T) { wantedMaxPendingConns := 23 params.MaxPendingConnections = wantedMaxPendingConns hosts := getNetHosts(t, ctx, 1) - routers := getGossipSubRouters(hosts, WithGossipSubParams(params)) - psubs := getGossipSubsWithRouters(ctx, hosts, routers) + psubs := getGossipsubs(ctx, hosts, + WithGossipSubParams(params)) if len(psubs) != 1 { t.Fatalf("incorrect number of pusbub objects received: wanted %d but got %d", 1, len(psubs)) @@ -1571,7 +1529,7 @@ func TestGossipsubNegativeScore(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 20) - psubs := getGossipSubs(ctx, hosts, + psubs := getGossipsubs(ctx, hosts, WithPeerScore( &PeerScoreParams{ AppSpecificScore: func(p peer.ID) float64 { @@ -1655,7 +1613,7 @@ func TestGossipsubScoreValidatorEx(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 3) - psubs := getGossipSubs(ctx, hosts, + psubs := getGossipsubs(ctx, hosts, WithPeerScore( &PeerScoreParams{ AppSpecificScore: func(p peer.ID) float64 { return 0 }, @@ -1744,7 +1702,7 @@ func TestGossipsubPiggybackControl(t *testing.T) { h := bhost.NewBlankHost(swarmt.GenSwarm(t)) defer h.Close() - ps := getGossipSub(ctx, h) + ps := getGossipsub(ctx, h) blah := peer.ID("bogotr0n") @@ -1792,7 +1750,7 @@ func TestGossipsubMultipleGraftTopics(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 2) - psubs := getGossipSubs(ctx, hosts) + psubs := getGossipsubs(ctx, hosts) sparseConnect(t, hosts) time.Sleep(time.Second * 1) @@ -1861,8 +1819,8 @@ func TestGossipsubOpportunisticGrafting(t *testing.T) { hosts := getNetHosts(t, ctx, 50) // pubsubs for the first 10 hosts - routers := getGossipSubRouters(hosts[:10], WithFloodPublish(true)) - psubs := getGossipSubsWithRouters(ctx, hosts[:10], routers, + psubs := getGossipsubs(ctx, hosts[:10], + WithFloodPublish(true), WithPeerScore( &PeerScoreParams{ AppSpecificScore: func(peer.ID) float64 { return 0 }, @@ -1962,8 +1920,8 @@ func TestGossipSubLeaveTopic(t *testing.T) { h := getNetHosts(t, ctx, 2) psubs := []*PubSub{ - getGossipSub(ctx, h[0]), - getGossipSub(ctx, h[1]), + getGossipsub(ctx, h[0]), + getGossipsub(ctx, h[1]), } connect(t, h[0], h[1]) @@ -2033,9 +1991,9 @@ func TestGossipSubJoinTopic(t *testing.T) { h := getNetHosts(t, ctx, 3) psubs := []*PubSub{ - getGossipSub(ctx, h[0]), - getGossipSub(ctx, h[1]), - getGossipSub(ctx, h[2]), + getGossipsub(ctx, h[0]), + getGossipsub(ctx, h[1]), + getGossipsub(ctx, h[2]), } connect(t, h[0], h[1]) @@ -2114,8 +2072,9 @@ func TestGossipsubPeerScoreInspect(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 2) + inspector := &mockPeerScoreInspector{} - psub1 := getGossipSub(ctx, hosts[0], + psub1 := getGossipsub(ctx, hosts[0], WithPeerScore( &PeerScoreParams{ Topics: map[string]*TopicScoreParams{ @@ -2138,9 +2097,8 @@ func TestGossipsubPeerScoreInspect(t *testing.T) { PublishThreshold: -10, GraylistThreshold: -1000, }), - WithPeerScoreInspect(inspector.inspect, time.Second), - ) - psub2 := getGossipSub(ctx, hosts[1]) + WithPeerScoreInspect(inspector.inspect, time.Second)) + psub2 := getGossipsub(ctx, hosts[1]) psubs := []*PubSub{psub1, psub2} connect(t, hosts[0], hosts[1]) @@ -2175,7 +2133,7 @@ func TestGossipsubPeerScoreResetTopicParams(t *testing.T) { hosts := getNetHosts(t, ctx, 1) - ps := getGossipSub(ctx, hosts[0], + ps := getGossipsub(ctx, hosts[0], WithPeerScore( &PeerScoreParams{ Topics: map[string]*TopicScoreParams{ @@ -2241,7 +2199,7 @@ func TestGossipsubRPCFragmentation(t *testing.T) { defer cancel() hosts := getNetHosts(t, ctx, 2) - ps := getGossipSub(ctx, hosts[0]) + ps := getGossipsub(ctx, hosts[0]) // make a fake peer that requests everything through IWANT gossip iwe := iwantEverything{h: hosts[1]} diff --git a/peer_gater.go b/peer_gater.go index af778539..4e26bc26 100644 --- a/peer_gater.go +++ b/peer_gater.go @@ -116,7 +116,7 @@ func DefaultPeerGaterParams() *PeerGaterParams { } // the gater object. -type PeerGater struct { +type peerGater struct { sync.Mutex host host.Host @@ -163,7 +163,7 @@ type peerGaterStats struct { // interval. func WithPeerGater(params *PeerGaterParams) Option { return func(ps *PubSub) error { - gs, ok := ps.rt.(GossipPubSubRouter) + gs, ok := ps.rt.(*GossipSubRouter) if !ok { return fmt.Errorf("pubsub router is not gossipsub") } @@ -173,14 +173,14 @@ func WithPeerGater(params *PeerGaterParams) Option { return err } - gs.SetPeerGater(newPeerGater(ps.ctx, ps.host, params)) + gs.gate = newPeerGater(ps.ctx, ps.host, params) // hook the tracer if ps.tracer != nil { - ps.tracer.raw = append(ps.tracer.raw, gs.GetPeerGater()) + ps.tracer.raw = append(ps.tracer.raw, gs.gate) } else { ps.tracer = &pubsubTracer{ - raw: []RawTracer{gs.GetPeerGater()}, + raw: []RawTracer{gs.gate}, pid: ps.host.ID(), idGen: ps.idGen, } @@ -190,8 +190,8 @@ func WithPeerGater(params *PeerGaterParams) Option { } } -func newPeerGater(ctx context.Context, host host.Host, params *PeerGaterParams) *PeerGater { - pg := &PeerGater{ +func newPeerGater(ctx context.Context, host host.Host, params *PeerGaterParams) *peerGater { + pg := &peerGater{ params: params, peerStats: make(map[peer.ID]*peerGaterStats), ipStats: make(map[string]*peerGaterStats), @@ -201,7 +201,7 @@ func newPeerGater(ctx context.Context, host host.Host, params *PeerGaterParams) return pg } -func (pg *PeerGater) background(ctx context.Context) { +func (pg *peerGater) background(ctx context.Context) { tick := time.NewTicker(pg.params.DecayInterval) defer tick.Stop() @@ -216,7 +216,7 @@ func (pg *PeerGater) background(ctx context.Context) { } } -func (pg *PeerGater) decayStats() { +func (pg *peerGater) decayStats() { pg.Lock() defer pg.Unlock() @@ -258,7 +258,7 @@ func (pg *PeerGater) decayStats() { } } -func (pg *PeerGater) getPeerStats(p peer.ID) *peerGaterStats { +func (pg *peerGater) getPeerStats(p peer.ID) *peerGaterStats { st, ok := pg.peerStats[p] if !ok { st = pg.getIPStats(p) @@ -267,7 +267,7 @@ func (pg *PeerGater) getPeerStats(p peer.ID) *peerGaterStats { return st } -func (pg *PeerGater) getIPStats(p peer.ID) *peerGaterStats { +func (pg *peerGater) getIPStats(p peer.ID) *peerGaterStats { ip := pg.getPeerIP(p) st, ok := pg.ipStats[ip] if !ok { @@ -277,7 +277,7 @@ func (pg *PeerGater) getIPStats(p peer.ID) *peerGaterStats { return st } -func (pg *PeerGater) getPeerIP(p peer.ID) string { +func (pg *peerGater) getPeerIP(p peer.ID) string { if pg.getIP != nil { return pg.getIP(p) } @@ -317,7 +317,7 @@ func (pg *PeerGater) getPeerIP(p peer.ID) string { } // router interface -func (pg *PeerGater) AcceptFrom(p peer.ID) AcceptStatus { +func (pg *peerGater) AcceptFrom(p peer.ID) AcceptStatus { if pg == nil { return AcceptAll } @@ -363,10 +363,10 @@ func (pg *PeerGater) AcceptFrom(p peer.ID) AcceptStatus { } // -- RawTracer interface methods -var _ RawTracer = (*PeerGater)(nil) +var _ RawTracer = (*peerGater)(nil) // tracer interface -func (pg *PeerGater) AddPeer(p peer.ID, proto protocol.ID) { +func (pg *peerGater) AddPeer(p peer.ID, proto protocol.ID) { pg.Lock() defer pg.Unlock() @@ -374,7 +374,7 @@ func (pg *PeerGater) AddPeer(p peer.ID, proto protocol.ID) { st.connected++ } -func (pg *PeerGater) RemovePeer(p peer.ID) { +func (pg *peerGater) RemovePeer(p peer.ID) { pg.Lock() defer pg.Unlock() @@ -385,19 +385,19 @@ func (pg *PeerGater) RemovePeer(p peer.ID) { delete(pg.peerStats, p) } -func (pg *PeerGater) Join(topic string) {} -func (pg *PeerGater) Leave(topic string) {} -func (pg *PeerGater) Graft(p peer.ID, topic string) {} -func (pg *PeerGater) Prune(p peer.ID, topic string) {} +func (pg *peerGater) Join(topic string) {} +func (pg *peerGater) Leave(topic string) {} +func (pg *peerGater) Graft(p peer.ID, topic string) {} +func (pg *peerGater) Prune(p peer.ID, topic string) {} -func (pg *PeerGater) ValidateMessage(msg *Message) { +func (pg *peerGater) ValidateMessage(msg *Message) { pg.Lock() defer pg.Unlock() pg.validate++ } -func (pg *PeerGater) DeliverMessage(msg *Message) { +func (pg *peerGater) DeliverMessage(msg *Message) { pg.Lock() defer pg.Unlock() @@ -413,7 +413,7 @@ func (pg *PeerGater) DeliverMessage(msg *Message) { st.deliver += weight } -func (pg *PeerGater) RejectMessage(msg *Message, reason string) { +func (pg *peerGater) RejectMessage(msg *Message, reason string) { pg.Lock() defer pg.Unlock() @@ -434,7 +434,7 @@ func (pg *PeerGater) RejectMessage(msg *Message, reason string) { } } -func (pg *PeerGater) DuplicateMessage(msg *Message) { +func (pg *peerGater) DuplicateMessage(msg *Message) { pg.Lock() defer pg.Unlock() @@ -442,12 +442,12 @@ func (pg *PeerGater) DuplicateMessage(msg *Message) { st.duplicate++ } -func (pg *PeerGater) ThrottlePeer(p peer.ID) {} +func (pg *peerGater) ThrottlePeer(p peer.ID) {} -func (pg *PeerGater) RecvRPC(rpc *RPC) {} +func (pg *peerGater) RecvRPC(rpc *RPC) {} -func (pg *PeerGater) SendRPC(rpc *RPC, p peer.ID) {} +func (pg *peerGater) SendRPC(rpc *RPC, p peer.ID) {} -func (pg *PeerGater) DropRPC(rpc *RPC, p peer.ID) {} +func (pg *peerGater) DropRPC(rpc *RPC, p peer.ID) {} -func (pg *PeerGater) UndeliverableMessage(msg *Message) {} +func (pg *peerGater) UndeliverableMessage(msg *Message) {} diff --git a/score.go b/score.go index eb84e262..ec6c8213 100644 --- a/score.go +++ b/score.go @@ -61,7 +61,7 @@ type topicStats struct { invalidMessageDeliveries float64 } -type PeerScore struct { +type peerScore struct { sync.Mutex // the score parameters @@ -85,7 +85,7 @@ type PeerScore struct { inspectPeriod time.Duration } -var _ RawTracer = (*PeerScore)(nil) +var _ RawTracer = (*peerScore)(nil) type messageDeliveries struct { seenMsgTTL time.Duration @@ -151,41 +151,41 @@ type TopicScoreSnapshot struct { // This option must be passed _after_ the WithPeerScore option. func WithPeerScoreInspect(inspect interface{}, period time.Duration) Option { return func(ps *PubSub) error { - gs, ok := ps.rt.(GossipPubSubRouter) + gs, ok := ps.rt.(*GossipSubRouter) if !ok { return fmt.Errorf("pubsub router is not gossipsub") } - if gs.GetPeerScore() == nil { + if gs.score == nil { return fmt.Errorf("peer scoring is not enabled") } - if gs.GetPeerScore().inspect != nil || gs.GetPeerScore().inspectEx != nil { + if gs.score.inspect != nil || gs.score.inspectEx != nil { return fmt.Errorf("duplicate peer score inspector") } switch i := inspect.(type) { case PeerScoreInspectFn: - gs.GetPeerScore().inspect = i + gs.score.inspect = i case ExtendedPeerScoreInspectFn: - gs.GetPeerScore().inspectEx = i + gs.score.inspectEx = i default: return fmt.Errorf("unknown peer score insector type: %v", inspect) } - gs.GetPeerScore().inspectPeriod = period + gs.score.inspectPeriod = period return nil } } // implementation -func newPeerScore(params *PeerScoreParams) *PeerScore { +func newPeerScore(params *PeerScoreParams) *peerScore { seenMsgTTL := params.SeenMsgTTL if seenMsgTTL == 0 { seenMsgTTL = TimeCacheDuration } - return &PeerScore{ + return &peerScore{ params: params, peerStats: make(map[peer.ID]*peerStats), peerIPs: make(map[string]map[peer.ID]struct{}), @@ -198,7 +198,7 @@ func newPeerScore(params *PeerScoreParams) *PeerScore { // If the topic previously had parameters and the parameters are lowering delivery caps, // then the score counters are recapped appropriately. // Note: assumes that the topic score parameters have already been validated -func (ps *PeerScore) SetTopicScoreParams(topic string, p *TopicScoreParams) error { +func (ps *peerScore) SetTopicScoreParams(topic string, p *TopicScoreParams) error { ps.Lock() defer ps.Unlock() @@ -241,7 +241,7 @@ func (ps *PeerScore) SetTopicScoreParams(topic string, p *TopicScoreParams) erro } // router interface -func (ps *PeerScore) Start(gs *GossipSubRouter) { +func (ps *peerScore) Start(gs *GossipSubRouter) { if ps == nil { return } @@ -251,7 +251,7 @@ func (ps *PeerScore) Start(gs *GossipSubRouter) { go ps.background(gs.p.ctx) } -func (ps *PeerScore) Score(p peer.ID) float64 { +func (ps *peerScore) Score(p peer.ID) float64 { if ps == nil { return 0 } @@ -262,7 +262,7 @@ func (ps *PeerScore) Score(p peer.ID) float64 { return ps.score(p) } -func (ps *PeerScore) score(p peer.ID) float64 { +func (ps *peerScore) score(p peer.ID) float64 { pstats, ok := ps.peerStats[p] if !ok { return 0 @@ -341,7 +341,7 @@ func (ps *PeerScore) score(p peer.ID) float64 { return score } -func (ps *PeerScore) ipColocationFactor(p peer.ID) float64 { +func (ps *peerScore) ipColocationFactor(p peer.ID) float64 { pstats, ok := ps.peerStats[p] if !ok { return 0 @@ -388,7 +388,7 @@ loop: } // behavioural pattern penalties -func (ps *PeerScore) AddPenalty(p peer.ID, count int) { +func (ps *peerScore) AddPenalty(p peer.ID, count int) { if ps == nil { return } @@ -405,7 +405,7 @@ func (ps *PeerScore) AddPenalty(p peer.ID, count int) { } // periodic maintenance -func (ps *PeerScore) background(ctx context.Context) { +func (ps *peerScore) background(ctx context.Context) { refreshScores := time.NewTicker(ps.params.DecayInterval) defer refreshScores.Stop() @@ -445,7 +445,7 @@ func (ps *PeerScore) background(ctx context.Context) { } // inspectScores dumps all tracked scores into the inspect function. -func (ps *PeerScore) inspectScores() { +func (ps *peerScore) inspectScores() { if ps.inspect != nil { ps.inspectScoresSimple() } @@ -454,7 +454,7 @@ func (ps *PeerScore) inspectScores() { } } -func (ps *PeerScore) inspectScoresSimple() { +func (ps *peerScore) inspectScoresSimple() { ps.Lock() scores := make(map[peer.ID]float64, len(ps.peerStats)) for p := range ps.peerStats { @@ -469,7 +469,7 @@ func (ps *PeerScore) inspectScoresSimple() { go ps.inspect(scores) } -func (ps *PeerScore) inspectScoresExtended() { +func (ps *peerScore) inspectScoresExtended() { ps.Lock() scores := make(map[peer.ID]*PeerScoreSnapshot, len(ps.peerStats)) for p, pstats := range ps.peerStats { @@ -501,7 +501,7 @@ func (ps *PeerScore) inspectScoresExtended() { // refreshScores decays scores, and purges score records for disconnected peers, // once their expiry has elapsed. -func (ps *PeerScore) refreshScores() { +func (ps *peerScore) refreshScores() { ps.Lock() defer ps.Unlock() @@ -565,7 +565,7 @@ func (ps *PeerScore) refreshScores() { } // refreshIPs refreshes IPs we know of peers we're tracking. -func (ps *PeerScore) refreshIPs() { +func (ps *peerScore) refreshIPs() { ps.Lock() defer ps.Unlock() @@ -584,7 +584,7 @@ func (ps *PeerScore) refreshIPs() { } } -func (ps *PeerScore) gcDeliveryRecords() { +func (ps *peerScore) gcDeliveryRecords() { ps.Lock() defer ps.Unlock() @@ -592,7 +592,7 @@ func (ps *PeerScore) gcDeliveryRecords() { } // tracer interface -func (ps *PeerScore) AddPeer(p peer.ID, proto protocol.ID) { +func (ps *peerScore) AddPeer(p peer.ID, proto protocol.ID) { ps.Lock() defer ps.Unlock() @@ -608,7 +608,7 @@ func (ps *PeerScore) AddPeer(p peer.ID, proto protocol.ID) { pstats.ips = ips } -func (ps *PeerScore) RemovePeer(p peer.ID) { +func (ps *peerScore) RemovePeer(p peer.ID) { ps.Lock() defer ps.Unlock() @@ -643,10 +643,10 @@ func (ps *PeerScore) RemovePeer(p peer.ID) { pstats.expire = time.Now().Add(ps.params.RetainScore) } -func (ps *PeerScore) Join(topic string) {} -func (ps *PeerScore) Leave(topic string) {} +func (ps *peerScore) Join(topic string) {} +func (ps *peerScore) Leave(topic string) {} -func (ps *PeerScore) Graft(p peer.ID, topic string) { +func (ps *peerScore) Graft(p peer.ID, topic string) { ps.Lock() defer ps.Unlock() @@ -666,7 +666,7 @@ func (ps *PeerScore) Graft(p peer.ID, topic string) { tstats.meshMessageDeliveriesActive = false } -func (ps *PeerScore) Prune(p peer.ID, topic string) { +func (ps *peerScore) Prune(p peer.ID, topic string) { ps.Lock() defer ps.Unlock() @@ -690,7 +690,7 @@ func (ps *PeerScore) Prune(p peer.ID, topic string) { tstats.inMesh = false } -func (ps *PeerScore) ValidateMessage(msg *Message) { +func (ps *peerScore) ValidateMessage(msg *Message) { ps.Lock() defer ps.Unlock() @@ -699,7 +699,7 @@ func (ps *PeerScore) ValidateMessage(msg *Message) { _ = ps.deliveries.getRecord(ps.idGen.ID(msg)) } -func (ps *PeerScore) DeliverMessage(msg *Message) { +func (ps *peerScore) DeliverMessage(msg *Message) { ps.Lock() defer ps.Unlock() @@ -725,7 +725,7 @@ func (ps *PeerScore) DeliverMessage(msg *Message) { } } -func (ps *PeerScore) RejectMessage(msg *Message, reason string) { +func (ps *peerScore) RejectMessage(msg *Message, reason string) { ps.Lock() defer ps.Unlock() @@ -792,7 +792,7 @@ func (ps *PeerScore) RejectMessage(msg *Message, reason string) { drec.peers = nil } -func (ps *PeerScore) DuplicateMessage(msg *Message) { +func (ps *peerScore) DuplicateMessage(msg *Message) { ps.Lock() defer ps.Unlock() @@ -826,15 +826,15 @@ func (ps *PeerScore) DuplicateMessage(msg *Message) { } } -func (ps *PeerScore) ThrottlePeer(p peer.ID) {} +func (ps *peerScore) ThrottlePeer(p peer.ID) {} -func (ps *PeerScore) RecvRPC(rpc *RPC) {} +func (ps *peerScore) RecvRPC(rpc *RPC) {} -func (ps *PeerScore) SendRPC(rpc *RPC, p peer.ID) {} +func (ps *peerScore) SendRPC(rpc *RPC, p peer.ID) {} -func (ps *PeerScore) DropRPC(rpc *RPC, p peer.ID) {} +func (ps *peerScore) DropRPC(rpc *RPC, p peer.ID) {} -func (ps *PeerScore) UndeliverableMessage(msg *Message) {} +func (ps *peerScore) UndeliverableMessage(msg *Message) {} // message delivery records func (d *messageDeliveries) getRecord(id string) *deliveryRecord { @@ -898,7 +898,7 @@ func (pstats *peerStats) getTopicStats(topic string, params *PeerScoreParams) (* // markInvalidMessageDelivery increments the "invalid message deliveries" // counter for all scored topics the message is published in. -func (ps *PeerScore) markInvalidMessageDelivery(p peer.ID, msg *Message) { +func (ps *peerScore) markInvalidMessageDelivery(p peer.ID, msg *Message) { pstats, ok := ps.peerStats[p] if !ok { return @@ -916,7 +916,7 @@ func (ps *PeerScore) markInvalidMessageDelivery(p peer.ID, msg *Message) { // markFirstMessageDelivery increments the "first message deliveries" counter // for all scored topics the message is published in, as well as the "mesh // message deliveries" counter, if the peer is in the mesh for the topic. -func (ps *PeerScore) markFirstMessageDelivery(p peer.ID, msg *Message) { +func (ps *peerScore) markFirstMessageDelivery(p peer.ID, msg *Message) { pstats, ok := ps.peerStats[p] if !ok { return @@ -948,7 +948,7 @@ func (ps *PeerScore) markFirstMessageDelivery(p peer.ID, msg *Message) { // markDuplicateMessageDelivery increments the "mesh message deliveries" counter // for messages we've seen before, as long the message was received within the // P3 window. -func (ps *PeerScore) markDuplicateMessageDelivery(p peer.ID, msg *Message, validated time.Time) { +func (ps *peerScore) markDuplicateMessageDelivery(p peer.ID, msg *Message, validated time.Time) { pstats, ok := ps.peerStats[p] if !ok { return @@ -981,7 +981,7 @@ func (ps *PeerScore) markDuplicateMessageDelivery(p peer.ID, msg *Message, valid } // getIPs gets the current IPs for a peer. -func (ps *PeerScore) getIPs(p peer.ID) []string { +func (ps *peerScore) getIPs(p peer.ID) []string { // in unit tests this can be nil if ps.host == nil { return nil @@ -1025,7 +1025,7 @@ func (ps *PeerScore) getIPs(p peer.ID) []string { // setIPs adds tracking for the new IPs in the list, and removes tracking from // the obsolete IPs. -func (ps *PeerScore) setIPs(p peer.ID, newips, oldips []string) { +func (ps *peerScore) setIPs(p peer.ID, newips, oldips []string) { addNewIPs: // add the new IPs to the tracking for _, ip := range newips { @@ -1066,7 +1066,7 @@ removeOldIPs: } // removeIPs removes an IP list from the tracking list for a peer. -func (ps *PeerScore) removeIPs(p peer.ID, ips []string) { +func (ps *peerScore) removeIPs(p peer.ID, ips []string) { for _, ip := range ips { peers, ok := ps.peerIPs[ip] if !ok { diff --git a/score_test.go b/score_test.go index 85979c02..6a6c6890 100644 --- a/score_test.go +++ b/score_test.go @@ -811,7 +811,7 @@ func TestScoreBehaviourPenalty(t *testing.T) { peerA := peer.ID("A") - var ps *PeerScore + var ps *peerScore // first check AddPenalty on a nil peerScore ps.AddPenalty(peerA, 1) @@ -1069,7 +1069,7 @@ func withinVariance(score float64, expected float64, variance float64) bool { } // hack to set IPs for a peer without having to spin up real hosts with shared IPs -func setIPsForPeer(t *testing.T, ps *PeerScore, p peer.ID, ips ...string) { +func setIPsForPeer(t *testing.T, ps *peerScore, p peer.ID, ips ...string) { t.Helper() ps.setIPs(p, ips, []string{}) pstats, ok := ps.peerStats[p] diff --git a/tag_tracer.go b/tag_tracer.go index 305b6637..d813c188 100644 --- a/tag_tracer.go +++ b/tag_tracer.go @@ -30,7 +30,7 @@ var ( GossipSubConnTagMessageDeliveryCap = 15 ) -// TagTracer is an internal tracer that applies connection manager tags to peer +// tagTracer is an internal tracer that applies connection manager tags to peer // connections based on their behavior. // // We tag a peer's connections for the following reasons: @@ -41,7 +41,7 @@ var ( // first. // The delivery tags have a maximum value, GossipSubConnTagMessageDeliveryCap, and they decay at // a rate of GossipSubConnTagDecayAmount / GossipSubConnTagDecayInterval. -type TagTracer struct { +type tagTracer struct { sync.RWMutex cmgr connmgr.ConnManager @@ -55,12 +55,12 @@ type TagTracer struct { nearFirst map[string]map[peer.ID]struct{} } -func newTagTracer(cmgr connmgr.ConnManager) *TagTracer { +func newTagTracer(cmgr connmgr.ConnManager) *tagTracer { decayer, ok := connmgr.SupportsDecay(cmgr) if !ok { log.Debugf("connection manager does not support decaying tags, delivery tags will not be applied") } - return &TagTracer{ + return &tagTracer{ cmgr: cmgr, idGen: newMsgIdGenerator(), decayer: decayer, @@ -69,7 +69,7 @@ func newTagTracer(cmgr connmgr.ConnManager) *TagTracer { } } -func (t *TagTracer) Start(gs *GossipSubRouter) { +func (t *tagTracer) Start(gs *GossipSubRouter) { if t == nil { return } @@ -78,7 +78,7 @@ func (t *TagTracer) Start(gs *GossipSubRouter) { t.direct = gs.direct } -func (t *TagTracer) tagPeerIfDirect(p peer.ID) { +func (t *tagTracer) tagPeerIfDirect(p peer.ID) { if t.direct == nil { return } @@ -90,12 +90,12 @@ func (t *TagTracer) tagPeerIfDirect(p peer.ID) { } } -func (t *TagTracer) tagMeshPeer(p peer.ID, topic string) { +func (t *tagTracer) tagMeshPeer(p peer.ID, topic string) { tag := topicTag(topic) t.cmgr.Protect(p, tag) } -func (t *TagTracer) untagMeshPeer(p peer.ID, topic string) { +func (t *tagTracer) untagMeshPeer(p peer.ID, topic string) { tag := topicTag(topic) t.cmgr.Unprotect(p, tag) } @@ -104,7 +104,7 @@ func topicTag(topic string) string { return fmt.Sprintf("pubsub:%s", topic) } -func (t *TagTracer) addDeliveryTag(topic string) { +func (t *tagTracer) addDeliveryTag(topic string) { if t.decayer == nil { return } @@ -125,7 +125,7 @@ func (t *TagTracer) addDeliveryTag(topic string) { t.decaying[topic] = tag } -func (t *TagTracer) removeDeliveryTag(topic string) { +func (t *tagTracer) removeDeliveryTag(topic string) { t.Lock() defer t.Unlock() tag, ok := t.decaying[topic] @@ -139,7 +139,7 @@ func (t *TagTracer) removeDeliveryTag(topic string) { delete(t.decaying, topic) } -func (t *TagTracer) bumpDeliveryTag(p peer.ID, topic string) error { +func (t *tagTracer) bumpDeliveryTag(p peer.ID, topic string) error { t.RLock() defer t.RUnlock() @@ -150,7 +150,7 @@ func (t *TagTracer) bumpDeliveryTag(p peer.ID, topic string) error { return tag.Bump(p, GossipSubConnTagBumpMessageDelivery) } -func (t *TagTracer) bumpTagsForMessage(p peer.ID, msg *Message) { +func (t *tagTracer) bumpTagsForMessage(p peer.ID, msg *Message) { topic := msg.GetTopic() err := t.bumpDeliveryTag(p, topic) if err != nil { @@ -159,7 +159,7 @@ func (t *TagTracer) bumpTagsForMessage(p peer.ID, msg *Message) { } // nearFirstPeers returns the peers who delivered the message while it was still validating -func (t *TagTracer) nearFirstPeers(msg *Message) []peer.ID { +func (t *tagTracer) nearFirstPeers(msg *Message) []peer.ID { t.Lock() defer t.Unlock() peersMap, ok := t.nearFirst[t.idGen.ID(msg)] @@ -174,17 +174,17 @@ func (t *TagTracer) nearFirstPeers(msg *Message) []peer.ID { } // -- RawTracer interface methods -var _ RawTracer = (*TagTracer)(nil) +var _ RawTracer = (*tagTracer)(nil) -func (t *TagTracer) AddPeer(p peer.ID, proto protocol.ID) { +func (t *tagTracer) AddPeer(p peer.ID, proto protocol.ID) { t.tagPeerIfDirect(p) } -func (t *TagTracer) Join(topic string) { +func (t *tagTracer) Join(topic string) { t.addDeliveryTag(topic) } -func (t *TagTracer) DeliverMessage(msg *Message) { +func (t *tagTracer) DeliverMessage(msg *Message) { nearFirst := t.nearFirstPeers(msg) t.bumpTagsForMessage(msg.ReceivedFrom, msg) @@ -198,19 +198,19 @@ func (t *TagTracer) DeliverMessage(msg *Message) { t.Unlock() } -func (t *TagTracer) Leave(topic string) { +func (t *tagTracer) Leave(topic string) { t.removeDeliveryTag(topic) } -func (t *TagTracer) Graft(p peer.ID, topic string) { +func (t *tagTracer) Graft(p peer.ID, topic string) { t.tagMeshPeer(p, topic) } -func (t *TagTracer) Prune(p peer.ID, topic string) { +func (t *tagTracer) Prune(p peer.ID, topic string) { t.untagMeshPeer(p, topic) } -func (t *TagTracer) ValidateMessage(msg *Message) { +func (t *tagTracer) ValidateMessage(msg *Message) { t.Lock() defer t.Unlock() @@ -222,7 +222,7 @@ func (t *TagTracer) ValidateMessage(msg *Message) { t.nearFirst[id] = make(map[peer.ID]struct{}) } -func (t *TagTracer) DuplicateMessage(msg *Message) { +func (t *tagTracer) DuplicateMessage(msg *Message) { t.Lock() defer t.Unlock() @@ -234,7 +234,7 @@ func (t *TagTracer) DuplicateMessage(msg *Message) { peers[msg.ReceivedFrom] = struct{}{} } -func (t *TagTracer) RejectMessage(msg *Message, reason string) { +func (t *tagTracer) RejectMessage(msg *Message, reason string) { t.Lock() defer t.Unlock() @@ -251,9 +251,9 @@ func (t *TagTracer) RejectMessage(msg *Message, reason string) { } } -func (t *TagTracer) RemovePeer(peer.ID) {} -func (t *TagTracer) ThrottlePeer(p peer.ID) {} -func (t *TagTracer) RecvRPC(rpc *RPC) {} -func (t *TagTracer) SendRPC(rpc *RPC, p peer.ID) {} -func (t *TagTracer) DropRPC(rpc *RPC, p peer.ID) {} -func (t *TagTracer) UndeliverableMessage(msg *Message) {} +func (t *tagTracer) RemovePeer(peer.ID) {} +func (t *tagTracer) ThrottlePeer(p peer.ID) {} +func (t *tagTracer) RecvRPC(rpc *RPC) {} +func (t *tagTracer) SendRPC(rpc *RPC, p peer.ID) {} +func (t *tagTracer) DropRPC(rpc *RPC, p peer.ID) {} +func (t *tagTracer) UndeliverableMessage(msg *Message) {} diff --git a/topic.go b/topic.go index de866383..c08b081b 100644 --- a/topic.go +++ b/topic.go @@ -56,18 +56,18 @@ func (t *Topic) SetScoreParams(p *TopicScoreParams) error { result := make(chan error, 1) update := func() { - gs, ok := t.p.rt.(GossipPubSubRouter) + gs, ok := t.p.rt.(*GossipSubRouter) if !ok { result <- fmt.Errorf("pubsub router is not gossipsub") return } - if gs.GetPeerScore() == nil { + if gs.score == nil { result <- fmt.Errorf("peer scoring is not enabled in router") return } - err := gs.GetPeerScore().SetTopicScoreParams(t.topic, p) + err := gs.score.SetTopicScoreParams(t.topic, p) result <- err } diff --git a/trace_test.go b/trace_test.go index 5a16d55f..fb8cb56d 100644 --- a/trace_test.go +++ b/trace_test.go @@ -28,10 +28,10 @@ func testWithTracer(t *testing.T, tracer EventTracer) { defer cancel() hosts := getNetHosts(t, ctx, 20) - routers := getGossipSubRouters(hosts, WithPeerExchange(true)) - psubs := getGossipSubsWithRouters(ctx, hosts, routers, + psubs := getGossipsubs(ctx, hosts, WithEventTracer(tracer), // to bootstrap from star topology + WithPeerExchange(true), // to exercise the score paths in the tracer WithPeerScore( &PeerScoreParams{