Skip to content

Commit 093c0ea

Browse files
authored
feat: resolve multiaddrs before dial (#782)
1 parent 61c36f9 commit 093c0ea

File tree

10 files changed

+267
-27
lines changed

10 files changed

+267
-27
lines changed

doc/CONFIGURATION.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ Dialing in libp2p can be configured to limit the rate of dialing, and how long d
465465
| maxParallelDials | `number` | How many multiaddrs we can dial in parallel. |
466466
| maxDialsPerPeer | `number` | How many multiaddrs we can dial per peer, in parallel. |
467467
| dialTimeout | `number` | Second dial timeout per peer in ms. |
468+
| resolvers | `object` | Dial [Resolvers](https://github.com/multiformats/js-multiaddr/blob/master/src/resolvers/index.js) for resolving multiaddrs |
468469

469470
The below configuration example shows how the dialer should be configured, with the current defaults:
470471

@@ -474,6 +475,8 @@ const TCP = require('libp2p-tcp')
474475
const MPLEX = require('libp2p-mplex')
475476
const { NOISE } = require('libp2p-noise')
476477

478+
const { dnsaddrResolver } = require('multiaddr/src/resolvers')
479+
477480
const node = await Libp2p.create({
478481
modules: {
479482
transport: [TCP],
@@ -483,7 +486,10 @@ const node = await Libp2p.create({
483486
dialer: {
484487
maxParallelDials: 100,
485488
maxDialsPerPeer: 4,
486-
dialTimeout: 30e3
489+
dialTimeout: 30e3,
490+
resolvers: {
491+
dnsaddr: dnsaddrResolver
492+
}
487493
}
488494
```
489495

doc/GETTING_STARTED.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,8 @@ const Bootstrap = require('libp2p-bootstrap')
204204

205205
// Known peers addresses
206206
const bootstrapMultiaddrs = [
207-
'/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
208-
'/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3'
207+
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
208+
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN'
209209
]
210210

211211
const node = await Libp2p.create({

examples/libp2p-in-the-browser/index.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,11 @@ document.addEventListener('DOMContentLoaded', async () => {
3131
[Bootstrap.tag]: {
3232
enabled: true,
3333
list: [
34-
'/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
35-
'/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
36-
'/dns4/sfo-3.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
37-
'/dns4/sgp-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
38-
'/dns4/nyc-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
39-
'/dns4/nyc-2.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64'
34+
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
35+
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
36+
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
37+
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
38+
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
4039
]
4140
}
4241
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"mafmt": "^8.0.0",
6565
"merge-options": "^2.0.0",
6666
"moving-average": "^1.0.0",
67-
"multiaddr": "^8.0.0",
67+
"multiaddr": "^8.1.0",
6868
"multicodec": "^2.0.0",
6969
"multistream-select": "^1.0.0",
7070
"mutable-proxy": "^1.0.0",

src/config.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
'use strict'
22

33
const mergeOptions = require('merge-options')
4+
const { dnsaddrResolver } = require('multiaddr/src/resolvers')
5+
46
const Constants = require('./constants')
57

68
const { FaultTolerance } = require('./transport-manager')
@@ -20,7 +22,10 @@ const DefaultConfig = {
2022
dialer: {
2123
maxParallelDials: Constants.MAX_PARALLEL_DIALS,
2224
maxDialsPerPeer: Constants.MAX_PER_PEER_DIALS,
23-
dialTimeout: Constants.DIAL_TIMEOUT
25+
dialTimeout: Constants.DIAL_TIMEOUT,
26+
resolvers: {
27+
dnsaddr: dnsaddrResolver
28+
}
2429
},
2530
metrics: {
2631
enabled: false

src/dialer/index.js

+65-7
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ class Dialer {
2727
* @param {number} [options.concurrency = MAX_PARALLEL_DIALS] - Number of max concurrent dials.
2828
* @param {number} [options.perPeerLimit = MAX_PER_PEER_DIALS] - Number of max concurrent dials per peer.
2929
* @param {number} [options.timeout = DIAL_TIMEOUT] - How long a dial attempt is allowed to take.
30+
* @param {object} [options.resolvers = {}] - multiaddr resolvers to use when dialing
3031
*/
3132
constructor ({
3233
transportManager,
3334
peerStore,
3435
concurrency = MAX_PARALLEL_DIALS,
3536
timeout = DIAL_TIMEOUT,
36-
perPeerLimit = MAX_PER_PEER_DIALS
37+
perPeerLimit = MAX_PER_PEER_DIALS,
38+
resolvers = {}
3739
}) {
3840
this.transportManager = transportManager
3941
this.peerStore = peerStore
@@ -42,6 +44,10 @@ class Dialer {
4244
this.perPeerLimit = perPeerLimit
4345
this.tokens = [...new Array(concurrency)].map((_, index) => index)
4446
this._pendingDials = new Map()
47+
48+
for (const [key, value] of Object.entries(resolvers)) {
49+
multiaddr.resolvers.set(key, value)
50+
}
4551
}
4652

4753
/**
@@ -69,7 +75,7 @@ class Dialer {
6975
* @returns {Promise<Connection>}
7076
*/
7177
async connectToPeer (peer, options = {}) {
72-
const dialTarget = this._createDialTarget(peer)
78+
const dialTarget = await this._createDialTarget(peer)
7379

7480
if (!dialTarget.addrs.length) {
7581
throw errCode(new Error('The dial request has no addresses'), codes.ERR_NO_VALID_ADDRESSES)
@@ -105,22 +111,28 @@ class Dialer {
105111
*
106112
* @private
107113
* @param {PeerId|Multiaddr|string} peer - A PeerId or Multiaddr
108-
* @returns {DialTarget}
114+
* @returns {Promise<DialTarget>}
109115
*/
110-
_createDialTarget (peer) {
116+
async _createDialTarget (peer) {
111117
const { id, multiaddrs } = getPeer(peer)
112118

113119
if (multiaddrs) {
114120
this.peerStore.addressBook.add(id, multiaddrs)
115121
}
116122

117-
let addrs = this.peerStore.addressBook.getMultiaddrsForPeer(id) || []
123+
let knownAddrs = this.peerStore.addressBook.getMultiaddrsForPeer(id) || []
118124

119125
// If received a multiaddr to dial, it should be the first to use
120126
// But, if we know other multiaddrs for the peer, we should try them too.
121127
if (multiaddr.isMultiaddr(peer)) {
122-
addrs = addrs.filter((addr) => !peer.equals(addr))
123-
addrs.unshift(peer)
128+
knownAddrs = knownAddrs.filter((addr) => !peer.equals(addr))
129+
knownAddrs.unshift(peer)
130+
}
131+
132+
const addrs = []
133+
for (const a of knownAddrs) {
134+
const resolvedAddrs = await this._resolve(a)
135+
resolvedAddrs.forEach(ra => addrs.push(ra))
124136
}
125137

126138
return {
@@ -190,6 +202,52 @@ class Dialer {
190202
log('token %d released', token)
191203
this.tokens.push(token)
192204
}
205+
206+
/**
207+
* Resolve multiaddr recursively.
208+
*
209+
* @param {Multiaddr} ma
210+
* @returns {Promise<Array<Multiaddr>>}
211+
*/
212+
async _resolve (ma) {
213+
// TODO: recursive logic should live in multiaddr once dns4/dns6 support is in place
214+
// Now only supporting resolve for dnsaddr
215+
const resolvableProto = ma.protoNames().includes('dnsaddr')
216+
217+
// Multiaddr is not resolvable? End recursion!
218+
if (!resolvableProto) {
219+
return [ma]
220+
}
221+
222+
const resolvedMultiaddrs = await this._resolveRecord(ma)
223+
const recursiveMultiaddrs = await Promise.all(resolvedMultiaddrs.map((nm) => {
224+
return this._resolve(nm)
225+
}))
226+
227+
return recursiveMultiaddrs.flat().reduce((array, newM) => {
228+
if (!array.find(m => m.equals(newM))) {
229+
array.push(newM)
230+
}
231+
return array
232+
}, []) // Unique addresses
233+
}
234+
235+
/**
236+
* Resolve a given multiaddr. If this fails, an empty array will be returned
237+
*
238+
* @param {Multiaddr} ma
239+
* @returns {Promise<Array<Multiaddr>>}
240+
*/
241+
async _resolveRecord (ma) {
242+
try {
243+
ma = multiaddr(ma.toString()) // Use current multiaddr module
244+
const multiaddrs = await ma.resolve()
245+
return multiaddrs
246+
} catch (_) {
247+
log.error(`multiaddr ${ma} could not be resolved`)
248+
return []
249+
}
250+
}
193251
}
194252

195253
module.exports = Dialer

src/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ class Libp2p extends EventEmitter {
134134
peerStore: this.peerStore,
135135
concurrency: this._options.dialer.maxParallelDials,
136136
perPeerLimit: this._options.dialer.maxDialsPerPeer,
137-
timeout: this._options.dialer.dialTimeout
137+
timeout: this._options.dialer.dialTimeout,
138+
resolvers: this._options.dialer.resolvers
138139
})
139140

140141
this._modules.transport.forEach((Transport) => {

test/dialing/direct.node.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,9 @@ describe('Dialing (direct, TCP)', () => {
158158

159159
it('should dial to the max concurrency', async () => {
160160
const addrs = [
161-
'/ip4/0.0.0.0/tcp/8000',
162-
'/ip4/0.0.0.0/tcp/8001',
163-
'/ip4/0.0.0.0/tcp/8002'
161+
multiaddr('/ip4/0.0.0.0/tcp/8000'),
162+
multiaddr('/ip4/0.0.0.0/tcp/8001'),
163+
multiaddr('/ip4/0.0.0.0/tcp/8002')
164164
]
165165
const dialer = new Dialer({
166166
transportManager: localTM,

test/dialing/direct.spec.js

-5
Original file line numberDiff line numberDiff line change
@@ -263,18 +263,13 @@ describe('Dialing (direct, WebSockets)', () => {
263263

264264
describe('libp2p.dialer', () => {
265265
let libp2p
266-
let remoteLibp2p
267266

268267
afterEach(async () => {
269268
sinon.restore()
270269
libp2p && await libp2p.stop()
271270
libp2p = null
272271
})
273272

274-
after(async () => {
275-
remoteLibp2p && await remoteLibp2p.stop()
276-
})
277-
278273
it('should create a dialer', () => {
279274
libp2p = new Libp2p({
280275
peerId,

0 commit comments

Comments
 (0)