Skip to content

Commit 52b760f

Browse files
committed
[wallet] Schedule tx rebroadcasts in wallet
Removes the now-unused Broadcast/ResendWalletTransactions interface from validationinterface. The wallet_resendwallettransactions.py needs a sleep added at the start to make sure that the rebroadcast scheduler is warmed up before the next block is mined.
1 parent f463cd1 commit 52b760f

File tree

9 files changed

+39
-32
lines changed

9 files changed

+39
-32
lines changed

src/interfaces/chain.cpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -207,16 +207,6 @@ class NotificationsHandlerImpl : public Handler, CValidationInterface
207207
m_notifications->UpdatedBlockTip();
208208
}
209209
void ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->ChainStateFlushed(locator); }
210-
void ResendWalletTransactions(CConnman*) override
211-
{
212-
// `cs_main` is always held when this method is called, so it is safe to
213-
// call `assumeLocked`. This is awkward, and the `assumeLocked` method
214-
// should be able to be removed entirely if `ResendWalletTransactions`
215-
// is replaced by a wallet timer as suggested in
216-
// https://github.com/bitcoin/bitcoin/issues/15619
217-
auto locked_chain = m_chain.assumeLocked();
218-
m_notifications->ResendWalletTransactions(*locked_chain);
219-
}
220210
Chain& m_chain;
221211
Chain::Notifications* m_notifications;
222212
};
@@ -351,6 +341,7 @@ class ChainImpl : public Chain
351341
CAmount maxTxFee() override { return ::maxTxFee; }
352342
bool getPruneMode() override { return ::fPruneMode; }
353343
bool p2pEnabled() override { return g_connman != nullptr; }
344+
bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !IsInitialBlockDownload(); }
354345
bool isInitialBlockDownload() override { return IsInitialBlockDownload(); }
355346
bool shutdownRequested() override { return ShutdownRequested(); }
356347
int64_t getAdjustedTime() override { return GetAdjustedTime(); }

src/interfaces/chain.h

Lines changed: 3 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

@@ -258,7 +261,6 @@ class Chain
258261
virtual void BlockDisconnected(const CBlock& block) {}
259262
virtual void UpdatedBlockTip() {}
260263
virtual void ChainStateFlushed(const CBlockLocator& locator) {}
261-
virtual void ResendWalletTransactions(Lock& locked_chain) {}
262264
};
263265

264266
//! Register handler for notifications.

src/net_processing.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3546,14 +3546,6 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
35463546
}
35473547
}
35483548

3549-
// Resend wallet transactions that haven't gotten in a block yet
3550-
// Except during reindex, importing and IBD, when old wallet
3551-
// transactions become unconfirmed and spams other nodes.
3552-
if (!fReindex && !fImporting && !IsInitialBlockDownload())
3553-
{
3554-
GetMainSignals().Broadcast(connman);
3555-
}
3556-
35573549
//
35583550
// Try sending block announcements via headers
35593551
//

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 (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));
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(CConnman* connman) {
179-
m_internals->Broadcast(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(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(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: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2114,8 +2114,21 @@ bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
21142114
return CTransaction(tx1) == CTransaction(tx2);
21152115
}
21162116

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.
21172126
void CWallet::ResendWalletTransactions(interfaces::Chain::Lock& locked_chain)
21182127
{
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+
21192132
// Do this infrequently and randomly to avoid giving away
21202133
// that these are our transactions.
21212134
if (GetTime() < nNextResend || !fBroadcastTransactions) return;
@@ -2149,7 +2162,13 @@ void CWallet::ResendWalletTransactions(interfaces::Chain::Lock& locked_chain)
21492162

21502163
/** @} */ // end of mapWallet
21512164

2152-
2165+
void MaybeResendWalletTxs()
2166+
{
2167+
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
2168+
auto locked_chain = pwallet->chain().lock();
2169+
pwallet->ResendWalletTransactions(*locked_chain);
2170+
}
2171+
}
21532172

21542173

21552174
/** @defgroup Actions

src/wallet/wallet.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,7 @@ class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notific
949949
ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate);
950950
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
951951
void ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
952-
void ResendWalletTransactions(interfaces::Chain::Lock& locked_chain) override;
952+
void ResendWalletTransactions(interfaces::Chain::Lock& locked_chain);
953953
struct Balance {
954954
CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
955955
CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
@@ -1232,6 +1232,12 @@ class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notific
12321232
friend struct WalletTestingSetup;
12331233
};
12341234

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+
12351241
/** A key allocated from the key pool. */
12361242
class CReserveKey final : public CReserveScript
12371243
{

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)