@@ -28,81 +28,83 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str
2828
2929TransactionError BroadcastTransaction (NodeContext& node, const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback)
3030{
31- // BroadcastTransaction can be called by either sendrawtransaction RPC or wallet RPCs .
32- // node.peerman is assigned both before chain clients and before RPC server is accepting calls,
33- // and reset after chain clients and RPC sever are stopped. node.peerman should never be null here .
34- assert (node.peerman );
31+ // BroadcastTransaction can be called by either sendrawtransaction RPC or the wallet .
32+ // chainman, mempool and peerman are initialized before the RPC server and wallet are started
33+ // and reset after the RPC sever and wallet are stopped .
34+ assert (node.chainman );
3535 assert (node.mempool );
36+ assert (node.peerman );
37+
3638 std::promise<void > promise;
3739 uint256 txid = tx->GetHash ();
3840 uint256 wtxid = tx->GetWitnessHash ();
3941 bool callback_set = false ;
4042
41- { // cs_main scope
42- assert (node.chainman );
43- LOCK (cs_main);
44- // If the transaction is already confirmed in the chain, don't do anything
45- // and return early.
46- CCoinsViewCache &view = node.chainman ->ActiveChainstate ().CoinsTip ();
47- for (size_t o = 0 ; o < tx->vout .size (); o++) {
48- const Coin& existingCoin = view.AccessCoin (COutPoint (txid, o));
49- // IsSpent doesn't mean the coin is spent, it means the output doesn't exist.
50- // So if the output does exist, then this transaction exists in the chain.
51- if (!existingCoin.IsSpent ()) return TransactionError::ALREADY_IN_CHAIN;
52- }
53- if (auto mempool_tx = node.mempool ->get (txid); mempool_tx) {
54- // There's already a transaction in the mempool with this txid. Don't
55- // try to submit this transaction to the mempool (since it'll be
56- // rejected as a TX_CONFLICT), but do attempt to reannounce the mempool
57- // transaction if relay=true.
58- //
59- // The mempool transaction may have the same or different witness (and
60- // wtxid) as this transaction. Use the mempool's wtxid for reannouncement.
61- wtxid = mempool_tx->GetWitnessHash ();
62- } else {
63- // Transaction is not already in the mempool.
64- if (max_tx_fee > 0 ) {
65- // First, call ATMP with test_accept and check the fee. If ATMP
66- // fails here, return error immediately.
43+ {
44+ LOCK (cs_main);
45+
46+ // If the transaction is already confirmed in the chain, don't do anything
47+ // and return early.
48+ CCoinsViewCache &view = node.chainman ->ActiveChainstate ().CoinsTip ();
49+ for (size_t o = 0 ; o < tx->vout .size (); o++) {
50+ const Coin& existingCoin = view.AccessCoin (COutPoint (txid, o));
51+ // IsSpent doesn't mean the coin is spent, it means the output doesn't exist.
52+ // So if the output does exist, then this transaction exists in the chain.
53+ if (!existingCoin.IsSpent ()) return TransactionError::ALREADY_IN_CHAIN;
54+ }
55+
56+ if (auto mempool_tx = node.mempool ->get (txid); mempool_tx) {
57+ // There's already a transaction in the mempool with this txid. Don't
58+ // try to submit this transaction to the mempool (since it'll be
59+ // rejected as a TX_CONFLICT), but do attempt to reannounce the mempool
60+ // transaction if relay=true.
61+ //
62+ // The mempool transaction may have the same or different witness (and
63+ // wtxid) as this transaction. Use the mempool's wtxid for reannouncement.
64+ wtxid = mempool_tx->GetWitnessHash ();
65+ } else {
66+ // Transaction is not already in the mempool.
67+ if (max_tx_fee > 0 ) {
68+ // First, call ATMP with test_accept and check the fee. If ATMP
69+ // fails here, return error immediately.
70+ const MempoolAcceptResult result = AcceptToMemoryPool (node.chainman ->ActiveChainstate (), *node.mempool , tx, false /* bypass_limits */ ,
71+ true /* test_accept */ );
72+ if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
73+ return HandleATMPError (result.m_state , err_string);
74+ } else if (result.m_base_fees .value () > max_tx_fee) {
75+ return TransactionError::MAX_FEE_EXCEEDED;
76+ }
77+ }
78+ // Try to submit the transaction to the mempool.
6779 const MempoolAcceptResult result = AcceptToMemoryPool (node.chainman ->ActiveChainstate (), *node.mempool , tx, false /* bypass_limits */ ,
68- true /* test_accept */ );
80+ false /* test_accept */ );
6981 if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
7082 return HandleATMPError (result.m_state , err_string);
71- } else if (result.m_base_fees .value () > max_tx_fee) {
72- return TransactionError::MAX_FEE_EXCEEDED;
7383 }
74- }
75- // Try to submit the transaction to the mempool.
76- const MempoolAcceptResult result = AcceptToMemoryPool (node.chainman ->ActiveChainstate (), *node.mempool , tx, false /* bypass_limits */ ,
77- false /* test_accept */ );
78- if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
79- return HandleATMPError (result.m_state , err_string);
80- }
8184
82- // Transaction was accepted to the mempool.
85+ // Transaction was accepted to the mempool.
8386
84- if (relay) {
85- // the mempool tracks locally submitted transactions to make a
86- // best-effort of initial broadcast
87- node.mempool ->AddUnbroadcastTx (txid);
88- }
87+ if (relay) {
88+ // the mempool tracks locally submitted transactions to make a
89+ // best-effort of initial broadcast
90+ node.mempool ->AddUnbroadcastTx (txid);
91+ }
8992
90- if (wait_callback) {
91- // For transactions broadcast from outside the wallet, make sure
92- // that the wallet has been notified of the transaction before
93- // continuing.
94- //
95- // This prevents a race where a user might call sendrawtransaction
96- // with a transaction to/from their wallet, immediately call some
97- // wallet RPC, and get a stale result because callbacks have not
98- // yet been processed.
99- CallFunctionInValidationInterfaceQueue ([&promise] {
100- promise.set_value ();
101- });
102- callback_set = true ;
93+ if (wait_callback) {
94+ // For transactions broadcast from outside the wallet, make sure
95+ // that the wallet has been notified of the transaction before
96+ // continuing.
97+ //
98+ // This prevents a race where a user might call sendrawtransaction
99+ // with a transaction to/from their wallet, immediately call some
100+ // wallet RPC, and get a stale result because callbacks have not
101+ // yet been processed.
102+ CallFunctionInValidationInterfaceQueue ([&promise] {
103+ promise.set_value ();
104+ });
105+ callback_set = true ;
106+ }
103107 }
104- }
105-
106108 } // cs_main
107109
108110 if (callback_set) {
0 commit comments