Commit Graph

1475 Commits

Author SHA1 Message Date
merge-script
975a314667 Merge bitcoin/bitcoin#35832: p2p: avoid block disk reads on unnecessary requests
28641fd195 p2p: reject empty getblocktxn requests (furszy)
9871fb726c p2p: reject filtered block inv early when bloom is disabled (furszy)
aaf9412026 refactor: split p2p_getdata.py in sub-cases (furszy)

Pull request description:

  Reject requests that make the node read blocks from disk unnecessarily:

  * `getblocktxn` is meant to request the txs a peer is missing. When a
    peer sends a `getblocktxn` with an empty index vector, it isn't missing
    anything, so it shouldn't have sent the message in the first place.

  * The peer should not request a filtered block when the node does not
    advertise the `NODE_BLOOM` service. Filtered blocks are built from
    the bloom filter, which can be loaded only when the `NODE_BLOOM`
    service is offered.

  Both are disconnected now.

  Note: can be split in two PRs if preferred.

ACKs for top commit:
  151henry151:
    ACK 28641fd195
  l0rinc:
    lightly tested ACK 28641fd195
  mzumsande:
    Code Review ACK 28641fd195
  winterrdog:
    tested ACK 28641fd195
  sedited:
    ACK 28641fd195

Tree-SHA512: 787ee0741fb797ea0898daab1bf3d7b3a21d91c9d940fc833e893806e95843e9d5a7a79a55ba67ac0f1550c08f2b6cee5ab2b625c082f56885ae795958cc608c
2026-08-04 11:50:22 +02:00
Hennadii Stepanov
0238aebf61 refactor: Annotate MakeAndPushFeature with [[maybe_unused]]
This is a follow-up to bitcoin/bitcoin#35221. The `MakeAndPushFeature`
member function template has no callers yet, which triggers
`-Wunused-template` now that Clang 23 enables it as part of `-Wall`.
2026-08-03 17:20:08 +01:00
furszy
28641fd195 p2p: reject empty getblocktxn requests
A getblocktxn msg is only needed when at least one tx is
missing from a compact block. If no txs are missing, the
block can be reconstructed without sending the request.

This avoids reading the requested block from disk
unnecessarily and also alerts the peer operator about
their node's buggy behavior.
2026-07-31 16:08:08 -04:00
furszy
9871fb726c p2p: reject filtered block inv early when bloom is disabled
A peer should not request filtered blocks from a node that
does not advertise NODE_BLOOM. Perform this check before
looking up the block to avoid an unnecessary disk read.

Note: currently, the request is ignored only after the
block has been read from disk, in the bloom filter
existence check.
2026-07-31 15:32:14 -04:00
merge-script
b33a7fcd7b Merge bitcoin/bitcoin#34628: p2p: Replace per-peer transaction rate-limiting with global rate limits
349c72ee00 net_processing: Drop unnecessary txid arg from InitiateTxBroadcastToAll (Anthony Towns)
12b0dc33c4 doc: Add release note for -txsendrate etc (Anthony Towns)
5cde66341a tests: basic functional test for tx rate limiting (Anthony Towns)
4842903ac1 rpc: report -txsendrate and bucket info via getnetworkinfo (Anthony Towns)
74a47a5207 init: add -txsendrate configuration parameter (Anthony Towns)
6307bd034b net_processing: Provide a 30bpm heartbeat log while inv backlog is in use (Anthony Towns)
df31ee57aa net_processing: add a global delay queue for sending txs (Anthony Towns)
7927650e56 util/tokenbucket.h: Provide a generic TokenBucket class (Anthony Towns)
749bb447f8 txmempool: Drop CompareMiningScoreWithTopology (Anthony Towns)
e1b7490fbc net_processing: Replace CompareInvMempoolOrder (Anthony Towns)
6cfc65d210 txmempool: Add ExtractBestByMiningScoreWithTopology (Anthony Towns)
026f70e05f net_processing: Remove per-peer rate-limiting (Anthony Towns)
46c8c471dc net_processing: bump last_inv_sequence for bip35 messages explicitly (Anthony Towns)

Pull request description:

  Per-peer `m_tx_inventory_to_send` queues have CPU and memory costs that scale with both queue size and peer count. Under high transaction volume, this has previously caused severe issues ([May 2023 disclosure][1]) and still can cause measurable delays ([Feb 2026 Runestone surge][2], with the msghand thread observed hitting 100% CPU and queue memory reaching ~95MB).

  This PR replaces the per-peer rate limiting with a global queue using dual token buckets (limiting transaction by both count and serialized size). Transactions that arrive within the bucket capacity still relay nearly immediately, but excess transactions queue in a global backlog and drain as the token buckets refill.

  Key parameters:
    - Count bucket: 14 tx/s, 420 capacity (30s buffer)
    - Size bucket: 20 kB/s (~12 MB/600s), 50 MB capacity
    - Outbound peers refill faster by a factor of 2.5

  Per-peer queues are retained solely for privacy batching and are always fully emptied, removing the old `INVENTORY_BROADCAST_MAX` cap.

  This reduces the memory and CPU burden during transaction spikes when the queuing logic is engaged from O(queue * peers) to O(queue), as the queued transactions no longer need to be retained per-peer or re-sorted per-peer.

  Design discussion: https://gist.github.com/ajtowns/d61bea974a07190fa6c6c8eaef3638b9

  [1]: https://bitcoincore.org/en/2024/10/08/disclose-large-inv-to-send/
  [2]: https://bnoc.xyz/t/increased-b-msghand-thread-utilization-due-to-runestone-transactions-on-2026-02-17/81

ACKs for top commit:
  sipa:
    Code review ACK 349c72ee00. I haven't tested it myself yet (though switched my well-connected node to it now), but the posted benchmarks and analyses look convincing.
  instagibbs:
    reACK 349c72ee00
  mzumsande:
    ACK 349c72ee00

