Skip to content

Commit 826fe9c

Browse files
author
MarcoFalke
committed
Merge #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 #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. #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 👁 gzhao408: ACK [`9e1cb1a`](bitcoin/bitcoin@9e1cb1a) Tree-SHA512: 0cd51c4ca368b9dce92d50d73ec6e9df278a259e609eef2858f24cb8595ad07acc3db781d9eb0c351715f18fca5a2b4526838981fdb34a522427e9dc868bdaa6
2 parents e478b11 + 9e1cb1a commit 826fe9c

File tree

9 files changed

+68
-22
lines changed

9 files changed

+68
-22
lines changed

doc/release-notes.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,28 @@ Notable changes
6565
P2P and network changes
6666
-----------------------
6767

68+
- The mempool now tracks whether transactions submitted via the wallet or RPCs
69+
have been successfully broadcast. Every 10-15 minutes, the node will try to
70+
announce unbroadcast transactions until a peer requests it via a `getdata`
71+
message or the transaction is removed from the mempool for other reasons.
72+
The node will not track the broadcast status of transactions submitted to the
73+
node using P2P relay. This version reduces the initial broadcast guarantees
74+
for wallet transactions submitted via P2P to a node running the wallet. (#18038)
75+
6876
Updated RPCs
6977
------------
7078

79+
- `getmempoolinfo` now returns an additional `unbroadcastcount` field. The
80+
mempool tracks locally submitted transactions until their initial broadcast
81+
is acknowledged by a peer. This field returns the count of transactions
82+
waiting for acknowledgement.
83+
84+
- Mempool RPCs such as `getmempoolentry` and `getrawmempool` with
85+
`verbose=true` now return an additional `unbroadcast` field. This indicates
86+
whether initial broadcast of the transaction has been acknowledged by a
87+
peer. `getmempoolancestors` and `getmempooldescendants` are also updated.
88+
89+
7190
Changes to Wallet or GUI related RPCs can be found in the GUI or Wallet section below.
7291

7392
New RPCs
@@ -87,6 +106,13 @@ New settings
87106
Wallet
88107
------
89108

109+
- To improve wallet privacy, the frequency of wallet rebroadcast attempts is
110+
reduced from approximately once every 15 minutes to once every 12-36 hours.
111+
To maintain a similar level of guarantee for initial broadcast of wallet
112+
transactions, the mempool tracks these transactions as a part of the newly
113+
introduced unbroadcast set. See the "P2P and network changes" section for
114+
more information on the unbroadcast set. (#18038)
115+
90116
#### Wallet RPC changes
91117

92118
- The `upgradewallet` RPC replaces the `-upgradewallet` command line option.

src/net_processing.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,8 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
827827
}
828828
}
829829

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

src/rpc/blockchain.cpp

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

420420
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
@@ -5067,12 +5067,18 @@ bool LoadMempool(CTxMemPool& pool)
50675067
pool.PrioritiseTransaction(i.first, i.second);
50685068
}
50695069

5070-
std::set<uint256> unbroadcast_txids;
5071-
file >> unbroadcast_txids;
5072-
unbroadcast = unbroadcast_txids.size();
5070+
// TODO: remove this try except in v0.22
5071+
try {
5072+
std::set<uint256> unbroadcast_txids;
5073+
file >> unbroadcast_txids;
5074+
unbroadcast = unbroadcast_txids.size();
50735075

5074-
for (const auto& txid : unbroadcast_txids) {
5076+
for (const auto& txid : unbroadcast_txids) {
50755077
pool.AddUnbroadcastTx(txid);
5078+
}
5079+
} catch (const std::exception&) {
5080+
// mempool.dat files created prior to v0.21 will not have an
5081+
// unbroadcast set. No need to log a failure if parsing fails here.
50765082
}
50775083

50785084
} 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
@@ -655,6 +655,8 @@ def on_inv(self, message):
655655
# save txid
656656
self.tx_invs_received[i.hash] += 1
657657

658+
super().on_inv(message)
659+
658660
def get_invs(self):
659661
with mininode_lock:
660662
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)