Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/gossipsub/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ export const GossipsubPrunePeers = 16
*/
export const GossipsubPruneBackoff = minute

/**
* GossipsubMaxPruneBackoff is the maximum backoff time we accept from a peer in a PRUNE message.
* Larger values are clamped to this.
*/
export const GossipsubMaxPruneBackoff = 60 * minute

/**
* Backoff to use when unsubscribing from a topic. Should not resubscribe to this topic before it expired.
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/gossipsub/src/gossipsub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1619,7 +1619,7 @@ export class GossipSub extends TypedEventEmitter<GossipSubEvents> implements Typ

const peersInMesh = this.mesh.get(topicID)
if (peersInMesh == null) {
return
continue
}

this.log('PRUNE: Remove mesh link to %s in %s', id, topicID)
Expand All @@ -1631,7 +1631,7 @@ export class GossipSub extends TypedEventEmitter<GossipSubEvents> implements Typ

// is there a backoff specified by the peer? if so obey it
if (typeof backoff === 'number' && backoff > 0) {
this.doAddBackoff(id, topicID, backoff * 1000)
this.doAddBackoff(id, topicID, Math.min(backoff * 1000, constants.GossipsubMaxPruneBackoff))
} else {
this.addBackoff(id, topicID)
}
Expand Down
48 changes: 48 additions & 0 deletions packages/gossipsub/test/prune.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { stop } from '@libp2p/interface'
import { expect } from 'aegir/chai'
import { GossipsubMaxPruneBackoff } from '../src/constants.ts'
import { createComponents } from './utils/create-pubsub.ts'
import { createPeerId } from './utils/index.ts'
import type { GossipSubAndComponents } from './utils/create-pubsub.ts'

const TOPIC = 'prune-topic'

describe('handlePrune', () => {
let node: GossipSubAndComponents
let peer: string

beforeEach(async () => {
node = await createComponents({ init: {} })
peer = (await createPeerId()).toString()
})

afterEach(async () => {
await stop(node.pubsub, ...Object.values(node.components))
})

it('caps the backoff accepted from a peer at GossipsubMaxPruneBackoff', async () => {
const gs = node.pubsub as any
gs.mesh.set(TOPIC, new Set([peer]))

const before = Date.now()
await gs.handlePrune(peer, [{ topicID: TOPIC, peers: [], backoff: Number.MAX_SAFE_INTEGER }])
const after = Date.now()

const expire = gs.backoff.get(TOPIC)?.get(peer)
expect(expire).to.be.at.least(before + GossipsubMaxPruneBackoff)
expect(expire).to.be.at.most(after + GossipsubMaxPruneBackoff)
})

it('processes remaining entries after a topic that is not in the mesh', async () => {
const gs = node.pubsub as any
gs.mesh.set(TOPIC, new Set([peer]))

await gs.handlePrune(peer, [
{ topicID: 'not-joined-topic', peers: [] },
{ topicID: TOPIC, peers: [] }
])

expect(node.pubsub.mesh.get(TOPIC)?.has(peer)).to.equal(false)
expect(gs.backoff.get(TOPIC)?.has(peer)).to.equal(true)
})
})
Loading