libp2p 2.3.1-4a85eb033 → 2.3.1-6ab85ea68

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.
@@ -1,3 +1,4 @@
1
+ import { isIPv4 } from '@chainsafe/is-ip'
1
2
  import { peerIdFromString } from '@libp2p/peer-id'
2
3
  import { debounce } from '@libp2p/utils/debounce'
3
4
  import { multiaddr, protocols } from '@multiformats/multiaddr'
@@ -5,6 +6,10 @@ import type { ComponentLogger, Libp2pEvents, Logger, TypedEventTarget, PeerId, P
5
6
  import type { AddressManager as AddressManagerInterface, TransportManager } from '@libp2p/interface-internal'
6
7
  import type { Multiaddr } from '@multiformats/multiaddr'
7
8
 
9
+ export const defaultValues = {
10
+ maxObservedAddresses: 10
11
+ }
12
+
8
13
  export interface AddressManagerInit {
9
14
  /**
10
15
  * Pass an function in this field to override the list of addresses
@@ -31,6 +36,11 @@ export interface AddressManagerInit {
31
36
  * A list of string multiaddrs to add to the list of announced addresses
32
37
  */
33
38
  appendAnnounce?: string[]
39
+
40
+ /**
41
+ * Limits the number of observed addresses we will store
42
+ */
43
+ maxObservedAddresses?: number
34
44
  }
35
45
 
36
46
  export interface AddressManagerComponents {
@@ -86,6 +96,11 @@ interface PublicAddressMapping {
86
96
  externalPort: number
87
97
  }
88
98
 
99
+ interface DNSMapping {
100
+ domain: string
101
+ confident: boolean
102
+ }
103
+
89
104
  export class AddressManager implements AddressManagerInterface {
90
105
  private readonly log: Logger
91
106
  private readonly components: AddressManagerComponents
@@ -95,8 +110,9 @@ export class AddressManager implements AddressManagerInterface {
95
110
  private readonly appendAnnounce: Set<string>
96
111
  private readonly observed: Map<string, ObservedAddressMetadata>
97
112
  private readonly announceFilter: AddressFilter
98
- private readonly ipDomainMappings: Map<string, string>
113
+ private readonly ipDomainMappings: Map<string, DNSMapping>
99
114
  private readonly publicAddressMappings: Map<string, PublicAddressMapping[]>
115
+ private readonly maxObservedAddresses: number
100
116
 
101
117
  /**
102
118
  * Responsible for managing the peer addresses.
@@ -116,6 +132,7 @@ export class AddressManager implements AddressManagerInterface {
116
132
  this.ipDomainMappings = new Map()
117
133
  this.publicAddressMappings = new Map()
118
134
  this.announceFilter = init.announceFilter ?? defaultAddressFilter
135
+ this.maxObservedAddresses = init.maxObservedAddresses ?? defaultValues.maxObservedAddresses
119
136
 
120
137
  // this method gets called repeatedly on startup when transports start listening so
121
138
  // debounce it so we don't cause multiple self:peer:update events to be emitted
@@ -149,7 +166,9 @@ export class AddressManager implements AddressManagerInterface {
149
166
  this.components.peerStore.patch(this.components.peerId, {
150
167
  multiaddrs: addrs
151
168
  })
152
- .catch(err => { this.log.error('error updating addresses', err) })
169
+ .catch(err => {
170
+ this.log.error('error updating addresses', err)
171
+ })
153
172
  }
154
173
 
155
174
  /**
@@ -184,6 +203,10 @@ export class AddressManager implements AddressManagerInterface {
184
203
  * Add peer observed addresses
185
204
  */
186
205
  addObservedAddr (addr: Multiaddr): void {
206
+ if (this.observed.size === this.maxObservedAddresses) {
207
+ return
208
+ }
209
+
187
210
  addr = stripPeerId(addr, this.components.peerId)
188
211
  const addrString = addr.toString()
189
212
 
@@ -266,7 +289,8 @@ export class AddressManager implements AddressManagerInterface {
266
289
  return
267
290
  }
268
291
 
269
- mappings.forEach(mapping => {
292
+ for (const mapping of mappings) {
293
+ tuples[0][0] = isIPv4(mapping.externalIp) ? CODEC_IP4 : CODEC_IP6
270
294
  tuples[0][1] = mapping.externalIp
271
295
  tuples[1][1] = `${mapping.externalPort}`
272
296
 
@@ -280,7 +304,7 @@ export class AddressManager implements AddressManagerInterface {
280
304
  }).join('/')
281
305
  }`)
282
306
  )
283
- })
307
+ }
284
308
  })
285
309
  multiaddrs = multiaddrs.concat(ipMappedMultiaddrs)
286
310
 
@@ -290,7 +314,11 @@ export class AddressManager implements AddressManagerInterface {
290
314
  const tuples = ma.stringTuples()
291
315
  let mappedIp = false
292
316
 
293
- for (const [ip, domain] of this.ipDomainMappings.entries()) {
317
+ for (const [ip, mapping] of this.ipDomainMappings.entries()) {
318
+ if (!mapping.confident) {
319
+ continue
320
+ }
321
+
294
322
  for (let i = 0; i < tuples.length; i++) {
295
323
  if (tuples[i][1] !== ip) {
296
324
  continue
@@ -298,13 +326,13 @@ export class AddressManager implements AddressManagerInterface {
298
326
 
299
327
  if (tuples[i][0] === CODEC_IP4) {
300
328
  tuples[i][0] = CODEC_DNS4
301
- tuples[i][1] = domain
329
+ tuples[i][1] = mapping.domain
302
330
  mappedIp = true
303
331
  }
304
332
 
305
333
  if (tuples[i][0] === CODEC_IP6) {
306
334
  tuples[i][0] = CODEC_DNS6
307
- tuples[i][1] = domain
335
+ tuples[i][1] = mapping.domain
308
336
  mappedIp = true
309
337
  }
310
338
  }
@@ -362,14 +390,23 @@ export class AddressManager implements AddressManagerInterface {
362
390
  addDNSMapping (domain: string, addresses: string[]): void {
363
391
  addresses.forEach(ip => {
364
392
  this.log('add DNS mapping %s to %s', ip, domain)
365
- this.ipDomainMappings.set(ip, domain)
393
+
394
+ // check ip/public ip mappings to see if we think we are contactable
395
+ const confident = [...this.publicAddressMappings.entries()].some(([key, mappings]) => {
396
+ return mappings.some(mapping => mapping.externalIp === ip)
397
+ })
398
+
399
+ this.ipDomainMappings.set(ip, {
400
+ domain,
401
+ confident
402
+ })
366
403
  })
367
404
  this._updatePeerStoreAddresses()
368
405
  }
369
406
 
370
407
  removeDNSMapping (domain: string): void {
371
- for (const [key, value] of this.ipDomainMappings.entries()) {
372
- if (value === domain) {
408
+ for (const [key, mapping] of this.ipDomainMappings.entries()) {
409
+ if (mapping.domain === domain) {
373
410
  this.log('remove DNS mapping for %s', domain)
374
411
  this.ipDomainMappings.delete(key)
375
412
  }
@@ -386,6 +423,17 @@ export class AddressManager implements AddressManagerInterface {
386
423
  })
387
424
 
388
425
  this.publicAddressMappings.set(key, mappings)
426
+
427
+ // update domain mappings to indicate we are now confident that any matching
428
+ // ip/domain combination can now be resolved externally
429
+ for (const [key, mapping] of this.ipDomainMappings.entries()) {
430
+ if (key === externalIp) {
431
+ mapping.confident = true
432
+
433
+ this.ipDomainMappings.set(key, mapping)
434
+ }
435
+ }
436
+
389
437
  this._updatePeerStoreAddresses()
390
438
  }
391
439
 
@@ -1,19 +1,33 @@
1
1
  import { isPrivateIp } from '@libp2p/utils/private-ip'
2
+ import { WebSockets } from '@multiformats/multiaddr-matcher'
2
3
  import type { ConnectionGater } from '@libp2p/interface'
3
4
  import type { Multiaddr } from '@multiformats/multiaddr'
4
5
 
6
+ const CODEC_IP4 = 0x04
7
+ const CODEC_IP6 = 0x29
8
+
5
9
  /**
6
- * Returns a connection gater that disallows dialling private addresses by
7
- * default. Browsers are severely limited in their resource usage so don't
8
- * waste time trying to dial undiallable addresses.
10
+ * Returns a connection gater that disallows dialling private addresses or
11
+ * insecure websockets by default.
12
+ *
13
+ * Browsers are severely limited in their resource usage so don't waste time
14
+ * trying to dial undiallable addresses, and they also print verbose error
15
+ * messages when making connections over insecure transports which causes
16
+ * confusion.
9
17
  */
10
18
  export function connectionGater (gater: ConnectionGater = {}): ConnectionGater {
11
19
  return {
12
20
  denyDialPeer: async () => false,
13
21
  denyDialMultiaddr: async (multiaddr: Multiaddr) => {
22
+ // do not connect to insecure websockets by default
23
+ if (WebSockets.matches(multiaddr)) {
24
+ return false
25
+ }
26
+
14
27
  const tuples = multiaddr.stringTuples()
15
28
 
16
- if (tuples[0][0] === 4 || tuples[0][0] === 41) {
29
+ // do not connect to private addresses by default
30
+ if (tuples[0][0] === CODEC_IP4 || tuples[0][0] === CODEC_IP6) {
17
31
  return Boolean(isPrivateIp(`${tuples[0][1]}`))
18
32
  }
19
33
 
@@ -1,11 +1,11 @@
1
1
  /* eslint-disable max-depth */
2
2
  import { TimeoutError, DialError, setMaxListeners, AbortError } from '@libp2p/interface'
3
3
  import { PeerMap } from '@libp2p/peer-collections'
4
- import { PriorityQueue, type PriorityQueueJobOptions } from '@libp2p/utils/priority-queue'
5
- import { type Multiaddr, type Resolver, resolvers, multiaddr } from '@multiformats/multiaddr'
4
+ import { PriorityQueue } from '@libp2p/utils/priority-queue'
5
+ import { resolvers, multiaddr } from '@multiformats/multiaddr'
6
6
  import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers'
7
7
  import { Circuit } from '@multiformats/multiaddr-matcher'
8
- import { type ClearableSignal, anySignal } from 'any-signal'
8
+ import { anySignal } from 'any-signal'
9
9
  import { CustomProgressEvent } from 'progress-events'
10
10
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
11
11
  import { DialDeniedError, NoValidAddressesError } from '../errors.js'
@@ -23,7 +23,9 @@ import { resolveMultiaddrs } from './utils.js'
23
23
  import { DEFAULT_DIAL_PRIORITY } from './index.js'
24
24
  import type { AddressSorter, ComponentLogger, Logger, Connection, ConnectionGater, Metrics, PeerId, Address, PeerStore, PeerRouting, IsDialableOptions, OpenConnectionProgressEvents } from '@libp2p/interface'
25
25
  import type { OpenConnectionOptions, TransportManager } from '@libp2p/interface-internal'
26
+ import type { PriorityQueueJobOptions } from '@libp2p/utils/priority-queue'
26
27
  import type { DNS } from '@multiformats/dns'
28
+ import type { Multiaddr, Resolver } from '@multiformats/multiaddr'
27
29
  import type { ProgressOptions } from 'progress-events'
28
30
 
29
31
  export interface PendingDialTarget {
@@ -204,7 +206,12 @@ export class DialQueue {
204
206
  options?.onProgress?.(new CustomProgressEvent('dial-queue:start-dial'))
205
207
  // create abort conditions - need to do this before `calculateMultiaddrs` as
206
208
  // we may be about to resolve a dns addr which can time out
207
- const signal = this.createDialAbortController(options?.signal)
209
+ const signal = anySignal([
210
+ this.shutDownController.signal,
211
+ options.signal
212
+ ])
213
+ setMaxListeners(Infinity, signal)
214
+
208
215
  let addrsToDial: Address[]
209
216
 
210
217
  try {
@@ -299,25 +306,11 @@ export class DialQueue {
299
306
  peerId,
300
307
  priority: options.priority ?? DEFAULT_DIAL_PRIORITY,
301
308
  multiaddrs: new Set(multiaddrs.map(ma => ma.toString())),
302
- signal: options.signal,
309
+ signal: options.signal ?? AbortSignal.timeout(this.dialTimeout),
303
310
  onProgress: options.onProgress
304
311
  })
305
312
  }
306
313
 
307
- private createDialAbortController (userSignal?: AbortSignal): ClearableSignal {
308
- // let any signal abort the dial
309
- const signal = anySignal([
310
- AbortSignal.timeout(this.dialTimeout),
311
- this.shutDownController.signal,
312
- userSignal
313
- ])
314
-
315
- // This emitter gets listened to a lot
316
- setMaxListeners(Infinity, signal)
317
-
318
- return signal
319
- }
320
-
321
314
  // eslint-disable-next-line complexity
322
315
  private async calculateMultiaddrs (peerId?: PeerId, multiaddrs: Set<string> = new Set<string>(), options: OpenConnectionOptions = {}): Promise<Address[]> {
323
316
  const addrs: Address[] = [...multiaddrs].map(ma => ({
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '2.3.1-4a85eb033'
2
- export const name = 'libp2p'
1
+ export const version = '2.3.1-6ab85ea68'
2
+ export const name = 'js-libp2p'
package/LICENSE DELETED
@@ -1,4 +0,0 @@
1
- This project is dual licensed under MIT and Apache-2.0.
2
-
3
- MIT: https://www.opensource.org/licenses/mit
4
- Apache-2.0: https://www.apache.org/licenses/license-2.0