Skip to content

Commit 07a9264

Browse files
vasildryanofsky
andcommitted
node: change a tx-relay on/off flag to enum
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. Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
1 parent 919e6d0 commit 07a9264

File tree

10 files changed

+116
-44
lines changed

10 files changed

+116
-44
lines changed

src/interfaces/chain.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <blockfilter.h>
99
#include <common/settings.h>
10+
#include <node/types.h>
1011
#include <primitives/transaction.h>
1112
#include <util/result.h>
1213

@@ -206,13 +207,19 @@ class Chain
206207
//! Check if transaction has descendants in mempool.
207208
virtual bool hasDescendantsInMempool(const Txid& txid) = 0;
208209

209-
//! Transaction is added to memory pool, if the transaction fee is below the
210-
//! amount specified by max_tx_fee, and broadcast to all peers if relay is set to true.
211-
//! Return false if the transaction could not be added due to the fee or for another reason.
210+
//! Process a local transaction, optionally adding it to the mempool and
211+
//! optionally broadcasting it to the network.
212+
//! @param[in] tx Transaction to process.
213+
//! @param[in] max_tx_fee Don't add the transaction to the mempool or
214+
//! broadcast it if its fee is higher than this.
215+
//! @param[in] broadcast_method Whether to add the transaction to the
216+
//! mempool and how/whether to broadcast it.
217+
//! @param[out] err_string Set if an error occurs.
218+
//! @return False if the transaction could not be added due to the fee or for another reason.
212219
virtual bool broadcastTransaction(const CTransactionRef& tx,
213-
const CAmount& max_tx_fee,
214-
bool relay,
215-
std::string& err_string) = 0;
220+
const CAmount& max_tx_fee,
221+
node::TxBroadcast broadcast_method,
222+
std::string& err_string) = 0;
216223

217224
//! Calculate mempool ancestor and descendant counts for the given transaction.
218225
virtual void getTransactionAncestry(const Txid& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) = 0;

src/node/interfaces.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,12 @@ class NodeImpl : public Node
363363
}
364364
TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, std::string& err_string) override
365365
{
366-
return BroadcastTransaction(*m_context, std::move(tx), err_string, max_tx_fee, /*relay=*/ true, /*wait_callback=*/ false);
366+
return BroadcastTransaction(*m_context,
367+
std::move(tx),
368+
err_string,
369+
max_tx_fee,
370+
TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL,
371+
/*wait_callback=*/false);
367372
}
368373
WalletLoader& walletLoader() override
369374
{
@@ -672,10 +677,10 @@ class ChainImpl : public Chain
672677
}
673678
bool broadcastTransaction(const CTransactionRef& tx,
674679
const CAmount& max_tx_fee,
675-
bool relay,
680+
TxBroadcast broadcast_method,
676681
std::string& err_string) override
677682
{
678-
const TransactionError err = BroadcastTransaction(m_node, tx, err_string, max_tx_fee, relay, /*wait_callback=*/false);
683+
const TransactionError err = BroadcastTransaction(m_node, tx, err_string, max_tx_fee, broadcast_method, /*wait_callback=*/false);
679684
// Chain clients only care about failures to accept the tx to the mempool. Disregard non-mempool related failures.
680685
// Note: this will need to be updated if BroadcastTransactions() is updated to return other non-mempool failures
681686
// that Chain clients do not need to know about.

src/node/transaction.cpp

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str
3131
}
3232
}
3333

34-
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback)
34+
TransactionError BroadcastTransaction(NodeContext& node,
35+
const CTransactionRef tx,
36+
std::string& err_string,
37+
const CAmount& max_tx_fee,
38+
TxBroadcast broadcast_method,
39+
bool wait_callback)
3540
{
3641
// BroadcastTransaction can be called by RPC or by the wallet.
3742
// chainman, mempool and peerman are initialized before the RPC server and wallet are started
@@ -62,7 +67,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
6267
// There's already a transaction in the mempool with this txid. Don't
6368
// try to submit this transaction to the mempool (since it'll be
6469
// rejected as a TX_CONFLICT), but do attempt to reannounce the mempool
65-
// transaction if relay=true.
70+
// transaction if broadcast_method is not TxBroadcast::MEMPOOL_NO_BROADCAST.
6671
//
6772
// The mempool transaction may have the same or different witness (and
6873
// wtxid) as this transaction. Use the mempool's wtxid for reannouncement.
@@ -79,18 +84,26 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
7984
return TransactionError::MAX_FEE_EXCEEDED;
8085
}
8186
}
82-
// Try to submit the transaction to the mempool.
83-
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ false);
84-
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
85-
return HandleATMPError(result.m_state, err_string);
86-
}
8787