Tree-SHA512: 2196a23308cb7fe36738cf638edf5c5b0e9ba32b11c083609fd8b50291e05bb33484f9921f8beab28d94c58d1adddea4c8ae1182a60a7f53f54be7370e2a0e47
2026-07-25 12:15:44 +02:00
Ava Chow
11ebbd9072 Merge bitcoin/bitcoin#28463: p2p: Increase inbound capacity for block-relay only connections
c11508406e doc: Update docs that refer to -maxconnections (Martin Zumsande)
69ce0dba2a test: add test that EvictTxPeerIfFull only evicts tx-relaying peers (brunoerg)
3ed7f06418 p2p: trigger possible eviction if we support bloom filters and change a peer to tx relay (Martin Zumsande)
0bd3d3dfa5 init: make inbound tx relay percentage configurable (Amiti Uttarwar)
cc59aee196 test: add functional test for inbound maxconnection limits (Amiti Uttarwar)
1b76e04736 net: increase inbound capacity for block-relay-only connections (Martin Zumsande)
87bca1c2ad net: add options to AttemptToEvictConnection (Martin Zumsande)

Pull request description:

  This is joint work with amitiuttarwar.

  See issue #28462 for a broader discussion on increasing the number of block-relay-only connections independent of this particular implementation proposal.

  We suggest to increase the number of inbound slots allocated to block-relay-only peers by increasing the default maximum connections from 125 to 200, with 50% of inbound slots accessible for tx-relaying peers.
  This is a prerequisite for being able to increase the default number of outgoing block-relay-only peers later, because the current inbound capacity of the network is not sufficient.
  In order to account for incoming tx-relaying peers separately from incoming block-relay peers, changes to the inbound eviction logic are necessary.

  See the next post in this thread for a more detailed explanation and motivation of the changes.

ACKs for top commit:
  instagibbs:
    ACK c11508406e
  achow101:
    ACK c11508406e
  dergoegge:
    crACK c11508406e
  marcofleon:
    ACK c11508406e

Tree-SHA512: c71e1481eb235429a6c9d7ce771c7bf825f850b135e904ccfa3505112628fef4188b560d0be0847c968e5ece43c1518590069b7e6e2480790d3ef1ce07d1ac38
2026-07-24 14:30:00 -07:00
Martin Zumsande
3ed7f06418 p2p: trigger possible eviction if we support bloom filters and change a peer to tx relay
Co-authored-by: Amiti Uttarwar <amiti@uttarwar.org>
2026-07-24 14:16:27 +02:00
Martin Zumsande
1b76e04736 net: increase inbound capacity for block-relay-only connections
..and adjust the eviction logic.
The new default max connection number is 200, the default maximum of tx-relaying
inbounds is limited to 50% of all inbound connections.
With 11 outbound connections, that is (200 - 11) * 0.5 = 94.5.
As a result, the tx-related maximum traffic should not change
drastically.

When we receive an inbound connection and don't have space for another
full-relay peer, we now attempt to evict specifically a full-relay inbound
after receiving the version message of the new peer.

Once this commit is widely deployed, the added inbound capacity will
allow us to increase the number of outgoing block-relay-only connections.

Co-authored-by: Amiti Uttarwar <amiti@uttarwar.org>
2026-07-24 14:16:27 +02:00
Anthony Towns
349c72ee00 net_processing: Drop unnecessary txid arg from InitiateTxBroadcastToAll 2026-07-12 09:12:27 +10:00
Anthony Towns
4842903ac1 rpc: report -txsendrate and bucket info via getnetworkinfo
Add `tx_send_rate` and `inv_buckets` fields to getnetworkinfo. The
`inv_buckets` field has separate `inbound` and `outbound` entries,
reporting backlog count, count tokens, and size tokens. Useful for
monitoring relay behavior.
2026-07-12 09:12:27 +10:00
Anthony Towns
74a47a5207 init: add -txsendrate configuration parameter
Adds a debug-only configuration option to set the target
transaction/second rate for relay to inbound connections. This is mostly
intended to be set to artificially low values to aid in testing behaviour
when a backlog occurs, but is also available in case the default 14tx/s
target is somehow too low in practice.
2026-07-12 09:12:27 +10:00
Anthony Towns
6307bd034b net_processing: Provide a 30bpm heartbeat log while inv backlog is in use 2026-07-12 09:12:27 +10:00
Anthony Towns
df31ee57aa net_processing: add a global delay queue for sending txs
Without the per-peer rate limiting, nodes can act as an amplifier for
transaction spam -- receiving many transactions from one node, but
relaying each of them to over 100 other nodes. Limit the impact of this
by providing a global rate limit.

This is implemented using dual token buckets, one that consumes a
token for every transaction, and one that consumes a token for every
serialized byte. This rate limits both per-tx resource usage (eg INV
messages) and overall relay bandwidth.

Main bucket parameters:
 * Count: 14tx/s rate, 420tx (30s) capacity
 * Size: 12MB/600s rate (4-6 blocks per target block interval), 50MB capacity

The size bucket is expected to be large enough to almost never have an
impact in normal usage, even during transaction storms, and is primarily
intended to mitigate attack-like scenarios.

Outbound connections get a separate pair of buckets, with rates boosted
by a 2.5x multiplier.

This avoids the excessive memory and CPU usage due to the 100x multiplier
from the queues being per-peer.

Note that this also reduces the size of INV messages we send for general
tx relay back to a more reasonable level of under 600 txs in 99.999%
of cases.
2026-07-12 09:12:27 +10:00
Anthony Towns
e1b7490fbc net_processing: Replace CompareInvMempoolOrder
Remove CompareInvMempoolOrder, replacing it with the new
ExtractBestByMiningScoreWithTopology. The trickle send code is reworked
accordingly.
2026-07-12 09:12:27 +10:00
Anthony Towns
026f70e05f net_processing: Remove per-peer rate-limiting
Per-peer rate limiting introduces storage and compute costs proportional
to the number of peers. This has caused severe bugs in the past, and
continues to be a risk in the event of periods of extremely high rates
of transaction submission. Avoid these problems by always completely
emptying the m_tx_inventory_to_send queue when processing it.

