Skip to content

Commit 1cf6ab1

Browse files
MarcoFalkeComputerCraftr
authored andcommitted
Merge bitcoin#18807: [doc / test / mempool] unbroadcast follow-ups
9e1cb1a [trivial/doc] Fix comment type (Amiti Uttarwar) 8f30260 [doc] Update unbroadcast description in RPC results (Amiti Uttarwar) 750456d [trivial] Remove misleading 'const' (Amiti Uttarwar) fa32e67 [test] Manage node connections better in mempool persist test (Amiti Uttarwar) 1f94bb0 [doc] Provide rationale for randomization in scheduling. (Amiti Uttarwar) 9c8a55d [mempool] Don't throw expected error message when upgrading (Amiti Uttarwar) ba54983 [test] Test that wallet transactions aren't rebroadcast before 12 hours (Amiti Uttarwar) 00d44a5 [test] P2P connection behavior should meet expectations (Amiti Uttarwar) bd093ca [test] updates to unbroadcast test (Amiti Uttarwar) dab298d [docs] add release notes (Amiti Uttarwar) Pull request description: This PR is a follow up to bitcoin#18038 which introduced the idea of an unbroadcast set & focuses mostly on documentation updates and test fixes. One small functionality update to not throw an expected error in `LoadMempool` when you upgrade software versions. bitcoin#18895 is another follow up to that addresses other functionality updates. Background context: The unbroadcast set is a mechanism for the mempool to track locally submitted transactions (via wallet or RPC). The node does a best-effort of delivering the transactions to the network via retries every 10-15 minutes until either a `GETDATA` is received or the transaction is removed from the mempool. ACKs for top commit: MarcoFalke: ACK 9e1cb1a eye gzhao408: ACK [`9e1cb1a`](9e1cb1a) Tree-SHA512: 0cd51c4ca368b9dce92d50d73ec6e9df278a259e609eef2858f24cb8595ad07acc3db781d9eb0c351715f18fca5a2b4526838981fdb34a522427e9dc868bdaa6
1 parent 4abc921 commit 1cf6ab1

File tree

8 files changed

+42
-22
lines changed

8 files changed

+42
-22
lines changed

src/net_processing.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,8 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
792792
}
793793
}
794794

795-
// schedule next run for 10-15 minutes in the future
795+
// Schedule next run for 10-15 minutes in the future.
796+
// We add randomness on every cycle to avoid the possibility of P2P fingerprinting.
796797
const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5});
797798
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
798799
}

src/rpc/blockchain.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ static std::vector<RPCResult> MempoolEntryDescription() { return {
399399
RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
400400
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
401401
RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"},
402-
RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet confirmed)"},
402+
RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
403403
};}
404404

405405
static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)