88-
// Transaction was accepted to the mempool.
88+
switch (broadcast_method) {
89+
case TxBroadcast::MEMPOOL_NO_BROADCAST:
90+
case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
91+
// Try to submit the transaction to the mempool.
92+
{
93+
const MempoolAcceptResult result =
94+
node.chainman->ProcessTransaction(tx, /*test_accept=*/false);
95+
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
96+
return HandleATMPError(result.m_state, err_string);
97+
}
98+
}
99+
// Transaction was accepted to the mempool.
89100

90-
if (relay) {
91-
// the mempool tracks locally submitted transactions to make a
92-
// best-effort of initial broadcast
93-
node.mempool->AddUnbroadcastTx(txid);
101+
if (broadcast_method == TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL) {
102+
// the mempool tracks locally submitted transactions to make a
103+
// best-effort of initial broadcast
104+
node.mempool->AddUnbroadcastTx(txid);
105+
}
106+
break;
94107
}
95108

96109
if (wait_callback && node.validation_signals) {
@@ -116,8 +129,12 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
116129
promise.get_future().wait();
117130
}
118131

119-
if (relay) {
132+
switch (broadcast_method) {
133+
case TxBroadcast::MEMPOOL_NO_BROADCAST:
134+
break;
135+
case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
120136
node.peerman->RelayTransaction(txid, wtxid);
137+
break;
121138
}
122139

123140
return TransactionError::OK;

src/node/transaction.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define BITCOIN_NODE_TRANSACTION_H
77

88
#include <common/messages.h>
9+
#include <node/types.h>
910
#include <policy/feerate.h>
1011
#include <primitives/transaction.h>
1112

@@ -45,11 +46,16 @@ static const CAmount DEFAULT_MAX_BURN_AMOUNT{0};
4546
* @param[in] tx the transaction to broadcast
4647
* @param[out] err_string reference to std::string to fill with error string if available
4748
* @param[in] max_tx_fee reject txs with fees higher than this (if 0, accept any fee)
48-
* @param[in] relay flag if both mempool insertion and p2p relay are requested
49+
* @param[in] broadcast_method whether to add the transaction to the mempool and how to broadcast it
4950
* @param[in] wait_callback wait until callbacks have been processed to avoid stale result due to a sequentially RPC.
5051
* return error
5152
*/
52-
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback);
53+
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node,
54+
CTransactionRef tx,
55+
std::string& err_string,
56+
const CAmount& max_tx_fee,
57+
TxBroadcast broadcast_method,
58+
bool wait_callback);
5359

5460
/**
5561
* Return transaction with a given hash.

src/node/types.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include <consensus/amount.h>
1717
#include <cstddef>
18+
#include <cstdint>
1819
#include <policy/policy.h>
1920
#include <script/script.h>
2021
#include <uint256.h>
@@ -97,6 +98,18 @@ struct BlockCheckOptions {
9798
*/
9899
bool check_pow{true};
99100
};
101+
102+
/**
103+
* How to broadcast a local transaction.
104+
* Used to influence `BroadcastTransaction()` and its callers.
105+
*/
106+
enum class TxBroadcast : uint8_t {
107+
/// Add the transaction to the mempool and broadcast to all peers for which tx relay is enabled.
108+
MEMPOOL_AND_BROADCAST_TO_ALL,
109+
/// Add the transaction to the mempool, but don't broadcast to anybody.
110+
MEMPOOL_NO_BROADCAST,
111+
};
112+
100113
} // namespace node
101114

