libp2p 0.35.9-rc.1 → 0.36.1

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 (49) hide show
  1. package/dist/index.min.js +55 -0
  2. package/dist/src/circuit/auto-relay.d.ts.map +1 -1
  3. package/dist/src/config.d.ts +2 -0
  4. package/dist/src/config.d.ts.map +1 -1
  5. package/dist/src/connection-manager/auto-dialler.d.ts.map +1 -1
  6. package/dist/src/connection-manager/index.d.ts.map +1 -1
  7. package/dist/src/dialer/index.d.ts +7 -2
  8. package/dist/src/dialer/index.d.ts.map +1 -1
  9. package/dist/src/errors.d.ts +2 -0
  10. package/dist/src/fetch/constants.d.ts +2 -0
  11. package/dist/src/fetch/constants.d.ts.map +1 -0
  12. package/dist/src/fetch/index.d.ts +76 -0
  13. package/dist/src/fetch/index.d.ts.map +1 -0
  14. package/dist/src/fetch/proto.d.ts +134 -0
  15. package/dist/src/identify/index.d.ts.map +1 -1
  16. package/dist/src/index.d.ts +20 -1
  17. package/dist/src/index.d.ts.map +1 -1
  18. package/dist/src/peer-store/address-book.d.ts +3 -1
  19. package/dist/src/peer-store/address-book.d.ts.map +1 -1
  20. package/dist/src/peer-store/index.d.ts +5 -2
  21. package/dist/src/peer-store/index.d.ts.map +1 -1
  22. package/dist/src/peer-store/store.d.ts.map +1 -1
  23. package/dist/src/ping/index.d.ts +32 -25
  24. package/dist/src/ping/index.d.ts.map +1 -1
  25. package/dist/src/types.d.ts +90 -0
  26. package/dist/src/types.d.ts.map +1 -0
  27. package/dist/src/upgrader.d.ts +8 -2
  28. package/dist/src/upgrader.d.ts.map +1 -1
  29. package/package.json +16 -16
  30. package/src/circuit/auto-relay.js +36 -22
  31. package/src/config.js +2 -0
  32. package/src/connection-manager/auto-dialler.js +15 -6
  33. package/src/connection-manager/index.js +3 -0
  34. package/src/dialer/index.js +20 -1
  35. package/src/errors.js +2 -0
  36. package/src/fetch/README.md +36 -0
  37. package/src/fetch/constants.js +6 -0
  38. package/src/fetch/index.js +159 -0
  39. package/src/fetch/proto.d.ts +134 -0
  40. package/src/fetch/proto.js +333 -0
  41. package/src/fetch/proto.proto +15 -0
  42. package/src/identify/index.js +21 -17
  43. package/src/index.js +49 -8
  44. package/src/peer-store/address-book.js +41 -28
  45. package/src/peer-store/index.js +4 -2
  46. package/src/peer-store/store.js +19 -6
  47. package/src/ping/index.js +51 -52
  48. package/src/types.ts +98 -0
  49. package/src/upgrader.js +29 -1
package/src/index.js CHANGED
@@ -29,8 +29,9 @@ const Upgrader = require('./upgrader')
29
29
  const PeerStore = require('./peer-store')
30
30
  const PubsubAdapter = require('./pubsub-adapter')
31
31
  const Registrar = require('./registrar')
32
- const ping = require('./ping')
33
32
  const IdentifyService = require('./identify')
33
+ const FetchService = require('./fetch')
34
+ const PingService = require('./ping')
34
35
  const NatManager = require('./nat-manager')
35
36
  const { updateSelfPeerRecord } = require('./record/utils')
36
37
 
@@ -47,6 +48,7 @@ const { updateSelfPeerRecord } = require('./record/utils')
47
48
  * @typedef {import('libp2p-interfaces/src/pubsub').PubsubOptions} PubsubOptions
48
49
  * @typedef {import('interface-datastore').Datastore} Datastore
49
50
  * @typedef {import('./pnet')} Protector
51
+ * @typedef {import('./types').ConnectionGater} ConnectionGater
50
52
  * @typedef {Object} PersistentPeerStoreOptions
