Skip to content

protocols/: Implement Direct Connection Upgrade through Relay (DCUtR)#2438

Merged
mxinden merged 27 commits into
libp2p:masterfrom
mxinden:dcutr-2
Feb 8, 2022
Merged

protocols/: Implement Direct Connection Upgrade through Relay (DCUtR)#2438
mxinden merged 27 commits into
libp2p:masterfrom
mxinden:dcutr-2

Conversation

@mxinden

@mxinden mxinden commented Jan 15, 2022

Copy link
Copy Markdown
Member

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:

Replaces #2076.

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.
Comment thread protocols/dcutr/src/handler.rs
@mxinden

mxinden commented Jan 19, 2022

Copy link
Copy Markdown
Member Author

This is the last step needed for basic hole punching in rust-libp2p (see also #2052).

Anyone interested in reviewing? @thomaseizinger maybe? 😇

@MarcoPolo MarcoPolo left a comment

Copy link
Copy Markdown
Contributor

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:

  1. The direct connection fails but the relay connection still works (can happen if the nat state gets reset for example).
  2. timeouts

and some questions in general from me.

Thanks!

Comment thread protocols/dcutr/tests/lib.rs Outdated
Comment thread protocols/dcutr/src/behaviour.rs Outdated
Comment thread protocols/dcutr/src/behaviour.rs Outdated
Comment thread protocols/dcutr/examples/client.rs Outdated
Comment thread protocols/dcutr/src/handler/relayed.rs
Comment thread protocols/dcutr/src/handler/relayed.rs
Comment thread protocols/dcutr/src/handler/relayed.rs
}
}

fn inject_listen_upgrade_error(

Copy link
Copy Markdown
Contributor

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?

Comment thread protocols/dcutr/src/handler/relayed.rs Outdated
Comment thread protocols/dcutr/src/handler/relayed.rs
@thomaseizinger

Copy link
Copy Markdown
Contributor

This is the last step needed for basic hole punching in rust-libp2p (see also #2052).

Anyone interested in reviewing? @thomaseizinger maybe? innocent

Sorry, GitHub for some reason filtered this out of my "review requested" list :(

@thomaseizinger thomaseizinger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some comments, nothing blocking :)

Comment thread protocols/dcutr/examples/client.rs Outdated
Comment thread protocols/dcutr/examples/client.rs Outdated
futures::select! {
event = swarm.next() => {
match event.unwrap() {
SwarmEvent::NewListenAddr { address, .. } => {

Copy link
Copy Markdown
Contributor

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?).

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.

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.

Comment thread protocols/dcutr/examples/client.rs Outdated
}
});

if matches!(opts.mode, Mode::Dial) {

Copy link
Copy Markdown
Contributor

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.

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.

👍 fixed with 10046f2. I think I previously carried something within the Dial variant, though no longer.

Comment thread protocols/dcutr/examples/client.rs Outdated
.unwrap();
}

block_on(futures::future::poll_fn(move |cx: &mut Context<'_>| {

Copy link
Copy Markdown
Contributor

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?

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.

Good point. Replaced with async in 128d801.

Comment thread protocols/dcutr/src/behaviour.rs Outdated
/// The events produced by the [`Behaviour`].
#[derive(Debug)]
pub enum Event {
InitiateDirectConnectionUpgrade {

Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor

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.

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.

Good idea. Renamed to InitiatedDirectConnectionUpgrade for now with 8384b4e48b27e807a2b066cf21f9aea26b8ac1f7.

I am fine with DirectConnectedUpgradeInitiated as well.

Comment on lines +109 to +118
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?;

Copy link
Copy Markdown
Contributor

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.

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.

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?

Comment thread 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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
enum Action {
enum ActionBuilder {

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.

👍 Done with f18a863.

Comment thread protocols/dcutr/src/handler/relayed.rs
Comment thread protocols/dcutr/src/behaviour.rs Outdated
Comment on lines +91 to +95
fn addresses_of_peer(&mut self, _peer_id: &PeerId) -> Vec<Multiaddr> {
vec![]
}

fn inject_connected(&mut self, _peer_id: &PeerId) {}

Copy link
Copy Markdown
Contributor

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.

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.

You are right. Removed with 831deb5.

Comment thread protocols/dcutr/src/behaviour.rs Outdated
Comment on lines +115 to +130
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(),
);

Copy link
Copy Markdown
Contributor

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.

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.

Sounds good to me. Done across the whole file with b50708b.

@mxinden mxinden left a comment

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.

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.

Comment thread protocols/dcutr/src/behaviour.rs Outdated
Comment thread protocols/dcutr/src/behaviour.rs Outdated
Comment on lines +91 to +95
fn addresses_of_peer(&mut self, _peer_id: &PeerId) -> Vec<Multiaddr> {
vec![]
}

fn inject_connected(&mut self, _peer_id: &PeerId) {}

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.

You are right. Removed with 831deb5.

Comment thread protocols/dcutr/src/behaviour.rs Outdated
/// The events produced by the [`Behaviour`].
#[derive(Debug)]
pub enum Event {
InitiateDirectConnectionUpgrade {

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.

Good idea. Renamed to InitiatedDirectConnectionUpgrade for now with 8384b4e48b27e807a2b066cf21f9aea26b8ac1f7.

I am fine with DirectConnectedUpgradeInitiated as well.

Comment thread protocols/dcutr/examples/client.rs Outdated
.unwrap();
}

block_on(futures::future::poll_fn(move |cx: &mut Context<'_>| {

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.

Good point. Replaced with async in 128d801.

Comment thread protocols/dcutr/examples/client.rs Outdated
}
});

if matches!(opts.mode, Mode::Dial) {

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.

👍 fixed with 10046f2. I think I previously carried something within the Dial variant, though no longer.

Comment thread protocols/dcutr/examples/client.rs Outdated
futures::select! {
event = swarm.next() => {
match event.unwrap() {
SwarmEvent::NewListenAddr { address, .. } => {

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.

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.

Comment thread protocols/dcutr/examples/client.rs Outdated
Comment thread protocols/dcutr/examples/client.rs Outdated
@mxinden

mxinden commented Feb 3, 2022

Copy link
Copy Markdown
Member Author

I addressed all the comments. Thanks for the reviews!

@thomaseizinger and @MarcoPolo would you mind taking another look?

@MarcoPolo MarcoPolo left a comment

Copy link
Copy Markdown
Contributor

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.

@iduartgomez iduartgomez mentioned this pull request Feb 3, 2022
10 tasks
@mxinden mxinden merged commit 0bb8ee9 into libp2p:master Feb 8, 2022
@mxinden

mxinden commented Feb 8, 2022

Copy link
Copy Markdown
Member Author

Thanks @MarcoPolo and @thomaseizinger for the help here!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants