-
Notifications
You must be signed in to change notification settings - Fork 446
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
feat(libp2p): direct connection through relay protocol (DCUtR) #1928
Conversation
Implements the [DCUtR protocol](https://github.com/libp2p/specs/blob/master/relay/DCUtR.md). Only supports TCP Simultaneous Connect since there is no QUIC support yet
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.
the updates with forced connections are great, and I think what was preventing my demo from succeeding.
@@ -37,7 +41,7 @@ export interface ConnectionManager { | |||
* const connection = await libp2p.connectionManager.openConnection(peerId) | |||
* ``` | |||
*/ | |||
openConnection: (peer: PeerId | Multiaddr | Multiaddr[], options?: AbortOptions) => Promise<Connection> | |||
openConnection: (peer: PeerId | Multiaddr | Multiaddr[], options?: OpenConnectionOptions) => Promise<Connection> |
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.
we could probably do this as a separate PR
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.
It's needed to pass the force
option to openConnection
when we have an existing transient connection.
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 mean, we could make this change in a smaller PR and get the code out prior to the whole DCUtR. it's beneficial in it's own right.
I put the latest of your changes up at https://github.com/SgtPooki/helia-playground/tree/feat/half-dcutr-poc and it still seems like dial-backs aren't quite enough for unblocking the golden-path for helia-authored content being accessible from ipfs.io. Am I missing something? I was thinking this PR (or at least the unilateral connection upgrade) would be enough to unblock the golden path scenario for Helia? |
one thing i'm noticing is that isPublicAndDialable isn't always filtering out p2p-circuit addresses: Here are some that i can see in my testing of helia-playground:
Opened multiformats/js-multiaddr-matcher#2 to address |
It's a bit of a weird one, network peers usually return their multiaddrs without a peer id, I guess the intention is including the peer id creates a lot of redundancy on the wire but it means we can't treat multiaddrs as opaque values. Taking the first address as an example:
We can't actually dial this address as it needs to be: So we need to append the destination peer id ourselves, I've added this, hopefully it improves things. |
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.
nits
const output = [] | ||
|
||
for (const addr of addrs) { | ||
if (addr == null || addr.length === 0) { | ||
continue | ||
} | ||
|
||
try { | ||
const ma = multiaddr(addr) | ||
|
||
if (!this.isPublicAndDialable(ma)) { | ||
continue | ||
} | ||
|
||
output.push(ma) | ||
} catch {} | ||
} | ||
|
||
return output |
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.
why not just filter
?
const output = [] | |
for (const addr of addrs) { | |
if (addr == null || addr.length === 0) { | |
continue | |
} | |
try { | |
const ma = multiaddr(addr) | |
if (!this.isPublicAndDialable(ma)) { | |
continue | |
} | |
output.push(ma) | |
} catch {} | |
} | |
return output | |
return addrs | |
.filter(addr => addr != null) | |
.filter(addr => addr.length !== 0) | |
.filter(addr => { | |
try { | |
const ma = multiaddr(addr) | |
return this.isPublicAndDialable(ma)) | |
} catch {} | |
}) |
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.
The suggestion iterates over the list three times. Doing for..of
means we only iterate over the list once.
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.
nice, what I meant was, it can be filtered out in a single iteration instead of pushing into an output array.
const output = [] | |
for (const addr of addrs) { | |
if (addr == null || addr.length === 0) { | |
continue | |
} | |
try { | |
const ma = multiaddr(addr) | |
if (!this.isPublicAndDialable(ma)) { | |
continue | |
} | |
output.push(ma) | |
} catch {} | |
} | |
return output | |
return addrs | |
.filter(addr => { | |
if (addr == null) return false | |
if (addr.length === 0) return false | |
try { | |
const ma = multiaddr(addr) | |
return this.isPublicAndDialable(ma)) | |
} catch { | |
// swallow | |
} finally { | |
return false | |
} | |
}) |
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.
The return type of the method is Multiaddr[]
but the input type is Array<Uint8Array | string | null | undefined>
so as implemented the suggestion returns Array<Uint8Array | string | null | undefined>
- e.g. it's missing a .map
step to turnUint8Array | string | null | undefined
into Multiaddr
but adding that will involve iterating over the list twice.
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.
It's also worth noting that for..of
is faster than JS array methods since we're not creating new function contexts/stacks/etc for each iteration - I haven't measured anything so it might not be a bottleneck here but .forEach
, .filter
, .map
etc are certainly not free.
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.
for loops are usually faster in all languages. forEach/map/etc are convenience methods that may improve readability, but for the majority of code we maintain, we should probably default to using for loops.
NOTE: some optimizations in v8 have made forEach/map/etc more efficient, but for loops are the "lowest level" loop we have access to and are faster by default
Co-authored-by: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com>
Co-authored-by: Marco Munizaga <git@marcopolo.io> Co-authored-by: Chad Nehemiah <chad.nehemiah94@gmail.com>
04e0c97
to
fa41c46
Compare
@SgtPooki @whizzzkid any further objections or can we merge this now? |
Implements the DCUtR protocol.
Only supports TCP Simultaneous Connect since there is no QUIC support yet