Skip to content

Feat: add Manual type to ConnectionKinds#866

Merged
Davidson-Souza merged 2 commits into
getfloresta:masterfrom
Davidson-Souza:feat/add-manual-peers
Mar 6, 2026
Merged

Feat: add Manual type to ConnectionKinds#866
Davidson-Souza merged 2 commits into
getfloresta:masterfrom
Davidson-Souza:feat/add-manual-peers

Conversation

@Davidson-Souza

Copy link
Copy Markdown
Member

Description and Notes

All peers that are added by the user — those added with the RPC
addnode or the CLI option --connect, should have kind Manual,
rather than Regular. This exempts them from:

  • Max peers quota — if we already have 10 peers and the user tries
    to addnode another one, we will now have 11 peers
  • They whitelisted and shouldn't be banned by misbehaving
  • They don't need to have any required service

This commit adds this new variant, and makes sure we are following
the above rules.

This is a small subset of #636, and will be useful for getting #865 unstuck (potentially can help with some edge cases as mentioned in #852 (comment))

@Davidson-Souza Davidson-Souza added this to the v0.9.0 milestone Mar 3, 2026
@Davidson-Souza Davidson-Souza requested a review from JoseSK999 March 3, 2026 20:28
@Davidson-Souza Davidson-Souza self-assigned this Mar 3, 2026
@Davidson-Souza Davidson-Souza added enhancement New feature or request code quality Generally improves code readability and maintainability refactor labels Mar 3, 2026
@Davidson-Souza Davidson-Souza changed the title Feat/add manual peers Feat: add Manual type to ConnectionKinds Mar 3, 2026
@Davidson-Souza Davidson-Souza mentioned this pull request Mar 3, 2026
20 tasks
}

#[derive(Debug, PartialEq, Clone, Copy)]
/// The kind of connection we see this peer as.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add a link to the Core counterpart, useful

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!

@Davidson-Souza

Davidson-Souza commented Mar 3, 2026

Copy link
Copy Markdown
Member Author

Fixed a lint error

/// A feeler connection is a short-lived connection used to check whether this peer is alive.
///
/// After handshake, we ask for addresses and when we receive an answer we just disconnect,
/// marking this peer as alive in our address manager

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
/// marking this peer as alive in our address manager
/// marking this peer as alive in our address manager.

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.

We don't apply punctuation very evenly. Perhaps we should add this to our coding style, and update all docs as we go (same we are doing with structs)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For single-line comments it is fine to not punctuate IMO, but for these longer docstrings better to do it

/// marking this peer as alive in our address manager
Feeler,

/// A regular peer, used to send requests to and learn about transactions and blocks

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
/// A regular peer, used to send requests to and learn about transactions and blocks
/// A regular peer, used to send requests to and learn about transactions and blocks.

/// A regular peer, used to send requests to and learn about transactions and blocks
Regular(ServiceFlags),

/// An extra peer specially created if our tip haven't moved for too long

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
/// An extra peer specially created if our tip haven't moved for too long
/// An extra peer specially created if our tip hasn't moved for too long.

Comment on lines +150 to +152
/// If more than [`NodeContext::ASSUME_STALE`] seconds has passed since the
/// processed block. We use this to make sure we are not in a partitioned subnet,
/// unable to learn about new blocks

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
/// If more than [`NodeContext::ASSUME_STALE`] seconds has passed since the
/// processed block. We use this to make sure we are not in a partitioned subnet,
/// unable to learn about new blocks
/// If more than [`NodeContext::ASSUME_STALE`] seconds have passed since the
/// last processed block, we use this to make sure we are not in a partitioned subnet,
/// unable to learn about new blocks.

@Davidson-Souza Davidson-Souza force-pushed the feat/add-manual-peers branch from 27b5f92 to b7a39cc Compare March 4, 2026 15:25
@Davidson-Souza

Copy link
Copy Markdown
Member Author

Pushed b7a39cc applying minor docs fixes by @JoseSK999

