Skip to content
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

Merged
merged 27 commits into from
Feb 8, 2022

Conversation

mxinden
Copy link
Member

@mxinden mxinden commented Jan 15, 2022

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.
@mxinden
Copy link
Member Author

mxinden commented Jan 19, 2022

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

Anyone interested in reviewing? @thomaseizinger maybe? 😇

Copy link
Contributor

@MarcoPolo MarcoPolo left a 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:

  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!

protocols/dcutr/tests/lib.rs Outdated Show resolved Hide resolved
protocols/dcutr/src/behaviour.rs Outdated Show resolved Hide resolved
protocols/dcutr/src/behaviour.rs Outdated Show resolved Hide resolved
protocols/dcutr/examples/client.rs Outdated Show resolved Hide resolved
protocols/dcutr/src/handler/relayed.rs Show resolved Hide resolved
protocols/dcutr/src/handler/relayed.rs Show resolved Hide resolved
protocols/dcutr/src/handler/relayed.rs Show resolved Hide resolved
}
}

fn inject_listen_upgrade_error(
Copy link
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?

protocols/dcutr/src/handler/relayed.rs Outdated Show resolved Hide resolved
protocols/dcutr/src/handler/relayed.rs Show resolved Hide resolved
@thomaseizinger
Copy link
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 :(

Copy link
Contributor

@thomaseizinger thomaseizinger left a 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 Show resolved Hide resolved
futures::select! {
event = swarm.next() => {
match event.unwrap() {
SwarmEvent::NewListenAddr { address, .. } => {
Copy link
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
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.

}
});

if matches!(opts.mode, Mode::Dial) {
Copy link
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
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.

.unwrap();
}

block_on(futures::future::poll_fn(move |cx: &mut Context<'_>| {
Copy link
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
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.

/// The events produced by the [`Behaviour`].
#[derive(Debug)]
pub enum Event {
InitiateDirectConnectionUpgrade {
Copy link
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
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
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
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
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
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?

/// 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
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
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.

protocols/dcutr/src/handler/relayed.rs Show resolved Hide resolved
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
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
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 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
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
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.

Copy link
Member Author

@mxinden mxinden left a 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 Show resolved Hide resolved
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
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.

/// The events produced by the [`Behaviour`].
#[derive(Debug)]
pub enum Event {
InitiateDirectConnectionUpgrade {
Copy link
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.

.unwrap();
}

block_on(futures::future::poll_fn(move |cx: &mut Context<'_>| {
Copy link
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.

}
});

if matches!(opts.mode, Mode::Dial) {
Copy link
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.

futures::select! {
event = swarm.next() => {
match event.unwrap() {
SwarmEvent::NewListenAddr { address, .. } => {
Copy link
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.

protocols/dcutr/examples/client.rs Outdated Show resolved Hide resolved
protocols/dcutr/examples/client.rs Outdated Show resolved Hide resolved
@mxinden
Copy link
Member Author

mxinden commented Feb 3, 2022

I addressed all the comments. Thanks for the reviews!

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

Copy link
Contributor

@MarcoPolo MarcoPolo left a 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.

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

mxinden commented Feb 8, 2022

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