102115
#endif // BITCOIN_NODE_TYPES_H

src/rpc/mempool.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,12 @@ static RPCHelpMan sendrawtransaction()
9797
std::string err_string;
9898
AssertLockNotHeld(cs_main);
9999
NodeContext& node = EnsureAnyNodeContext(request.context);
100-
const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay=*/true, /*wait_callback=*/true);
100+
const TransactionError err = BroadcastTransaction(node,
101+
tx,
102+
err_string,
103+
max_raw_tx_fee,
104+
node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL,
105+
/*wait_callback=*/true);
101106
if (TransactionError::OK != err) {
102107
throw JSONRPCTransactionError(err, err_string);
103108
}
@@ -1066,7 +1071,12 @@ static RPCHelpMan submitpackage()
10661071

10671072
// We do not expect an error here; we are only broadcasting things already/still in mempool
10681073
std::string err_string;
1069-
const auto err = BroadcastTransaction(node, tx, err_string, /*max_tx_fee=*/0, /*relay=*/true, /*wait_callback=*/true);
1074+
const auto err = BroadcastTransaction(node,
1075+
tx,
1076+
err_string,
1077+
/*max_tx_fee=*/0,
1078+
node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL,
1079+
/*wait_callback=*/true);
10701080
if (err != TransactionError::OK) {
10711081
throw JSONRPCTransactionError(err,
10721082
strprintf("transaction broadcast failed: %s (%d transactions were broadcast successfully)",

src/wallet/rpc/backup.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <interfaces/chain.h>
1010
#include <key_io.h>
1111
#include <merkleblock.h>
12+
#include <node/types.h>
1213
#include <rpc/util.h>
1314
#include <script/descriptor.h>
1415
#include <script/script.h>
@@ -400,7 +401,7 @@ RPCHelpMan importdescriptors()
400401
// Rescan the blockchain using the lowest timestamp
401402
if (rescan) {
402403
int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver, /*update=*/true);
403-
pwallet->ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
404+
pwallet->ResubmitWalletTransactions(node::TxBroadcast::MEMPOOL_NO_BROADCAST, /*force=*/true);
404405

405406
if (pwallet->IsAbortingRescan()) {
406407
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");

src/wallet/test/wallet_tests.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <interfaces/chain.h>
1414
#include <key_io.h>
1515
#include <node/blockstorage.h>
16+
#include <node/types.h>
1617
#include <policy/policy.h>
1718
#include <rpc/server.h>
1819
#include <script/solver.h>
@@ -618,7 +619,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
618619
auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
619620
m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
620621
auto mempool_tx = TestSimpleSpend(*m_coinbase_txns[1], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
621-
BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error));
622+
BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, node::TxBroadcast::MEMPOOL_NO_BROADCAST, error));
622623

623624

624625
// Reload wallet and make sure new transactions are detected despite events
@@ -660,7 +661,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
660661
block_tx = TestSimpleSpend(*m_coinbase_txns[2], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
661662
m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
662663
mempool_tx = TestSimpleSpend(*m_coinbase_txns[3], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
663-
BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error));
664+
BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, node::TxBroadcast::MEMPOOL_NO_BROADCAST, error));
664665
m_node.validation_signals->SyncWithValidationInterfaceQueue();
665666
});
666667
wallet = TestLoadWallet(context);

src/wallet/wallet.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,7 +1959,9 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
19591959
return result;
19601960
}
19611961

