-
Notifications
You must be signed in to change notification settings - Fork 515
feat: auto relay network query #749
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
77d3889
41b7b82
ca1d7ec
f026f49
8147383
9a69af3
6b4ee3c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,17 +4,22 @@ const debug = require('debug') | |
| const log = debug('libp2p:auto-relay') | ||
| log.error = debug('libp2p:auto-relay:error') | ||
|
|
||
| const isPrivate = require('libp2p-utils/src/multiaddr/is-private') | ||
|
|
||
| const uint8ArrayFromString = require('uint8arrays/from-string') | ||
| const uint8ArrayToString = require('uint8arrays/to-string') | ||
| const multiaddr = require('multiaddr') | ||
| const PeerId = require('peer-id') | ||
|
|
||
| const { relay: multicodec } = require('./multicodec') | ||
| const { canHop } = require('./circuit/hop') | ||
|
|
||
| const circuitProtoCode = 290 | ||
| const hopMetadataKey = 'hop_relay' | ||
| const hopMetadataValue = 'true' | ||
| const { namespaceToCid } = require('./utils') | ||
| const { | ||
| CIRCUIT_PROTO_CODE, | ||
| HOP_METADATA_KEY, | ||
| HOP_METADATA_VALUE, | ||
| RELAY_RENDEZVOUS_NS | ||
| } = require('./constants') | ||
|
|
||
| class AutoRelay { | ||
| /** | ||
|
|
@@ -76,15 +81,15 @@ class AutoRelay { | |
| const connection = this._connectionManager.get(peerId) | ||
|
|
||
| // Do not hop on a relayed connection | ||
| if (connection.remoteAddr.protoCodes().includes(circuitProtoCode)) { | ||
| if (connection.remoteAddr.protoCodes().includes(CIRCUIT_PROTO_CODE)) { | ||
| log(`relayed connection to ${id} will not be used to hop on`) | ||
| return | ||
| } | ||
|
|
||
| const supportsHop = await canHop({ connection }) | ||
|
|
||
| if (supportsHop) { | ||
| this._peerStore.metadataBook.set(peerId, hopMetadataKey, uint8ArrayFromString(hopMetadataValue)) | ||
| this._peerStore.metadataBook.set(peerId, HOP_METADATA_KEY, uint8ArrayFromString(HOP_METADATA_VALUE)) | ||
| await this._addListenRelay(connection, id) | ||
| } | ||
| } catch (err) { | ||
|
|
@@ -125,15 +130,25 @@ class AutoRelay { | |
| } | ||
|
|
||
| // Create relay listen addr | ||
| let listenAddr, remoteMultiaddr | ||
| let listenAddr, remoteMultiaddr, remoteAddrs | ||
|
|
||
| try { | ||
| const remoteAddrs = this._peerStore.addressBook.get(connection.remotePeer) | ||
| // TODO: HOP Relays should avoid advertising private addresses! | ||
| // Get peer known addresses and sort them per public addresses first | ||
| remoteAddrs = this._peerStore.addressBook.get(connection.remotePeer) | ||
| // TODO: This sort should be customizable in the config (dialer addr sort) | ||
| remoteAddrs.sort(multiaddrsCompareFunction) | ||
|
|
||
| remoteMultiaddr = remoteAddrs.find(a => a.isCertified).multiaddr // Get first announced address certified | ||
| // TODO: HOP Relays should avoid advertising private addresses! | ||
| } catch (_) { | ||
| log.error(`${id} does not have announced certified multiaddrs`) | ||
| return | ||
|
|
||
| // Attempt first if existing | ||
| if (!remoteAddrs || !remoteAddrs.length) { | ||
| return | ||
| } | ||
|
|
||
| remoteMultiaddr = remoteAddrs[0].multiaddr | ||
| } | ||
|
|
||
| if (!remoteMultiaddr.protoNames().includes('p2p')) { | ||
|
|
@@ -194,10 +209,10 @@ class AutoRelay { | |
| continue | ||
| } | ||
|
|
||
| const supportsHop = metadataMap.get(hopMetadataKey) | ||
| const supportsHop = metadataMap.get(HOP_METADATA_KEY) | ||
|
|
||
| // Continue to next if it does not support Hop | ||
| if (!supportsHop || uint8ArrayToString(supportsHop) !== hopMetadataValue) { | ||
| if (!supportsHop || uint8ArrayToString(supportsHop) !== HOP_METADATA_VALUE) { | ||
| continue | ||
| } | ||
|
|
||
|
|
@@ -229,8 +244,49 @@ class AutoRelay { | |
| } | ||
| } | ||
|
|
||
| // TODO: Try to find relays to hop on the network | ||
| // Try to find relays to hop on the network | ||
| try { | ||
| const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS) | ||
| for await (const provider of this._libp2p.contentRouting.findProviders(cid)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't have support to do this right now, but it would be great to actually prioritize the results of this based on latency of the connection. Worth adding an issue for a future enhancement here I think
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that would be good! I can create an issue for that. We should get that after the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| if (!provider.multiaddrs.length) { | ||
| continue | ||
| } | ||
| const peerId = provider.id | ||
|
|
||
| this._peerStore.addressBook.add(peerId, provider.multiaddrs) | ||
| const connection = await this._libp2p.dial(peerId) | ||
|
|
||
| await this._addListenRelay(connection, peerId.toB58String()) | ||
|
|
||
| // Check if already listening on enough relays | ||
| if (this._listenRelays.size >= this.maxListeners) { | ||
| return | ||
| } | ||
| } | ||
| } catch (err) { | ||
| log.error(err) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Compare function for array.sort(). | ||
| * This sort aims to move the private adresses to the end of the array. | ||
| * | ||
| * @param {Address} a | ||
| * @param {Address} b | ||
| * @returns {number} | ||
| */ | ||
| function multiaddrsCompareFunction (a, b) { | ||
| const isAPrivate = isPrivate(a.multiaddr) | ||
| const isBPrivate = isPrivate(b.multiaddr) | ||
|
|
||
| if (isAPrivate && !isBPrivate) { | ||
| return 1 | ||
| } else if (!isAPrivate && isBPrivate) { | ||
| return -1 | ||
| } | ||
| return 0 | ||
| } | ||
|
|
||
| module.exports = AutoRelay | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| 'use strict' | ||
|
|
||
| const minute = 60 * 1000 | ||
|
|
||
| module.exports = { | ||
jacobheun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ADVERTISE_BOOT_DELAY: 15 * minute, // Delay before HOP relay service is advertised on the network | ||
| ADVERTISE_TTL: 30 * minute, // Delay Between HOP relay service advertisements on the network | ||
| CIRCUIT_PROTO_CODE: 290, // Multicodec code | ||
| HOP_METADATA_KEY: 'hop_relay', // PeerStore metadaBook key for HOP relay service | ||
| HOP_METADATA_VALUE: 'true', // PeerStore metadaBook value for HOP relay service | ||
| RELAY_RENDEZVOUS_NS: '/libp2p/relay' // Relay HOP relay service namespace for discovery | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While there is no support for signed peer records via delegate nodes (HTTP API), we need to fallback here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This address selection logic isn't great. If we don't have a certified record we might just be taking their private address if it's first (which I am concerned could be pretty common). The known public relays won't have this problem because they don't advertise private addresses, but we should be doing some level of sorting here to get the "best" address
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a sorter. In follow up PRs, I will add the support for custom sort function for dial and custom filter for announce. Then, it will be bubbled up to here. But I wanted to keep things simple in this PR