Skip to content

Commit e1abad6

Browse files
committed
fix: tag mesh peers on graft, remove tag on pruning (ChainSafe#383)
1 parent b7e832b commit e1abad6

File tree

6 files changed

+116
-103
lines changed

6 files changed

+116
-103
lines changed

CHANGELOG.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* chore: update cd action by @mpetrunic in https://github.com/ChainSafe/js-libp2p-gossipsub/pull/245
1616

1717

18-
<<<<<<< HEAD
1918
## [5.4.1](https://github.com/ChainSafe/js-libp2p-gossipsub/compare/v5.4.0...v5.4.1) (2022-12-23)
2019

2120

@@ -35,8 +34,6 @@
3534

3635
* tracer to track delivered message if duplicate ([#385](https://github.com/ChainSafe/js-libp2p-gossipsub/issues/385)) ([0c8ddee](https://github.com/ChainSafe/js-libp2p-gossipsub/commit/0c8ddee13a94b44f182ea685cdddc6b7cee43ec4))
3736

38-
=======
39-
>>>>>>> 84e30bc (chore(master): release 5.3.0 (#379))
4037
## [5.3.0](https://github.com/ChainSafe/js-libp2p-gossipsub/compare/v5.2.1...v5.3.0) (2022-12-01)
4138

4239

package-lock.json

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
"@libp2p/interface-connection-manager": "^1.3.0",
7272
"@libp2p/interface-keys": "^1.0.3",
7373
"@libp2p/interface-peer-id": "^1.0.4",
74-
"@libp2p/interface-peer-store": "^1.2.2",
74+
"@libp2p/interface-peer-store": "^1.2.6",
7575
"@libp2p/interface-pubsub": "^3.0.0",
7676
"@libp2p/interface-registrar": "^2.0.3",
7777
"@libp2p/interfaces": "^3.0.3",

src/index.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,7 +1499,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
14991499
return []
15001500
}
15011501

1502-
this.components.peerStore.tagPeer(peerIdFromString(id), 'gossipsub-mesh-peer', {
1502+
await this.components.peerStore.tagPeer(peerIdFromString(id), 'gossipsub-mesh-peer', {
15031503
value: 100 // value should be 0-100
15041504
})
15051505

@@ -1552,7 +1552,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
15521552
}
15531553
}
15541554

1555-
this.components.peerStore.unTagPeer(peerIdFromString(id), 'gossipsub-mesh-peer')
1555+
await this.components.peerStore.unTagPeer(peerIdFromString(id), 'gossipsub-mesh-peer')
15561556
}
15571557

15581558
/**
@@ -2535,8 +2535,6 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
25352535
} else {
25362536
topics.push(topic)
25372537
}
2538-
// Untag peer upon pruning
2539-
this.components.peerStore.unTagPeer(peerIdFromString(id), 'gossipsub-mesh-peer')
25402538
}
25412539

25422540
const graftPeer = (id: PeerIdStr, reason: InclusionReason): void => {
@@ -2555,11 +2553,6 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
25552553
} else {
25562554
topics.push(topic)
25572555
}
2558-
2559-
// Tag the peer upon grafting
2560-
this.components.peerStore.tagPeer(peerIdFromString(id), 'gossipsub-mesh-peer', {
2561-
value: 100 // value should be 0-100
2562-
})
25632556
}
25642557

25652558
// drop all peers with negative score, without PX

test/e2e/go-gossipsub.spec.ts

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import { mockNetwork } from '@libp2p/interface-mocks'
2424
import { stop } from '@libp2p/interfaces/startable'
2525
import { TopicScoreParams } from '../../src/score/peer-score-params.js'
2626
import { awaitEvents, checkReceivedSubscription, checkReceivedSubscriptions } from '../utils/events.js'
27+
import sinon, { SinonStubbedInstance } from 'sinon'
28+
import { Tag } from '@libp2p/interface-peer-store'
2729

2830
/**
2931
* These tests were translated from:
@@ -76,6 +78,115 @@ describe('go-libp2p-pubsub gossipsub tests', function () {
7678
mockNetwork.reset()
7779
})
7880

81+
it('should add the tag gossipsub-mesh-peer upon grafting', async function () {
82+
// Create 20 gossipsub nodes
83+
// Sparsely connect nodes
84+
// Subscribe to the topic, all nodes, waiting for each subscription to propagate first
85+
// Publish 100 messages, each from a random node
86+
// Assert that the subscribed nodes receive every message
87+
psubs = await createComponentsArray({
88+
number: 20,
89+
init: {
90+
scoreParams: {
91+
IPColocationFactorThreshold: 20,
92+
behaviourPenaltyWeight: 0
93+
}
94+
}
95+
})
96+
const topic = 'foobar'
97+
98+
await sparseConnect(psubs)
99+
100+
const nodeAGraftSpy = psubs[0].pubsub as Partial<GossipSub> as SinonStubbedInstance<{
101+
handleGraft: GossipSub['handleGraft']
102+
}>
103+
sinon.spy(nodeAGraftSpy, 'handleGraft')
104+
105+
// add subscriptions to each node
106+
for (const ps of psubs) {
107+
ps.pubsub.subscribe(topic)
108+
// wait for announce to propagate
109+
await delay(100)
110+
}
111+
// await mesh rebalancing
112+
await Promise.all(psubs.map(async (ps) => await awaitEvents(ps.pubsub, 'gossipsub:heartbeat', 2)))
113+
114+
const allTags: Tag[][] = []
115+
116+
psubs.forEach(async (subscribedPeer) => {
117+
const tagArray = await subscribedPeer.components.peerStore.getTags(subscribedPeer.components.peerId)
118+
allTags.push(tagArray)
119+
})
120+
121+
expect(nodeAGraftSpy.handleGraft.callCount).to.be.greaterThan(0)
122+
expect(
123+
allTags.map((tags) => {
124+
tags.map((tag) => {
125+
tag.name.toString()
126+
})
127+
})
128+
).to.include('gossipsub-mesh-peer')
129+
})
130+
it('should remove the tag gossipsub-mesh-peer upon pruning', async function () {
131+
// Create 20 gossipsub nodes
132+
// Sparsely connect nodes
133+
// Subscribe to the topic, all nodes, waiting for each subscription to propagate first
134+
// Publish 100 messages, each from a random node
135+
// Assert that the subscribed nodes receive every message
136+
psubs = await createComponentsArray({
137+
number: 20,
138+
init: {
139+
scoreParams: {
140+
IPColocationFactorThreshold: 20,
141+
behaviourPenaltyWeight: 0
142+
}
143+
}
144+
})
145+
const topic = 'foobar'
146+
psubs.forEach((ps) => ps.pubsub.subscribe(topic))
147+
148+
const unsubscribedPeers: GossipSubAndComponents[] = []
149+
150+
// every node connected to every other
151+
await denseConnect(psubs)
152+
// wait a bit to take effect
153+
await Promise.all(psubs.map(async (ps) => await awaitEvents(ps.pubsub, 'gossipsub:heartbeat', 2)))
154+
155+
// disconnect some peers from the mesh to get some PRUNEs
156+
psubs.slice(0, 5).forEach((ps) => {
157+
unsubscribedPeers.push(ps)
158+
ps.pubsub.unsubscribe(topic)
159+
})
160+
161+
// wait a bit to take effect
162+
await Promise.all(psubs.map(async (ps) => await awaitEvents(ps.pubsub, 'gossipsub:heartbeat', 2)))
163+
164+
const nodeAPruneSpy = unsubscribedPeers[0].pubsub as Partial<GossipSub> as SinonStubbedInstance<{
165+
handlePrune: GossipSub['handlePrune']
166+
}>
167+
sinon.spy(nodeAPruneSpy, 'handlePrune')
168+
169+
// await mesh rebalancing
170+
await Promise.all(psubs.map(async (ps) => await awaitEvents(ps.pubsub, 'gossipsub:heartbeat', 2)))
171+
172+
const allTags: Tag[][] = []
173+
174+
unsubscribedPeers.forEach(async (unsubscribedPeer) => {
175+
const tagArray = await unsubscribedPeer.components.peerStore.getTags(unsubscribedPeer.components.peerId)
176+
console.log('pushing tag array: ', tagArray)
177+
allTags.push(tagArray)
178+
})
179+
180+
expect(nodeAPruneSpy.handlePrune.callCount).to.be.greaterThan(0)
181+
expect(
182+
allTags.map((tags) => {
183+
tags.map((tag) => {
184+
tag.name.toString()
185+
})
186+
})
187+
).not.to.include('gossipsub-mesh-peer')
188+
})
189+
79190
it('test sparse gossipsub', async function () {
80191
// Create 20 gossipsub nodes
81192
// Subscribe to the topic, all nodes
@@ -924,7 +1035,7 @@ describe('go-libp2p-pubsub gossipsub tests', function () {
9241035
// Assert that the nodes are connected
9251036
// Subscribe to the topic, all nodes
9261037
// Publish a message from each node
927-
// Assert that all nodes receive the messages
1038+
// Assert that all nodes receive t he messages
9281039
// Disconnect peers
9291040
// Assert peers reconnect
9301041
// Publish a message from each node

test/gossip.spec.ts

Lines changed: 1 addition & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { mockNetwork } from '@libp2p/interface-mocks'
1010
import { stubInterface } from 'ts-sinon'
1111
import { Registrar } from '@libp2p/interface-registrar'
1212
import { createEd25519PeerId } from '@libp2p/peer-id-factory'
13-
import { PeerStore, Tag } from '@libp2p/interface-peer-store'
13+
import { PeerStore } from '@libp2p/interface-peer-store'
1414
import { ConnectionManager } from '@libp2p/interface-connection-manager'
1515

1616
describe('gossip', () => {
@@ -80,86 +80,6 @@ describe('gossip', () => {
8080
nodeASpy.pushGossip.restore()
8181
})
8282

83-
it('should add the tag gossipsub-mesh-peer upon grafting', async function () {
84-
this.timeout(10e4)
85-
const topic = 'Z'
86-
// add subscriptions to each node
87-
nodes.forEach((n) => n.pubsub.subscribe(topic))
88-
89-
// every node connected to every other
90-
await connectAllPubSubNodes(nodes)
91-
92-
// wait for subscriptions to be transmitted
93-
await Promise.all(nodes.map(async (n) => await pEvent(n.pubsub, 'subscription-change')))
94-
95-
// await mesh rebalancing
96-
await Promise.all(nodes.map(async (n) => await pEvent(n.pubsub, 'gossipsub:heartbeat')))
97-
98-
// const allTags: Tag[][] = []
99-
100-
// nodes.forEach(async (subscribedPeer) => {
101-
// allTags.push(await subscribedPeer.components.peerStore.getTags(subscribedPeer.components.peerId))
102-
// })
103-
104-
// // gossip happens during the heartbeat
105-
// await pEvent(nodes[nodes.length - 1].pubsub, 'gossipsub:heartbeat')
106-
107-
// expect(
108-
// allTags.map((tags) => {
109-
// tags.map((tag) => {
110-
// tag.name.toString()
111-
// })
112-
// })
113-
// ).to.include('gossipsub-mesh-peer')
114-
115-
await pEvent(nodes[nodes.length - 1].pubsub, 'gossipsub:heartbeat')
116-
117-
expect(
118-
(await nodes[0].components.peerStore.getTags(nodes[0].components.peerId)).map((tag) => tag.name.toString())
119-
).to.include('gossipsub-mesh-peer')
120-
})
121-
122-
it('should remove the tag gossipsub-mesh-peer upon pruning', async function () {
123-
this.timeout(10e4)
124-
const topic = 'Z'
125-
// add subscriptions to each node
126-
nodes.forEach((n) => n.pubsub.subscribe(topic))
127-
128-
// every node connected to every other
129-
await connectAllPubSubNodes(nodes)
130-
131-
// wait for subscriptions to be transmitted
132-
await Promise.all(nodes.map(async (n) => await pEvent(n.pubsub, 'subscription-change')))
133-
134-
// disconnect some peers from the mesh to get some PRUNEs and the store the disconnected peers
135-
const unsubscribedPeers: GossipSubAndComponents[] = []
136-
137-
nodes.slice(0, 5).forEach((ps) => {
138-
ps.pubsub.unsubscribe(topic)
139-
unsubscribedPeers.push(ps)
140-
})
141-
142-
// await mesh rebalancing
143-
await Promise.all(nodes.map(async (n) => await pEvent(n.pubsub, 'gossipsub:heartbeat')))
144-
145-
const allTags: Tag[][] = []
146-
147-
unsubscribedPeers.forEach(async (unsubscribedPeer) => {
148-
allTags.push(await unsubscribedPeer.components.peerStore.getTags(unsubscribedPeer.components.peerId))
149-
})
150-
151-
// gossip happens during the heartbeat
152-
await pEvent(unsubscribedPeers[4].pubsub, 'gossipsub:heartbeat')
153-
154-
expect(
155-
allTags.map((tags) => {
156-
tags.map((tag) => {
157-
tag.name.toString()
158-
})
159-
})
160-
).to.not.include('gossipsub-mesh-peer')
161-
})
162-
16383
it('should reject incoming messages bigger than maxInboundDataLength limit', async function () {
16484
this.timeout(10e4)
16585
const nodeA = nodes[0]

0 commit comments

Comments
 (0)