51
53
  * @property {number} [threshold]
52
54
  */
@@ -105,6 +107,7 @@ const { updateSelfPeerRecord } = require('./record/utils')
105
107
  * @property {Libp2pModules} modules libp2p modules to use
106
108
  * @property {import('./address-manager').AddressManagerOptions} [addresses]
107
109
  * @property {import('./connection-manager').ConnectionManagerOptions} [connectionManager]
110
+ * @property {Partial<import('./types').ConnectionGater>} [connectionGater]
108
111
  * @property {Datastore} [datastore]
109
112
  * @property {import('./dialer').DialerOptions} [dialer]
110
113
  * @property {import('./identify/index').HostProperties} [host] libp2p host
@@ -171,10 +174,25 @@ class Libp2p extends EventEmitter {
171
174
  this.metrics = metrics
172
175
  }
173
176
 
177
+ /** @type {ConnectionGater} */
178
+ this.connectionGater = {
179
+ denyDialPeer: async () => Promise.resolve(false),
180
+ denyDialMultiaddr: async () => Promise.resolve(false),
181
+ denyInboundConnection: async () => Promise.resolve(false),
182
+ denyOutboundConnection: async () => Promise.resolve(false),
183
+ denyInboundEncryptedConnection: async () => Promise.resolve(false),
184
+ denyOutboundEncryptedConnection: async () => Promise.resolve(false),
185
+ denyInboundUpgradedConnection: async () => Promise.resolve(false),
186
+ denyOutboundUpgradedConnection: async () => Promise.resolve(false),
187
+ filterMultiaddrForPeer: async () => Promise.resolve(true),
188
+ ...this._options.connectionGater
189
+ }
190
+
174
191
  /** @type {import('./peer-store/types').PeerStore} */
175
192
  this.peerStore = new PeerStore({
176
193
  peerId: this.peerId,
177
- datastore: (this.datastore && this._options.peerStore.persistence) ? this.datastore : new MemoryDatastore()
194
+ datastore: (this.datastore && this._options.peerStore.persistence) ? this.datastore : new MemoryDatastore(),
195
+ addressFilter: this.connectionGater.filterMultiaddrForPeer
178
196
  })
179
197
 
180
198
  // Addresses {listen, announce, noAnnounce}
