Skip to content

Commit 93de9ab

Browse files
committed
Merge #15632: Remove ResendWalletTransactions from the Validation Interface
833d98a [wallet] Remove unnecessary Chain::Lock parameter from ResendWalletTransactions (John Newbery) 52b760f [wallet] Schedule tx rebroadcasts in wallet (John Newbery) f463cd1 [wallet] Keep track of the best block time in the wallet (John Newbery) Pull request description: Remove the `Broadcast()`/`ResendWalletTransactions()` notification from the Validation interface. Closes #15619. See that issue for discussion. ACKs for commit 833d98: ryanofsky: utACK 833d98a. No changes, just rebase. Tree-SHA512: 7689f2083608ebad8c95ab6692f7842754e1ebe5508bc926a89cad7105cce41007648f37341ba5feb92b30a7aa87acd3abf264a4f1874e35a7161553f6ff3595
2 parents 23712f8 + 833d98a commit 93de9ab

File tree

9 files changed

+56
-41
lines changed

9 files changed

+56
-41
lines changed

src/interfaces/chain.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,11 @@ class NotificationsHandlerImpl : public Handler, CValidationInterface
202202
{
203203
m_notifications->BlockDisconnected(*block);
204204
}
205-
void ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->ChainStateFlushed(locator); }
206-
void ResendWalletTransactions(int64_t best_block_time, CConnman*) override
205+
void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override
207206
{
208-
// `cs_main` is always held when this method is called, so it is safe to
209-
// call `assumeLocked`. This is awkward, and the `assumeLocked` method
210-
// should be able to be removed entirely if `ResendWalletTransactions`
211-
// is replaced by a wallet timer as suggested in
212-
// https://github.com/bitcoin/bitcoin/issues/15619
213-
auto locked_chain = m_chain.assumeLocked();
214-
m_notifications->ResendWalletTransactions(*locked_chain, best_block_time);
207+
m_notifications->UpdatedBlockTip();
215208
}
209+
void ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->ChainStateFlushed(locator); }
216210
Chain& m_chain;
217211
Chain::Notifications* m_notifications;
218212
};
@@ -347,6 +341,7 @@ class ChainImpl : public Chain
347341
CAmount maxTxFee() override { return ::maxTxFee; }
348342
bool getPruneMode() override { return ::fPruneMode; }
349343
bool p2pEnabled() override { return g_connman != nullptr; }
344+
bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !IsInitialBlockDownload(); }
350345
bool isInitialBlockDownload() override { return IsInitialBlockDownload(); }
351346
bool shutdownRequested() override { return ShutdownRequested(); }
352347
int64_t getAdjustedTime() override { return GetAdjustedTime(); }

src/interfaces/chain.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ class Chain
223223
//! Check if p2p enabled.
224224
virtual bool p2pEnabled() = 0;
225225

226+
//! Check if the node is ready to broadcast transactions.
227+
virtual bool isReadyToBroadcast() = 0;
228+
226229
//! Check if in IBD.
227230
virtual bool isInitialBlockDownload() = 0;
228231

@@ -256,8 +259,8 @@ class Chain
256259
virtual void TransactionRemovedFromMempool(const CTransactionRef& ptx) {}
257260
virtual void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& tx_conflicted) {}
258261
virtual void BlockDisconnected(const CBlock& block) {}
262+
virtual void UpdatedBlockTip() {}
259263
virtual void ChainStateFlushed(const CBlockLocator& locator) {}
260-
virtual void ResendWalletTransactions(Lock& locked_chain, int64_t best_block_time) {}
261264
};
262265

263266
//! Register handler for notifications.

src/net_processing.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,6 @@ namespace {
175175
/** Expiration-time ordered list of (expire time, relay map entry) pairs. */
176176
std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration GUARDED_BY(cs_main);
177177

178-
std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
179-
180178
struct IteratorComparator
181179
{
182180
template<typename I>
@@ -1121,8 +1119,6 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
11211119
});
11221120
connman->WakeMessageHandler();
11231121
}
1124-
1125-
nTimeBestReceived = GetTime();
11261122
}
11271123