Note that this increases the potential size of INV messages we send
for normal tx relay from ~1000 (limited by INVENTORY_BROADCAST_MAX)
to potentially 50000 (limited by MAX_INV_SZ).
2026-07-12 09:07:33 +10:00
Anthony Towns
46c8c471dc net_processing: bump last_inv_sequence for bip35 messages explicitly
This avoids relying on the bump that occurs through the normal INV
process, which is confusing.
2026-07-12 09:06:41 +10:00
merge-script
6f1c56f03a Merge bitcoin/bitcoin#35670: net: optimize compact block extra tx iteration
1a3cbf1bd2 net: optimize compact block extra tx iteration (Lőrinc)

Pull request description:

  **Problem:** `vExtraTxnForCompact` gives compact block reconstruction one more source for recently removed transactions.
  Before this PR, the first insertion resized the cache to its configured capacity, so before the cache was full, `PartiallyDownloadedBlock::InitData()` also scanned default `{Wtxid::ZERO, nullptr}` entries that had never been added to the cache.
  Those unused entries could still participate in reconstruction short-id matching.

  **Fix:** Reserve the configured capacity and append entries until the cache is full.
  Once full, keep the same ring-buffer overwrite behavior, configured maximum, and replacement order.

  **Risk:** Triggering the affected duplicate-match branch requires a block-specific 6-byte short-id collision while the extra-txn cache still contains default null slots.
  With a 16-thread benchmark of the `CBlockHeaderAndShortTxIDs` nonce-grinding path, the 50% collision time was ~178 days on my machine.

ACKs for top commit:
  davidgumberg:
    crACK 1a3cbf1bd2
  darosior:
    utACK 1a3cbf1bd2
  w0xlt:
    ACK 1a3cbf1bd2
  sedited:
    ACK 1a3cbf1bd2

Tree-SHA512: 9022a2ea5f3ff4279bdee7fa0316d6e5c922be3f7c566743b4d16708c49c0c8dc8a8dcbd60530a0128277db6f192355be88312419952213ae06353a0d937c507
2026-07-09 11:22:40 +02:00
Ava Chow
f0da26cfc8 Merge bitcoin/bitcoin#34997: p2p: Don't participate in addr relay with feelers
b0735336ee p2p: Don't participate in addr relay with feeler connections (Daniela Brozzoni)

Pull request description:

  Feeler connections are short-lived connection made to check that a node is alive, useful for test-before-evict, and for moving addresses from the new to the tried table.

  We currently send a GETADDR message to feelers, but then disconnect before being able to receive a response. This GETADDR is not useful and can be removed.

  I couldn't find any previous discussion about this, but I found PR #22777, that similarly made sure that we don't ask for tx relay to feelers.

  ---

  I noticed this behavior on my peer-observer instance: I would see the number of sent GETADDR messages increase over time, but the number of ADDR messages with >100 addresses received (which are likely GETADDR responses and not self announcements relays) wouldn't increase as much. I later realized that it was my node opening feeler connections, sending a GETADDR, and closing the connection.

  You can see the same behavior using this command - the node is making feeler connections, sending getaddr to them, closing before receiving the addr response:

  ```
  ~ ₿ tail -f ~/.bitcoin/debug.log | grep -E "(Making feeler connection|Added connection to|sending getaddr|feeler connection completed|Received addr: [0-9]{2,} addresses)"

  2026-04-02T13:25:50Z [net] Making feeler connection to xyz.onion:8333
  2026-04-02T13:26:06Z [net] Added connection to xyz.onion:8333 peer=27
  2026-04-02T13:26:08Z [net] sending getaddr (0 bytes) peer=27
  2026-04-02T13:26:08Z [net] feeler connection completed, disconnecting peer=27, peeraddr=xyz.onion:8333
  ```

  On a node that accepts inbounds connections, this command can be used to see in the logs all the nodes that connected, sent a getaddr, and disconnected before receiving a reply. It is possible that these nodes connected to us as a feeler:
  ```
  ~ ₿ cat .bitcoin/debug.log | awk '

    /received: getaddr/ {
        split($0, a, "peer=")
        got_getaddr[a[2]] = $0
    }

    /sending addr/ {
        split($0, a, "peer=")
        sent_addr[a[2]] = 1
    }

    /socket closed/ {
        split($0, a, "peer=")
        id = a[2]

        if (id in got_getaddr && !(id in sent_addr)) {
            print "possible feeler: " got_getaddr[id]
            print "                 " $0
        }

        delete got_getaddr[id]
        delete sent_addr[id]
    }
  '

  possible feeler: 2026-04-01T21:45:13Z [net] received: getaddr (0 bytes) peer=2311974
                   2026-04-01T21:45:13Z [net] socket closed, disconnecting peer=2311974
  possible feeler: 2026-04-02T00:18:58Z [net] received: getaddr (0 bytes) peer=2426389
                   2026-04-02T00:18:58Z [net] socket closed, disconnecting peer=2426389
  ...
  ```

  Then, you can manually inspect one of them:
  ```
  ~ ₿ cat .bitcoin/debug.log | grep -E "peer=2311974"
  2026-04-01T21:45:13Z [net] Added connection peer=2311974
  2026-04-01T21:45:13Z [net] received: version (102 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] sending version (102 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] send version message: version 70016, blocks=943279, txrelay=0, peer=2311974
  2026-04-01T21:45:13Z [net] sending wtxidrelay (0 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] sending sendaddrv2 (0 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] sending verack (0 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] receive version message: /Satoshi:27.0.0/: version 70016, blocks=943279, us=x.x.x.x:8333, txrelay=0, peer=2311974
  2026-04-01T21:45:13Z [net] received: wtxidrelay (0 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] received: sendaddrv2 (0 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] received: verack (0 bytes) peer=2311974
  2026-04-01T21:45:13Z New inbound v1 peer connected: version: 70016, blocks=943279, peer=2311974
  2026-04-01T21:45:13Z [net] sending sendcmpct (9 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] sending ping (8 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] sending getheaders (1029 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] initial getheaders (943278) to peer=2311974 (startheight:943279)
  2026-04-01T21:45:13Z [net] received: getaddr (0 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] Advertising address x.x.x.x:8333 to peer=2311974
  2026-04-01T21:45:13Z [net] socket closed, disconnecting peer=2311974
  2026-04-01T21:45:13Z [net] Resetting socket for peer=2311974
  2026-04-01T21:45:13Z [net] sending addrv2 (24665 bytes) peer=2311974
  2026-04-01T21:45:13Z [net] Cleared nodestate for peer=2311974
  ```