1962-
bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, bool relay) const
1962+
bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx,
1963+
std::string& err_string,
1964+
node::TxBroadcast broadcast_method) const
19631965
{
19641966
AssertLockHeld(cs_wallet);
19651967

@@ -1973,8 +1975,16 @@ bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string
19731975
// Don't try to submit conflicted or confirmed transactions.
19741976
if (GetTxDepthInMainChain(wtx) != 0) return false;
19751977

1976-
// Submit transaction to mempool for relay
1977-
WalletLogPrintf("Submitting wtx %s to mempool for relay\n", wtx.GetHash().ToString());
1978+
const char* what{""};
1979+
switch (broadcast_method) {
1980+
case node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
1981+
what = "to mempool and for broadcast to peers";
1982+
break;
1983+
case node::TxBroadcast::MEMPOOL_NO_BROADCAST:
1984+
what = "to mempool without broadcast";
1985+
break;
1986+
}
1987+
WalletLogPrintf("Submitting wtx %s %s\n", wtx.GetHash().ToString(), what);
19781988
// We must set TxStateInMempool here. Even though it will also be set later by the
19791989
// entered-mempool callback, if we did not there would be a race where a
19801990
// 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
19841994
// If broadcast fails for any reason, trying to set wtx.m_state here would be incorrect.
19851995
// If transaction was previously in the mempool, it should be updated when
19861996
// TransactionRemovedFromMempool fires.
1987-
bool ret = chain().broadcastTransaction(wtx.tx, m_default_max_tx_fee, relay, err_string);
1997+
bool ret = chain().broadcastTransaction(wtx.tx, m_default_max_tx_fee, broadcast_method, err_string);
19881998
if (ret) wtx.m_state = TxStateInMempool{};
19891999
return ret;
19902000
}
@@ -2039,10 +2049,11 @@ NodeClock::time_point CWallet::GetDefaultNextResend() { return FastRandomContext
20392049
//
20402050
// The `force` option results in all unconfirmed transactions being submitted to
20412051
// the mempool. This does not necessarily result in those transactions being relayed,
2042-
// that depends on the `relay` option. Periodic rebroadcast uses the pattern
2043-
// relay=true force=false, while loading into the mempool
2044-
// (on start, or after import) uses relay=false force=true.
2045-
void CWallet::ResubmitWalletTransactions(bool relay, bool force)
2052+
// that depends on the `broadcast_method` option. Periodic rebroadcast uses the pattern
2053+
// broadcast_method=TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL force=false, while loading into
2054+
// the mempool (on start, or after import) uses
2055+
// broadcast_method=TxBroadcast::MEMPOOL_NO_BROADCAST force=true.
2056+
void CWallet::ResubmitWalletTransactions(node::TxBroadcast broadcast_method, bool force)
20462057
{
20472058
// Don't attempt to resubmit if the wallet is configured to not broadcast,
20482059
// even if forcing.
@@ -2068,7 +2079,7 @@ void CWallet::ResubmitWalletTransactions(bool relay, bool force)
20682079
// Now try submitting the transactions to the memory pool and (optionally) relay them.
20692080
for (auto wtx : to_submit) {
20702081
std::string unused_err_string;
2071-
if (SubmitTxMemoryPoolAndRelay(*wtx, unused_err_string, relay)) ++submitted_tx_count;
2082+
if (SubmitTxMemoryPoolAndRelay(*wtx, unused_err_string, broadcast_method)) ++submitted_tx_count;
20722083
}
20732084
} // cs_wallet
20742085

@@ -2083,7 +2094,7 @@ void MaybeResendWalletTxs(WalletContext& context)
20832094
{
20842095
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
20852096
if (!pwallet->ShouldResend()) continue;
2086-
pwallet->ResubmitWalletTransactions(/*relay=*/true, /*force=*/false);
2097+
pwallet->ResubmitWalletTransactions(node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL, /*force=*/false);
20872098
pwallet->SetNextResend();
20882099
}
20892100
}
@@ -2284,7 +2295,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
22842295
}
22852296

22862297
std::string err_string;
2287-
if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, true)) {
2298+
if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL)) {
22882299
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
22892300
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
22902301
}
@@ -3233,7 +3244,7 @@ void CWallet::postInitProcess()
32333244
{
32343245
// Add wallet transactions that aren't already in a block to mempool
32353246
// Do this here as mempool requires genesis block to be loaded
3236-
ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
3247+
ResubmitWalletTransactions(node::TxBroadcast::MEMPOOL_NO_BROADCAST, /*force=*/true);
32373248

32383249
// Update wallet transactions with current mempool transactions.
32393250
WITH_LOCK(cs_wallet, chain().requestMempoolTransactions(*this));

0 commit comments

Comments
 (0)