11281124
/**
@@ -3550,14 +3546,6 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
35503546
}
35513547
}
35523548

3553-
// Resend wallet transactions that haven't gotten in a block yet
3554-
// Except during reindex, importing and IBD, when old wallet
3555-
// transactions become unconfirmed and spams other nodes.
3556-
if (!fReindex && !fImporting && !IsInitialBlockDownload())
3557-
{
3558-
GetMainSignals().Broadcast(nTimeBestReceived, connman);
3559-
}
3560-
35613549
//
35623550
// Try sending block announcements via headers
35633551
//

src/validationinterface.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ struct ValidationInterfaceConnections {
2525
boost::signals2::scoped_connection BlockDisconnected;
2626
boost::signals2::scoped_connection TransactionRemovedFromMempool;
2727
boost::signals2::scoped_connection ChainStateFlushed;
28-
boost::signals2::scoped_connection Broadcast;
2928
boost::signals2::scoped_connection BlockChecked;
3029
boost::signals2::scoped_connection NewPoWValidBlock;
3130
};
@@ -37,7 +36,6 @@ struct MainSignalsInstance {
3736
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
3837
boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
3938
boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
40-
boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
4139
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
4240
boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
4341

@@ -101,7 +99,6 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
10199
conns.BlockDisconnected = g_signals.m_internals->BlockDisconnected.connect(std::bind(&CValidationInterface::BlockDisconnected, pwalletIn, std::placeholders::_1));
102100
conns.TransactionRemovedFromMempool = g_signals.m_internals->TransactionRemovedFromMempool.connect(std::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, std::placeholders::_1));
103101
conns.ChainStateFlushed = g_signals.m_internals->ChainStateFlushed.connect(std::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, std::placeholders::_1));
104-
conns.Broadcast = g_signals.m_internals->Broadcast.connect(std::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, std::placeholders::_1, std::placeholders::_2));
105102
conns.BlockChecked = g_signals.m_internals->BlockChecked.connect(std::bind(&CValidationInterface::BlockChecked, pwalletIn, std::placeholders::_1, std::placeholders::_2));
106103
conns.NewPoWValidBlock = g_signals.m_internals->NewPoWValidBlock.connect(std::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, std::placeholders::_1, std::placeholders::_2));
107104
}
@@ -175,10 +172,6 @@ void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
175172
});
176173
}
177174

178-
void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) {
179-
m_internals->Broadcast(nBestBlockTime, connman);
180-
}
181-
182175
void CMainSignals::BlockChecked(const CBlock& block, const CValidationState& state) {
183176
m_internals->BlockChecked(block, state);
184177
}

src/validationinterface.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,6 @@ class CValidationInterface {
134134
* Called on a background thread.
135135
*/
136136
virtual void ChainStateFlushed(const CBlockLocator &locator) {}
137-
/** Tells listeners to broadcast their data. */
138-
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
139137
/**
140138
* Notifies listeners of a block validation result.
141139
* If the provided CValidationState IsValid, the provided block
@@ -184,7 +182,6 @@ class CMainSignals {
184182
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &);
185183
void BlockDisconnected(const std::shared_ptr<const CBlock> &);
186184
void ChainStateFlushed(const CBlockLocator &);
187-
void Broadcast(int64_t nBestBlockTime, CConnman* connman);
188185
void BlockChecked(const CBlock&, const CValidationState&);
189186
void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&);
190187
};

src/wallet/init.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,9 @@ void StartWallets(CScheduler& scheduler)
211211
pwallet->postInitProcess();
212212
}
213213

214-
// Run a thread to flush wallet periodically
214+
// Schedule periodic wallet flushes and tx rebroadcasts
215215
scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
216+
scheduler.scheduleEvery(MaybeResendWalletTxs, 1000);
216217
}
217218

218219
void FlushWallets()

src/wallet/wallet.cpp

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,10 @@ void CWallet::BlockDisconnected(const CBlock& block) {
12761276
}
12771277
}
12781278

1279+
void CWallet::UpdatedBlockTip()
1280+
{
1281+
m_best_block_time = GetTime();
1282+
}
12791283

12801284

12811285
void CWallet::BlockUntilSyncedToCurrentChain() {
@@ -2110,8 +2114,21 @@ bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
21102114
return CTransaction(tx1) == CTransaction(tx2);
21112115
}
21122116

2113-
void CWallet::ResendWalletTransactions(interfaces::Chain::Lock& locked_chain, int64_t nBestBlockTime)
2117+
// Rebroadcast transactions from the wallet. We do this on a random timer
2118+
// to slightly obfuscate which transactions come from our wallet.
2119+
//
2120+
// Ideally, we'd only resend transactions that we think should have been
2121+
// mined in the most recent block. Any transaction that wasn't in the top
2122+
// blockweight of transactions in the mempool shouldn't have been mined,
2123+
// and so is probably just sitting in the mempool waiting to be confirmed.
2124+
// Rebroadcasting does nothing to speed up confirmation and only damages
2125+
// privacy.
2126+
void CWallet::ResendWalletTransactions()
21142127
{
2128+
// During reindex, importing and IBD, old wallet transactions become
2129+
// unconfirmed. Don't resend them as that would spam other nodes.
2130+
if (!chain().isReadyToBroadcast()) return;
2131+
21152132
// Do this infrequently and randomly to avoid giving away
21162133
// that these are our transactions.
21172134
if (GetTime() < nNextResend || !fBroadcastTransactions) return;
@@ -2120,23 +2137,24 @@ void CWallet::ResendWalletTransactions(interfaces::Chain::Lock& locked_chain, in
21202137
if (fFirst) return;
21212138

21222139
// Only do it if there's been a new block since last time
2123-
if (nBestBlockTime < nLastResend) return;
2140+
if (m_best_block_time < nLastResend) return;
21242141
nLastResend = GetTime();
21252142

21262143
int relayed_tx_count = 0;
21272144

2128-
{ // cs_wallet scope
2145+
{ // locked_chain and cs_wallet scope
2146+
auto locked_chain = chain().lock();
21292147
LOCK(cs_wallet);
21302148

21312149
// Relay transactions
21322150
for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
21332151
CWalletTx& wtx = item.second;
21342152
// only rebroadcast unconfirmed txes older than 5 minutes before the
21352153
// last block was found
2136-
if (wtx.nTimeReceived > nBestBlockTime - 5 * 60) continue;
2137-
relayed_tx_count += wtx.RelayWalletTransaction(locked_chain) ? 1 : 0;
2154+
if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
2155+
if (wtx.RelayWalletTransaction(*locked_chain)) ++relayed_tx_count;
21382156
}
2139-
} // cs_wallet
2157+
} // locked_chain and cs_wallet
21402158

21412159
if (relayed_tx_count > 0) {
21422160
WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed_tx_count);
@@ -2145,7 +2163,12 @@ void CWallet::ResendWalletTransactions(interfaces::Chain::Lock& locked_chain, in
21452163

21462164
/** @} */ // end of mapWallet
21472165

