Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 13 additions & 134 deletions packages/libp2p/src/connection-manager/address-sorter.ts
Original file line number Diff line number Diff line change
@@ -1,118 +1,14 @@
import { isLoopback, isPrivate } from '@libp2p/utils'
import { Circuit, WebSockets, WebSocketsSecure, WebRTC, WebRTCDirect, WebTransport, TCP } from '@multiformats/multiaddr-matcher'
import {
reliableTransportsFirst,
loopbackAddressLast,
publicAddressesFirst,
circuitRelayAddressesLast
} from '@libp2p/utils'
import type { Address } from '@libp2p/interface'
import type { Multiaddr } from '@multiformats/multiaddr'

/**
* Sorts addresses by order of reliability, where they have presented the fewest
* problems:
*
* TCP -> WebSockets/Secure -> WebRTC -> WebRTCDirect -> WebTransport
*/
// eslint-disable-next-line complexity
export function reliableTransportsFirst (a: Address, b: Address): -1 | 0 | 1 {
const isATcp = TCP.exactMatch(a.multiaddr)
const isBTcp = TCP.exactMatch(b.multiaddr)

if (isATcp && !isBTcp) {
return -1
}

if (!isATcp && isBTcp) {
return 1
}

const isAWebSocketSecure = WebSocketsSecure.exactMatch(a.multiaddr)
const isBWebSocketSecure = WebSocketsSecure.exactMatch(b.multiaddr)

if (isAWebSocketSecure && !isBWebSocketSecure) {
return -1
}

if (!isAWebSocketSecure && isBWebSocketSecure) {
return 1
}

const isAWebSocket = WebSockets.exactMatch(a.multiaddr)
const isBWebSocket = WebSockets.exactMatch(b.multiaddr)

if (isAWebSocket && !isBWebSocket) {
return -1
}

if (!isAWebSocket && isBWebSocket) {
return 1
}

const isAWebRTC = WebRTC.exactMatch(a.multiaddr)
const isBWebRTC = WebRTC.exactMatch(b.multiaddr)

if (isAWebRTC && !isBWebRTC) {
return -1
}

if (!isAWebRTC && isBWebRTC) {
return 1
}

const isAWebRTCDirect = WebRTCDirect.exactMatch(a.multiaddr)
const isBWebRTCDirect = WebRTCDirect.exactMatch(b.multiaddr)

if (isAWebRTCDirect && !isBWebRTCDirect) {
return -1
}

if (!isAWebRTCDirect && isBWebRTCDirect) {
return 1
}

const isAWebTransport = WebTransport.exactMatch(a.multiaddr)
const isBWebTransport = WebTransport.exactMatch(b.multiaddr)

if (isAWebTransport && !isBWebTransport) {
return -1
}

if (!isAWebTransport && isBWebTransport) {
return 1
}

// ... everything else
return 0
}

/**
* Compare function for array.sort() that moves loopback addresses to the end
* of the array.
*/
export function loopbackAddressLast (a: Address, b: Address): -1 | 0 | 1 {
const isALoopback = isLoopback(a.multiaddr)
const isBLoopback = isLoopback(b.multiaddr)

if (isALoopback && !isBLoopback) {
return 1
} else if (!isALoopback && isBLoopback) {
return -1
}

return 0
}

/**
* Compare function for array.sort() that moves public addresses to the start
* of the array.
*/
export function publicAddressesFirst (a: Address, b: Address): -1 | 0 | 1 {
const isAPrivate = isPrivate(a.multiaddr)
const isBPrivate = isPrivate(b.multiaddr)

if (isAPrivate && !isBPrivate) {
return 1
} else if (!isAPrivate && isBPrivate) {
return -1
}

return 0
}
const asMultiaddr = (fn: (a: Multiaddr, b: Multiaddr) => -1 | 0 | 1) =>
(a: Address, b: Address): -1 | 0 | 1 => fn(a.multiaddr, b.multiaddr)

/**
* Compare function for array.sort() that moves certified addresses to the start
Expand All @@ -128,28 +24,11 @@ export function certifiedAddressesFirst (a: Address, b: Address): -1 | 0 | 1 {
return 0
}

/**
* Compare function for array.sort() that moves circuit relay addresses to the
* end of the array.
*/
export function circuitRelayAddressesLast (a: Address, b: Address): -1 | 0 | 1 {
const isACircuit = Circuit.exactMatch(a.multiaddr)
const isBCircuit = Circuit.exactMatch(b.multiaddr)

if (isACircuit && !isBCircuit) {
return 1
} else if (!isACircuit && isBCircuit) {
return -1
}

return 0
}