ACKs for top commit:
  0xB10C:
    ACK b0735336ee
  achow101:
    ACK b0735336ee
  andrewtoth:
    ACK b0735336ee
  stratospher:
    ACK b073533. didn't see any addr message from feelers in my node's last 24 hours/it would disconnect before addr message is received. so consistent with today's behaviour.

Tree-SHA512: 1ac220dfd8361c4687399546a0d968d268e447446053fb8b90ba6b987482cc038e2ad94e670f33181d08d6d0c576882328bb5d0a8b8b1175a5e2ec31ff051833
2026-07-08 11:49:10 -07:00
merge-script
4498fa5d5b Merge bitcoin/bitcoin#35406: private broadcast: limit outstanding txs to count of 10,000
4e29de719e private broadcast: add release note for limited cap (Gregory Sanders)
cbf8c107c1 Release cs_main between individual private tx re-attempts (Greg Sanders)
5aea3d0373 private broadcast: limit outstanding txs to count of 10,000 (Gregory Sanders)

Pull request description:

  Add a belt-and-suspenders feature, limit the amount of memory and cpu possible when unlucky or simply misconfigured. The worst case limit is roughly 400kB * 10,000 = 4GB, regardless of usage pattern.

  Before this change, sheer volume of broadcasts, mismatches in standardness rules, or simply fee mismatches may result in unbounded growth of memory usage. As the feature may be expanded in the future, explicit bounds helps reasoning going forward.

ACKs for top commit:
  frankomosh:
    tACK 4e29de719e. Ran private_broadcast_tests and p2p_private_broadcast_cap.py. Great to have an explicit bound as the belt-and-suspenders against unbounded queue growth.
  vasild:
    ACK 4e29de719e
  andrewtoth:
    ACK 4e29de719e
  stickies-v:
    ACK 4e29de719e

Tree-SHA512: 18161755f37d07cca185a09e782dbe2fd0025b8befd4f6660e988865cc3a9b705d41769b816161e8142fe6ce31a56e0288bd78efc25135cedfc47fc855011799
2026-07-07 15:10:13 +01:00
Ava Chow
69fc991791 Merge bitcoin/bitcoin#32606: p2p: Drop unsolicited CMPCTBLOCK from non-HB peer and when blocksonly
55e3a57f22 qa: Avoid UTXO reuse between test functions (Hodlinator)
9c5dd2926a p2p: Ignore CMPCTBLOCK from peer that hasn't sent SENDCMPCT (David Gumberg)
bf9884f4e5 p2p: make blocksonly nodes ignore CMPCTBLOCK messages (David Gumberg)
92cea63c71 test: (Un)solicited invalid cb -> get disconnected. (David Gumberg)
e845e26344 test: p2p: Nodes ignore unsolicited CMPCTBLOCK's (David Gumberg)
8313591715 p2p: Drop unsolicited CMPCTBLOCK from non-HB peer (David Gumberg)
44f377a71f refactor: test: Static assert_highbandwidth_states (David Gumberg)
25457a3272 test: Tighten getblocktxn checks in parallel cb reconstruction test. (David Gumberg)
51dd90fb50 refactor: Merge announce_cmpct_block() defs into one (Hodlinator)

Pull request description:

  Processing unsolicited `CMPCTBLOCK`'s from a peer that has not been marked high bandwidth is not well-specified behavior in BIP-0152, in fact the BIP seems to imply that it is not permitted:

  > "[...] method is not useful for compact blocks because `cmpctblock` blocks can be sent unsolicitedly in high-bandwidth mode"

  See https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki#separate-version-for-segregated-witness

  This PR disables processing of CMPCTBLOCK messages in three cases:
  $1$. When the block is unsolicited and from a non-HB peer.
  $2$. When this node is running in `-blocksonly` mode.
  $3$. When the peer has not advertised `CMPCTBLOCK` support with a `SENDCMPCT` message.

  Not processing unsolicited blocks slightly raises the cost of discovering a peer's mempool via `CMPCTBLOCK` as described in #28272. As pointed out there, getting an HB slot is relatively easy, so this does not prevent an attacker from doing this, it just slightly raises the bar.

  Probably more important is not processing `CMPCTBLOCK` messages as a `-blocksonly` node. A blocksonly node has a lot less surface area for leaking its mempool since it does no transaction relay, and leaking a blocksonly node's mempool is pretty dangerous since it is very likely to be the origin for all of the transactions in its mempool.

ACKs for top commit:
  achow101:
    ACK 55e3a57f22
  w0xlt:
    reACK 55e3a57f22
  hodlinator:
    re-ACK 55e3a57f22
  polespinasa:
    lgtm ACK 55e3a57f22

Tree-SHA512: 118bea55adca01dbd6467ba5ae3adf420d960794a6a2c40dd30fcc7d79aa944e01af0f6dd6bd6ff6d33dc9155171f6f4f497cbd7e8eb6d3c4c89e12740b51c05
2026-07-06 15:52:10 -07:00
Lőrinc
1a3cbf1bd2 net: optimize compact block extra tx iteration
`vExtraTxnForCompact` was resized to its configured capacity on the first insertion.
Before the ring was filled, compact block reconstruction scanned default `{Wtxid::ZERO, nullptr}` entries created for unused slots.

Reserve the configured capacity and append entries until the cache is full, then keep the same ring-buffer overwrite behavior.
This preserves the configured maximum and replacement order while keeping reconstruction from scanning unused capacity.
2026-07-06 11:57:38 -07:00
Daniela Brozzoni
b0735336ee p2p: Don't participate in addr relay with feeler connections
Feeler connections are short-lived connections made to check that a node
is alive, useful for test-before-evict from addrman, and for moving
addresses from the new to the tried table.

