@libp2p/gossipsub 14.1.1-6059227cb

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.
Files changed (160) hide show
  1. package/README.md +85 -0
  2. package/dist/index.min.js +19 -0
  3. package/dist/index.min.js.map +7 -0
  4. package/dist/src/config.d.ts +32 -0
  5. package/dist/src/config.d.ts.map +1 -0
  6. package/dist/src/config.js +2 -0
  7. package/dist/src/config.js.map +1 -0
  8. package/dist/src/constants.d.ts +213 -0
  9. package/dist/src/constants.d.ts.map +1 -0
  10. package/dist/src/constants.js +217 -0
  11. package/dist/src/constants.js.map +1 -0
  12. package/dist/src/errors.d.ts +9 -0
  13. package/dist/src/errors.d.ts.map +1 -0
  14. package/dist/src/errors.js +15 -0
  15. package/dist/src/errors.js.map +1 -0
  16. package/dist/src/gossipsub.d.ts +419 -0
  17. package/dist/src/gossipsub.d.ts.map +1 -0
  18. package/dist/src/gossipsub.js +2520 -0
  19. package/dist/src/gossipsub.js.map +1 -0
  20. package/dist/src/index.d.ts +344 -0
  21. package/dist/src/index.d.ts.map +1 -0
  22. package/dist/src/index.js +43 -0
  23. package/dist/src/index.js.map +1 -0
  24. package/dist/src/message/decodeRpc.d.ts +11 -0
  25. package/dist/src/message/decodeRpc.d.ts.map +1 -0
  26. package/dist/src/message/decodeRpc.js +10 -0
  27. package/dist/src/message/decodeRpc.js.map +1 -0
  28. package/dist/src/message/index.d.ts +2 -0
  29. package/dist/src/message/index.d.ts.map +1 -0
  30. package/dist/src/message/index.js +2 -0
  31. package/dist/src/message/index.js.map +1 -0
  32. package/dist/src/message/rpc.d.ts +99 -0
  33. package/dist/src/message/rpc.d.ts.map +1 -0
  34. package/dist/src/message/rpc.js +663 -0
  35. package/dist/src/message/rpc.js.map +1 -0
  36. package/dist/src/message-cache.d.ts +80 -0
  37. package/dist/src/message-cache.d.ts.map +1 -0
  38. package/dist/src/message-cache.js +144 -0
  39. package/dist/src/message-cache.js.map +1 -0
  40. package/dist/src/metrics.d.ts +467 -0
  41. package/dist/src/metrics.d.ts.map +1 -0
  42. package/dist/src/metrics.js +896 -0
  43. package/dist/src/metrics.js.map +1 -0
  44. package/dist/src/score/compute-score.d.ts +4 -0
  45. package/dist/src/score/compute-score.d.ts.map +1 -0
  46. package/dist/src/score/compute-score.js +75 -0
  47. package/dist/src/score/compute-score.js.map +1 -0
  48. package/dist/src/score/index.d.ts +4 -0
  49. package/dist/src/score/index.d.ts.map +1 -0
  50. package/dist/src/score/index.js +4 -0
  51. package/dist/src/score/index.js.map +1 -0
  52. package/dist/src/score/message-deliveries.d.ts +45 -0
  53. package/dist/src/score/message-deliveries.d.ts.map +1 -0
  54. package/dist/src/score/message-deliveries.js +75 -0
  55. package/dist/src/score/message-deliveries.js.map +1 -0
  56. package/dist/src/score/peer-score-params.d.ts +125 -0
  57. package/dist/src/score/peer-score-params.d.ts.map +1 -0
  58. package/dist/src/score/peer-score-params.js +159 -0
  59. package/dist/src/score/peer-score-params.js.map +1 -0
  60. package/dist/src/score/peer-score-thresholds.d.ts +31 -0
  61. package/dist/src/score/peer-score-thresholds.d.ts.map +1 -0
  62. package/dist/src/score/peer-score-thresholds.js +32 -0
  63. package/dist/src/score/peer-score-thresholds.js.map +1 -0
  64. package/dist/src/score/peer-score.d.ts +119 -0
  65. package/dist/src/score/peer-score.d.ts.map +1 -0
  66. package/dist/src/score/peer-score.js +459 -0
  67. package/dist/src/score/peer-score.js.map +1 -0
  68. package/dist/src/score/peer-stats.d.ts +32 -0
  69. package/dist/src/score/peer-stats.d.ts.map +1 -0
  70. package/dist/src/score/peer-stats.js +2 -0
  71. package/dist/src/score/peer-stats.js.map +1 -0
  72. package/dist/src/score/scoreMetrics.d.ts +23 -0
  73. package/dist/src/score/scoreMetrics.d.ts.map +1 -0
  74. package/dist/src/score/scoreMetrics.js +155 -0
  75. package/dist/src/score/scoreMetrics.js.map +1 -0
  76. package/dist/src/stream.d.ts +30 -0
  77. package/dist/src/stream.d.ts.map +1 -0
  78. package/dist/src/stream.js +55 -0
  79. package/dist/src/stream.js.map +1 -0
  80. package/dist/src/tracer.d.ts +53 -0
  81. package/dist/src/tracer.d.ts.map +1 -0
  82. package/dist/src/tracer.js +155 -0
  83. package/dist/src/tracer.js.map +1 -0
  84. package/dist/src/types.d.ts +148 -0
  85. package/dist/src/types.d.ts.map +1 -0
  86. package/dist/src/types.js +90 -0
  87. package/dist/src/types.js.map +1 -0
  88. package/dist/src/utils/buildRawMessage.d.ts +20 -0
  89. package/dist/src/utils/buildRawMessage.d.ts.map +1 -0
  90. package/dist/src/utils/buildRawMessage.js +151 -0
  91. package/dist/src/utils/buildRawMessage.js.map +1 -0
  92. package/dist/src/utils/create-gossip-rpc.d.ts +7 -0
  93. package/dist/src/utils/create-gossip-rpc.d.ts.map +1 -0
  94. package/dist/src/utils/create-gossip-rpc.js +31 -0
  95. package/dist/src/utils/create-gossip-rpc.js.map +1 -0
  96. package/dist/src/utils/index.d.ts +4 -0
  97. package/dist/src/utils/index.d.ts.map +1 -0
  98. package/dist/src/utils/index.js +4 -0
  99. package/dist/src/utils/index.js.map +1 -0
  100. package/dist/src/utils/messageIdToString.d.ts +5 -0
  101. package/dist/src/utils/messageIdToString.d.ts.map +1 -0
  102. package/dist/src/utils/messageIdToString.js +8 -0
  103. package/dist/src/utils/messageIdToString.js.map +1 -0
  104. package/dist/src/utils/msgIdFn.d.ts +10 -0
  105. package/dist/src/utils/msgIdFn.d.ts.map +1 -0
  106. package/dist/src/utils/msgIdFn.js +23 -0
  107. package/dist/src/utils/msgIdFn.js.map +1 -0
  108. package/dist/src/utils/multiaddr.d.ts +3 -0
  109. package/dist/src/utils/multiaddr.d.ts.map +1 -0
  110. package/dist/src/utils/multiaddr.js +15 -0
  111. package/dist/src/utils/multiaddr.js.map +1 -0
  112. package/dist/src/utils/publishConfig.d.ts +8 -0
  113. package/dist/src/utils/publishConfig.d.ts.map +1 -0
  114. package/dist/src/utils/publishConfig.js +25 -0
  115. package/dist/src/utils/publishConfig.js.map +1 -0
  116. package/dist/src/utils/set.d.ts +14 -0
  117. package/dist/src/utils/set.d.ts.map +1 -0
  118. package/dist/src/utils/set.js +41 -0
  119. package/dist/src/utils/set.js.map +1 -0
  120. package/dist/src/utils/shuffle.d.ts +7 -0
  121. package/dist/src/utils/shuffle.d.ts.map +1 -0
  122. package/dist/src/utils/shuffle.js +21 -0
  123. package/dist/src/utils/shuffle.js.map +1 -0
  124. package/dist/src/utils/time-cache.d.ts +22 -0
  125. package/dist/src/utils/time-cache.d.ts.map +1 -0
  126. package/dist/src/utils/time-cache.js +54 -0
  127. package/dist/src/utils/time-cache.js.map +1 -0
  128. package/package.json +142 -0
  129. package/src/config.ts +31 -0
  130. package/src/constants.ts +261 -0
  131. package/src/errors.ts +17 -0
  132. package/src/gossipsub.ts +3061 -0
  133. package/src/index.ts +404 -0
  134. package/src/message/decodeRpc.ts +19 -0
  135. package/src/message/index.ts +1 -0
  136. package/src/message/rpc.proto +58 -0
  137. package/src/message/rpc.ts +848 -0
  138. package/src/message-cache.ts +196 -0
  139. package/src/metrics.ts +1014 -0
  140. package/src/score/compute-score.ts +98 -0
  141. package/src/score/index.ts +3 -0
  142. package/src/score/message-deliveries.ts +95 -0
  143. package/src/score/peer-score-params.ts +316 -0
  144. package/src/score/peer-score-thresholds.ts +70 -0
  145. package/src/score/peer-score.ts +565 -0
  146. package/src/score/peer-stats.ts +33 -0
  147. package/src/score/scoreMetrics.ts +215 -0
  148. package/src/stream.ts +79 -0
  149. package/src/tracer.ts +177 -0
  150. package/src/types.ts +178 -0
  151. package/src/utils/buildRawMessage.ts +174 -0
  152. package/src/utils/create-gossip-rpc.ts +34 -0
  153. package/src/utils/index.ts +3 -0
  154. package/src/utils/messageIdToString.ts +8 -0
  155. package/src/utils/msgIdFn.ts +24 -0
  156. package/src/utils/multiaddr.ts +19 -0
  157. package/src/utils/publishConfig.ts +33 -0
  158. package/src/utils/set.ts +43 -0
  159. package/src/utils/shuffle.ts +21 -0
  160. package/src/utils/time-cache.ts +71 -0
