Skip to content

Commit dfdd29d

Browse files
committed
feat: custom dialer addr sorter
1 parent 81c5c43 commit dfdd29d

File tree

8 files changed

+83
-38
lines changed

8 files changed

+83
-38
lines changed

doc/CONFIGURATION.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ Dialing in libp2p can be configured to limit the rate of dialing, and how long d
498498
| maxDialsPerPeer | `number` | How many multiaddrs we can dial per peer, in parallel. |
499499
| dialTimeout | `number` | Second dial timeout per peer in ms. |
500500
| resolvers | `object` | Dial [Resolvers](https://github.com/multiformats/js-multiaddr/blob/master/src/resolvers/index.js) for resolving multiaddrs |
501+
| addressSorter | `(Array<Address>) => Array<Address>` | Sort the known addresses of a peer before trying to dial. |
501502

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

@@ -508,6 +509,7 @@ const MPLEX = require('libp2p-mplex')
508509
const { NOISE } = require('libp2p-noise')
509510

510511
const { dnsaddrResolver } = require('multiaddr/src/resolvers')
512+
const { sortPublicAddressesFirst } = require('libp2p/src/dialer/utils')
511513

512514
const node = await Libp2p.create({
513515
modules: {
@@ -521,7 +523,8 @@ const node = await Libp2p.create({
521523
dialTimeout: 30e3,
522524
resolvers: {
523525
dnsaddr: dnsaddrResolver
524-
}
526+
},
527+
addressSorter: sortPublicAddressesFirst
525528
}
526529
```
527530

src/circuit/auto-relay.js

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ const debug = require('debug')
44
const log = debug('libp2p:auto-relay')
55
log.error = debug('libp2p:auto-relay:error')
66

7-
const isPrivate = require('libp2p-utils/src/multiaddr/is-private')
8-
97
const uint8ArrayFromString = require('uint8arrays/from-string')
108
const uint8ArrayToString = require('uint8arrays/to-string')
119
const multiaddr = require('multiaddr')
@@ -36,6 +34,7 @@ class AutoRelay {
3634
this._peerStore = libp2p.peerStore
3735
this._connectionManager = libp2p.connectionManager
3836
this._transportManager = libp2p.transportManager
37+
this._addressSorter = libp2p.dialer.addressSorter
3938

4039
this.maxListeners = maxListeners
4140

@@ -134,12 +133,14 @@ class AutoRelay {
134133

135134
try {
136135
// Get peer known addresses and sort them per public addresses first
137-
remoteAddrs = this._peerStore.addressBook.get(connection.remotePeer)
138-
// TODO: This sort should be customizable in the config (dialer addr sort)
139-
remoteAddrs.sort(multiaddrsCompareFunction)
136+
// Sorting addresses to public first
137+
remoteAddrs = this._addressSorter(
138+
this._peerStore.addressBook.get(connection.remotePeer) || []
139+
)
140140

141141
remoteMultiaddr = remoteAddrs.find(a => a.isCertified).multiaddr // Get first announced address certified
142142
// TODO: HOP Relays should avoid advertising private addresses!
143+
// TODO ^: Log error if happens? with Example PR
143144
} catch (_) {
144145
log.error(`${id} does not have announced certified multiaddrs`)
145146

@@ -269,24 +270,4 @@ class AutoRelay {
269270
}
270271
}
271272

272-
/**
273-
* Compare function for array.sort().
274-
* This sort aims to move the private adresses to the end of the array.
275-
*
276-
* @param {Address} a
277-
* @param {Address} b
278-
* @returns {number}
279-
*/
280-
function multiaddrsCompareFunction (a, b) {
281-
const isAPrivate = isPrivate(a.multiaddr)
282-
const isBPrivate = isPrivate(b.multiaddr)
283-
284-
if (isAPrivate && !isBPrivate) {
285-
return 1
286-
} else if (!isAPrivate && isBPrivate) {
287-
return -1
288-
}
289-
return 0
290-
}
291-
292273
module.exports = AutoRelay

src/config.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const { dnsaddrResolver } = require('multiaddr/src/resolvers')
66
const Constants = require('./constants')
77
const RelayConstants = require('./circuit/constants')
88

9+
const { sortPublicAddressesFirst } = require('./dialer/utils')
910
const { FaultTolerance } = require('./transport-manager')
1011

1112
const DefaultConfig = {
@@ -26,7 +27,8 @@ const DefaultConfig = {
2627
dialTimeout: Constants.DIAL_TIMEOUT,
2728
resolvers: {
2829
dnsaddr: dnsaddrResolver
29-
}
30+
},
31+
addressSorter: sortPublicAddressesFirst
3032
},
3133
metrics: {
3234
enabled: false

src/dialer/index.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const log = debug('libp2p:dialer')
99
log.error = debug('libp2p:dialer:error')
1010

1111
const { DialRequest } = require('./dial-request')
12+
const { sortPublicAddressesFirst } = require('./utils')
1213
const getPeer = require('../get-peer')
1314

1415
const { codes } = require('../errors')
@@ -24,6 +25,7 @@ class Dialer {
2425
* @param {object} options
2526
* @param {TransportManager} options.transportManager
2627
* @param {Peerstore} options.peerStore
28+
* @param {(addresses: Array<Address) => Array<Address>} [options.addressSorter = sortPublicAddressesFirst] - Sort the known addresses of a peer before trying to dial.
2729
* @param {number} [options.concurrency = MAX_PARALLEL_DIALS] - Number of max concurrent dials.
2830
* @param {number} [options.perPeerLimit = MAX_PER_PEER_DIALS] - Number of max concurrent dials per peer.
2931
* @param {number} [options.timeout = DIAL_TIMEOUT] - How long a dial attempt is allowed to take.
@@ -32,13 +34,15 @@ class Dialer {
3234
constructor ({
3335
transportManager,
3436
peerStore,
37+
addressSorter = sortPublicAddressesFirst,
3538
concurrency = MAX_PARALLEL_DIALS,
3639
timeout = DIAL_TIMEOUT,
3740
perPeerLimit = MAX_PER_PEER_DIALS,
3841
resolvers = {}
3942
}) {
4043
this.transportManager = transportManager
4144
this.peerStore = peerStore
45+
this.addressSorter = addressSorter
4246
this.concurrency = concurrency
4347
this.timeout = timeout
4448
this.perPeerLimit = perPeerLimit
@@ -120,7 +124,16 @@ class Dialer {
120124
this.peerStore.addressBook.add(id, multiaddrs)
121125
}
122126

123-
let knownAddrs = this.peerStore.addressBook.getMultiaddrsForPeer(id) || []
127+
let knownAddrs = this.addressSorter(
128+
this.peerStore.addressBook.get(id) || []
129+
).map((address) => {
130+
const multiaddr = address.multiaddr
131+
132+
const idString = multiaddr.getPeerId()
133+
if (idString && idString === id.toB58String()) return multiaddr
134+
135+
return multiaddr.encapsulate(`/p2p/${id.toB58String()}`)
136+
})
124137

125138
// If received a multiaddr to dial, it should be the first to use
126139
// But, if we know other multiaddrs for the peer, we should try them too.

src/dialer/utils.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict'
2+
3+
const isPrivate = require('libp2p-utils/src/multiaddr/is-private')
4+
5+
// TODO: Move to libp2p-utils?
6+
7+
/**
8+
* Compare function for array.sort().
9+
* This sort aims to move the private adresses to the end of the array.
10+
* In case of equality, a cerified address will come first.
11+
*
12+
* @param {Address} a
13+
* @param {Address} b
14+
* @returns {number}
15+
*/
16+
function addressesPublicFirstCompareFunction (a, b) {
17+
const isAPrivate = isPrivate(a.multiaddr)
18+
const isBPrivate = isPrivate(b.multiaddr)
19+
20+
if (isAPrivate && !isBPrivate) {
21+
return 1
22+
} else if (!isAPrivate && isBPrivate) {
23+
return -1
24+
}
25+
// Check certified?
26+
if (a.isCertified && !b.isCertified) {
27+
return -1
28+
} else if (!a.isCertified && b.isCertified) {
29+
return 1
30+
}
31+
32+
return 0
33+
}
34+
35+
/**
36+
* Sort given addresses by putting public addresses first.
37+
*
38+
* @param {Array<Address>} addresses
39+
* @returns {Array<Address>}
40+
*/
41+
function sortPublicAddressesFirst (addresses) {
42+
return [...addresses].sort(addressesPublicFirstCompareFunction)
43+
}
44+
45+
module.exports.sortPublicAddressesFirst = sortPublicAddressesFirst

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ class Libp2p extends EventEmitter {
136136
concurrency: this._options.dialer.maxParallelDials,
137137
perPeerLimit: this._options.dialer.maxDialsPerPeer,
138138
timeout: this._options.dialer.dialTimeout,
139-
resolvers: this._options.dialer.resolvers
139+
resolvers: this._options.dialer.resolvers,
140+
addressSorter: this._options.dialer.addressSorter
140141
})
141142

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

test/dialing/direct.node.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ describe('Dialing (direct, TCP)', () => {
132132
peerStore: {
133133
addressBook: {
134134
add: () => {},
135-
getMultiaddrsForPeer: () => [unsupportedAddr]
135+
get: () => [{ multiaddr: unsupportedAddr }]
136136
}
137137
}
138138
})
@@ -175,7 +175,7 @@ describe('Dialing (direct, TCP)', () => {
175175
peerStore: {
176176
addressBook: {
177177
add: () => {},
178-
getMultiaddrsForPeer: () => addrs
178+
get: () => addrs.map(a => ({ multiaddr: multiaddr(a) }))
179179
}
180180
}
181181
})

test/dialing/direct.spec.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe('Dialing (direct, WebSockets)', () => {
8585
peerStore: {
8686
addressBook: {
8787
add: () => {},
88-
getMultiaddrsForPeer: () => [remoteAddr]
88+
get: () => [{ multiaddr: remoteAddr.decapsulate('/p2p') }]
8989
}
9090
}
9191
})
@@ -101,7 +101,7 @@ describe('Dialing (direct, WebSockets)', () => {
101101
peerStore: {
102102
addressBook: {
103103
add: () => {},
104-
getMultiaddrsForPeer: () => [remoteAddr]
104+
get: () => [{ multiaddr: remoteAddr.decapsulate('/p2p') }]
105105
}
106106
}
107107
})
@@ -125,7 +125,7 @@ describe('Dialing (direct, WebSockets)', () => {
125125
peerStore: {
126126
addressBook: {
127127
add: () => {},
128-
getMultiaddrsForPeer: () => [remoteAddr]
128+
get: () => [{ multiaddr: remoteAddr.decapsulate('/p2p') }]
129129
}
130130
}
131131
})
@@ -141,7 +141,7 @@ describe('Dialing (direct, WebSockets)', () => {
141141
peerStore: {
142142
addressBook: {
143143
set: () => {},
144-
getMultiaddrsForPeer: () => [unsupportedAddr]
144+
get: () => [{ multiaddr: unsupportedAddr.decapsulate('/p2p') }]
145145
}
146146
}
147147
})
@@ -158,7 +158,7 @@ describe('Dialing (direct, WebSockets)', () => {
158158
peerStore: {
159159
addressBook: {
160160
add: () => {},
161-
getMultiaddrsForPeer: () => [remoteAddr]
161+
get: () => [{ multiaddr: remoteAddr.decapsulate('/p2p') }]
162162
}
163163
}
164164
})
@@ -183,7 +183,7 @@ describe('Dialing (direct, WebSockets)', () => {
183183
peerStore: {
184184
addressBook: {
185185
set: () => {},
186-
getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
186+
get: () => [{ multiaddr: remoteAddr }, { multiaddr: remoteAddr }, { multiaddr: remoteAddr }]
187187
}
188188
}
189189
})
@@ -221,7 +221,7 @@ describe('Dialing (direct, WebSockets)', () => {
221221
peerStore: {
222222
addressBook: {
223223
set: () => {},
224-
getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
224+
get: () => [{ multiaddr: remoteAddr }, { multiaddr: remoteAddr }, { multiaddr: remoteAddr }]
225225
}
226226
}
227227
})

0 commit comments

Comments
 (0)