Skip to content

feat(wire): allow NodeInterface to request peer banning#872

Open
Micah-Shallom wants to merge 1 commit intogetfloresta:masterfrom
Micah-Shallom:wire/add-ban-peer-to-node-interface
Open

feat(wire): allow NodeInterface to request peer banning#872
Micah-Shallom wants to merge 1 commit intogetfloresta:masterfrom
Micah-Shallom:wire/add-ban-peer-to-node-interface

Conversation

@Micah-Shallom
Copy link
Copy Markdown

@Micah-Shallom Micah-Shallom commented Mar 6, 2026

Description

Closes #809.

Adds ban_peer() to NodeInterface, allowing external consumers to ban a peer by address and port. Follows the same pattern as the existing disconnect_peer() flow. Internally calls the existing disconnect_and_ban() method.

AI was used for codebase understanding; but all code was written manually.

@Micah-Shallom Micah-Shallom force-pushed the wire/add-ban-peer-to-node-interface branch from 65d637f to 2256574 Compare March 6, 2026 21:21
@moisesPompilio moisesPompilio added enhancement New feature or request BDK Floresta Issues related to the integration of Floresta in BDK labels Mar 7, 2026
Copy link
Copy Markdown
Collaborator

@jaoleal jaoleal left a comment

Choose a reason for hiding this comment

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

Thanks for taking a look at this.