src/txmempool.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ class CTxMemPool
704704
/** Adds a transaction to the unbroadcast set */
705705
void AddUnbroadcastTx(const uint256& txid) {
706706
LOCK(cs);
707-
/** Sanity Check: the transaction should also be in the mempool */
707+
// Sanity Check: the transaction should also be in the mempool
708708
if (exists(txid)) {
709709
m_unbroadcast_txids.insert(txid);
710710
}
@@ -714,12 +714,12 @@ class CTxMemPool
714714
void RemoveUnbroadcastTx(const uint256& txid, const bool unchecked = false);
715715

716716
/** Returns transactions in unbroadcast set */
717-
const std::set<uint256> GetUnbroadcastTxs() const {
717+
std::set<uint256> GetUnbroadcastTxs() const {
718718
LOCK(cs);
719719
return m_unbroadcast_txids;
720720
}
721721

722-
// Returns if a txid is in the unbroadcast set
722+
/** Returns whether a txid is in the unbroadcast set */
723723
bool IsUnbroadcastTx(const uint256& txid) const {
724724
LOCK(cs);
725725
return (m_unbroadcast_txids.count(txid) != 0);

src/validation.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5032,12 +5032,18 @@ bool LoadMempool(CTxMemPool& pool)
50325032
pool.PrioritiseTransaction(i.first, i.second);
50335033
}
50345034

5035-
std::set<uint256> unbroadcast_txids;
5036-
file >> unbroadcast_txids;
5037-
unbroadcast = unbroadcast_txids.size();
5035+
// TODO: remove this try except in v0.22
5036+
try {
5037+
std::set<uint256> unbroadcast_txids;
5038+
file >> unbroadcast_txids;
5039+
unbroadcast = unbroadcast_txids.size();
50385040

5039-
for (const auto& txid : unbroadcast_txids) {
5041+
for (const auto& txid : unbroadcast_txids) {
50405042
pool.AddUnbroadcastTx(txid);
5043+
}
5044+
} catch (const std::exception&) {
5045+
// mempool.dat files created prior to v0.21 will not have an
5046+
// unbroadcast set. No need to log a failure if parsing fails here.
50415047
}
50425048

50435049
} catch (const std::exception& e) {

test/functional/mempool_persist.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ def run_test(self):
8484
assert_greater_than_or_equal(tx_creation_time_higher, tx_creation_time)
8585

8686
# disconnect nodes & make a txn that remains in the unbroadcast set.
87-
disconnect_nodes(self.nodes[0], 2)
87+
disconnect_nodes(self.nodes[0], 1)
88+
assert(len(self.nodes[0].getpeerinfo()) == 0)
89+
assert(len(self.nodes[0].p2ps) == 0)
8890
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("12"))
8991
connect_nodes(self.nodes[0], 2)
9092

@@ -157,8 +159,10 @@ def test_persist_unbroadcast(self):
157159
# clear out mempool
158160
node0.generate(1)
159161

160-
# disconnect nodes to make a txn that remains in the unbroadcast set.
161-
disconnect_nodes(node0, 1)
162+
# ensure node0 doesn't have any connections
163+
# make a transaction that will remain in the unbroadcast set
164+
assert(len(node0.getpeerinfo()) == 0)
165+
assert(len(node0.p2ps) == 0)
162166
node0.sendtoaddress(self.nodes[1].getnewaddress(), Decimal("12"))
163167

164168
# shutdown, then startup with wallet disabled

test/functional/mempool_unbroadcast.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
disconnect_nodes,
1717
)
1818

19+
MAX_INITIAL_BROADCAST_DELAY = 15 * 60 # 15 minutes in seconds
1920

2021
class MempoolUnbroadcastTest(BitcoinTestFramework):
2122
def set_test_params(self):
@@ -72,7 +73,7 @@ def test_broadcast(self):
7273
connect_nodes(node, 1)
7374

7475
# fast forward into the future & ensure that the second node has the txns
75-
node.mockscheduler(15 * 60) # 15 min in seconds
76+
node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY)
7677
self.sync_mempools(timeout=30)
7778
mempool = self.nodes[1].getrawmempool()
7879
assert rpc_tx_hsh in mempool
@@ -86,15 +87,16 @@ def test_broadcast(self):
8687
self.log.info("Add another connection & ensure transactions aren't broadcast again")
8788

8889
conn = node.add_p2p_connection(P2PTxInvStore())
89-
node.mockscheduler(15 * 60)
90-
time.sleep(5)
90+
node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY)
91+
time.sleep(2) # allow sufficient time for possibility of broadcast
9192
assert_equal(len(conn.get_invs()), 0)
9293

94+
disconnect_nodes(node, 1)
95+
node.disconnect_p2ps()
96+
9397
def test_txn_removal(self):
9498
self.log.info("Test that transactions removed from mempool are removed from unbroadcast set")
9599
node = self.nodes[0]
96-
disconnect_nodes(node, 1)
97-
node.disconnect_p2ps
98100

99101
# since the node doesn't have any connections, it will not receive
100102
# any GETDATAs & thus the transaction will remain in the unbroadcast set.

test/functional/test_framework/mininode.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,8 @@ def on_inv(self, message):
651651
# save txid
652652
self.tx_invs_received[i.hash] += 1
653653

654+
super().on_inv(message)
655+
654656
def get_invs(self):
655657
with mininode_lock:
656658
return list(self.tx_invs_received.keys())

test/functional/wallet_resendwallettransactions.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,21 @@ def run_test(self):
4949
block.solve()
5050
node.submitblock(ToHex(block))
5151

52-
# Transaction should not be rebroadcast
5352
node.syncwithvalidationinterfacequeue()
54-
node.p2ps[1].sync_with_ping()
55-
assert_equal(node.p2ps[1].tx_invs_received[txid], 0)
53+
now = int(time.time())
54+
55+
# Transaction should not be rebroadcast within first 12 hours
56+
# Leave 2 mins for buffer
57+
twelve_hrs = 12 * 60 * 60
58+
two_min = 2 * 60
59+
node.setmocktime(now + twelve_hrs - two_min)
60+
time.sleep(2) # ensure enough time has passed for rebroadcast attempt to occur
61+
assert_equal(txid in node.p2ps[1].get_invs(), False)
5662

5763
self.log.info("Bump time & check that transaction is rebroadcast")
5864
# Transaction should be rebroadcast approximately 24 hours in the future,
5965
# but can range from 12-36. So bump 36 hours to be sure.
60-
rebroadcast_time = int(time.time()) + 36 * 60 * 60
61-
node.setmocktime(rebroadcast_time)
66+
node.setmocktime(now + 36 * 60 * 60)
6267
wait_until(lambda: node.p2ps[1].tx_invs_received[txid] >= 1, lock=mininode_lock)
6368

6469

0 commit comments

Comments
 (0)