Skip to content

Fix two issues in p2p_private_broadcast.py#34646

Merged
achow101 merged 2 commits intobitcoin:masterfrom
vasild:test_pb_aborttx_at_end
Feb 23, 2026
Merged

Fix two issues in p2p_private_broadcast.py#34646
achow101 merged 2 commits intobitcoin:masterfrom
vasild:test_pb_aborttx_at_end

Conversation

@vasild
Copy link
Contributor

@vasild vasild commented Feb 21, 2026

test: move abortprivatebroadcast test at the end

The piece of p2p_private_broadcast.py which tests the correctness of
abortprivatebroadcast issues a new sendrawtransaction call. That
call schedules up to 3 new connections: peer=13, peer=14 and possibly
peer=15 before it gets aborted.

These up to 3 in-the-process-of-opening private broadcast connections
have CNode::m_connected set early - when the CNode object is
created. Later in the test the mock time is advanced by 20 minutes and
those "old" connections pick a transaction for rebroadcast but that
triggers PRIVATE_BROADCAST_MAX_CONNECTION_LIFETIME immediately:

2026-02-21T13:28:14.209766Z [privbcast] [net.cpp:4006] [CNode] [net] Added connection peer=20
2026-02-21T13:28:14.309792Z (mocktime: 2026-02-21T13:48:14Z) [msghand] [net.cpp:4074] [PushMessage] [net] sending inv (37 bytes) peer=20
2026-02-21T13:28:14.309801Z (mocktime: 2026-02-21T13:48:14Z) [msghand] [net_processing.cpp:5745] [SendMessages] [privatebroadcast] Disconnecting: did not complete the transaction send within 180 seconds, peer=20

This prematurely stops the private broadcast connection and results in
a failure like:

AssertionError: ... not({} == {'ping': 1, 'tx': 1})

test: don't always assert NUM_PRIVATE_BROADCAST_PER_TX broadcasts

In p2p_private_broadcast.py in the function check_broadcasts() we
should assert that the broadcast was done to broadcasts_to_expect
peers, not to NUM_PRIVATE_BROADCAST_PER_TX. This is because in the
"Basic" test we check the first broadcast manually because it is done to
nodes[1] and then check the other two by
check_broadcasts(..., NUM_PRIVATE_BROADCAST_PER_TX - 1, ...).
The first broadcast might not have fully concluded by the time we call
check_broadcasts() to check the remaining 2.

Demanding always NUM_PRIVATE_BROADCAST_PER_TX can lead to:

Traceback (most recent call last):
  File "/home/vd/gh/bitcoin/bitcoin/test/functional/test_framework/test_framework.py", line 142, in main
    self.run_test()
    ~~~~~~~~~~~~~^^
  File "/tmp/build/clang22/test/functional/p2p_private_broadcast.py", line 347, in run_test
    self.check_broadcasts("Basic", txs[0], NUM_PRIVATE_BROADCAST_PER_TX - 1, NUM_INITIAL_CONNECTIONS + 1)
    ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/build/clang22/test/functional/p2p_private_broadcast.py", line 313, in check_broadcasts
    assert_greater_than_or_equal(sum(1 for p in peers if "received" in p), NUM_PRIVATE_BROADCAST_PER_TX)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vd/gh/bitcoin/bitcoin/test/functional/test_framework/util.py", line 94, in assert_greater_than_or_equal
    raise AssertionError("%s < %s" % (str(thing1), str(thing2)))
AssertionError: 2 < 3

The piece of `p2p_private_broadcast.py` which tests the correctness of
`abortprivatebroadcast` issues a new `sendrawtransaction` call. That
call schedules up to 3 new connections: peer=13, peer=14 and possibly
peer=15 before it gets aborted.

These up to 3 in-the-process-of-opening private broadcast connections
have `CNode::m_connected` set early - when the `CNode` object is
created. Later in the test the mock time is advanced by 20 minutes and
those "old" connections pick a transaction for rebroadcast but that
triggers `PRIVATE_BROADCAST_MAX_CONNECTION_LIFETIME` immediately:

```
2026-02-21T13:28:14.209766Z [privbcast] [net.cpp:4006] [CNode] [net] Added connection peer=20
2026-02-21T13:28:14.309792Z (mocktime: 2026-02-21T13:48:14Z) [msghand] [net.cpp:4074] [PushMessage] [net] sending inv (37 bytes) peer=20
2026-02-21T13:28:14.309801Z (mocktime: 2026-02-21T13:48:14Z) [msghand] [net_processing.cpp:5745] [SendMessages] [privatebroadcast] Disconnecting: did not complete the transaction send within 180 seconds, peer=20
```

This prematurely stops the private broadcast connection and results in
a failure like:

```
AssertionError: ... not({} == {'ping': 1, 'tx': 1})
```
In `p2p_private_broadcast.py` in the function `check_broadcasts()` we
should assert that the broadcast was done to `broadcasts_to_expect`
peers, not to `NUM_PRIVATE_BROADCAST_PER_TX`. This is because in the
"Basic" test we check the first broadcast manually because it is done to
`nodes[1]` and then check the other two by
`check_broadcasts(..., NUM_PRIVATE_BROADCAST_PER_TX - 1, ...)`.
The first broadcast might not have fully concluded by the time we call
`check_broadcasts()` to check the remaining 2.

Demanding always `NUM_PRIVATE_BROADCAST_PER_TX` can lead to:

```
Traceback (most recent call last):
  File "/home/vd/gh/bitcoin/bitcoin/test/functional/test_framework/test_framework.py", line 142, in main
    self.run_test()
    ~~~~~~~~~~~~~^^
  File "/tmp/build/clang22/test/functional/p2p_private_broadcast.py", line 347, in run_test
    self.check_broadcasts("Basic", txs[0], NUM_PRIVATE_BROADCAST_PER_TX - 1, NUM_INITIAL_CONNECTIONS + 1)
    ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/build/clang22/test/functional/p2p_private_broadcast.py", line 313, in check_broadcasts
    assert_greater_than_or_equal(sum(1 for p in peers if "received" in p), NUM_PRIVATE_BROADCAST_PER_TX)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vd/gh/bitcoin/bitcoin/test/functional/test_framework/util.py", line 94, in assert_greater_than_or_equal
    raise AssertionError("%s < %s" % (str(thing1), str(thing2)))
AssertionError: 2 < 3
```
@DrahtBot
Copy link
Contributor

DrahtBot commented Feb 21, 2026

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK andrewtoth, l0rinc, achow101

If your review is incorrectly listed, please copy-paste <!--meta-tag:bot-skip--> into the comment that the bot should ignore.

Copy link
Contributor

@andrewtoth andrewtoth left a comment

Choose a reason for hiding this comment

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

ACK c462e54

@l0rinc
Copy link
Contributor

l0rinc commented Feb 23, 2026

ACK c462e54

This PR is meant to fix private broadcast test flakiness (affecting most new PRs) by removing two races:

  • in-progress private-broadcast connections created during an abort test polluting the following tests;
  • an overly broad assertion when the test is only expecting a subset to have completed.

The first commit is move-only; the second narrows the assertion without weakening peer-selection coverage.
I don't think this move is just papering over a real failure, seem like a mocktime artifact.
Rebased locally, all tests are passing.


Note: the failure is very likely not caused by the PR.

@achow101
Copy link
Member

ACK c462e54

@achow101 achow101 merged commit ab8a7af into bitcoin:master Feb 23, 2026
25 of 26 checks passed
@vasild vasild deleted the test_pb_aborttx_at_end branch February 24, 2026 05:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants