https://github.com/bitcoin/bitcoin/pull/28488

Motivation

This PR tries to fix ‣ and it is somewhat related to PR #28538

Having outbound peers with mempool limits that greatly differ from ours is detrimental, given such links won’t be useful to propagate data we sent trough them (the transactions that fit under our limits but not theirs will bounce). Therefore, is it desirable to maintain a minimum number of outbound connections to peers with mempool limits similar to ours.

Notice how, similar to https://github.com/bitcoin/bitcoin/pull/28538, a low usable outbound connection count is also detrimental to our node’s privacy, given transaction originated from us will have a smaller initial flooding source set.

Thoughts/Questions

Code

Once we’ve made sure that were are not in IBD (it does not make sense to evict outbound peers due to mempool limits in IBD) and we’ve reached the outbound connection limit, we can start to get picky about what outbound connections we want to keep arround.

m_connman.ForEachNode([&](CNode* pnode) {
    if (!pnode->IsFullOutboundConn() || pnode->fDisconnect) return;
    auto peer = GetPeerRef(pnode->GetId());

    if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
        if (tx_relay->m_fee_filter_received) {
            peer_fee_filters.push_back(std::make_pair(pnode, tx_relay->m_fee_filter_received.load()));
        } else {
            ++around_our_percent_fee_filter;
        }
    }
});

if (peer_fee_filters.size() == 0) return;

For each of the nodes we are connected to we are not fully connected to (done internally by CConnman::ForEachNode and the ones that are not fully outbound (!pnode-> IsFullOutboundConn(), i.e. blocks-only, block relay, …). From the remaining ones, we check out transaction relay details, and whether a filter has been set by the peer.

We record all peers that set a filter (peer_fee_filters) and accumulate the others as having limits similar to ours (around_our_percent_fee_filter).

If after all this there are no filters set, we are done.

std::sort(peer_fee_filters.begin(), peer_fee_filters.end());

for (auto [node_id, fee_filter] : peer_fee_filters) {
    if (fee_filter == 0) ++around_our_percent_fee_filter;
    // Lower than our filter means they are more permissive.
    if (CFeeRate{fee_filter} <= our_min_feerate) ++around_our_percent_fee_filter;;
}

if (around_our_percent_fee_filter < 4) {
    // Drop one peer at a time to preserve a sufficient total
    // number of connections, and to avoid network DoS.
    // TODO add randomness chosing among low-feerate candidates.
    peer_fee_filters.back().first->fDisconnect = true;
}