Merge bitcoin/bitcoin#33567: node: change a tx-relay on/off flag to enum

07a926474b node: change a tx-relay on/off flag to enum (Vasil Dimov)

Pull request description:

  Previously the `bool relay` argument to `BroadcastTransaction()` designated:

  ```
  relay=true: add to the mempool and broadcast to all peers
  relay=false: add to the mempool
  ```

  Change this to an `enum`, so it is more readable and easier to extend with a 3rd option. Consider these example call sites:

  ```cpp
  Paint(true);
  // Or
  Paint(/*is_red=*/true);
  ```

  vs

  ```cpp
  Paint(RED);
  ```

  The idea for putting `TxBroadcastMethod` into `node/types.h` by Ryan.

  ---

  This is part of [#29415 Broadcast own transactions only via short-lived Tor or I2P connections](https://github.com/bitcoin/bitcoin/pull/29415). Putting it in its own PR to reduce the size of #29415 and because it does not logically depend on the other commits from there.

ACKs for top commit:
  optout21:
    ACK 07a926474b
  kevkevinpal:
    ACK [07a9264](https://github.com/bitcoin/bitcoin/pull/33567/commits/07a926474b5a6fa1d3d4656362a0117611f6da2f)
  laanwj:
    Concept and code review ACK 07a926474b. Agree with the general reasoning and the change in #29415 is a valid motivation to change this interface.
  glozow:
    utACK 07a926474b

Tree-SHA512: ec8f6fa56a6d2422a0fbd5941ff2792685e8d8e7b9dd50bba9f3e21ed9b4a4a26c89b0d7e4895d48f30b7a635f2eddd894af26b5266410952cbdaf5c40b42966
This commit is contained in:
merge-script committed 2025-10-31 14:59:58 -04:00
commit 25c45bb0d0
10 files changed
+116 -44

No files matched your search

+2 -1
View File
@@ -9,6 +9,7 @@
#include <interfaces/chain.h>
#include <key_io.h>
#include <merkleblock.h>
#include <node/types.h>
#include <rpc/util.h>
#include <script/descriptor.h>
#include <script/script.h>
@@ -402,7 +403,7 @@ RPCHelpMan importdescriptors()
// Rescan the blockchain using the lowest timestamp
if (rescan) {
int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver, /*update=*/true);
pwallet->ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
pwallet->ResubmitWalletTransactions(node::TxBroadcast::MEMPOOL_NO_BROADCAST, /*force=*/true);
if (pwallet->IsAbortingRescan()) {
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
+3 -2
View File
@@ -13,6 +13,7 @@
#include <interfaces/chain.h>
#include <key_io.h>
#include <node/blockstorage.h>
#include <node/types.h>
#include <policy/policy.h>
#include <rpc/server.h>
#include <script/solver.h>
@@ -618,7 +619,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
auto mempool_tx = TestSimpleSpend(*m_coinbase_txns[1], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error));
BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, node::TxBroadcast::MEMPOOL_NO_BROADCAST, error));
// Reload wallet and make sure new transactions are detected despite events
@@ -660,7 +661,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
block_tx = TestSimpleSpend(*m_coinbase_txns[2], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
mempool_tx = TestSimpleSpend(*m_coinbase_txns[3], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error));
BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, node::TxBroadcast::MEMPOOL_NO_BROADCAST, error));
m_node.validation_signals->SyncWithValidationInterfaceQueue();
});
wallet = TestLoadWallet(context);
+23 -12
View File
@@ -1959,7 +1959,9 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
return result;
}
bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, bool relay) const
bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx,
std::string& err_string,
node::TxBroadcast broadcast_method) const
{
AssertLockHeld(cs_wallet);
@@ -1973,8 +1975,16 @@ bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string
// Don't try to submit conflicted or confirmed transactions.
if (GetTxDepthInMainChain(wtx) != 0) return false;
// Submit transaction to mempool for relay
WalletLogPrintf("Submitting wtx %s to mempool for relay\n", wtx.GetHash().ToString());
const char* what{""};
switch (broadcast_method) {
case node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
what = "to mempool and for broadcast to peers";
break;
case node::TxBroadcast::MEMPOOL_NO_BROADCAST:
what = "to mempool without broadcast";
break;
}
WalletLogPrintf("Submitting wtx %s %s\n", wtx.GetHash().ToString(), what);
// We must set TxStateInMempool here. Even though it will also be set later by the
// entered-mempool callback, if we did not there would be a race where a
// user could call sendmoney in a loop and hit spurious out of funds errors
@@ -1984,7 +1994,7 @@ bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string
// If broadcast fails for any reason, trying to set wtx.m_state here would be incorrect.
// If transaction was previously in the mempool, it should be updated when
// TransactionRemovedFromMempool fires.
bool ret = chain().broadcastTransaction(wtx.tx, m_default_max_tx_fee, relay, err_string);
bool ret = chain().broadcastTransaction(wtx.tx, m_default_max_tx_fee, broadcast_method, err_string);
if (ret) wtx.m_state = TxStateInMempool{};
return ret;
}
@@ -2039,10 +2049,11 @@ NodeClock::time_point CWallet::GetDefaultNextResend() { return FastRandomContext
//
// The `force` option results in all unconfirmed transactions being submitted to
// the mempool. This does not necessarily result in those transactions being relayed,
// that depends on the `relay` option. Periodic rebroadcast uses the pattern
// relay=true force=false, while loading into the mempool
// (on start, or after import) uses relay=false force=true.
void CWallet::ResubmitWalletTransactions(bool relay, bool force)
// that depends on the `broadcast_method` option. Periodic rebroadcast uses the pattern
// broadcast_method=TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL force=false, while loading into
// the mempool (on start, or after import) uses
// broadcast_method=TxBroadcast::MEMPOOL_NO_BROADCAST force=true.
void CWallet::ResubmitWalletTransactions(node::TxBroadcast broadcast_method, bool force)
{
// Don't attempt to resubmit if the wallet is configured to not broadcast,
// even if forcing.
@@ -2068,7 +2079,7 @@ void CWallet::ResubmitWalletTransactions(bool relay, bool force)
// Now try submitting the transactions to the memory pool and (optionally) relay them.
for (auto wtx : to_submit) {
std::string unused_err_string;
if (SubmitTxMemoryPoolAndRelay(*wtx, unused_err_string, relay)) ++submitted_tx_count;
if (SubmitTxMemoryPoolAndRelay(*wtx, unused_err_string, broadcast_method)) ++submitted_tx_count;
}
} // cs_wallet
@@ -2083,7 +2094,7 @@ void MaybeResendWalletTxs(WalletContext& context)
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
if (!pwallet->ShouldResend()) continue;
pwallet->ResubmitWalletTransactions(/*relay=*/true, /*force=*/false);
pwallet->ResubmitWalletTransactions(node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL, /*force=*/false);
pwallet->SetNextResend();
}
}
@@ -2284,7 +2295,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
}
std::string err_string;
if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, true)) {
if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
}
@@ -3233,7 +3244,7 @@ void CWallet::postInitProcess()
{
// Add wallet transactions that aren't already in a block to mempool
// Do this here as mempool requires genesis block to be loaded
ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
ResubmitWalletTransactions(node::TxBroadcast::MEMPOOL_NO_BROADCAST, /*force=*/true);
// Update wallet transactions with current mempool transactions.
WITH_LOCK(cs_wallet, chain().requestMempoolTransactions(*this));
+4 -3
View File
@@ -12,6 +12,7 @@
#include <interfaces/handler.h>
#include <kernel/cs_main.h>
#include <logging.h>
#include <node/types.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
@@ -654,7 +655,7 @@ public:
void SetNextResend() { m_next_resend = GetDefaultNextResend(); }
/** Return true if all conditions for periodically resending transactions are met. */
bool ShouldResend() const;
void ResubmitWalletTransactions(bool relay, bool force);
void ResubmitWalletTransactions(node::TxBroadcast broadcast_method, bool force);
OutputType TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const;
@@ -698,8 +699,8 @@ public:
*/
void CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm);
/** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
bool SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, bool relay) const
/** Pass this transaction to node for optional mempool insertion and relay to peers. */
bool SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, node::TxBroadcast broadcast_method) const
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Updates wallet birth time if 'time' is below it */