UserRequest::BanPeer((addr, port)) => {
let node_response = match self.handle_ban_peer(addr, port) {
Ok(_) => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Ok(_) => {
Ok(()) => {

Comment on lines +510 to +524
pub fn handle_ban_peer(&mut self, addr: IpAddr, port: u16) -> Result<(), WireError> {
let peer_id = self
.peers
.iter()
.find(|(_, peer)| addr == peer.address && port == peer.port)
.map(|(&peer_id, _)| peer_id);

match peer_id {
Some(peer_id) => {
self.disconnect_and_ban(peer_id)?;
Ok(())
}
None => Err(WireError::PeerNotFoundAtAddress(addr, port)),
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Here some suggestions:

I think that you can make port optional, you can also simplify the handling on 518.

    /// Bans a peer for `T::BAN_TIME`.
    pub fn handle_ban_peer(&mut self, addr: IpAddr, port: Option<u16>) -> Result<(), WireError> {
        let peer_id = self
            .peers
            .iter()
            .find(|(_, peer)| {
                addr == peer.address && port.unwrap_or(get_port(&self.network)) == peer.port
            })
            .map(|(&peer_id, _)| peer_id);

        match peer_id {
            Some(peer_id) => self.disconnect_and_ban(peer_id),
            None => Err(WireError::PeerNotFoundAtAddress(
                addr,
                port.unwrap_or(get_port(&self.network)),
            )),
        }
    }

Also, it appears that Bitcoin Core allows banning peers that arent known by the time. cc @Davidson-Souza

@Micah-Shallom Micah-Shallom force-pushed the wire/add-ban-peer-to-node-interface branch from 2256574 to 2beb1b9 Compare March 9, 2026 01:31
@Micah-Shallom
Copy link
Copy Markdown
Author

hi @jaoleal ...thank you for your review.. i have addressed your suggestions.
Also regarding banning unknown peers, i will be happy to implement that if @Davidson-Souza confirms it's in scope for this PR, or i can follow up in a seperate PR

@jaoleal
Copy link
Copy Markdown
Collaborator

jaoleal commented Mar 9, 2026

Okay, the changes LGTM.

@jaoleal jaoleal requested review from Davidson-Souza, JoseSK999 and luisschwab and removed request for JoseSK999 March 9, 2026 14:22
Ok(())
}

/// Bans a peer for `T::BAN_TIME`.
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.

This works only for connected peers, right? I think the comment should mention that

.peers
.iter()
.find(|(_, peer)| {
addr == peer.address && port.unwrap_or(Self::get_port(self.network)) == peer.port
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.

Perhaps if a port isn't provided, we should ban all peers on that ip?

Wdyt @luisschwab?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just chiming in.

It seems that banning all peers is the way to go. Right now None is the default port number.

ban_peer(1.2.3.4, None) -> ban_peer(1.2.3.4, 8333)

But an attacker could reconnect on 1.2.3.4:50000

Some(peer_id) => self.disconnect_and_ban(peer_id),
None => Err(WireError::PeerNotFoundAtAddress(
addr,
port.unwrap_or(Self::get_port(self.network)),
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 return an option here

UserRequest::BanPeer((addr, port)) => {
let node_response = match self.handle_ban_peer(addr, port) {
Ok(()) => {
info!("Banned peer {addr}:{}",port.unwrap_or(Self::get_port(self.network)));
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
info!("Banned peer {addr}:{}",port.unwrap_or(Self::get_port(self.network)));
info!("Banned peer {addr}:{}", port.unwrap_or(Self::get_port(self.network)));

@luisschwab
Copy link
Copy Markdown
Member

I think we should have an actual BanMan before tackling this one.

@jaoleal
Copy link
Copy Markdown
Collaborator

jaoleal commented Mar 9, 2026

I think we should have an actual BanMan before tackling this one.

I think the proposed PR aggregates on floresta as it is. Cant we work on a BanMan after ? I say that because i thought this would enable setban rpc method, by looking at is specs it appears that such a BanMan needs its own PR.

We could open a tracking issue for that, I did some looking around of what we need to satisfy setban and the changes are trivial.

@Davidson-Souza
Copy link
Copy Markdown
Member

Davidson-Souza commented Mar 9, 2026

@luisschwab

I think we should have an actual BanMan before tackling this one.

This interface (which is pretty much what #809 asks for) is not the same as setban. setban takes in an ip address only, and idk if it disconnects already connected peers. Maybe using just this could be useful? For bonsai this would basically be what you need IIRC.

Since you opened the issue, it's up to you whether this is useful or not.

Edit: forgot to quote

@luisschwab
Copy link
Copy Markdown
Member

setban takes in an ip address only

No, it takes extra arguments: https://developer.bitcoin.org/reference/rpc/setban.html

I think these should also be exposed to the NodeInterface instead of just banning a peer absolutely. This approach also won't allow banning a whole subnet, as we can only pass an IP address.

@Davidson-Souza
Copy link
Copy Markdown
Member

setban takes in an ip address only

No, it takes extra arguments: https://developer.bitcoin.org/reference/rpc/setban.html

I meant it doesn't take a port, just an ip. I like the idea of adding a bantime. Banning forever isn't a good idea.

Since the way to go seems to be adding the BanMan to ban a range, we should merge #820 and #809 and better define the approach.

@luisschwab
Copy link
Copy Markdown
Member

we should merge #820 and #809 and better define the approach.

done

@Micah-Shallom
Copy link
Copy Markdown
Author

thanks for your reviews @jaoleal @Davidson-Souza @luisschwab ...seeing that the conversation is pretty much drifting into the banman implementation #820 . If y'all will be okay with me looking into it, i could go through the docs and the existing banman code over the next few days and see what i can come up with.. if thats fine i'll appreciate being assigned to it @luisschwab 🙏🏾🙏🏾

.peers
.iter()
.find(|(_, peer)| {
addr == peer.address && port.unwrap_or(Self::get_port(self.network)) == peer.port
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just chiming in.

It seems that banning all peers is the way to go. Right now None is the default port number.

ban_peer(1.2.3.4, None) -> ban_peer(1.2.3.4, 8333)

But an attacker could reconnect on 1.2.3.4:50000

Some(peer_id) => self.disconnect_and_ban(peer_id),
None => Err(WireError::PeerNotFoundAtAddress(
addr,
port.unwrap_or(Self::get_port(self.network)),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

port.unwrap_or(Self::get_port(self.network)) is being computed twice.

TransactionBroadcastResult(Result<Txid, AcceptToMempoolError>),

/// A response indicating whether a peer was successfully banned.
BanPeer(bool),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you considered a richer type instead a bool?

Only bool loses information. What happens with the error computed in handle_ban_peer if the bool is false?

Disconnect((IpAddr, u16)),

/// Ban a peer by its address.
BanPeer((IpAddr, Option<u16>)),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This look odd to users of the API. If the default port inference is useful here, it should probably also apply to Disconnect (and also other operations like Remove) since they are "similar" operations.

Should we keep port an optional? Any take on that @Davidson-Souza, @luisschwab, @jaoleal ?

@Micah-Shallom Micah-Shallom force-pushed the wire/add-ban-peer-to-node-interface branch from 2beb1b9 to 3ba5b8e Compare March 10, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BDK Floresta Issues related to the integration of Floresta in BDK enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wire: allow NodeInterface to request peer banning

6 participants