@libp2p/gossipsub 15.0.23 → 16.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -6
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +3 -3
- package/dist/src/constants.d.ts +4 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +4 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/gossipsub.d.ts +7 -1
- package/dist/src/gossipsub.d.ts.map +1 -1
- package/dist/src/gossipsub.js +65 -31
- package/dist/src/gossipsub.js.map +1 -1
- package/dist/src/message/decodeRpc.d.ts.map +1 -1
- package/dist/src/message/decodeRpc.js +8 -7
- package/dist/src/message/decodeRpc.js.map +1 -1
- package/package.json +2 -2
- package/src/constants.ts +5 -0
- package/src/gossipsub.ts +70 -31
- package/src/message/decodeRpc.ts +8 -7
package/src/gossipsub.ts
CHANGED
|
@@ -183,6 +183,9 @@ export class GossipSub extends TypedEventEmitter<GossipSubEvents> implements Typ
|
|
|
183
183
|
/** Number of messages we have asked from peer in the last heartbeat */
|
|
184
184
|
private readonly iasked = new Map<PeerIdStr, number>()
|
|
185
185
|
|
|
186
|
+
/** Number of IWANT messages we have received from peer in the last heartbeat */
|
|
187
|
+
private readonly iwantCounts = new Map<PeerIdStr, number>()
|
|
188
|
+
|
|
186
189
|
/** Prune backoff map */
|
|
187
190
|
private readonly backoff = new Map<TopicStr, Map<PeerIdStr, number>>()
|
|
188
191
|
|
|
@@ -409,7 +412,7 @@ export class GossipSub extends TypedEventEmitter<GossipSubEvents> implements Typ
|
|
|
409
412
|
this.allowedTopics = (opts.allowedTopics != null) ? new Set(opts.allowedTopics) : null
|
|
410
413
|
}
|
|
411
414
|
|
|
412
|
-
readonly [Symbol.toStringTag] = '@
|
|
415
|
+
readonly [Symbol.toStringTag] = '@libp2p/gossipsub'
|
|
413
416
|
|
|
414
417
|
readonly [serviceCapabilities]: string[] = [
|
|
415
418
|
'@libp2p/pubsub'
|
|
@@ -592,6 +595,7 @@ export class GossipSub extends TypedEventEmitter<GossipSubEvents> implements Typ
|
|
|
592
595
|
this.control.clear()
|
|
593
596
|
this.peerhave.clear()
|
|
594
597
|
this.iasked.clear()
|
|
598
|
+
this.iwantCounts.clear()
|
|
595
599
|
this.backoff.clear()
|
|
596
600
|
this.outbound.clear()
|
|
597
601
|
this.gossipTracer.clear()
|
|
@@ -850,6 +854,29 @@ export class GossipSub extends TypedEventEmitter<GossipSubEvents> implements Typ
|
|
|
850
854
|
|
|
851
855
|
// MESSAGE METHODS
|
|
852
856
|
|
|
857
|
+
/**
|
|
858
|
+
* Decode an inbound RPC, enforcing this.decodeRpcLimits.
|
|
859
|
+
*/
|
|
860
|
+
private decodeRpc (rpcBytes: Uint8Array | Uint8ArrayList): RPC {
|
|
861
|
+
return RPC.decode(rpcBytes, {
|
|
862
|
+
limits: {
|
|
863
|
+
subscriptions: this.decodeRpcLimits.maxSubscriptions,
|
|
864
|
+
messages: this.decodeRpcLimits.maxMessages,
|
|
865
|
+
control: {
|
|
866
|
+
ihave: this.decodeRpcLimits.maxControlMessages,
|
|
867
|
+
ihave$: { messageIDs: this.decodeRpcLimits.maxIhaveMessageIDs },
|
|
868
|
+
iwant: this.decodeRpcLimits.maxControlMessages,
|
|
869
|
+
iwant$: { messageIDs: this.decodeRpcLimits.maxIwantMessageIDs },
|
|
870
|
+
graft: this.decodeRpcLimits.maxControlMessages,
|
|
871
|
+
prune: this.decodeRpcLimits.maxControlMessages,
|
|
872
|
+
prune$: { peers: this.decodeRpcLimits.maxPeerInfos },
|
|
873
|
+
idontwant: this.decodeRpcLimits.maxControlMessages,
|
|
874
|
+
idontwant$: { messageIDs: this.decodeRpcLimits.maxIdontwantMessageIDs }
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
})
|
|
878
|
+
}
|
|
879
|
+
|
|
853
880
|
/**
|
|
854
881
|
* Responsible for processing each RPC message received by other peers.
|
|
855
882
|
*/
|
|
@@ -862,25 +889,7 @@ export class GossipSub extends TypedEventEmitter<GossipSubEvents> implements Typ
|
|
|
862
889
|
const rpcBytes = data.subarray()
|
|
863
890
|
// Note: This function may throw, it must be wrapped in a try {} catch {} to prevent closing the stream.
|
|
864
891
|
// TODO: What should we do if the entire RPC is invalid?
|
|
865
|
-
const rpc =
|
|
866
|
-
limits: {
|
|
867
|
-
subscriptions: this.decodeRpcLimits.maxSubscriptions,
|
|
868
|
-
messages: this.decodeRpcLimits.maxMessages,
|
|
869
|
-
control$: {
|
|
870
|
-
ihave: this.decodeRpcLimits.maxIhaveMessageIDs,
|
|
871
|
-
iwant: this.decodeRpcLimits.maxIwantMessageIDs,
|
|
872
|
-
graft: this.decodeRpcLimits.maxControlMessages,
|
|
873
|
-
prune: this.decodeRpcLimits.maxControlMessages,
|
|
874
|
-
prune$: {
|
|
875
|
-
peers: this.decodeRpcLimits.maxPeerInfos
|
|
876
|
-
},
|
|
877
|
-
idontwant: this.decodeRpcLimits.maxControlMessages,
|
|
878
|
-
idontwant$: {
|
|
879
|
-
messageIDs: this.decodeRpcLimits.maxIdontwantMessageIDs
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
})
|
|
892
|
+
const rpc = this.decodeRpc(rpcBytes)
|
|
884
893
|
|
|
885
894
|
this.metrics?.onRpcRecv(rpc, rpcBytes.length)
|
|
886
895
|
|
|
@@ -1316,23 +1325,32 @@ export class GossipSub extends TypedEventEmitter<GossipSubEvents> implements Typ
|
|
|
1316
1325
|
// string msgId => msgId
|
|
1317
1326
|
const iwant = new Map<MsgIdStr, Uint8Array>()
|
|
1318
1327
|
|
|
1319
|
-
|
|
1328
|
+
// Cap the message ids we examine per call at GossipsubMaxIHaveLength
|
|
1329
|
+
let processed = 0
|
|
1330
|
+
// eslint-disable-next-line no-labels
|
|
1331
|
+
out: for (const { topicID, messageIDs } of ihave) {
|
|
1320
1332
|
if (topicID == null || (messageIDs == null) || !this.mesh.has(topicID)) {
|
|
1321
|
-
|
|
1333
|
+
continue
|
|
1322
1334
|
}
|
|
1323
1335
|
|
|
1324
1336
|
let idonthave = 0
|
|
1325
1337
|
|
|
1326
|
-
|
|
1338
|
+
for (const msgId of messageIDs) {
|
|
1339
|
+
if (processed >= constants.GossipsubMaxIHaveLength) {
|
|
1340
|
+
// eslint-disable-next-line no-labels
|
|
1341
|
+
break out
|
|
1342
|
+
}
|
|
1343
|
+
processed++
|
|
1344
|
+
|
|
1327
1345
|
const msgIdStr = this.msgIdToStrFn(msgId)
|
|
1328
1346
|
if (!this.seenCache.has(msgIdStr)) {
|
|
1329
1347
|
iwant.set(msgIdStr, msgId)
|
|
1330
1348
|
idonthave++
|
|
1331
1349
|
}
|
|
1332
|
-
}
|
|
1350
|
+
}
|
|
1333
1351
|
|
|
1334
1352
|
this.metrics?.onIhaveRcv(topicID, messageIDs.length, idonthave)
|
|
1335
|
-
}
|
|
1353
|
+
}
|
|
1336
1354
|
|
|
1337
1355
|
if (iwant.size === 0) {
|
|
1338
1356
|
return []
|
|
@@ -1378,29 +1396,49 @@ export class GossipSub extends TypedEventEmitter<GossipSubEvents> implements Typ
|
|
|
1378
1396
|
return []
|
|
1379
1397
|
}
|
|
1380
1398
|
|
|
1399
|
+
// IWANT flood protection
|
|
1400
|
+
const iwantCount = (this.iwantCounts.get(id) ?? 0) + 1
|
|
1401
|
+
this.iwantCounts.set(id, iwantCount)
|
|
1402
|
+
if (iwantCount > constants.GossipsubMaxIWantMessages) {
|
|
1403
|
+
this.log('IWANT: peer %s has requested too many times within this heartbeat interval; ignoring', id)
|
|
1404
|
+
return []
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1381
1407
|
const ihave = new Map<MsgIdStr, RPC.Message>()
|
|
1382
1408
|
const iwantByTopic = new Map<TopicStr, number>()
|
|
1383
1409
|
let iwantDonthave = 0
|
|
1384
1410
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1411
|
+
// Cap the message ids we examine per call at GossipsubMaxIHaveLength
|
|
1412
|
+
let processed = 0
|
|
1413
|
+
// eslint-disable-next-line no-labels
|
|
1414
|
+
out: for (const { messageIDs } of iwant) {
|
|
1415
|
+
if (messageIDs == null) {
|
|
1416
|
+
continue
|
|
1417
|
+
}
|
|
1418
|
+
for (const msgId of messageIDs) {
|
|
1419
|
+
if (processed >= constants.GossipsubMaxIHaveLength) {
|
|
1420
|
+
// eslint-disable-next-line no-labels
|
|
1421
|
+
break out
|
|
1422
|
+
}
|
|
1423
|
+
processed++
|
|
1424
|
+
|
|
1387
1425
|
const msgIdStr = this.msgIdToStrFn(msgId)
|
|
1388
1426
|
const entry = this.mcache.getWithIWantCount(msgIdStr, id)
|
|
1389
1427
|
if (entry == null) {
|
|
1390
1428
|
iwantDonthave++
|
|
1391
|
-
|
|
1429
|
+
continue
|
|
1392
1430
|
}
|
|
1393
1431
|
|
|
1394
1432
|
iwantByTopic.set(entry.msg.topic, 1 + (iwantByTopic.get(entry.msg.topic) ?? 0))
|
|
1395
1433
|
|
|
1396
1434
|
if (entry.count > constants.GossipsubGossipRetransmission) {
|
|
1397
1435
|
this.log('IWANT: Peer %s has asked for message %s too many times: ignoring request', id, msgId)
|
|
1398
|
-
|
|
1436
|
+
continue
|
|
1399
1437
|
}
|
|
1400
1438
|
|
|
1401
1439
|
ihave.set(msgIdStr, entry.msg)
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1404
1442
|
|
|
1405
1443
|
this.metrics?.onIwantRcv(iwantByTopic, iwantDonthave)
|
|
1406
1444
|
|
|
@@ -2615,6 +2653,7 @@ export class GossipSub extends TypedEventEmitter<GossipSubEvents> implements Typ
|
|
|
2615
2653
|
this.peerhave.clear()
|
|
2616
2654
|
this.metrics?.cacheSize.set({ cache: 'iasked' }, this.iasked.size)
|
|
2617
2655
|
this.iasked.clear()
|
|
2656
|
+
this.iwantCounts.clear()
|
|
2618
2657
|
|
|
2619
2658
|
// apply IWANT request penalties
|
|
2620
2659
|
this.applyIwantPenalties()
|
package/src/message/decodeRpc.ts
CHANGED
|
@@ -9,11 +9,12 @@ export interface DecodeRPCLimits {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export const defaultDecodeRpcLimits: DecodeRPCLimits = {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
maxControlMessages:
|
|
18
|
-
|
|
12
|
+
// 5000 = GossipsubMaxIHaveLength, used as a generous upper bound for these
|
|
13
|
+
maxSubscriptions: 5000,
|
|
14
|
+
maxMessages: 5000,
|
|
15
|
+
maxIhaveMessageIDs: 5000,
|
|
16
|
+
maxIwantMessageIDs: 5000,
|
|
17
|
+
maxControlMessages: 5000,
|
|
18
|
+
maxIdontwantMessageIDs: 512, // GossipsubIdontwantMaxMessages
|
|
19
|
+
maxPeerInfos: 16 // GossipsubPrunePeers
|
|
19
20
|
}
|