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

feat: new behavior for network bootstrapping #1257

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

cylewitruk
Copy link
Member

@cylewitruk cylewitruk commented Jan 22, 2025

Description

Closes: #1133

Changes

  • Adds a new libp2p swarm behavior for handling network bootstrapping.
  • The swarm will now re-try connections to all seed peers if the connection count is 0, and re-try every minute until the swarm has peers.

Testing Information

Existing tests pass and I've tested this quite a bit in devenv. I'll add some actual tests for the behavior, but PR'ing this now for preview.

Checklist:

  • I have performed a self-review of my code
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

@cylewitruk cylewitruk added sbtc signer binary The sBTC Bootstrap Signer. signer communication Communication across sBTC bootstrap signers. labels Jan 22, 2025
@cylewitruk cylewitruk added this to the sBTC: Deposits milestone Jan 22, 2025
@cylewitruk cylewitruk self-assigned this Jan 22, 2025
@cylewitruk cylewitruk linked an issue Jan 22, 2025 that may be closed by this pull request
1 task
@aldur aldur requested a review from Jiloc January 27, 2025 14:34
signer/src/network/libp2p/bootstrap.rs Outdated Show resolved Hide resolved
signer/src/network/libp2p/event_loop.rs Outdated Show resolved Hide resolved
signer/src/network/libp2p/bootstrap.rs Outdated Show resolved Hide resolved
signer/src/network/libp2p/bootstrap.rs Outdated Show resolved Hide resolved
signer/src/network/libp2p/bootstrap.rs Outdated Show resolved Hide resolved
signer/src/network/libp2p/bootstrap.rs Outdated Show resolved Hide resolved
signer/src/network/libp2p/bootstrap.rs Outdated Show resolved Hide resolved
signer/src/network/libp2p/bootstrap.rs Outdated Show resolved Hide resolved
signer/src/network/libp2p/bootstrap.rs Outdated Show resolved Hide resolved
signer/src/network/libp2p/bootstrap.rs Outdated Show resolved Hide resolved
@cylewitruk
Copy link
Member Author

@matteojug I ended up doing quite a bit of refactoring, sorry :( So this probably needs a fresh look.

Comment on lines +80 to +87
let mut addr = Multiaddr::empty();
for part in self.iter() {
if matches!(part, Protocol::P2p(_)) {
break;
}
addr.push(part.clone());
}
addr
Copy link
Collaborator

Choose a reason for hiding this comment

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

If it's it guaranteed that p2p is the last part (as assumed by the check above), why not self.clone() + pop()?

Comment on lines +239 to +250
let multiaddr = Multiaddr::empty()
.with(Protocol::Ip4(IP4_LOOPBACK))
.with(Protocol::Tcp(8080))
.with(Protocol::P2p(PeerId::random()));
let expected = Multiaddr::empty()
.with(Protocol::Ip4(IP4_LOOPBACK))
.with(Protocol::Tcp(8080));
assert_eq!(
multiaddr.without_p2p_protocol(),
expected,
"ip4 + tcp + p2p"
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

isn't this one the same as the one above?

Comment on lines +252 to +263
let multiaddr = Multiaddr::empty()
.with(Protocol::Ip4(IP4_LOOPBACK))
.with(Protocol::Tcp(8080))
.with(Protocol::P2p(PeerId::random()));
let expected = Multiaddr::empty()
.with(Protocol::Ip4(IP4_LOOPBACK))
.with(Protocol::Tcp(8080));
assert_eq!(
multiaddr.without_p2p_protocol(),
expected,
"ip4 + tcp + p2p"
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Also this one seems the same as the one above

pub fn new(local_peer_id: PeerId) -> Self {
Self {
local_peer_id,
seed_addresses: Default::default(),
bootstrap_interval: Duration::from_secs(60),
initial_delay: Duration::from_secs(0),
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Duration::ZERO?

}

/// Events emitted by the bootstrapping behavior.
#[allow(dead_code)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: do we need the #[allow(dead_code)] here?

}

/// Gets the [`PeerId`]s of all connected peers.
pub fn connected_peers(&self) -> Vec<(PeerId, Multiaddr)> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Now that this is a vec, we no longer need to collect (let peers = swarm.connected_peers().collect::<Vec<_>>();) in libp2p event loop.

But I'm wondering if we really want this, eg we usually count the number of PeerId in logs, while with this now we may have duplicated (with different addresses). OTOH, it may be useful to have the addrs info. Maybe we should still return a map?

// that this is a seed peer dial that we've initiated, so we publish
// the `DialingSeed` event.
if self.pending_connections.contains(&connection_id) {
tracing::debug!(%connection_id, addresses = ?addresses, peer_id = ?maybe_peer, "attempting to dial seed peer");
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit?

Suggested change
tracing::debug!(%connection_id, addresses = ?addresses, peer_id = ?maybe_peer, "attempting to dial seed peer");
tracing::debug!(%connection_id, ?addresses, peer_id = ?maybe_peer, "attempting to dial seed peer");

) -> std::task::Poll<libp2p::swarm::ToSwarm<Self::ToSwarm, libp2p::swarm::THandlerInEvent<Self>>>
{
// If we have any pending events, we return them immediately.
if let Some(event) = self.pending_events.pop_front() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: use self.next_pending_event()? It may end up uglier, but at least we are using the same interface

Comment on lines +393 to +398
// If we've attempted to bootstrap recently, we wait until the interval has passed.
if let Some(last_bootstrap) = self.last_attempted_at {
if last_bootstrap.elapsed() < self.config.bootstrap_interval {
return self.next_pending_event();
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Sanity check: we are going to get poll'd even if there's no swarm activity, right? I'm thinking if it could happen that we need to bootstrap again, but we consume all the events so if we don't get poll'd anymore we would be stuck.

if first_attempt.elapsed() < self.config.initial_delay {
return self.next_pending_event();
}
} else if self.config.initial_delay > Duration::from_secs(0) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Duration::ZERO?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
sbtc signer binary The sBTC Bootstrap Signer. signer communication Communication across sBTC bootstrap signers. wait-until-next-release
Projects
Status: In Review
Development

Successfully merging this pull request may close these issues.

[Feature]: Retry p2p seed peers
3 participants