I think so, but this drags some further changes, are you ok with those:
<details>
<summary>[patch] move TxBroadcastMethod from primitives/transaction.h to node/transaction.h</summary>
diff --git i/src/interfaces/chain.h w/src/interfaces/chain.h
index 315211ae49..eeb5fef42b 100644
--- i/src/interfaces/chain.h
+++ w/src/interfaces/chain.h
@@ -4,12 +4,13 @@
#ifndef BITCOIN_INTERFACES_CHAIN_H
#define BITCOIN_INTERFACES_CHAIN_H
#include <blockfilter.h>
#include <common/settings.h>
+#include <node/transaction.h>
#include <primitives/transaction.h> // For CTransactionRef
#include <util/result.h>
#include <functional>
#include <memory>
#include <optional>
@@ -215,13 +216,13 @@ public:
//! [@param](/bitcoin-bitcoin/contributor/param/)[in] broadcast_method Whether to add the transaction to the
//! mempool and how/whether to broadcast it.
//! [@param](/bitcoin-bitcoin/contributor/param/)[out] err_string Set if an error occurs.
//! [@return](/bitcoin-bitcoin/contributor/return/) False if the transaction could not be added due to the fee or for another reason.
virtual bool broadcastTransaction(const CTransactionRef& tx,
const CAmount& max_tx_fee,
- TxBroadcastMethod broadcast_method,
+ node::TxBroadcastMethod broadcast_method,
std::string& err_string) = 0;
//! Calculate mempool ancestor and descendant counts for the given transaction.
virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) = 0;
//! For each outpoint, calculate the fee-bumping cost to spend this outpoint at the specified
diff --git i/src/node/transaction.h w/src/node/transaction.h
index 75c50bab28..01cdfaf1ff 100644
--- i/src/node/transaction.h
+++ w/src/node/transaction.h
@@ -29,12 +29,26 @@ static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10};
/** Maximum burn value for sendrawtransaction, submitpackage, and testmempoolaccept RPC calls.
* By default, a transaction with a burn value higher than this will be rejected
* by these RPCs and the GUI. This can be overridden with the maxburnamount argument.
*/
static const CAmount DEFAULT_MAX_BURN_AMOUNT{0};
+/**
+ * Methods to broadcast a local transaction.
+ * Used to influence BroadcastTransaction() and its callers.
+ */
+enum TxBroadcastMethod : uint8_t {
+ /// Add the transaction to the mempool and broadcast to all currently connected peers.
+ ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL,
+ /// Add the transaction to the mempool, but don't broadcast to anybody.
+ ADD_TO_MEMPOOL_NO_BROADCAST,
+ /// Omit the mempool and directly send the transaction via a few dedicated connections to
+ /// peers on privacy networks.
+ NO_MEMPOOL_PRIVATE_BROADCAST,
+};
+
/**
* Submit a transaction to the mempool and (optionally) relay it to all P2P peers.
*
* Mempool submission can be synchronous (will await mempool entry notification
* over the CValidationInterface) or asynchronous (will submit and not wait for
* notification), depending on the value of wait_callback. wait_callback MUST
diff --git i/src/primitives/transaction.h w/src/primitives/transaction.h
index 2051ebae5a..ccbeb3ec49 100644
--- i/src/primitives/transaction.h
+++ w/src/primitives/transaction.h
@@ -436,21 +436,7 @@ public:
bool IsWtxid() const { return m_is_wtxid; }
const uint256& GetHash() const LIFETIMEBOUND { return m_hash; }
friend bool operator==(const GenTxid& a, const GenTxid& b) { return a.m_is_wtxid == b.m_is_wtxid && a.m_hash == b.m_hash; }
friend bool operator<(const GenTxid& a, const GenTxid& b) { return std::tie(a.m_is_wtxid, a.m_hash) < std::tie(b.m_is_wtxid, b.m_hash); }
};
-/**
- * Methods to broadcast a local transaction.
- * Used to influence BroadcastTransaction() and its callers.
- */
-enum TxBroadcastMethod : uint8_t {
- /// Add the transaction to the mempool and broadcast to all currently connected peers.
- ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL,
- /// Add the transaction to the mempool, but don't broadcast to anybody.
- ADD_TO_MEMPOOL_NO_BROADCAST,
- /// Omit the mempool and directly send the transaction via a few dedicated connections to
- /// peers on privacy networks.
- NO_MEMPOOL_PRIVATE_BROADCAST,
-};
-
#endif // BITCOIN_PRIMITIVES_TRANSACTION_H
diff --git i/src/rpc/mempool.cpp w/src/rpc/mempool.cpp
index 14d303a0b1..ba69446f36 100644
--- i/src/rpc/mempool.cpp
+++ w/src/rpc/mempool.cpp
@@ -8,12 +8,13 @@
#include <kernel/mempool_persist.h>
#include <chainparams.h>
#include <core_io.h>
#include <kernel/mempool_entry.h>
#include <node/mempool_persist_args.h>
+#include <node/transaction.h>
#include <policy/rbf.h>
#include <policy/settings.h>
#include <primitives/transaction.h>
#include <rpc/server.h>
#include <rpc/server_util.h>
#include <rpc/util.h>
@@ -91,13 +92,13 @@ static RPCHelpMan sendrawtransaction()
AssertLockNotHeld(cs_main);
NodeContext& node = EnsureAnyNodeContext(request.context);
const TransactionError err = BroadcastTransaction(node,
tx,
err_string,
max_raw_tx_fee,
- ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL,
+ node::ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL,
/*wait_callback=*/true);
if (TransactionError::OK != err) {
throw JSONRPCTransactionError(err, err_string);
}
return tx->GetHash().GetHex();
@@ -953,13 +954,13 @@ static RPCHelpMan submitpackage()
// We do not expect an error here; we are only broadcasting things already/still in mempool
std::string err_string;
const auto err = BroadcastTransaction(node,
tx,
err_string,
/*max_tx_fee=*/0,
- ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL,
+ node::ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL,
/*wait_callback=*/true);
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err,
strprintf("transaction broadcast failed: %s (%d transactions were broadcast successfully)",
err_string, num_broadcast));
}
diff --git i/src/wallet/rpc/backup.cpp w/src/wallet/rpc/backup.cpp
index fdbd44ffe5..2e61a0e945 100644
--- i/src/wallet/rpc/backup.cpp
+++ w/src/wallet/rpc/backup.cpp
@@ -10,12 +10,13 @@
#include <clientversion.h>
#include <core_io.h>
#include <hash.h>
#include <interfaces/chain.h>
#include <key_io.h>
#include <merkleblock.h>
+#include <node/transaction.h>
#include <rpc/util.h>
#include <script/descriptor.h>
#include <script/script.h>
#include <script/solver.h>
#include <sync.h>
#include <uint256.h>
@@ -308,13 +309,13 @@ RPCHelpMan importaddress()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
}
}
if (fRescan)
{
RescanWallet(*pwallet, reserver);
- pwallet->ResubmitWalletTransactions(ADD_TO_MEMPOOL_NO_BROADCAST, /*force=*/true);
+ pwallet->ResubmitWalletTransactions(node::ADD_TO_MEMPOOL_NO_BROADCAST, /*force=*/true);
}
return UniValue::VNULL;
},
};
}
@@ -479,13 +480,13 @@ RPCHelpMan importpubkey()
pwallet->ImportPubKeys({pubKey.GetID()}, {{pubKey.GetID(), pubKey}} , /*key_origins=*/{}, /*add_keypool=*/false, /*internal=*/false, /*timestamp=*/1);
}
if (fRescan)
{
RescanWallet(*pwallet, reserver);
- pwallet->ResubmitWalletTransactions(ADD_TO_MEMPOOL_NO_BROADCAST, /*force=*/true);
+ pwallet->ResubmitWalletTransactions(node::ADD_TO_MEMPOOL_NO_BROADCAST, /*force=*/true);
}
return UniValue::VNULL;
},
};
}
@@ -1400,13 +1401,13 @@ RPCHelpMan importmulti()
nLowestTimestamp = timestamp;
}
}
}
if (fRescan && fRunScan && requests.size()) {
int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, /*update=*/true);
- pwallet->ResubmitWalletTransactions(ADD_TO_MEMPOOL_NO_BROADCAST, /*force=*/true);
+ pwallet->ResubmitWalletTransactions(node::ADD_TO_MEMPOOL_NO_BROADCAST, /*force=*/true);
if (pwallet->IsAbortingRescan()) {
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
}
if (scannedTime > nLowestTimestamp) {
std::vector<UniValue> results = response.getValues();
@@ -1694,13 +1695,13 @@ RPCHelpMan importdescriptors()
pwallet->ConnectScriptPubKeyManNotifiers();
}
// Rescan the blockchain using the lowest timestamp
if (rescan) {
int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver, /*update=*/true);
- pwallet->ResubmitWalletTransactions(ADD_TO_MEMPOOL_NO_BROADCAST, /*force=*/true);
+ pwallet->ResubmitWalletTransactions(node::ADD_TO_MEMPOOL_NO_BROADCAST, /*force=*/true);
if (pwallet->IsAbortingRescan()) {
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
}
if (scanned_time > lowest_timestamp) {
diff --git i/src/wallet/test/wallet_tests.cpp w/src/wallet/test/wallet_tests.cpp
index 003d1aab29..2377c71e21 100644
--- i/src/wallet/test/wallet_tests.cpp
+++ w/src/wallet/test/wallet_tests.cpp
@@ -10,12 +10,13 @@
#include <vector>
#include <addresstype.h>
#include <interfaces/chain.h>
#include <key_io.h>
#include <node/blockstorage.h>
+#include <node/transaction.h>
#include <policy/policy.h>
#include <rpc/server.h>
#include <script/solver.h>
#include <test/util/logging.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
@@ -819,13 +820,13 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
});
std::string error;
m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
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, ADD_TO_MEMPOOL_NO_BROADCAST, error));
+ BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, node::ADD_TO_MEMPOOL_NO_BROADCAST, error));
// Reload wallet and make sure new transactions are detected despite events
// being blocked
// Loading will also ask for current mempool transactions
wallet = TestLoadWallet(context);
@@ -861,13 +862,13 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
auto handler = HandleLoadWallet(context, [&](std::unique_ptr<interfaces::Wallet> wallet) {
BOOST_CHECK(rescan_completed);
m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
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, ADD_TO_MEMPOOL_NO_BROADCAST, error));
+ BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, node::ADD_TO_MEMPOOL_NO_BROADCAST, error));
m_node.validation_signals->SyncWithValidationInterfaceQueue();
});
wallet = TestLoadWallet(context);
// Since mempool transactions are requested at the end of loading, there will
// be 2 additional AddToWallet calls, one from the previous test, and a duplicate for mempool_tx
BOOST_CHECK_EQUAL(addtx_count, 2 + 2);
diff --git i/src/wallet/wallet.cpp w/src/wallet/wallet.cpp
index 0904399926..2566b2d543 100644
--- i/src/wallet/wallet.cpp
+++ w/src/wallet/wallet.cpp
@@ -24,12 +24,13 @@
#include <interfaces/wallet.h>
#include <kernel/chain.h>
#include <kernel/mempool_removal_reason.h>
#include <key.h>
#include <key_io.h>
#include <logging.h>
+#include <node/transaction.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <psbt.h>
#include <pubkey.h>
@@ -1999,13 +2000,13 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
}
return result;
}
bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx,
std::string& err_string,
- TxBroadcastMethod broadcast_method) const
+ node::TxBroadcastMethod broadcast_method) const
{
AssertLockHeld(cs_wallet);
// Can't relay if wallet is not broadcasting
if (!GetBroadcastTransactions()) return false;
// Don't relay abandoned transactions
@@ -2016,19 +2017,19 @@ bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx,
// Don't try to submit conflicted or confirmed transactions.
if (GetTxDepthInMainChain(wtx) != 0) return false;
// Submit transaction to mempool for relay
const char* what{""};
switch (broadcast_method) {
- case ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL:
+ case node::ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL:
what = "to mempool and for broadcast to all peers";
break;
- case ADD_TO_MEMPOOL_NO_BROADCAST:
+ case node::ADD_TO_MEMPOOL_NO_BROADCAST:
what = "to mempool without broadcast";
break;
- case NO_MEMPOOL_PRIVATE_BROADCAST:
+ case node::NO_MEMPOOL_PRIVATE_BROADCAST:
what = "for private broadcast without adding to the mempool";
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
@@ -2095,13 +2096,13 @@ 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 `broadcast_method` option. Periodic rebroadcast uses the pattern
// broadcast_method=ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL force=false, while loading into
// the mempool (on start, or after import) uses
// broadcast_method=ADD_TO_MEMPOOL_NO_BROADCAST force=true.
-void CWallet::ResubmitWalletTransactions(TxBroadcastMethod broadcast_method, bool force)
+void CWallet::ResubmitWalletTransactions(node::TxBroadcastMethod broadcast_method, bool force)
{
// Don't attempt to resubmit if the wallet is configured to not broadcast,
// even if forcing.
if (!fBroadcastTransactions) return;
int submitted_tx_count = 0;
@@ -2136,13 +2137,13 @@ void CWallet::ResubmitWalletTransactions(TxBroadcastMethod broadcast_method, boo
/** @} */ // end of mapWallet
void MaybeResendWalletTxs(WalletContext& context)
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
if (!pwallet->ShouldResend()) continue;
- pwallet->ResubmitWalletTransactions(ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL, /*force=*/false);
+ pwallet->ResubmitWalletTransactions(node::ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL, /*force=*/false);
pwallet->SetNextResend();
}
}
/** [@defgroup](/bitcoin-bitcoin/contributor/defgroup/) Actions
@@ -2344,13 +2345,13 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
if (!fBroadcastTransactions) {
// Don't submit tx to the mempool
return;
}
std::string err_string;
- if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, ADD_TO_MEMPOOL_AND_BROADCAST_TO_ALL)) {
+ if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, node::ADD_TO_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.
}
}
DBErrors CWallet::LoadWallet()
@@ -3389,13 +3390,13 @@ bool CWallet::UpgradeWallet(int version, bilingual_str& error)
}
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(ADD_TO_MEMPOOL_NO_BROADCAST, /*force=*/true);
+ ResubmitWalletTransactions(node::ADD_TO_MEMPOOL_NO_BROADCAST, /*force=*/true);
// Update wallet transactions with current mempool transactions.
WITH_LOCK(cs_wallet, chain().requestMempoolTransactions(*this));
}
bool CWallet::BackupWallet(const std::string& strDest) const
diff --git i/src/wallet/wallet.h w/src/wallet/wallet.h
index 8f9881c304..ceadabf165 100644
--- i/src/wallet/wallet.h
+++ w/src/wallet/wallet.h
@@ -9,12 +9,13 @@
#include <addresstype.h>
#include <consensus/amount.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <kernel/cs_main.h>
#include <logging.h>
+#include <node/transaction.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
#include <script/interpreter.h>
#include <script/script.h>
#include <support/allocators/secure.h>
@@ -631,13 +632,13 @@ public:
ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate, const bool save_progress);
void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override;
/** Set the next time this wallet should resend transactions to 12-36 hours from now, ~1 day on average. */
void SetNextResend() { m_next_resend = GetDefaultNextResend(); }
/** Return true if all conditions for periodically resending transactions are met. */
bool ShouldResend() const;
- void ResubmitWalletTransactions(TxBroadcastMethod broadcast_method, bool force);
+ void ResubmitWalletTransactions(node::TxBroadcastMethod broadcast_method, bool force);
OutputType TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const;
/** Fetch the inputs and sign with SIGHASH_ALL. */
bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Sign the tx given the input coins and sighash. */
@@ -676,13 +677,13 @@ public:
* [@param](/bitcoin-bitcoin/contributor/param/)[in] mapValue key-values to be set on the transaction.
* [@param](/bitcoin-bitcoin/contributor/param/)[in] orderForm BIP 70 / BIP 21 order form details to be set on the transaction.
*/
void CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm);
/** Pass this transaction to node for optional mempool insertion and relay to peers. */
- bool SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, TxBroadcastMethod broadcast_method) const
+ bool SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, node::TxBroadcastMethod broadcast_method) const
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportScripts(const std::set<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
</details>