mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-05-16 17:04:05 +02:00
Merge bitcoin/bitcoin#34873: net: fix premature stale flagging of unpicked private broadcast txs
325afe664dnet: delay stale evaluation and expose time_added in private broadcast (Mccalabrese)999d18ab1cnet: 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: ACK325afe664dandrewtoth: ACK325afe664dvasild: ACK325afe664dTree-SHA512: b7790aa5468f7c161ed93e99e9a6d8b4db39ff7d6d6a920764afd18825e08d83bc30b3fb0debeb6175730b5d2496c6be67f3be8674be93f4d07b1e77d17b4a14
This commit is contained in:
@@ -90,6 +90,13 @@ BOOST_AUTO_TEST_CASE(basic)
|
||||
BOOST_CHECK(!pb.DidNodeConfirmReception(recipient2));
|
||||
BOOST_CHECK(!pb.DidNodeConfirmReception(nonexistent_recipient));
|
||||
|
||||
// 1. Freshly added transactions should NOT be stale yet.
|
||||
BOOST_CHECK_EQUAL(pb.GetStale().size(), 0);
|
||||
|
||||
// 2. Fast-forward the mock clock past the INITIAL_STALE_DURATION.
|
||||
SetMockTime(Now<NodeSeconds>() + PrivateBroadcast::INITIAL_STALE_DURATION + 1min);
|
||||
|
||||
// 3. Now that the initial duration has passed, both unconfirmed transactions should be stale.
|
||||
BOOST_CHECK_EQUAL(pb.GetStale().size(), 2);
|
||||
|
||||
// Confirm reception by recipient1.
|
||||
@@ -102,20 +109,21 @@ BOOST_AUTO_TEST_CASE(basic)
|
||||
const auto infos{pb.GetBroadcastInfo()};
|
||||
BOOST_CHECK_EQUAL(infos.size(), 2);
|
||||
{
|
||||
const auto& [tx, peers]{find_tx_info(infos, tx_for_recipient1)};
|
||||
const auto& peers{find_tx_info(infos, tx_for_recipient1).peers};
|
||||
BOOST_CHECK_EQUAL(peers.size(), 1);
|
||||
BOOST_CHECK_EQUAL(peers[0].address.ToStringAddrPort(), addr1.ToStringAddrPort());
|
||||
BOOST_CHECK(peers[0].received.has_value());
|
||||
}
|
||||
{
|
||||
const auto& [tx, peers]{find_tx_info(infos, tx_for_recipient2)};
|
||||
const auto& peers{find_tx_info(infos, tx_for_recipient2).peers};
|
||||
BOOST_CHECK_EQUAL(peers.size(), 1);
|
||||
BOOST_CHECK_EQUAL(peers[0].address.ToStringAddrPort(), addr2.ToStringAddrPort());
|
||||
BOOST_CHECK(!peers[0].received.has_value());
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(pb.GetStale().size(), 1);
|
||||
BOOST_CHECK_EQUAL(pb.GetStale()[0], tx_for_recipient2);
|
||||
const auto stale_state{pb.GetStale()};
|
||||
BOOST_CHECK_EQUAL(stale_state.size(), 1);
|
||||
BOOST_CHECK_EQUAL(stale_state[0], tx_for_recipient2);
|
||||
|
||||
SetMockTime(Now<NodeSeconds>() + 10h);
|
||||
|
||||
@@ -131,4 +139,22 @@ BOOST_AUTO_TEST_CASE(basic)
|
||||
BOOST_CHECK(!pb.PickTxForSend(/*will_send_to_nodeid=*/nonexistent_recipient, /*will_send_to_address=*/addr_nonexistent).has_value());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(stale_unpicked_tx)
|
||||
{
|
||||
SetMockTime(Now<NodeSeconds>());
|
||||
|
||||
PrivateBroadcast pb;
|
||||
const auto tx{MakeDummyTx(/*id=*/42, /*num_witness=*/0)};
|
||||
BOOST_REQUIRE(pb.Add(tx));
|
||||
|
||||
// Unpicked transactions use the longer INITIAL_STALE_DURATION.
|
||||
BOOST_CHECK_EQUAL(pb.GetStale().size(), 0);
|
||||
SetMockTime(Now<NodeSeconds>() + PrivateBroadcast::INITIAL_STALE_DURATION - 1min);
|
||||
BOOST_CHECK_EQUAL(pb.GetStale().size(), 0);
|
||||
SetMockTime(Now<NodeSeconds>() + 2min);
|
||||
const auto stale_state{pb.GetStale()};
|
||||
BOOST_REQUIRE_EQUAL(stale_state.size(), 1);
|
||||
BOOST_CHECK_EQUAL(stale_state[0], tx);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
Reference in New Issue
Block a user