We currently send a GETADDR message to feelers, but then disconnect
before being able to receive a response. This wastes some bandwidth, so
we can avoid sending the GETADDR altogether.

Not sending the initial GETADDR will effectively disable addr relay:
we initialize addr relay for the peer when we send GETADDR, and the peer
initializes addr relay to us when they receive it. So the
peer will not relay any announcement to us, and we will not relay any
to them either. This is ok, since the use of feelers is to test if there
is a bitcoin node behind an address, not exchange addresses with them.
2026-07-06 16:02:30 +02:00
Greg Sanders
cbf8c107c1 Release cs_main between individual private tx re-attempts
This queue may be hundreds to thousands of items, each of
which are being revalidated at short interval. Allow the
node to be more responsive in this scenario.
2026-06-30 14:53:59 -04:00
Gregory Sanders
5aea3d0373 private broadcast: limit outstanding txs to count of 10,000
Add a belt-and-suspenders feature, limit the amount of
memory and cpu possible when unlucky or simply misconfigured.
The worst case limit is roughly 400kB * 10,000 = 4GB, regardless
of usage pattern.

Before this change, sheer volume of broadcasts, mismatches in
standardness rules, or simply fee mismatches may result in unbounded
growth of memory usage. As the feature may be expanded in
the future, explicit bounds helps reasoning going forward.
2026-06-30 14:53:59 -04:00
David Gumberg
9c5dd2926a p2p: Ignore CMPCTBLOCK from peer that hasn't sent SENDCMPCT
This commit also changes the default sendcmpct version in the functional
test to `2`, since this is the version that nodes expect, prior to this
commit, nowhere in the functional test framework was the default version
value used:

git grep -P 'msg_sendcmpct\((?![^)]*version\s*=)' HEAD^

`version=2` is a sensible default, since this is the version nodes
currently expect in the SENDCMPCT handshake.
2026-06-18 16:50:44 -07:00
David Gumberg
bf9884f4e5 p2p: make blocksonly nodes ignore CMPCTBLOCK messages
blocksonly nodes don't benefit from compact blocks, since they don't
have a mempool to aid in reconstruction, so they should not process
CMPCTBLOCK messages.

This is not just belt-and-suspenders, as a blocksonly node will
trivially reveal exactly which transactions in a block are its own in
the GETBLOCKTXN response to a CMPCTBLOCK. Since it will be missing every
transaction in the block, except for its own.

See discussion: https://github.com/bitcoin/bitcoin/issues/28272
2026-06-18 16:50:44 -07:00
David Gumberg
8313591715 p2p: Drop unsolicited CMPCTBLOCK from non-HB peer
Processing unsolicited CMPCTBLOCK's from a peer that has not been marked
high bandwidth is not well-specified behavior in BIP-0152, in fact the
BIP seems to imply that it is not permitted:

"[...] method is not useful for compact blocks because `cmpctblock`
blocks can be sent unsolicitedly in high-bandwidth mode"

See https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki#separate-version-for-segregated-witness

This partially mitigates a mempool leak described in
[#28272](https://github.com/bitcoin/bitcoin/issues/28272), but that
particular issue will persist for peers that have been selected as high
bandwidth.

This also mitigates potential DoS / bandwidth-wasting / abusive
behavior that is discussed in the comments of #28272.
2026-06-18 16:50:44 -07:00
brunoerg
2d0dce0af5 net_processing: fix BIP152 first integer interpretation 2026-06-17 14:51:46 -03:00
merge-script
2cfb10b668 Merge bitcoin/bitcoin#35498: net: move cs_main up in FetchBlock to fix rpc assert crash
359680b74d net: move cs_main up in FetchBlock to fix rpc assert crash (Eugene Siegel)

Pull request description:

  A benign, racy assert can fail when calling FetchBlock and the peer is being cleaned up.

  1. FetchBlock runs in a http worker thread. It acquires a PeerRef, locks cs_main, then may later call BlockRequested which asserts that CNodeState exists for the peer.

  2. FinalizeNode may run in either the bitcoind or b-net threads. It locks cs_main, fetches a PeerRef from RemovePeer, fetches a CNodeState, and later removes it from m_node_states.

  Because of the lock placement in FetchBlock, the http worker thread in 1) can acquire a valid PeerRef and block while the b-net thread in 2) is cleaning up the peer in FinalizeNode. When the worker thread later acquires cs_main, it may crash in BlockRequested since no CNodeState exists. Fix this by acquiring the lock earlier in FetchBlock.

  I tested the assert can be hit and the fix works by adding sleeps. Was introduced in https://github.com/bitcoin/bitcoin/pull/25514 which moved the lock down.

ACKs for top commit:
  maflcko:
    lgtm  ACK 359680b74d
  dergoegge:
    utACK 359680b74d
  sedited:
    ACK 359680b74d

Tree-SHA512: dd29db28bc95c781b32b1cb6e782190fc8abd28bba36a5149fb35c86eaf56448e58a5c68a1c858a50348dd2c940e423942c67340a52e934818711932f538d708
2026-06-14 15:50:32 +02:00
Ava Chow
8598ec2204 Merge bitcoin/bitcoin#35221: BIP 434 Support: Peer feature negotiation
da74ff9ca4 test: Add functional test for BIP434 (Fabian Jahr)
01b8a117d2 test_framework: BIP 434 support (Anthony Towns)
6a129983c9 BIP434: FEATURE message support (Anthony Towns)
3210fc477a net: Add AdvertisedVersion() for protocol version advertised to a peer (Anthony Towns)
94ed45427c serialize: add LimitedVectorFormatter (Anthony Towns)
1b3f776ebb serialize: string_view serialization (Anthony Towns)

Pull request description:

  Adds support for [BIP 434](https://github.com/bitcoin/bips/blob/master/bip-0434.md).

ACKs for top commit:
  fjahr:
    ACK da74ff9ca4
  pseudoramdom:
    ACK da74ff9ca4
  achow101:
    ACK da74ff9ca4
  darosior:
    ACK da74ff9ca4
  w0xlt:
    reACK da74ff9ca4
  sedited:
    ACK da74ff9ca4

Tree-SHA512: 74aa01b9b296a1a498b3aa119af6db906453f0809ec7ae271fc26690491c3f5677bf2cd03817caf9e287f5b3bc977768cdfefbe74ed2dd0da1cd339e043fe010
2026-06-10 13:40:45 -07:00
Eugene Siegel
359680b74d net: move cs_main up in FetchBlock to fix rpc assert crash
1. FetchBlock runs in a http worker thread. It acquires a PeerRef,
locks cs_main, then may later call BlockRequested which asserts
that CNodeState exists for the peer.

2. FinalizeNode may run in either the bitcoind or b-net threads. It
locks cs_main, fetches a PeerRef from RemovePeer, fetches a CNodeState,
and later removes it from m_node_states.

Because of the lock placement in FetchBlock, the http worker thread in 1)
can acquire a valid PeerRef and block while the b-net thread in 2) is
cleaning up the peer in FinalizeNode. When the worker thread later acquires
cs_main, it may crash in BlockRequested since no CNodeState exists. Fix
this by acquiring the lock earlier in FetchBlock.

The lock can be replaced with a net-specific lock when the remaining
CNodeState fields are moved to Peer.
2026-06-09 15:44:57 -04:00
merge-script
17ed7f5060 Merge bitcoin/bitcoin#35297: p2p: Release m_peer_mutex early in InitiateTxBroadcastToAll
fa2afba28b p2p: Release m_peer_mutex early in InitiateTxBroadcastToAll (MarcoFalke)

