-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
swarm: fix selection of transport for dialing #1653
Conversation
@@ -57,3 +67,34 @@ func TestAddressesWithoutListening(t *testing.T) { | |||
require.NoError(t, err) | |||
require.Empty(t, a1, "expected to be listening on no addresses") | |||
} | |||
|
|||
func TestDialAddressSelection(t *testing.T) { |
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'm slightly concerned about the amount of dependencies that this test adds. Not that much of a problem, now that we have a mono-repo, but maybe this would be nicer if we used a mock transport.Transport
interface?
} | ||
if transport.Proxy() { | ||
return transport | ||
if isRelayAddr(a) { |
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 don't really like this logic here. Instead, we could have transports reject /p2p-circuit
address. wdyt?
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.
Let's not change this just yet since that would involve changing every transport. Let's revisit after discussing multiformats/multiaddr#140
Side node: We might want to introduce a |
With WebTransport's /webtransport/certhash/xyz addresses, the assumption that the last component of a multiaddr identifies the transport to use for dialing doesn't hold any more. Note that WebRTC will probably also use the certhash multiaddr component to encode its certificate hashes.
b1219fd
to
bc8df9a
Compare
Rebased the PR. This should be ready for review now. |
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 think this logic should change a bit after we discuss the multiaddr interpretation strategy. But at the very least I think we should be traversing the multiaddr backwards when looking for a transport. i.e. we don't want quic
to match before webtransport
in .../quic/webtransport
.
if isRelayAddr(a) { | ||
return s.transports.m[ma.P_CIRCUIT] | ||
} | ||
for _, t := range s.transports.m { |
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 think in general this needs to go the other way. We should be interpreting this right to left.
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 don't understand this comment. We're iterating over a map[int]transport.Transport
here. Inverting the order of iteration shouldn't have any effect, since map iterations are (pseudo-) randomized anyway.
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 misunderstood. I thought we were iterating of the multiaddr components. I see now that we're iterating over all registered transports.
I think this isn't ideal because:
- we have to make sure every registered transport doesn't intersect with any other transport. It would be tricky behavior if two transports return
CanDial -> true
for the samea
since we would randomly pick one of the two. I think simply a guaranteed ordering here would make it better. - This means the transport owns the interpretation of the full multiaddr. This doesn't compose well. Imagine we have a
percentencode
component and alenprefixencode
component, we don't want every transport to have to know how to read the encoded data from the multiaddr. Instead they should be given the decoded data from something else and deal with their part of the multiaddr. This relates to Interpreting multiaddrs multiformats/multiaddr#140.
I'll draft a patch of what I'm thinking here and how it relates to multiformats/multiaddr#140
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.
Here's concretely what I mean: https://github.com/libp2p/go-libp2p/compare/fix-dial-transport-selection...marco/layered-transports?expand=1#diff-1d9e588fd7c4fedee40f7f985be2346b02ba540389251f4603cbc13e8f8fc371R94
Notice this gives us the benefit of removing the "extract certhash" logic out of webtransport completely both in the Dial
and the CanDial
method (I didn't actually do this refactor, since this is a demo).
} | ||
} | ||
|
||
return s.transports.m[protocols[len(protocols)-1].Code] |
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.
What if we have a TransportForComponent
function that takes the component and the rest of the multiaddr (and maybe the list of registered transports) and returns the transport to use. This fits with the encapsulation idea of multiaddrs.
This function could be a method of an interface and components could implement this interface. The certhash component would simply return the next transport in the multiaddr along with the certhash parameters.
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.
That would be a layer violation, wouldn't it?
Components are a multiaddr concept. Multiaddr doesn't know anything about transports.
I agree that this is a mess and should be changed, but can we focus on getting something landed to unblock WebTransport for v0.23.0? |
Ok. Then maybe it's worth asking if anything here will make things worse than than they already are, or is there something that will make backwards compatibility harder? I don't think so. This has the same behavior as before if no two transports return
I hope my patch clarifies this, but let me know if things are still unclear.
Yeah, this is the one multiaddr format that doesn't match up with how the rest work. We can still deal with it following the same principles but it would involve some changes (see multiformats/multiaddr#140). Right now we deal with this with a special case. We could keep doing that too, but it would be better if we can unify the interpretation of multiaddrs. |
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 don't think this changes the old behavior, and it unblocks webtransport. This code needs refactoring but I don't want to block webtransport. Let's revisit after discussing multiformats/multiaddr#140
} | ||
if transport.Proxy() { | ||
return transport | ||
if isRelayAddr(a) { |
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.
Let's not change this just yet since that would involve changing every transport. Let's revisit after discussing multiformats/multiaddr#140
Merging this PR, with the understanding that this will get refactored very soon. |
With WebTransport's
/webtransport/certhash/xyz
addresses, the assumption that the last component of a multiaddr identifies the transport to use for dialing doesn't hold any more.Note that WebRTC will probably also use the
certhash
multiaddr component to encode its certificate hashes.This is a draft until we actually move the webtransport transport into this repo (#1737), but I'm asking for reviews nonetheless. Please review carefully, this has the potential to break us in painful ways.