@@ -0,0 +1,98 @@
1
+ import type { PeerScoreParams } from './peer-score-params.js'
2
+ import type { PeerStats } from './peer-stats.js'
3
+
4
+ export function computeScore (
5
+ peer: string,
6
+ pstats: PeerStats,
7
+ params: PeerScoreParams,
8
+ peerIPs: Map<string, Set<string>>
9
+ ): number {
10
+ let score = 0
11
+
12
+ // topic stores
13
+ Object.entries(pstats.topics).forEach(([topic, tstats]) => {
14
+ // the topic parameters
15
+ const topicParams = params.topics[topic]
16
+ if (topicParams === undefined) {
17
+ // we are not scoring this topic
18
+ return
19
+ }
20
+
21
+ let topicScore = 0
22
+
23
+ // P1: time in Mesh
24
+ if (tstats.inMesh) {
25
+ let p1 = tstats.meshTime / topicParams.timeInMeshQuantum
26
+ if (p1 > topicParams.timeInMeshCap) {
27
+ p1 = topicParams.timeInMeshCap
28
+ }
29
+ topicScore += p1 * topicParams.timeInMeshWeight
30
+ }
31
+
32
+ // P2: first message deliveries
33
+ let p2 = tstats.firstMessageDeliveries
34
+ if (p2 > topicParams.firstMessageDeliveriesCap) {
35
+ p2 = topicParams.firstMessageDeliveriesCap
36
+ }
37
+ topicScore += p2 * topicParams.firstMessageDeliveriesWeight
38
+
39
+ // P3: mesh message deliveries
40
+ if (
41
+ tstats.meshMessageDeliveriesActive &&
42
+ tstats.meshMessageDeliveries < topicParams.meshMessageDeliveriesThreshold
43
+ ) {
44
+ const deficit = topicParams.meshMessageDeliveriesThreshold - tstats.meshMessageDeliveries
45
+ const p3 = deficit * deficit
46
+ topicScore += p3 * topicParams.meshMessageDeliveriesWeight
47
+ }
48
+
49
+ // P3b:
50
+ // NOTE: the weight of P3b is negative (validated in validateTopicScoreParams) so this detracts
51
+ const p3b = tstats.meshFailurePenalty
52
+ topicScore += p3b * topicParams.meshFailurePenaltyWeight
53
+
54
+ // P4: invalid messages
55
+ // NOTE: the weight of P4 is negative (validated in validateTopicScoreParams) so this detracts
56
+ const p4 = tstats.invalidMessageDeliveries * tstats.invalidMessageDeliveries
57
+ topicScore += p4 * topicParams.invalidMessageDeliveriesWeight
58
+
59
+ // update score, mixing with topic weight
60
+ score += topicScore * topicParams.topicWeight
61
+ })
62
+
63
+ // apply the topic score cap, if any
64
+ if (params.topicScoreCap > 0 && score > params.topicScoreCap) {
65
+ score = params.topicScoreCap
66
+ }
67
+
68
+ // P5: application-specific score
69
+ const p5 = params.appSpecificScore(peer)
70
+ score += p5 * params.appSpecificWeight
71
+
72
+ // P6: IP colocation factor
73
+ pstats.knownIPs.forEach((ip) => {
74
+ if (params.IPColocationFactorWhitelist.has(ip)) {
75
+ return
76
+ }
77
+
78
+ // P6 has a cliff (IPColocationFactorThreshold)
79
+ // It's only applied if at least that many peers are connected to us from that source IP addr.
80
+ // It is quadratic, and the weight is negative (validated in validatePeerScoreParams)
81
+ const peersInIP = peerIPs.get(ip)
82
+ const numPeersInIP = (peersInIP != null) ? peersInIP.size : 0
83
+ if (numPeersInIP > params.IPColocationFactorThreshold) {
84
+ const surplus = numPeersInIP - params.IPColocationFactorThreshold
85
+ const p6 = surplus * surplus
86
+ score += p6 * params.IPColocationFactorWeight
87
+ }
88
+ })
89
+
90
+ // P7: behavioural pattern penalty
91
+ if (pstats.behaviourPenalty > params.behaviourPenaltyThreshold) {
92
+ const excess = pstats.behaviourPenalty - params.behaviourPenaltyThreshold
93
+ const p7 = excess * excess
94
+ score += p7 * params.behaviourPenaltyWeight
95
+ }
96
+
97
+ return score
98
+ }
@@ -0,0 +1,3 @@
1
+ export * from './peer-score-params.js'
2
+ export * from './peer-score-thresholds.js'
3
+ export * from './peer-score.js'
@@ -0,0 +1,95 @@
1
+ import Denque from 'denque'
2
+ import { TimeCacheDuration } from '../constants.js'
3
+
4
+ export enum DeliveryRecordStatus {
5
+ /**
6
+ * we don't know (yet) if the message is valid
7
+ */
8
+ unknown,
9
+ /**
10
+ * we know the message is valid
11
+ */
12
+ valid,
13
+ /**
14
+ * we know the message is invalid
15
+ */
16
+ invalid,
17
+ /**
18
+ * we were instructed by the validator to ignore the message
19
+ */
20
+ ignored
21
+ }
22
+
23
+ export interface DeliveryRecord {
24
+ status: DeliveryRecordStatus
25
+ firstSeenTsMs: number
26
+ validated: number
27
+ peers: Set<string>
28
+ }
29
+
30
+ interface DeliveryQueueEntry {
31
+ msgId: string
32
+ expire: number
33
+ }
34
+
35
+ /**
36
+ * Map of canonical message ID to DeliveryRecord
37
+ *
38
+ * Maintains an internal queue for efficient gc of old messages
39
+ */
40
+ export class MessageDeliveries {
41
+ private readonly records: Map<string, DeliveryRecord>
42
+ public queue: Denque<DeliveryQueueEntry>
43
+
44
+ constructor () {
45
+ this.records = new Map()
46
+ this.queue = new Denque()
47
+ }
48
+
49
+ getRecord (msgIdStr: string): DeliveryRecord | undefined {
50
+ return this.records.get(msgIdStr)
51
+ }
52
+
53
+ ensureRecord (msgIdStr: string): DeliveryRecord {
54
+ let drec = this.records.get(msgIdStr)
55
+ if (drec != null) {
56
+ return drec
57
+ }
58
+
59
+ // record doesn't exist yet
60
+ // create record
61
+ drec = {
62
+ status: DeliveryRecordStatus.unknown,
63
+ firstSeenTsMs: Date.now(),
64
+ validated: 0,
65
+ peers: new Set()
66
+ }
67
+ this.records.set(msgIdStr, drec)
68
+
69
+ // and add msgId to the queue
70
+ const entry: DeliveryQueueEntry = {
71
+ msgId: msgIdStr,
72
+ expire: Date.now() + TimeCacheDuration
73
+ }
74
+ this.queue.push(entry)
75
+
76
+ return drec
77
+ }
78
+
79
+ gc (): void {
80
+ const now = Date.now()
81
+ // queue is sorted by expiry time
82
+ // remove expired messages, remove from queue until first un-expired message found
83
+ let head = this.queue.peekFront()
84
+ while ((head != null) && head.expire < now) {
85
+ this.records.delete(head.msgId)
86
+ this.queue.shift()
87
+ head = this.queue.peekFront()
88
+ }
89
+ }
90
+
91
+ clear (): void {
92
+ this.records.clear()
93
+ this.queue.clear()
94
+ }
95
+ }
@@ -0,0 +1,316 @@
1
+ import { InvalidPeerScoreParamsError } from '../errors.js'
2
+
3
+ // This file defines PeerScoreParams and TopicScoreParams interfaces
4
+ // as well as constructors, default constructors, and validation functions
5
+ // for these interfaces
6
+
7
+ export interface PeerScoreParams {
8
+ /**
9
+ * Score parameters per topic.
10
+ */
11
+ topics: Record<string, TopicScoreParams>
12
+
13
+ /**
14
+ * Aggregate topic score cap; this limits the total contribution of topics towards a positive
15
+ * score. It must be positive (or 0 for no cap).
16
+ */
17
+ topicScoreCap: number
18
+
19
+ /**
20
+ * P5: Application-specific peer scoring
21
+ */
22
+ appSpecificScore(p: string): number
23
+ appSpecificWeight: number
24
+
25
+ /**
26
+ * P6: IP-colocation factor.
27
+ * The parameter has an associated counter which counts the number of peers with the same IP.
28
+ * If the number of peers in the same IP exceeds IPColocationFactorThreshold, then the value
29
+ * is the square of the difference, ie (PeersInSameIP - IPColocationThreshold)^2.
30
+ * If the number of peers in the same IP is less than the threshold, then the value is 0.
31
+ * The weight of the parameter MUST be negative, unless you want to disable for testing.
32
+ * Note: In order to simulate many IPs in a managable manner when testing, you can set the weight to 0
33
+ * thus disabling the IP colocation penalty.
34
+ */
35
+ IPColocationFactorWeight: number
36
+ IPColocationFactorThreshold: number
37
+ IPColocationFactorWhitelist: Set<string>
38
+
39
+ /**
40
+ * P7: behavioural pattern penalties.
41
+ * This parameter has an associated counter which tracks misbehaviour as detected by the
42
+ * router. The router currently applies penalties for the following behaviors:
43
+ * - attempting to re-graft before the prune backoff time has elapsed.
44
+ * - not following up in IWANT requests for messages advertised with IHAVE.
45
+ *
46
+ * The value of the parameter is the square of the counter, which decays with BehaviourPenaltyDecay.
47
+ * The weight of the parameter MUST be negative (or zero to disable).
48
+ */
49
+ behaviourPenaltyWeight: number
50
+ behaviourPenaltyThreshold: number
51
+ behaviourPenaltyDecay: number
52
+
53
+ /**
54
+ * the decay interval for parameter counters.
55
+ */
56
+ decayInterval: number
57
+
58
+ /**
59
+ * counter value below which it is considered 0.
60
+ */
61
+ decayToZero: number
62
+
63
+ /**
64
+ * time to remember counters for a disconnected peer.
65
+ */
66
+ retainScore: number
67
+ }
68
+
69
+ export interface TopicScoreParams {
70
+ /**
71
+ * The weight of the topic.
72
+ */
73
+ topicWeight: number
74
+
75
+ /**
76
+ * P1: time in the mesh
77
+ * This is the time the peer has ben grafted in the mesh.
78
+ * The value of the parameter is the time/TimeInMeshQuantum, capped by TimeInMeshCap
79
+ * The weight of the parameter MUST be positive (or zero to disable).
80
+ */
81
+ timeInMeshWeight: number
82
+ timeInMeshQuantum: number
83
+ timeInMeshCap: number
84
+
85
+ /**
86
+ * P2: first message deliveries
87
+ * This is the number of message deliveries in the topic.
88
+ * The value of the parameter is a counter, decaying with FirstMessageDeliveriesDecay, and capped
89
+ * by FirstMessageDeliveriesCap.
90
+ * The weight of the parameter MUST be positive (or zero to disable).
91
+ */
92
+ firstMessageDeliveriesWeight: number
93
+ firstMessageDeliveriesDecay: number
94
+ firstMessageDeliveriesCap: number
95
+
96
+ /**
97
+ * P3: mesh message deliveries
98
+ * This is the number of message deliveries in the mesh, within the MeshMessageDeliveriesWindow of
99
+ * message validation; deliveries during validation also count and are retroactively applied
100
+ * when validation succeeds.
101
+ * This window accounts for the minimum time before a hostile mesh peer trying to game the score
102
+ * could replay back a valid message we just sent them.
103
+ * It effectively tracks first and near-first deliveries, ie a message seen from a mesh peer
104
+ * before we have forwarded it to them.
105
+ * The parameter has an associated counter, decaying with MeshMessageDeliveriesDecay.
106
+ * If the counter exceeds the threshold, its value is 0.
107
+ * If the counter is below the MeshMessageDeliveriesThreshold, the value is the square of
108
+ * the deficit, ie (MessageDeliveriesThreshold - counter)^2
109
+ * The penalty is only activated after MeshMessageDeliveriesActivation time in the mesh.
110
+ * The weight of the parameter MUST be negative (or zero to disable).
111
+ */
112
+ meshMessageDeliveriesWeight: number
113
+ meshMessageDeliveriesDecay: number
114
+ meshMessageDeliveriesCap: number
115
+ meshMessageDeliveriesThreshold: number
116
+ meshMessageDeliveriesWindow: number
117
+ meshMessageDeliveriesActivation: number
118
+
119
+ /**
120
+ * P3b: sticky mesh propagation failures
121
+ * This is a sticky penalty that applies when a peer gets pruned from the mesh with an active
122
+ * mesh message delivery penalty.
123
+ * The weight of the parameter MUST be negative (or zero to disable)
124
+ */
125
+ meshFailurePenaltyWeight: number
126
+ meshFailurePenaltyDecay: number
127
+
128
+ /**
129
+ * P4: invalid messages
130
+ * This is the number of invalid messages in the topic.
131
+ * The value of the parameter is the square of the counter, decaying with
132
+ * InvalidMessageDeliveriesDecay.
133
+ * The weight of the parameter MUST be negative (or zero to disable).
134
+ */
135
+ invalidMessageDeliveriesWeight: number
136
+ invalidMessageDeliveriesDecay: number
137
+ }
138
+
139
+ export const defaultPeerScoreParams: PeerScoreParams = {
140
+ topics: {},
141
+ topicScoreCap: 10.0,
142
+ appSpecificScore: () => 0.0,
143
+ appSpecificWeight: 10.0,
144
+ IPColocationFactorWeight: -5.0,
145
+ IPColocationFactorThreshold: 10.0,
146
+ IPColocationFactorWhitelist: new Set(),
147
+ behaviourPenaltyWeight: -10.0,
148
+ behaviourPenaltyThreshold: 0.0,
149
+ behaviourPenaltyDecay: 0.2,
150
+ decayInterval: 1000.0,
151
+ decayToZero: 0.1,
152
+ retainScore: 3600 * 1000
153
+ }
154
+
155
+ export const defaultTopicScoreParams: TopicScoreParams = {
156
+ topicWeight: 0.5,
157
+ timeInMeshWeight: 1,
158
+ timeInMeshQuantum: 1,
159
+ timeInMeshCap: 3600,
160
+
161
+ firstMessageDeliveriesWeight: 1,
162
+ firstMessageDeliveriesDecay: 0.5,
163
+ firstMessageDeliveriesCap: 2000,
164
+
165
+ meshMessageDeliveriesWeight: -1,
166
+ meshMessageDeliveriesDecay: 0.5,
167
+ meshMessageDeliveriesCap: 100,
168
+ meshMessageDeliveriesThreshold: 20,
169
+ meshMessageDeliveriesWindow: 10,
170
+ meshMessageDeliveriesActivation: 5000,
171
+
172
+ meshFailurePenaltyWeight: -1,
173
+ meshFailurePenaltyDecay: 0.5,
174
+
175
+ invalidMessageDeliveriesWeight: -1,
176
+ invalidMessageDeliveriesDecay: 0.3
177
+ }
178
+
179
+ export function createPeerScoreParams (p: Partial<PeerScoreParams> = {}): PeerScoreParams {
180
+ return {
181
+ ...defaultPeerScoreParams,
182
+ ...p,
183
+ topics: (p.topics != null)
184
+ ? Object.entries(p.topics).reduce<Record<string, TopicScoreParams>>((topics, [topic, topicScoreParams]) => {
185
+ topics[topic] = createTopicScoreParams(topicScoreParams)
186
+ return topics
187
+ }, {})
188
+ : {}
189
+ }
190
+ }
191
+
192
+ export function createTopicScoreParams (p: Partial<TopicScoreParams> = {}): TopicScoreParams {
193
+ return {
194
+ ...defaultTopicScoreParams,
195
+ ...p
196
+ }
197
+ }
198
+
199
+ // peer score parameter validation
200
+ export function validatePeerScoreParams (p: PeerScoreParams): void {
201
+ for (const [topic, params] of Object.entries(p.topics)) {
202
+ try {
203
+ validateTopicScoreParams(params)
204
+ } catch (e) {
205
+ throw new InvalidPeerScoreParamsError(`invalid score parameters for topic ${topic}: ${(e as Error).message}`)
206
+ }
207
+ }
208
+
209
+ // check that the topic score is 0 or something positive
210
+ if (p.topicScoreCap < 0) {
211
+ throw new InvalidPeerScoreParamsError('invalid topic score cap; must be positive (or 0 for no cap)')
212
+ }
213
+
214
+ // check that we have an app specific score; the weight can be anything (but expected positive)
215
+ if (p.appSpecificScore === null || p.appSpecificScore === undefined) {
216
+ throw new InvalidPeerScoreParamsError('missing application specific score function')
217
+ }
218
+
219
+ // check the IP colocation factor
220
+ if (p.IPColocationFactorWeight > 0) {
221
+ throw new InvalidPeerScoreParamsError('invalid IPColocationFactorWeight; must be negative (or 0 to disable)')
222
+ }
223
+ if (p.IPColocationFactorWeight !== 0 && p.IPColocationFactorThreshold < 1) {
224
+ throw new InvalidPeerScoreParamsError('invalid IPColocationFactorThreshold; must be at least 1')
225
+ }
226
+
227
+ // check the behaviour penalty
228
+ if (p.behaviourPenaltyWeight > 0) {
229
+ throw new InvalidPeerScoreParamsError('invalid BehaviourPenaltyWeight; must be negative (or 0 to disable)')
230
+ }
231
+ if (p.behaviourPenaltyWeight !== 0 && (p.behaviourPenaltyDecay <= 0 || p.behaviourPenaltyDecay >= 1)) {
232
+ throw new InvalidPeerScoreParamsError('invalid BehaviourPenaltyDecay; must be between 0 and 1')
233
+ }
234
+
235
+ // check the decay parameters
236
+ if (p.decayInterval < 1000) {
237
+ throw new InvalidPeerScoreParamsError('invalid DecayInterval; must be at least 1s')
238
+ }
239
+ if (p.decayToZero <= 0 || p.decayToZero >= 1) {
240
+ throw new InvalidPeerScoreParamsError('invalid DecayToZero; must be between 0 and 1')
241
+ }
242
+
243
+ // no need to check the score retention; a value of 0 means that we don't retain scores
244
+ }
245
+
246
+ // eslint-disable-next-line complexity
247
+ export function validateTopicScoreParams (p: TopicScoreParams): void {
248
+ // make sure we have a sane topic weight
249
+ if (p.topicWeight < 0) {
250
+ throw new InvalidPeerScoreParamsError('invalid topic weight; must be >= 0')
251
+ }
252
+
253
+ // check P1
254
+ if (p.timeInMeshQuantum === 0) {
255
+ throw new InvalidPeerScoreParamsError('invalid TimeInMeshQuantum; must be non zero')
256
+ }
257
+ if (p.timeInMeshWeight < 0) {
258
+ throw new InvalidPeerScoreParamsError('invalid TimeInMeshWeight; must be positive (or 0 to disable)')
259
+ }
260
+ if (p.timeInMeshWeight !== 0 && p.timeInMeshQuantum <= 0) {
261
+ throw new InvalidPeerScoreParamsError('invalid TimeInMeshQuantum; must be positive')
262
+ }
263
+ if (p.timeInMeshWeight !== 0 && p.timeInMeshCap <= 0) {
264
+ throw new InvalidPeerScoreParamsError('invalid TimeInMeshCap; must be positive')
265
+ }
266
+
267
+ // check P2
268
+ if (p.firstMessageDeliveriesWeight < 0) {
269
+ throw new InvalidPeerScoreParamsError('invallid FirstMessageDeliveriesWeight; must be positive (or 0 to disable)')
270
+ }
271
+ if (
272
+ p.firstMessageDeliveriesWeight !== 0 &&
273
+ (p.firstMessageDeliveriesDecay <= 0 || p.firstMessageDeliveriesDecay >= 1)
274
+ ) {
275
+ throw new InvalidPeerScoreParamsError('invalid FirstMessageDeliveriesDecay; must be between 0 and 1')
276
+ }
277
+ if (p.firstMessageDeliveriesWeight !== 0 && p.firstMessageDeliveriesCap <= 0) {
278
+ throw new InvalidPeerScoreParamsError('invalid FirstMessageDeliveriesCap; must be positive')
279
+ }
280
+
281
+ // check P3
282
+ if (p.meshMessageDeliveriesWeight > 0) {
283
+ throw new InvalidPeerScoreParamsError('invalid MeshMessageDeliveriesWeight; must be negative (or 0 to disable)')
284
+ }
285
+ if (p.meshMessageDeliveriesWeight !== 0 && (p.meshMessageDeliveriesDecay <= 0 || p.meshMessageDeliveriesDecay >= 1)) {
286
+ throw new InvalidPeerScoreParamsError('invalid MeshMessageDeliveriesDecay; must be between 0 and 1')
287
+ }
288
+ if (p.meshMessageDeliveriesWeight !== 0 && p.meshMessageDeliveriesCap <= 0) {
289
+ throw new InvalidPeerScoreParamsError('invalid MeshMessageDeliveriesCap; must be positive')
290
+ }
291
+ if (p.meshMessageDeliveriesWeight !== 0 && p.meshMessageDeliveriesThreshold <= 0) {
292
+ throw new InvalidPeerScoreParamsError('invalid MeshMessageDeliveriesThreshold; must be positive')
293
+ }
294
+ if (p.meshMessageDeliveriesWindow < 0) {
295
+ throw new InvalidPeerScoreParamsError('invalid MeshMessageDeliveriesWindow; must be non-negative')
296
+ }
297
+ if (p.meshMessageDeliveriesWeight !== 0 && p.meshMessageDeliveriesActivation < 1000) {
298
+ throw new InvalidPeerScoreParamsError('invalid MeshMessageDeliveriesActivation; must be at least 1s')
299
+ }
300
+
301
+ // check P3b
302
+ if (p.meshFailurePenaltyWeight > 0) {
303
+ throw new InvalidPeerScoreParamsError('invalid MeshFailurePenaltyWeight; must be negative (or 0 to disable)')
304
+ }
305
+ if (p.meshFailurePenaltyWeight !== 0 && (p.meshFailurePenaltyDecay <= 0 || p.meshFailurePenaltyDecay >= 1)) {
306
+ throw new InvalidPeerScoreParamsError('invalid MeshFailurePenaltyDecay; must be between 0 and 1')
307
+ }
308
+
309
+ // check P4
310
+ if (p.invalidMessageDeliveriesWeight > 0) {
311
+ throw new InvalidPeerScoreParamsError('invalid InvalidMessageDeliveriesWeight; must be negative (or 0 to disable)')
312
+ }
313
+ if (p.invalidMessageDeliveriesDecay <= 0 || p.invalidMessageDeliveriesDecay >= 1) {
314
+ throw new InvalidPeerScoreParamsError('invalid InvalidMessageDeliveriesDecay; must be between 0 and 1')
315
+ }
316
+ }
@@ -0,0 +1,70 @@
1
+ import { InvalidPeerScoreThresholdsError } from '../errors.js'
2
+
3
+ // This file defines PeerScoreThresholds interface
4
+ // as well as a constructor, default constructor, and validation function
5
+ // for this interface
6
+
7
+ export interface PeerScoreThresholds {
8
+ /**
9
+ * gossipThreshold is the score threshold below which gossip propagation is supressed;
10
+ * should be negative.
11
+ */
12
+ gossipThreshold: number
13
+
14
+ /**
15
+ * publishThreshold is the score threshold below which we shouldn't publish when using flood
16
+ * publishing (also applies to fanout and floodsub peers); should be negative and <= GossipThreshold.
17
+ */
18
+ publishThreshold: number
19
+
20
+ /**
21
+ * graylistThreshold is the score threshold below which message processing is supressed altogether,
22
+ * implementing an effective graylist according to peer score; should be negative and <= PublisThreshold.
23
+ */
24
+ graylistThreshold: number
25
+
26
+ /**
27
+ * acceptPXThreshold is the score threshold below which PX will be ignored; this should be positive
28
+ * and limited to scores attainable by bootstrappers and other trusted nodes.
29
+ */
30
+ acceptPXThreshold: number
31
+
32
+ /**
33
+ * opportunisticGraftThreshold is the median mesh score threshold before triggering opportunistic
34
+ * grafting; this should have a small positive value.
35
+ */
36
+ opportunisticGraftThreshold: number
37
+ }
38
+
39
+ export const defaultPeerScoreThresholds: PeerScoreThresholds = {
40
+ gossipThreshold: -10,
41
+ publishThreshold: -50,
42
+ graylistThreshold: -80,
43
+ acceptPXThreshold: 10,
44
+ opportunisticGraftThreshold: 20
45
+ }
46
+
47
+ export function createPeerScoreThresholds (p: Partial<PeerScoreThresholds> = {}): PeerScoreThresholds {
48
+ return {
49
+ ...defaultPeerScoreThresholds,
50
+ ...p
51
+ }
52
+ }
53
+
54
+ export function validatePeerScoreThresholds (p: PeerScoreThresholds): void {
55
+ if (p.gossipThreshold > 0) {
56
+ throw new InvalidPeerScoreThresholdsError('invalid gossip threshold; it must be <= 0')
57
+ }
58
+ if (p.publishThreshold > 0 || p.publishThreshold > p.gossipThreshold) {
59
+ throw new InvalidPeerScoreThresholdsError('invalid publish threshold; it must be <= 0 and <= gossip threshold')
60
+ }
61
+ if (p.graylistThreshold > 0 || p.graylistThreshold > p.publishThreshold) {
62
+ throw new InvalidPeerScoreThresholdsError('invalid graylist threshold; it must be <= 0 and <= publish threshold')
63
+ }
64
+ if (p.acceptPXThreshold < 0) {
65
+ throw new InvalidPeerScoreThresholdsError('invalid accept PX threshold; it must be >= 0')
66
+ }
67
+ if (p.opportunisticGraftThreshold < 0) {
68
+ throw new InvalidPeerScoreThresholdsError('invalid opportunistic grafting threshold; it must be >= 0')
69
+ }
70
+ }