Pull request description:

  The `InitiateTxBroadcastToAll` method holds the `m_peer_mutex` while updating the bloom filters for all peers. This is perfectly fine, because updating the bloom filters is fast. Though, from a style-perspective, the lock does not need to be held for the whole function. Also, holding the lock longer, may confuse Tsan into a lock-order inversion false-positive (ref: https://github.com/bitcoin/bitcoin/issues/19303#issuecomment-1514926359).

  So "fix" both issues in this style-refactor.

ACKs for top commit:
  xyzconstant:
    Code review ACK fa2afba28b
  shuv-amp:
    ACK fa2afba28b
  danielabrozzoni:
    Code Review ACK fa2afba28b
  sedited:
    ACK fa2afba28b

Tree-SHA512: c47849a4c3cc11c74b61fec3425db8ec7f78db4ca43d7bf3145ce640f7b0872701c09495f0dfe77109d09d5716d920ad3d7308483fe41564c30867b3e80432e7
2026-06-09 11:26:22 +02:00
Pol Espinasa
7b821ef9b7 rpc: getprivatebroadcastinfo and abortprivatebroadcast throw if -privatebroadcast is disabled 2026-06-08 15:13:09 +02:00
Anthony Towns
6a129983c9 BIP434: FEATURE message support
BIP434 defines FEATURE messages which are sent between VERSION and VERACK
to indicate support for new P2P protocol features. This commit provides
the infrastructure for easily using BIP434 negotiation when implementing
such new P2P protocol features. Note that advertised protocol version
is bumped to 70017, as per BIP434's specification.
2026-06-04 21:58:56 +10:00
Anthony Towns
3210fc477a net: Add AdvertisedVersion() for protocol version advertised to a peer 2026-06-04 21:58:55 +10:00
Ryan Ofsky
735b1cf431 Merge bitcoin/bitcoin#34806: refactor: logging: Various API improvements
02b2c41103 logging: use util/log.h where possible (Anthony Towns)
57d7495fe5 IWYU fixes (Anthony Towns)
611878b46f scripted-diff: logging: Drop LogAcceptCategory (Anthony Towns)
34332dba2f util/log, logging: Provide ShouldDebugLog and ShouldTraceLog instead of a generic ShouldLog (Anthony Towns)
abea304dd6 logging: Move GetLogCategory into Logger class (Anthony Towns)
58113e5833 util/log: Rename LogPrintLevel_ into detail_ namespace (Anthony Towns)
f69d1ae56d util/log: Provide util::log::NO_RATE_LIMIT to avoid rate limits (Anthony Towns)
72e92d67df logging: Protect ShrinkDebugFile by m_cs (Anthony Towns)
904c0d07bb util/stdmutex: Drop StdLockGuard (Anthony Towns)

Pull request description:

  `ShrinkDebugFile` now takes the logging mutex for its entire run; though it's only called in init so shouldn't have any races in the first place.

  Adds a `NO_RATE_LIMIT` tag that can be used with info/warning/error logs to avoid rate-limiting. This allows `LogPrintLevel_` to be restricted to being an internal API.

  The `GetLogCategory` function is moved out of the global namespace.

  `ShouldLog` is split into separate `ShouldDebugLog` and `ShouldTraceLog` so that filtering checks are somewhat more enforced via function signature checks.

  Redundant `LogAcceptCategory` function is removed.

  More files are pointed at util/log.h instead of logging.h.

ACKs for top commit:
  maflcko:
    review ACK 02b2c41103 📅
  sedited:
    Re-ACK 02b2c41103
  l0rinc:
    untested ACK 02b2c41103
  ryanofsky:
    Code review ACK 02b2c41103435d8dbaa77a526e484066471b2b8c! Overall a lot of nice improvements here.

Tree-SHA512: 3bffdca91afbe5c45a522815fe82e6f4cfa96529a4a243b29aad21234650502d6cac780126b584ee3e7ec129d8fdd50670d8a05036cc5c36e586b8c4c3563970
2026-05-21 23:01:09 -04:00
Anthony Towns
611878b46f scripted-diff: logging: Drop LogAcceptCategory
-BEGIN VERIFY SCRIPT-
sed -i 's/LogAcceptCategory(\(.*\), [a-zA-Z:]*::Level::Debug)/util::log::ShouldDebugLog(\1)/g' $(git grep -l LogAcceptCategory -- '*.cpp')
sed -i 's/LogAcceptCategory(\(.*\), [a-zA-Z:]*::Level::Trace)/util::log::ShouldTraceLog(\1)/g' $(git grep -l LogAcceptCategory -- '*.cpp')
sed -i '/Return true if log accepts specified category/,/^$/d' src/logging.h
-END VERIFY SCRIPT-
2026-05-16 03:36:51 +10:00
MarcoFalke
fa2afba28b p2p: Release m_peer_mutex early in InitiateTxBroadcastToAll 2026-05-15 11:14:29 +02:00
merge-script
c89c8ddbb3 Merge bitcoin/bitcoin#33300: fuzz: compact block harness
c8d688f41c fuzz: send blocktxn messages in cmpctblock harness (Eugene Siegel)
d0333bfe99 fuzz: send compact blocks in cmpctblock harness (Eugene Siegel)
3c58efe2ac fuzz: mine blocks and send headers for them in cmpctblock harness (Eugene Siegel)
651622432d fuzz: create and send transactions in cmpctblock harness (Eugene Siegel)
8c9a3fd0e8 net, fuzz: move CMPCTBLOCK_VERSION to header, use in cmpctblock harness (Eugene Siegel)
6cd480f62f fuzz: initial compact block fuzz harness (Eugene Siegel)

Pull request description:

  Adds a fuzz harness for testing compact blocks, similar to `process_message(s)`. It can make (in)valid blocks, reconstruct blocks with in-mempool txns, mark peers as HB, and has high stability in AFL++ (~98-99%).

  Coverage is [here](https://crypt-iq.github.io/fuzz_coverage_reports/cmpctblock-aflpp-inputs-09182025/) (look in `src/blockencodings.cpp`, relevant compact block bits in `src/net_processing.cpp`).

ACKs for top commit:
  instagibbs:
    ACK c8d688f41c
  marcofleon:
    ACK c8d688f41c

Tree-SHA512: d904066921211b32eb75c9602908929f46ecd25482c518db36ed9f30e012b350b38a2df382f0faa21decd43bc2cabcc859193207da34be76010a32df1d9ac5c8
2026-05-04 22:28:54 +01:00
Vasil Dimov
aec4fa2de0 net: drop the only recursive usage of CConnman::m_nodes_mutex
The only recursive usage of `CConnman::m_nodes_mutex` is from
`PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs()` which uses
nested calls to `CConnman::ForNode()` to trim the size of
`lNodesAnnouncingHeaderAndIDs` to `<= 3`. This need not be nested, so
take it out.

Before:
```
fornode(newnode)
    if (size >= 3)
        fornode(front) handle removal of front
        pop front
    push back newnode
```

After:
```
fornode(newnode)
    push back newnode
if (size > 3)
    fornode(front) handle removal of front
    pop front
```

`lNodesAnnouncingHeaderAndIDs` is protected by `cs_main` which is locked
during the entire operation.
2026-04-30 13:21:46 +02:00
Eugene Siegel
8c9a3fd0e8 net, fuzz: move CMPCTBLOCK_VERSION to header, use in cmpctblock harness
The cmpctblock harness now adds peers and processes the sendcmpct message.
2026-04-27 11:23:33 -04:00
MarcoFalke
fa204100e1 streams: Remove confusing DataStream::in_avail()
The alias of the size() method is confusing, because:

* It claims to be part of the Bitcoin Core stream subset (streams
  interface), but this is not used by any other stream interface. Mostly
  the `write(std::span)` and `read(std::span)` define the stream
  interface.
* It casts the size_t to i32, but the only place that calls the function
  casts that back to size_t.
* Providing this alias for size() without a proper reason is confusing.

Fix all issues by removing it and using the size() method.
2026-04-24 12:46:03 +02:00
MarcoFalke
fa5ab0220e move-only: Extract ProcessPong() helper
This commit can be reviewed with the git options:
--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space
2026-04-24 12:44:51 +02:00
Ava Chow
0cbd220294 Merge bitcoin/bitcoin#34440: refactor: Change CChain methods to use references, add tests
7c75244ade Change pindexMostWork parameter of ActivateBestChainStep() to reference (optout)
c5eb283bca Change CChain::FindFork() to take ref (optout)
20b58e281a Change CChain::Next() to take reference (optout)
fe2d6e25e0 Change CChain::Contains() to take reference (optout)
db56bcd692 test: Add CChain::FindFork() tests (optout)
8333abdd91 test: Add CChain basic tests (optout)

Pull request description:

  Refactor `CChain` methods (`Contains()`, `Next()`, `FindFork()`) to use references instead of pointers, to minimize the risk of accidental `nullptr` dereference (memory access violation). Also add missing unit tests to the `CChain` class.

  The `CChain::Contains()` method (in `src/chain.h`) dereferences its input without checking. The `Next()` method also calls into this with a `nullptr` if invoked with `nullptr`. While most call sites have indirect guarantee that the input is not `nullptr`, it's not easy to establish this to all call sites with high confidence. These methods are publicly available. There is no known high-level use case to trigger this error, but the fix is easy, and makes the code safer.

  Changes:

  - Add basic unit tests for `CChain` class methods
  - Add unit tests for `CChain::FindFork()`
  - Change `CChain::Contains()` to take reference
  - Change `CChain::Next()` to take reference
  - Change `CChain::FindFork()` to take reference
  - Change `pindexMostWork` parameter of `ActivateBestChainStep()` to reference
  - Rename changed parameters (`* pindex` --> `& index`)

  Alternative. A simpler change is to stick with pointers, with extra checks where needed, see #34416 .

  This change is remotely related to and indirectly triggered by #32875 .

  Further ideas, not considered in this PR:

  - Change `InvalidateBlock()` and `PreciousBlock()` to take references.
  - Change `CChain` internals to store references instead of pointers
  - Change CChain to always have at least one element (genesis), that way there is always genesis and tip.
  - Check related methods to return reference (guaranteed non-null) -- `FindFork`, `FindEarliestAtLeast`, `FindForkInGlobalIndex`, `blockman.AddToBlockIndex`, etc.

ACKs for top commit:
  l0rinc:
    reACK 7c75244ade
  maflcko:
    re-review ACK 7c75244ade 🌅
  achow101:
    ACK 7c75244ade
  hodlinator:
    re-ACK 7c75244ade

Tree-SHA512: 122f40120058f7e1f0273b3afed9c54966c05f06b6f2fee45bc48430617f24a5e4320a9bb7bb0ac986f2accfa22fabae5cc941b949758ddca2e9fcd472b46c33
2026-04-22 15:49:51 -07:00
merge-script
1a4371cc3d Merge bitcoin/bitcoin#34882: refactor: Use NodeClock::time_point in more places
fa1015bbcb refactor: Use NodeClock::time_point for m_connected (MarcoFalke)
fa244b984c refactor: Use NodeClock::time_point for m_last_send/recv and m_ping_start (MarcoFalke)
fa2605b204 refactor: Use NodeClock::time_point for CNetMessage::m_time (MarcoFalke)
fa644e625b refactor: Use NodeClock::duration for m_last_ping_time/m_min_ping_time/m_ping_wait (MarcoFalke)
333316f6be doc: Fix typo "eviction criterium" -> "eviction criterion" (MarcoFalke)
fa54fb0129 refactor: gui: Accept up to nanoseconds in formatDurationStr, but clarify they are ignored (MarcoFalke)
fab88884b7 refactor: Avoid manual chrono casts with * or / (MarcoFalke)
facfce37f6 util: Add NodeClock::epoch alias (MarcoFalke)
fa41e072b3 refactor: Use NodeClock alias over deprecated GetTime (MarcoFalke)

Pull request description:

  It is a bit confusing to have some code use the deprecated `GetTime`, which returns a duration and not a time point, and other code to use `NodeClock` time points.

  Fix a few more places to properly use time_point types.

ACKs for top commit:
  stickies-v:
    re-ACK fa1015bbcb
  seduless:
    re-ACK fa1015bbcb
  naiyoma:
    ACK fa1015bbcb
  sedited:
    ACK fa1015bbcb

Tree-SHA512: 7c8df1a9025271b08a40fd0d176bcbbf90920bc4d83a6e1c8cfaad2a894632af2b9a1aca5c3c9ddc3803e559dd168244121fd188ef22f399d60075ff194a9140
2026-04-21 22:46:25 +02:00
optout
20b58e281a Change CChain::Next() to take reference
To minimize chance of erroneous nullptr dereference, `CChain::Next()`
is changed to take a reference instead of a pointer.
Call sites have been adapted. Notably, NextSyncBlock() now checks
the FindFork() result before calling into Next(), because
the fork lookup may return null.
2026-04-20 08:55:44 +02:00
optout
fe2d6e25e0 Change CChain::Contains() to take reference
The `CChain::Contains()` method dereferences its input without checking,
potentially resulting in nullptr-dereference if invoked with `nullptr`.
To avoid this possibility, its input is changed to a reference instead.
Call sites are adapted accoringly, extra nullptr-check is added as
needed.
2026-04-20 08:55:26 +02:00
MarcoFalke
fa1015bbcb refactor: Use NodeClock::time_point for m_connected
Also, increase the precision to the native one, over prescribing second
precision.
2026-04-13 09:49:13 +02:00
Vasil Dimov
1ed1a12402 net_processing: don't modify addrman for private broadcast connections
It is best if the internal addrman database is not modified with
information coming from private broadcast connections because that
information can potentially later be sent via other connections.

Co-authored-by: Greg Sanders <gsanders87@gmail.com>
Co-authored-by: Lőrinc <pap.lorinc@gmail.com>
2026-04-09 14:56:33 +02:00
Ava Chow
a7c30da1f6 Merge bitcoin/bitcoin#34873: net: fix premature stale flagging of unpicked private broadcast txs
325afe664d net: delay stale evaluation and expose time_added in private broadcast (Mccalabrese)
999d18ab1c net: introduce TxSendStatus internal state container (Mccalabrese)

Pull request description:

  **Motivation**
  Currently, freshly added transactions in `private_broadcast` are almost immediately flagged and logged as stale by the `resend-stale` job.

  **The Bug**
  `m_transactions` maps a transaction to a `std::vector<SendStatus>`. When `try_emplace` adds a new transaction, this vector is empty. When `GetStale()` runs, `DerivePriority()` evaluates the empty vector and returns a default `Priority` struct where `last_confirmed` evaluates to the Unix Epoch (Jan 1, 1970). The stale checker sees a 50-year-old timestamp and flags it on the next resend-stale cycle.

  **The Fix**
  Rather than modifying the transient `Priority` struct or creating a "Zombie Transaction" edge case by ignoring transactions with 0 picks, this PR modifies the state container:
  * Wraps the `SendStatus` vector in a new `TxSendStatus` struct inside `private_broadcast.h`.
  * `TxSendStatus` automatically captures `time_added` upon emplace.
  * `GetStale()` now checks `p.num_confirmed == 0` to measure age against `time_added` using a new 5-minute `INITIAL_STALE_DURATION` grace period, falling back to `last_confirmed` and the standard 1-minute `STALE_DURATION` once network interaction begins.

  **Additional Polish**
  * Exposed `time_added` via the `getprivatebroadcastinfo`  RPC endpoint so users can see when a transaction entered the queue.
  * Added a dedicated `stale_unpicked_tx` test case and updated `private_broadcast_tests.cpp` to properly mock the passage of time for the new grace period.
  Closes #34862

ACKs for top commit:
  achow101:
    ACK 325afe664d
  andrewtoth:
    ACK 325afe664d
  vasild:
    ACK 325afe664d

Tree-SHA512: b7790aa5468f7c161ed93e99e9a6d8b4db39ff7d6d6a920764afd18825e08d83bc30b3fb0debeb6175730b5d2496c6be67f3be8674be93f4d07b1e77d17b4a14
2026-04-03 17:46:32 -07:00