export function defaultAddressSorter (addresses: Address[]): Address[] {
return addresses
.sort(reliableTransportsFirst)
.sort(asMultiaddr(reliableTransportsFirst))
.sort(certifiedAddressesFirst)
.sort(circuitRelayAddressesLast)
.sort(publicAddressesFirst)
.sort(loopbackAddressLast)
.sort(asMultiaddr(circuitRelayAddressesLast))
.sort(asMultiaddr(publicAddressesFirst))
.sort(asMultiaddr(loopbackAddressLast))
}
1 change: 1 addition & 0 deletions packages/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@libp2p/interface": "^3.2.2",
"@libp2p/logger": "^6.2.6",
"@multiformats/multiaddr": "^13.0.1",
"@multiformats/multiaddr-matcher": "^3.0.1",
"@sindresorhus/fnv1a": "^3.1.0",
"any-signal": "^4.1.1",
"cborg": "^5.1.0",
Expand Down
1 change: 1 addition & 0 deletions packages/utils/src/multiaddr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './is-link-local.ts'
export * from './is-loopback.ts'
export * from './is-network-address.ts'
export * from './is-private.ts'
export * from './sorters.ts'
147 changes: 147 additions & 0 deletions packages/utils/src/multiaddr/sorters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { Circuit, WebSockets, WebSocketsSecure, WebRTC, WebRTCDirect, WebTransport, TCP } from '@multiformats/multiaddr-matcher'
import { isLoopback } from './is-loopback.ts'
import { isPrivate } from './is-private.ts'
import type { Multiaddr } from '@multiformats/multiaddr'

/**
* Sorts addresses by order of reliability, where they have presented the fewest
* problems:
*
* TCP -> WebSockets/Secure -> WebRTC -> WebRTCDirect -> WebTransport
Comment on lines +6 to +10

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering if this needs to be updated.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think this is still the best order

*/
// eslint-disable-next-line complexity
export function reliableTransportsFirst (a: Multiaddr, b: Multiaddr): -1 | 0 | 1 {
const isATcp = TCP.exactMatch(a)
const isBTcp = TCP.exactMatch(b)

if (isATcp && !isBTcp) {
return -1
}

if (!isATcp && isBTcp) {
return 1
}

const isAWebSocketSecure = WebSocketsSecure.exactMatch(a)
const isBWebSocketSecure = WebSocketsSecure.exactMatch(b)

if (isAWebSocketSecure && !isBWebSocketSecure) {
return -1
}

if (!isAWebSocketSecure && isBWebSocketSecure) {
return 1
}

const isAWebSocket = WebSockets.exactMatch(a)
const isBWebSocket = WebSockets.exactMatch(b)

if (isAWebSocket && !isBWebSocket) {
return -1
}

if (!isAWebSocket && isBWebSocket) {
return 1
}

const isAWebRTC = WebRTC.exactMatch(a)
const isBWebRTC = WebRTC.exactMatch(b)

if (isAWebRTC && !isBWebRTC) {
return -1
}

if (!isAWebRTC && isBWebRTC) {
return 1
}

const isAWebRTCDirect = WebRTCDirect.exactMatch(a)
const isBWebRTCDirect = WebRTCDirect.exactMatch(b)

if (isAWebRTCDirect && !isBWebRTCDirect) {
return -1
}

if (!isAWebRTCDirect && isBWebRTCDirect) {
return 1
}

const isAWebTransport = WebTransport.exactMatch(a)
const isBWebTransport = WebTransport.exactMatch(b)

if (isAWebTransport && !isBWebTransport) {
return -1
}

if (!isAWebTransport && isBWebTransport) {
return 1
}

// ... everything else
return 0
}

/**
* Compare function for array.sort() that moves loopback addresses to the end
* of the array.
*/
export function loopbackAddressLast (a: Multiaddr, b: Multiaddr): -1 | 0 | 1 {
const isALoopback = isLoopback(a)
const isBLoopback = isLoopback(b)

if (isALoopback && !isBLoopback) {
return 1
} else if (!isALoopback && isBLoopback) {
return -1
}

return 0
}

/**
* Compare function for array.sort() that moves public addresses to the start
* of the array.
*/
export function publicAddressesFirst (a: Multiaddr, b: Multiaddr): -1 | 0 | 1 {
const isAPrivate = isPrivate(a)
const isBPrivate = isPrivate(b)

if (isAPrivate && !isBPrivate) {
return 1
} else if (!isAPrivate && isBPrivate) {
return -1
}

return 0
}

/**
* Compare function for array.sort() that moves circuit relay addresses to the
* end of the array.
*/
export function circuitRelayAddressesLast (a: Multiaddr, b: Multiaddr): -1 | 0 | 1 {
const isACircuit = Circuit.exactMatch(a)
const isBCircuit = Circuit.exactMatch(b)

if (isACircuit && !isBCircuit) {
return 1
} else if (!isACircuit && isBCircuit) {
return -1
}

return 0
}

/**
* Sort multiaddrs by the default multiaddr-only ordering: loopback addresses
* last, public addresses first, circuit-relay addresses last, with reliable
* transports as the innermost tiebreaker.
*/
export function defaultMultiaddrSorter (multiaddrs: Multiaddr[]): Multiaddr[] {
return multiaddrs.sort((a, b) =>
loopbackAddressLast(a, b) ||
publicAddressesFirst(a, b) ||
circuitRelayAddressesLast(a, b) ||
reliableTransportsFirst(a, b)
Comment on lines +141 to +145

@tabcat tabcat May 5, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

equivalent to .sort chain (although primary key order changes). could make this change to defaultAddressSorter.

)
}
Loading
Loading