2148-
2166+
void MaybeResendWalletTxs()
2167+
{
2168+
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
2169+
pwallet->ResendWalletTransactions();
2170+
}
2171+
}
21492172

21502173

21512174
/** @defgroup Actions

src/wallet/wallet.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,8 @@ class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notific
657657
int64_t nNextResend = 0;
658658
int64_t nLastResend = 0;
659659
bool fBroadcastTransactions = false;
660+
// Local time that the tip block was received. Used to schedule wallet rebroadcasts.
661+
std::atomic<int64_t> m_best_block_time {0};
660662

661663
/**
662664
* Used to keep track of spent outpoints, and
@@ -926,6 +928,7 @@ class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notific
926928
void TransactionAddedToMempool(const CTransactionRef& tx) override;
927929
void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) override;
928930
void BlockDisconnected(const CBlock& block) override;
931+
void UpdatedBlockTip() override;
929932
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
930933

931934
struct ScanResult {
@@ -946,7 +949,7 @@ class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notific
946949
ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate);
947950
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
948951
void ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
949-
void ResendWalletTransactions(interfaces::Chain::Lock& locked_chain, int64_t nBestBlockTime) override;
952+
void ResendWalletTransactions();
950953
struct Balance {
951954
CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
952955
CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
@@ -1229,6 +1232,12 @@ class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notific
12291232
friend struct WalletTestingSetup;
12301233
};
12311234

1235+
/**
1236+
* Called periodically by the schedule thread. Prompts individual wallets to resend
1237+
* their transactions. Actual rebroadcast schedule is managed by the wallets themselves.
1238+
*/
1239+
void MaybeResendWalletTxs();
1240+
12321241
/** A key allocated from the key pool. */
12331242
class CReserveKey final : public CReserveScript
12341243
{

test/functional/wallet_resendwallettransactions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ def run_test(self):
3939
self.log.info("Create a new transaction and wait until it's broadcast")
4040
txid = int(node.sendtoaddress(node.getnewaddress(), 1), 16)
4141

42+
# Wallet rebroadcast is first scheduled 1 sec after startup (see
43+
# nNextResend in ResendWalletTransactions()). Sleep for just over a
44+
# second to be certain that it has been called before the first
45+
# setmocktime call below.
46+
time.sleep(1.1)
47+
4248
# Can take a few seconds due to transaction trickling
4349
wait_until(lambda: node.p2p.tx_invs_received[txid] >= 1, lock=mininode_lock)
4450

0 commit comments

Comments
 (0)