Comment on lines +61 to +66
pub(crate) fn create_connection(&mut self, kind: ConnectionKind) -> Result<(), WireError> {
// Connection with fixed peers should be marked as `manual`, rather than `regular`
let connection_kind = match (self.fixed_peer.as_ref(), kind) {
(Some(_), ConnectionKind::Regular(_)) => ConnectionKind::Manual,
_ => kind,
};

@JoseSK999 JoseSK999 Mar 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What if we change the signature to mut kind: ConnectionKind, and mutate it to Manual if needed. To avoid having two kind variables within scope.

I think this is nice, so you can reuse the is_fixed variable at L120:

    pub(crate) fn create_connection(&mut self, mut kind: ConnectionKind) -> Result<(), WireError> {
        let is_fixed = self.fixed_peer.is_some();

        // Connection with fixed peers should be marked as `manual`, rather than `regular`
        if is_fixed && matches!(kind, ConnectionKind::Regular(_)) {
            kind = ConnectionKind::Manual;
        }

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!

@Davidson-Souza Davidson-Souza force-pushed the feat/add-manual-peers branch from b7a39cc to 2821fd5 Compare March 4, 2026 18:19

@JoseSK999 JoseSK999 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Last nits I think

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Remove line, not needed 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.

Wait, the is_fixed one? I think it's still relevant, we need to allow fallback for manual peers. We would also need to change peer.rs to always fallback on manual peers

@JoseSK999 JoseSK999 Mar 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You have that variable defined twice after applying my suggestion, I mean. You can remove this one.

match kind {
ConnectionKind::Feeler => self.last_feeler = Instant::now(),
ConnectionKind::Regular(_) => self.last_connection = Instant::now(),
// Note: Crating a manual peer intentionally don't prevent us from checking our peers

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Note: Crating a manual peer intentionally don't prevent us from checking our peers
// Note: creating a manual peer intentionally doesn't prevent us from checking our peers

But I don't understand the point you are trying to make in this 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.

We still call check_connections in the same moment we would, regardless of already having a new connection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ahh, perhaps a rephrase to say manual peers don’t affect the check connection waiting time

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.

Rephrased to mention the timer

Comment on lines +109 to +110
// Connections that will continue open for as long as we are running (and the other
// peer don't die)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Connections that will continue open for as long as we are running (and the other
// peer don't die)
// Connections expected to remain open if the peer doesn't die

Comment on lines +112 to +113
|| matches!(p.kind, ConnectionKind::Manual);
p.state == PeerStatus::Ready && long_lived_connection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

space?

Comment on lines +424 to +425
|| matches!(p.kind, ConnectionKind::Manual);
if long_lived && p.state == PeerStatus::Ready {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

space too?

@@ -137,12 +138,11 @@
// peer and create a utreexo and CBS connection
if !self.has_utreexo_peers() {
if self.peer_ids.len() == 10 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe we can do this now that you touch it:

Suggested change
if self.peer_ids.len() == 10 {
if self.peer_ids.len() >= RunningNode::MAX_OUTGOING_PEERS {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Or should we use connected_peers to not count feelers?

@@ -153,12 +153,11 @@
return Ok(());
}
if self.peer_ids.len() == 10 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

and here

Suggested change
if self.peer_ids.len() == 10 {
if self.peer_ids.len() >= RunningNode::MAX_OUTGOING_PEERS {

info.kind = ConnectionKind::Regular(peer_info.services);
});
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why were we updating peer info here 🤨

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.

I think that was actually a bug 👀

If a feeler connection sent us headers, they would be promoted to Regular


// disconnect the peer with the lowest score
if let Some(peer) = peer_to_disconnect {
self.send_to_peer(peer, NodeRequest::Shutdown)?;

@jaoleal jaoleal Mar 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just a comment:

I saw a lot of these cases where the connection kind where checked to see if a peer should be disconnected, would be good to have unified methods for the each ConnectionKind exception.

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.

I've added some helper methods in LocalPeerView and used them. I think we can't trivially remove anything else

@Davidson-Souza Davidson-Souza force-pushed the feat/add-manual-peers branch 2 times, most recently from 2459abc to a4ba5f6 Compare March 4, 2026 22:44
@Davidson-Souza

Davidson-Souza commented Mar 4, 2026

Copy link
Copy Markdown
Member Author

Pushed a4ba5f6b355f34394a539748d555c316e8c4906d

  • Minor style and comments changes @JoseSK999
  • Used MAX_OUTGOING_PEERS instead of magic values in check_connections
  • Used connected_peers instead of self.peers.len() in check_connections
  • Added some utility methods to LocalPeerVew that wraps some matches! invocations to make the code cleaner

@JoseSK999 JoseSK999 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LAST nits

match kind {
ConnectionKind::Feeler => self.last_feeler = Instant::now(),
ConnectionKind::Regular(_) => self.last_connection = Instant::now(),
// Note: Creating a manual peer intentionally doesn't affect the `last_connection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Note: Creating a manual peer intentionally doesn't affect the `last_connection
// Note: Creating a manual peer intentionally doesn't affect the `last_connection`

Comment on lines +240 to +243
pub(crate) const fn is_long_lived(&self) -> bool {
matches!(self.kind, ConnectionKind::Regular(_))
|| matches!(self.kind, ConnectionKind::Manual)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Tiny nit

Suggested change
pub(crate) const fn is_long_lived(&self) -> bool {
matches!(self.kind, ConnectionKind::Regular(_))
|| matches!(self.kind, ConnectionKind::Manual)
}
pub(crate) const fn is_long_lived(&self) -> bool {
self.is_regular_peer() || self.is_manual_peer()
}

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.

Tiny fix pushed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since you are at it too

Suggested change
let total_peers = self.connected_peers();

@Davidson-Souza Davidson-Souza force-pushed the feat/add-manual-peers branch from a4ba5f6 to 525cc0c Compare March 5, 2026 20:55
@Davidson-Souza

Copy link
Copy Markdown
Member Author

Pushed 525cc0c fixing nits by @JoseSK999

@JoseSK999 JoseSK999 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK 525cc0c

@JoseSK999

JoseSK999 commented Mar 5, 2026

Copy link
Copy Markdown
Member

Note there's a case where we would disconnect the manual peer, which is when it sends an invalid block, and we call disconnect_and_ban.

But probably we want to call disconnect_and_ban inside increase_banscore tho. The latter doesn't update the address manager, so we may connect again with the banned peer. disconnect_and_ban does update the address manager which we want, I think.

EDIT: I think we can just add a return here to fix it and then in a followup merge a bit these two functions.

Comment on lines 502 to 503
if let Some(peer) = self.peers.get(&peer) {
let ban_state = AddressState::Banned(T::BAN_TIME);

@JoseSK999 JoseSK999 Mar 5, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add early return to fix it

if let Some(peer) = self.peers.get(&peer) {
    // Manual connections are exempt from being punished
    if peer.is_manual_peer() {
        return Ok(());
    }

    let ban_state = AddressState::Banned(T::BAN_TIME);

@Davidson-Souza Davidson-Souza force-pushed the feat/add-manual-peers branch from 525cc0c to e0d9a85 Compare March 5, 2026 22:04
@Davidson-Souza

Copy link
Copy Markdown
Member Author

Note there's a case where we would disconnect the manual peer, which is when it sends an invalid block, and we call disconnect_and_ban.

But probably we want to call disconnect_and_ban inside increase_banscore tho. The latter doesn't update the address manager, so we may connect again with the banned peer. disconnect_and_ban does update the address manager which we want, I think.

EDIT: I think we can just add a return here to fix it and then in a followup merge a bit these two functions.

Oh shoot, right! Pushed the temporary fix, ACK on refactoring those functions.

@JoseSK999 JoseSK999 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK e0d9a85

All peers that are added by the user — those added with the RPC
`addnode` or the CLI option `--connect`, should have kind `Manual`,
rather than `Regular`. This excempts them from:
  - Max peers quota — if we already have 10 peers and the user tries
    to addnode another one, we will now have 11 peers
  - They whitelisted and shouldn't be banned by misbehaving
  - They don't need to have any required service

This commit adds this new variant, and makes sure we are following
the above rules.
Manual connections are whitelisted and should not be banned, even
if they misbehave. This commit makes sure we are not doing so.

I've removed all the times we just disconnect a misbehaving peer with
a call to `increase_banscore` with the maximum `banscore` as the
increase amount (so they will be banned right away)
@Davidson-Souza Davidson-Souza force-pushed the feat/add-manual-peers branch from e0d9a85 to 6de9fa1 Compare March 6, 2026 14:57
@Davidson-Souza

Davidson-Souza commented Mar 6, 2026

Copy link
Copy Markdown
Member Author

Sorry, @JoseSK999. But I've fixed that annoying but where we would get nine peers and get stuck.

@JoseSK999 JoseSK999 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

re-ACK 6de9fa1

@jaoleal

jaoleal commented Mar 6, 2026

Copy link
Copy Markdown
Member

ACK 6de9fa1

@JoseSK999

Copy link
Copy Markdown
Member

Next on: batch connections 👨🏻‍🍳

@Davidson-Souza Davidson-Souza merged commit fe045d8 into getfloresta:master Mar 6, 2026
11 checks passed
@csgui csgui added this to Floresta Mar 12, 2026
@github-project-automation github-project-automation Bot moved this to Done in Floresta Mar 12, 2026
@csgui csgui added reliability Related to runtime reliability, stability and production readiness and removed code quality Generally improves code readability and maintainability labels Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request refactor reliability Related to runtime reliability, stability and production readiness

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants