-
Notifications
You must be signed in to change notification settings - Fork 999
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
protocols/: Implement Direct Connection Upgrade through Relay (DCUtR) #2438
Conversation
Enables two peers to coordinate a hole punch (direct connection upgrade) via a relayed connection. See https://github.com/libp2p/specs/blob/master/relay/DCUtR.md for specification.
This is the last step needed for basic hole punching in rust-libp2p (see also #2052). Anyone interested in reviewing? @thomaseizinger maybe? 😇 |
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.
First pass. Looking good! I can't believe this is so simple.
Overall I guess I'm wondering how much of the failure cases have to be handled by the end user vs the library. Things such as:
- The direct connection fails but the relay connection still works (can happen if the nat state gets reset for example).
- timeouts
and some questions in general from me.
Thanks!
} | ||
} | ||
|
||
fn inject_listen_upgrade_error( |
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 know this isn't related at all, but shouldn't these be renamed to inject_error_inbound
to match the above inject_fully_negotiated_inbound
, or is there some subtlety that I'm missing?
Sorry, GitHub for some reason filtered this out of my "review requested" list :( |
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.
Some comments, nothing blocking :)
protocols/dcutr/examples/client.rs
Outdated
futures::select! { | ||
event = swarm.next() => { | ||
match event.unwrap() { | ||
SwarmEvent::NewListenAddr { 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.
Why not return from this if that is what we are waiting for (based on the comment?).
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.
Yes, this is misleading. We are giving Swarm
some time to set up listeners. Though it might be more than one, depending on the number of network interfaces, thus returning after a single NewListenAddr
might wait for one network interface, but not all.
Waiting for a second instead is a hack, but a hack that works. Happy for alternative suggestions.
At least better documented with 21ae03b.
protocols/dcutr/examples/client.rs
Outdated
} | ||
}); | ||
|
||
if matches!(opts.mode, Mode::Dial) { |
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.
Nit: Could be a ==
if you would derive PartialEq
.
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.
👍 fixed with 10046f2. I think I previously carried something within the Dial
variant, though no longer.
protocols/dcutr/examples/client.rs
Outdated
.unwrap(); | ||
} | ||
|
||
block_on(futures::future::poll_fn(move |cx: &mut Context<'_>| { |
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 poll_fn
here and and async
loop above?
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.
Good point. Replaced with async
in 128d801.
protocols/dcutr/src/behaviour.rs
Outdated
/// The events produced by the [`Behaviour`]. | ||
#[derive(Debug)] | ||
pub enum Event { | ||
InitiateDirectConnectionUpgrade { |
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 naming of this variant doesn't indicate an event but a command. Is that on purpose?
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.
Unless I misunderstand the logic, this could be named InitiatedDirectConnectionUpgrade?
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.
DirectConnectedUpgradeInitiated
might be even better but then it is not in line with RemoteInitiatedDirectConnectionUpgrade
.
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.
Good idea. Renamed to InitiatedDirectConnectionUpgrade
for now with 8384b4e48b27e807a2b066cf21f9aea26b8ac1f7.
I am fine with DirectConnectedUpgradeInitiated
as well.
let msg = HolePunch { | ||
r#type: hole_punch::Type::Sync.into(), | ||
obs_addrs: vec![], | ||
}; | ||
|
||
let mut encoded_msg = BytesMut::new(); | ||
msg.encode(&mut encoded_msg) | ||
.expect("BytesMut to have sufficient capacity."); | ||
|
||
substream.send(encoded_msg.freeze()).await?; |
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.
Might be worthwhile to extract some helpers to hide the details of constructing one of these messages from the overall flow.
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.
Good idea. We can even go one step further and implement asynchronous_codec::Encoder
and asynchronous_codec::Decoder
for a custom Codec
that takes care of all the prost::Message
encoding, wrapping a UviBytes
.
What do you think of 354900f @thomaseizinger?
We could take this one step further, making Codec
abstract over prost::Message
and thus use the same Codec
in all protocols/XXX
. What do you think?
protocols/dcutr/src/behaviour.rs
Outdated
/// A [`NetworkBehaviourAction`], either complete, or still requiring data from [`PollParameters`] | ||
/// before being returned in [`Behaviour::poll`]. | ||
#[allow(clippy::large_enum_variant)] | ||
enum Action { |
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.
enum Action { | |
enum ActionBuilder { |
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.
👍 Done with f18a863.
protocols/dcutr/src/behaviour.rs
Outdated
fn addresses_of_peer(&mut self, _peer_id: &PeerId) -> Vec<Multiaddr> { | ||
vec![] | ||
} | ||
|
||
fn inject_connected(&mut self, _peer_id: &PeerId) {} |
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.
Nit: I think these have a default 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.
You are right. Removed with 831deb5.
protocols/dcutr/src/behaviour.rs
Outdated
self.queued_actions.push_back(Action::Connect { | ||
peer_id: *peer_id, | ||
attempt: 1, | ||
handler: NotifyHandler::One(*connection_id), | ||
}); | ||
let local_addr = match connected_point { | ||
ConnectedPoint::Listener { local_addr, .. } => local_addr, | ||
ConnectedPoint::Dialer { .. } => unreachable!("Due to outer if."), | ||
}; | ||
self.queued_actions.push_back( | ||
NetworkBehaviourAction::GenerateEvent(Event::InitiateDirectConnectionUpgrade { | ||
remote_peer_id: *peer_id, | ||
local_relayed_addr: local_addr.clone(), | ||
}) | ||
.into(), | ||
); |
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 could be written as a single .extend
of an array to make it clear that the actions belong together.
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.
Sounds good to me. Done across the whole file with b50708b.
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.
Sorry for the delay here, still not yet done going through all the comments. Very much appreciate that you two took the time for a review @MarcoPolo and @thomaseizinger.
protocols/dcutr/src/behaviour.rs
Outdated
fn addresses_of_peer(&mut self, _peer_id: &PeerId) -> Vec<Multiaddr> { | ||
vec![] | ||
} | ||
|
||
fn inject_connected(&mut self, _peer_id: &PeerId) {} |
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.
You are right. Removed with 831deb5.
protocols/dcutr/src/behaviour.rs
Outdated
/// The events produced by the [`Behaviour`]. | ||
#[derive(Debug)] | ||
pub enum Event { | ||
InitiateDirectConnectionUpgrade { |
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.
Good idea. Renamed to InitiatedDirectConnectionUpgrade
for now with 8384b4e48b27e807a2b066cf21f9aea26b8ac1f7.
I am fine with DirectConnectedUpgradeInitiated
as well.
protocols/dcutr/examples/client.rs
Outdated
.unwrap(); | ||
} | ||
|
||
block_on(futures::future::poll_fn(move |cx: &mut Context<'_>| { |
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.
Good point. Replaced with async
in 128d801.
protocols/dcutr/examples/client.rs
Outdated
} | ||
}); | ||
|
||
if matches!(opts.mode, Mode::Dial) { |
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.
👍 fixed with 10046f2. I think I previously carried something within the Dial
variant, though no longer.
protocols/dcutr/examples/client.rs
Outdated
futures::select! { | ||
event = swarm.next() => { | ||
match event.unwrap() { | ||
SwarmEvent::NewListenAddr { 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.
Yes, this is misleading. We are giving Swarm
some time to set up listeners. Though it might be more than one, depending on the number of network interfaces, thus returning after a single NewListenAddr
might wait for one network interface, but not all.
Waiting for a second instead is a hack, but a hack that works. Happy for alternative suggestions.
At least better documented with 21ae03b.
I addressed all the comments. Thanks for the reviews! @thomaseizinger and @MarcoPolo would you mind taking another look? |
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.
TIL about the custom codec. Neat.
Thanks @MarcoPolo and @thomaseizinger for the help here! |
Enables two peers to coordinate a hole punch (direct connection upgrade)
via a relayed connection.
See https://github.com/libp2p/specs/blob/master/relay/DCUtR.md for
specification.
Depends on either:
Transport::dial_as_listener
#2363Replaces #2076.