@@ -219,6 +237,7 @@ class Libp2p extends EventEmitter {
219
237
 
220
238
  // Setup the Upgrader
221
239
  this.upgrader = new Upgrader({
240
+ connectionGater: this.connectionGater,
222
241
  localPeer: this.peerId,
223
242
  metrics: this.metrics,
224
243
  onConnection: (connection) => this.connectionManager.onConnect(connection),
@@ -261,6 +280,7 @@ class Libp2p extends EventEmitter {
261
280
 
262
281
  this.dialer = new Dialer({
263
282
  transportManager: this.transportManager,
283
+ connectionGater: this.connectionGater,
264
284
  peerStore: this.peerStore,
265
285
  metrics: this.metrics,
266
286
  ...this._options.dialer
@@ -319,10 +339,10 @@ class Libp2p extends EventEmitter {
319
339
  this.peerRouting = new PeerRouting(this)
320
340
  this.contentRouting = new ContentRouting(this)
321
341
 
322
- // Mount default protocols
323
- ping.mount(this)
324
-
325
342
  this._onDiscoveryPeer = this._onDiscoveryPeer.bind(this)
343
+
344
+ this.fetchService = new FetchService(this)
345
+ this.pingService = new PingService(this)
326
346
  }
327
347
 
328
348
  /**
@@ -356,6 +376,14 @@ class Libp2p extends EventEmitter {
356
376
  await this.handle(Object.values(IdentifyService.getProtocolStr(this)), this.identifyService.handleMessage)
357
377
  }
358
378
 
379
+ if (this.fetchService) {
380
+ await this.handle(FetchService.PROTOCOL, this.fetchService.handleMessage)
381
+ }
382
+
383
+ if (this.pingService) {
384
+ await this.handle(PingService.getProtocolStr(this), this.pingService.handleMessage)
385
+ }
386
+
359
387
  try {
360
388
  await this._onStarting()
361
389
  await this._onDidStart()
@@ -407,7 +435,9 @@ class Libp2p extends EventEmitter {
407
435
  await this.natManager.stop()
408
436
  await this.transportManager.close()
409
437
 
410
- ping.unmount(this)
438
+ await this.unhandle(FetchService.PROTOCOL)
439
+ await this.unhandle(PingService.getProtocolStr(this))
440
+
411
441
  this.dialer.destroy()
412
442
  } catch (/** @type {any} */ err) {
413
443
  if (err) {
@@ -559,6 +589,17 @@ class Libp2p extends EventEmitter {
559
589
  )
560
590
  }
561
591
 
592
+ /**
593
+ * Sends a request to fetch the value associated with the given key from the given peer.
594
+ *
595
+ * @param {PeerId|Multiaddr} peer
596
+ * @param {string} key
597
+ * @returns {Promise<Uint8Array | null>}
598
+ */
599
+ fetch (peer, key) {
600
+ return this.fetchService.fetch(peer, key)
601
+ }
602
+
562
603
  /**
563
604
  * Pings the given peer in order to obtain the operation latency.
564
605
  *
@@ -570,10 +611,10 @@ class Libp2p extends EventEmitter {
570
611
 
571
612
  // If received multiaddr, ping it
572
613
  if (multiaddrs) {
573
- return ping(this, multiaddrs[0])
614
+ return this.pingService.ping(multiaddrs[0])
574
615
  }
575
616
 
576
- return ping(this, id)
617
+ return this.pingService.ping(id)
577
618
  }
578
619
 
579
620
  /**
@@ -7,6 +7,11 @@ const PeerId = require('peer-id')
7
7
  const { codes } = require('../errors')
8
8
  const PeerRecord = require('../record/peer-record')
9
9
  const Envelope = require('../record/envelope')
10
+ const { pipe } = require('it-pipe')
11
+ const all = require('it-all')
12
+ const filter = require('it-filter')
13
+ const map = require('it-map')
14
+ const each = require('it-foreach')
10
15
 
11
16
  /**
12
17
  * @typedef {import('./types').PeerStore} PeerStore
@@ -27,10 +32,12 @@ class PeerStoreAddressBook {
27
32
  /**
28
33
  * @param {PeerStore["emit"]} emit
29
34
  * @param {import('./types').Store} store
35
+ * @param {(peerId: PeerId, multiaddr: Multiaddr) => Promise<boolean>} addressFilter
30
36
  */
31
- constructor (emit, store) {
37
+ constructor (emit, store, addressFilter) {
32
38
  this._emit = emit
33
39
  this._store = store
40
+ this._addressFilter = addressFilter
34
41
  }
35
42
 
36
43
  /**
@@ -88,7 +95,7 @@ class PeerStoreAddressBook {
88
95
  // Replace unsigned addresses by the new ones from the record
89
96
  // TODO: Once we have ttls for the addresses, we should merge these in
90
97
  updatedPeer = await this._store.patchOrCreate(peerId, {
91
- addresses: convertMultiaddrsToAddresses(multiaddrs, true),
98
+ addresses: await filterMultiaddrs(peerId, multiaddrs, this._addressFilter, true),
92
99
  peerRecordEnvelope: envelope.marshal()
93
100
  })
94
101
 
@@ -180,6 +187,11 @@ class PeerStoreAddressBook {
180
187
  throw errcode(new Error('peerId must be an instance of peer-id'), codes.ERR_INVALID_PARAMETERS)
181
188
  }
182
189
 
190
+ if (!Array.isArray(multiaddrs)) {
191
+ log.error('multiaddrs must be an array of Multiaddrs')
192
+ throw errcode(new Error('multiaddrs must be an array of Multiaddrs'), codes.ERR_INVALID_PARAMETERS)
193
+ }
194
+
183
195
  log('set await write lock')
184
196
  const release = await this._store.lock.writeLock()
185
197
  log('set got write lock')
@@ -188,7 +200,7 @@ class PeerStoreAddressBook {
188
200
  let updatedPeer
189
201
 
190
202
  try {
191
- const addresses = convertMultiaddrsToAddresses(multiaddrs)
203
+ const addresses = await filterMultiaddrs(peerId, multiaddrs, this._addressFilter)
192
204
 
193
205
  // No valid addresses found
194
206
  if (!addresses.length) {
@@ -238,6 +250,11 @@ class PeerStoreAddressBook {
238
250
  throw errcode(new Error('peerId must be an instance of peer-id'), codes.ERR_INVALID_PARAMETERS)
239
251
  }
240
252
 
253
+ if (!Array.isArray(multiaddrs)) {
254
+ log.error('multiaddrs must be an array of Multiaddrs')
255
+ throw errcode(new Error('multiaddrs must be an array of Multiaddrs'), codes.ERR_INVALID_PARAMETERS)
256
+ }
257
+
241
258
  log('add await write lock')
242
259
  const release = await this._store.lock.writeLock()
243
260
  log('add got write lock')
@@ -246,7 +263,7 @@ class PeerStoreAddressBook {
246
263
  let updatedPeer
247
264
 
248
265
  try {
249
- const addresses = convertMultiaddrsToAddresses(multiaddrs)
266
+ const addresses = await filterMultiaddrs(peerId, multiaddrs, this._addressFilter)
250
267
 
251
268
  // No valid addresses found
252
269
  if (!addresses.length) {
@@ -337,33 +354,29 @@ class PeerStoreAddressBook {
337
354
  }
338
355
 
339
356
  /**
340
- * Transforms received multiaddrs into Address.
341
- *
342
- * @private
357
+ * @param {PeerId} peerId
343
358
  * @param {Multiaddr[]} multiaddrs
344
- * @param {boolean} [isCertified]
345
- * @returns {Address[]}
359
+ * @param {(peerId: PeerId, multiaddr: Multiaddr) => Promise<boolean>} addressFilter
360
+ * @param {boolean} isCertified
346
361
  */
347
- function convertMultiaddrsToAddresses (multiaddrs, isCertified = false) {
348
- if (!multiaddrs) {
349
- log.error('multiaddrs must be provided to store data')
350
- throw errcode(new Error('multiaddrs must be provided'), codes.ERR_INVALID_PARAMETERS)
351
- }
352
-
353
- // create Address for each address with no duplicates
354
- return Array.from(
355
- new Set(multiaddrs.map(ma => ma.toString()))
356
- )
357
- .map(addr => {
358
- try {
359
- return {
360
- multiaddr: new Multiaddr(addr),
361
- isCertified
362
- }
363
- } catch (err) {
364
- throw errcode(err, codes.ERR_INVALID_PARAMETERS)
362
+ function filterMultiaddrs (peerId, multiaddrs, addressFilter, isCertified = false) {
363
+ return pipe(
364
+ multiaddrs,
365
+ (source) => each(source, (multiaddr) => {
366
+ if (!Multiaddr.isMultiaddr(multiaddr)) {
367
+ log.error('multiaddr must be an instance of Multiaddr')
368
+ throw errcode(new Error('multiaddr must be an instance of Multiaddr'), codes.ERR_INVALID_PARAMETERS)
365
369
  }
366
- })
370
+ }),
371
+ (source) => filter(source, (multiaddr) => addressFilter(peerId, multiaddr)),
372
+ (source) => map(source, (multiaddr) => {
373
+ return {
374
+ multiaddr: new Multiaddr(multiaddr.toString()),
375
+ isCertified
376
+ }
377
+ }),
378
+ (source) => all(source)
379
+ )
367
380
  }
368
381
 
369
382
  module.exports = PeerStoreAddressBook
@@ -12,6 +12,7 @@ const Store = require('./store')
12
12
  * @typedef {import('./types').PeerStore} PeerStore
13
13
  * @typedef {import('./types').Peer} Peer
14
14
  * @typedef {import('peer-id')} PeerId
15
+ * @typedef {import('multiaddr').Multiaddr} Multiaddr
15
16
  */
16
17
 
17
18
  const log = Object.assign(debug('libp2p:peer-store'), {
@@ -28,14 +29,15 @@ class DefaultPeerStore extends EventEmitter {
28
29
  * @param {object} properties
29
30
  * @param {PeerId} properties.peerId
30
31
  * @param {import('interface-datastore').Datastore} properties.datastore
32
+ * @param {(peerId: PeerId, multiaddr: Multiaddr) => Promise<boolean>} properties.addressFilter
31
33
  */
32
- constructor ({ peerId, datastore }) {
34
+ constructor ({ peerId, datastore, addressFilter }) {
33
35
  super()
34
36
 
35
37
  this._peerId = peerId
36
38
  this._store = new Store(datastore)
37
39
 
38
- this.addressBook = new AddressBook(this.emit.bind(this), this._store)
40
+ this.addressBook = new AddressBook(this.emit.bind(this), this._store, addressFilter)
39
41
  this.keyBook = new KeyBook(this.emit.bind(this), this._store)
40
42
  this.metadataBook = new MetadataBook(this.emit.bind(this), this._store)
41
43
  this.protoBook = new ProtoBook(this.emit.bind(this), this._store)
@@ -100,13 +100,26 @@ class PersistentStore {
100
100
  throw errcode(new Error('publicKey bytes do not match peer id publicKey bytes'), codes.ERR_INVALID_PARAMETERS)
101
101
  }
102
102
 
103
+ // dedupe addresses
104
+ const addressSet = new Set()
105
+
103
106
  const buf = PeerPB.encode({
104
- addresses: peer.addresses.sort((a, b) => {
105
- return a.multiaddr.toString().localeCompare(b.multiaddr.toString())
106
- }).map(({ multiaddr, isCertified }) => ({
107
- multiaddr: multiaddr.bytes,
108
- isCertified
109
- })),
107
+ addresses: peer.addresses
108
+ .filter(address => {
109
+ if (addressSet.has(address.multiaddr.toString())) {
110
+ return false
111
+ }
112
+
113
+ addressSet.add(address.multiaddr.toString())
114
+ return true
115
+ })
116
+ .sort((a, b) => {
117
+ return a.multiaddr.toString().localeCompare(b.multiaddr.toString())
118
+ })
119
+ .map(({ multiaddr, isCertified }) => ({
120
+ multiaddr: multiaddr.bytes,
121
+ isCertified
122
+ })),
110
123
  protocols: peer.protocols.sort(),
111
124
  pubKey: peer.pubKey ? marshalPublicKey(peer.pubKey) : undefined,
112
125
  metadata: [...peer.metadata.keys()].sort().map(key => ({ key, value: peer.metadata.get(key) })),
package/src/ping/index.js CHANGED
@@ -22,64 +22,63 @@ const { PROTOCOL_NAME, PING_LENGTH, PROTOCOL_VERSION } = require('./constants')
22
22
  * @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
23
23
  */
24
24
 
25
- /**
26
- * Ping a given peer and wait for its response, getting the operation latency.
27
- *
28
- * @param {Libp2p} node
29
- * @param {PeerId|Multiaddr} peer
30
- * @returns {Promise<number>}
31
- */
32
- async function ping (node, peer) {
33
- const protocol = `/${node._config.protocolPrefix}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`
34
- // @ts-ignore multiaddr might not have toB58String
35
- log('dialing %s to %s', protocol, peer.toB58String ? peer.toB58String() : peer)
25
+ class PingService {
26
+ /**
27
+ * @param {import('../')} libp2p
28
+ */
29
+ static getProtocolStr (libp2p) {
30
+ return `/${libp2p._config.protocolPrefix}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`
31
+ }
36
32
 
37
- const connection = await node.dial(peer)
38
- const { stream } = await connection.newStream(protocol)
33
+ /**
34
+ * @param {Libp2p} libp2p
35
+ */
36
+ constructor (libp2p) {
37
+ this._libp2p = libp2p
38
+ }
39
39
 
40
- const start = Date.now()
41
- const data = crypto.randomBytes(PING_LENGTH)
40
+ /**
41
+ * A handler to register with Libp2p to process ping messages
42
+ *
43
+ * @param {Object} options
44
+ * @param {MuxedStream} options.stream
45
+ */
46
+ handleMessage ({ stream }) {
47
+ return pipe(stream, stream)
48
+ }
42
49
 
43
- const [result] = await pipe(
44
- [data],
45
- stream,
46
- (/** @type {MuxedStream} */ stream) => take(1, stream),
47
- toBuffer,
48
- collect
49
- )
50
- const end = Date.now()
50
+ /**
51
+ * Ping a given peer and wait for its response, getting the operation latency.
52
+ *
53
+ * @param {PeerId|Multiaddr} peer
54
+ * @returns {Promise<number>}
55
+ */
56
+ async ping (peer) {
57
+ const protocol = `/${this._libp2p._config.protocolPrefix}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`
58
+ // @ts-ignore multiaddr might not have toB58String
59
+ log('dialing %s to %s', protocol, peer.toB58String ? peer.toB58String() : peer)
51
60
 
52
- if (!equals(data, result)) {
53
- throw errCode(new Error('Received wrong ping ack'), codes.ERR_WRONG_PING_ACK)
54
- }
61
+ const connection = await this._libp2p.dial(peer)
62
+ const { stream } = await connection.newStream(protocol)
55
63
 
56
- return end - start
57
- }
64
+ const start = Date.now()
65
+ const data = crypto.randomBytes(PING_LENGTH)
58
66
 
59
- /**
60
- * Subscribe ping protocol handler.
61
- *
62
- * @param {Libp2p} node
63
- */
64
- function mount (node) {
65
- node.handle(`/${node._config.protocolPrefix}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`, ({ stream }) => pipe(stream, stream))
66
- .catch(err => {
67
- log.error(err)
68
- })
69
- }
67
+ const [result] = await pipe(
68
+ [data],
69
+ stream,
70
+ (/** @type {MuxedStream} */ stream) => take(1, stream),
71
+ toBuffer,
72
+ collect
73
+ )
74
+ const end = Date.now()
70
75
 
71
- /**
72
- * Unsubscribe ping protocol handler.
73
- *
74
- * @param {Libp2p} node
75
- */
76
- function unmount (node) {
77
- node.unhandle(`/${node._config.protocolPrefix}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`)
78
- .catch(err => {
79
- log.error(err)
80
- })
76
+ if (!equals(data, result)) {
77
+ throw errCode(new Error('Received wrong ping ack'), codes.ERR_WRONG_PING_ACK)
78
+ }
79
+
80
+ return end - start
81
+ }
81
82
  }
82
83
 
83
- exports = module.exports = ping
84
- exports.mount = mount
85
- exports.unmount = unmount
84
+ module.exports = PingService
package/src/types.ts ADDED
@@ -0,0 +1,98 @@
1
+ import type PeerId from 'peer-id'
2
+ import type { Multiaddr } from 'multiaddr'
3
+ import type { MultiaddrConnection } from 'libp2p-interfaces/src/transport/types'
4
+
5
+ export interface ConnectionGater {
6
+ /**
7
+ * denyDialMultiaddr tests whether we're permitted to Dial the
8
+ * specified peer.
9
+ *
10
+ * This is called by the dialer.connectToPeer implementation before
11
+ * dialling a peer.
12
+ *
13
+ * Return true to prevent dialing the passed peer.
14
+ */
15
+ denyDialPeer: (peerId: PeerId) => Promise<boolean>
16
+
17
+ /**
18
+ * denyDialMultiaddr tests whether we're permitted to dial the specified
19
+ * multiaddr for the given peer.
20
+ *
21
+ * This is called by the dialer.connectToPeer implementation after it has
22
+ * resolved the peer's addrs, and prior to dialling each.
23
+ *
24
+ * Return true to prevent dialing the passed peer on the passed multiaddr.
25
+ */
26
+ denyDialMultiaddr: (peerId: PeerId, multiaddr: Multiaddr) => Promise<boolean>
27
+
28
+ /**
29
+ * denyInboundConnection tests whether an incipient inbound connection is allowed.
30
+ *
31
+ * This is called by the upgrader, or by the transport directly (e.g. QUIC,
32
+ * Bluetooth), straight after it has accepted a connection from its socket.
33
+ *
34
+ * Return true to deny the incoming passed connection.
35
+ */
36
+ denyInboundConnection: (maConn: MultiaddrConnection) => Promise<boolean>
37
+
38
+ /**
39
+ * denyOutboundConnection tests whether an incipient outbound connection is allowed.
40
+ *
41
+ * This is called by the upgrader, or by the transport directly (e.g. QUIC,
42
+ * Bluetooth), straight after it has created a connection with its socket.
43
+ *
44
+ * Return true to deny the incoming passed connection.
45
+ */
46
+ denyOutboundConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
47
+
48
+ /**
49
+ * denyInboundEncryptedConnection tests whether a given connection, now encrypted,
50
+ * is allowed.
51
+ *
52
+ * This is called by the upgrader, after it has performed the security
53
+ * handshake, and before it negotiates the muxer, or by the directly by the
54
+ * transport, at the exact same checkpoint.
55
+ *
56
+ * Return true to deny the passed secured connection.
57
+ */
58
+ denyInboundEncryptedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
59
+
60
+ /**
61
+ * denyOutboundEncryptedConnection tests whether a given connection, now encrypted,
62
+ * is allowed.
63
+ *
64
+ * This is called by the upgrader, after it has performed the security
65
+ * handshake, and before it negotiates the muxer, or by the directly by the
66
+ * transport, at the exact same checkpoint.
67
+ *
68
+ * Return true to deny the passed secured connection.
69
+ */
70
+ denyOutboundEncryptedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
71
+
72
+ /**
73
+ * denyInboundUpgradedConnection tests whether a fully capable connection is allowed.
74
+ *
75
+ * This is called after encryption has been negotiated and the connection has been
76
+ * multiplexed, if a multiplexer is configured.
77
+ *
78
+ * Return true to deny the passed upgraded connection.
79
+ */
80
+ denyInboundUpgradedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
81
+
82
+ /**
83
+ * denyOutboundUpgradedConnection tests whether a fully capable connection is allowed.
84
+ *
85
+ * This is called after encryption has been negotiated and the connection has been
86
+ * multiplexed, if a multiplexer is configured.
87
+ *
88
+ * Return true to deny the passed upgraded connection.
89
+ */
90
+ denyOutboundUpgradedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
91
+
92
+ /**
93
+ * Used by the address book to filter passed addresses.
94
+ *
95
+ * Return true to allow storing the passed multiaddr for the passed peer.
96
+ */
97
+ filterMultiaddrForPeer: (peer: PeerId, multiaddr: Multiaddr) => Promise<boolean>
98
+ }
package/src/upgrader.js CHANGED
@@ -5,7 +5,6 @@ const log = Object.assign(debug('libp2p:upgrader'), {
5
5
  error: debug('libp2p:upgrader:err')
6
6
  })
7
7
  const errCode = require('err-code')
8
- // @ts-ignore multistream-select does not export types
9
8
  const Multistream = require('multistream-select')
10
9
  const { Connection } = require('libp2p-interfaces/src/connection')
11
10
  const PeerId = require('peer-id')
@@ -23,6 +22,7 @@ const { codes } = require('./errors')
23
22
  * @typedef {import('libp2p-interfaces/src/crypto/types').Crypto} Crypto
24
23
  * @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
25
24
  * @typedef {import('multiaddr').Multiaddr} Multiaddr
25
+ * @typedef {import('./types').ConnectionGater} ConnectionGater
26
26
  */
27
27
 
28
28
  /**
@@ -36,6 +36,8 @@ class Upgrader {
36
36
  /**
37
37
  * @param {object} options
38
38
  * @param {PeerId} options.localPeer
39
+ * @param {ConnectionGater} options.connectionGater
40
+ *
39
41
  * @param {import('./metrics')} [options.metrics]
40
42
  * @param {Map<string, Crypto>} [options.cryptos]
41
43
  * @param {Map<string, MuxerFactory>} [options.muxers]
@@ -45,11 +47,13 @@ class Upgrader {
45
47
  constructor ({
46
48
  localPeer,
47
49
  metrics,
50
+ connectionGater,
48
51
  cryptos = new Map(),
49
52
  muxers = new Map(),
50
53
  onConnectionEnd = () => {},
51
54
  onConnection = () => {}
52
55
  }) {
56
+ this.connectionGater = connectionGater
53
57
  this.localPeer = localPeer
54
58
  this.metrics = metrics
55
59
  this.cryptos = cryptos
@@ -77,6 +81,10 @@ class Upgrader {
77
81
  let setPeer
78
82
  let proxyPeer
79
83
 
84
+ if (await this.connectionGater.denyInboundConnection(maConn)) {
85
+ throw errCode(new Error('The multiaddr connection is blocked by gater.acceptConnection'), codes.ERR_CONNECTION_INTERCEPTED)
86
+ }
87
+
80
88
  if (this.metrics) {
81
89
  ({ setTarget: setPeer, proxy: proxyPeer } = mutableProxy())
82
90
  const idString = (Math.random() * 1e9).toString(36) + Date.now()
@@ -100,6 +108,10 @@ class Upgrader {
100
108
  protocol: cryptoProtocol
101
109
  } = await this._encryptInbound(this.localPeer, protectedConn, this.cryptos))
102
110
 
111
+ if (await this.connectionGater.denyInboundEncryptedConnection(remotePeer, encryptedConn)) {
112
+ throw errCode(new Error('The multiaddr connection is blocked by gater.acceptEncryptedConnection'), codes.ERR_CONNECTION_INTERCEPTED)
113
+ }
114
+
103
115
  // Multiplex the connection
104
116
  if (this.muxers.size) {
105
117
  ({ stream: upgradedConn, Muxer } = await this._multiplexInbound(encryptedConn, this.muxers))
@@ -112,6 +124,10 @@ class Upgrader {
112
124
  throw err
113
125
  }
114
126
 
127
+ if (await this.connectionGater.denyInboundUpgradedConnection(remotePeer, encryptedConn)) {
128
+ throw errCode(new Error('The multiaddr connection is blocked by gater.acceptEncryptedConnection'), codes.ERR_CONNECTION_INTERCEPTED)
129
+ }
130
+
115
131
  if (this.metrics) {
116
132
  this.metrics.updatePlaceholder(proxyPeer, remotePeer)
117
133
  setPeer(remotePeer)
@@ -144,6 +160,10 @@ class Upgrader {
144
160
 
145
161
  const remotePeerId = PeerId.createFromB58String(idStr)
146
162
 
163
+ if (await this.connectionGater.denyOutboundConnection(remotePeerId, maConn)) {
164
+ throw errCode(new Error('The multiaddr connection is blocked by connectionGater.denyOutboundConnection'), codes.ERR_CONNECTION_INTERCEPTED)
165
+ }
166
+
147
167
  let encryptedConn
148
168
  let remotePeer
149
169
  let upgradedConn
@@ -175,6 +195,10 @@ class Upgrader {
175
195
  protocol: cryptoProtocol
176
196
  } = await this._encryptOutbound(this.localPeer, protectedConn, remotePeerId, this.cryptos))
177
197
 
198
+ if (await this.connectionGater.denyOutboundEncryptedConnection(remotePeer, encryptedConn)) {
199
+ throw errCode(new Error('The multiaddr connection is blocked by gater.acceptEncryptedConnection'), codes.ERR_CONNECTION_INTERCEPTED)
200
+ }
201
+
178
202
  // Multiplex the connection
179
203
  if (this.muxers.size) {
180
204
  ({ stream: upgradedConn, Muxer } = await this._multiplexOutbound(encryptedConn, this.muxers))
@@ -187,6 +211,10 @@ class Upgrader {
187
211
  throw err
188
212
  }
189
213
 
214
+ if (await this.connectionGater.denyOutboundUpgradedConnection(remotePeer, encryptedConn)) {
215
+ throw errCode(new Error('The multiaddr connection is blocked by gater.acceptEncryptedConnection'), codes.ERR_CONNECTION_INTERCEPTED)
216
+ }
217
+
190
218
  if (this.metrics) {
191
219
  this.metrics.updatePlaceholder(proxyPeer, remotePeer)
192
220
  setPeer(remotePeer)