1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-present The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 6 #include <consensus/validation.h>
7 #include <index/txindex.h>
8 #include <net.h>
9 #include <net_processing.h>
10 #include <node/blockstorage.h>
11 #include <node/context.h>
12 #include <node/types.h>
13 #include <txmempool.h>
14 #include <validation.h>
15 #include <validationinterface.h>
16 #include <node/transaction.h>
17 18 namespace node {
19 static TransactionError HandleATMPError(const TxValidationState& state, std::string& err_string_out)
20 {
21 err_string_out = state.ToString();
22 if (state.IsInvalid()) {
23 if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
24 return TransactionError::MISSING_INPUTS;
25 }
26 return TransactionError::MEMPOOL_REJECTED;
27 } else {
28 return TransactionError::MEMPOOL_ERROR;
29 }
30 }
31 32 TransactionError BroadcastTransaction(NodeContext& node,
33 const CTransactionRef tx,
34 std::string& err_string,
35 const CAmount& max_tx_fee,
36 TxBroadcast broadcast_method,
37 bool wait_callback)
38 {
39 // BroadcastTransaction can be called by RPC or by the wallet.
40 // chainman, mempool and peerman are initialized before the RPC server and wallet are started
41 // and reset after the RPC sever and wallet are stopped.
42 assert(node.chainman);
43 assert(node.mempool);
44 assert(node.peerman);
45 46 Txid txid = tx->GetHash();
47 Wtxid wtxid = tx->GetWitnessHash();
48 bool callback_set = false;
49 50 {
51 LOCK(cs_main);
52 53 // If the transaction is already confirmed in the chain, don't do anything
54 // and return early.
55 CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
56 for (size_t o = 0; o < tx->vout.size(); o++) {
57 const Coin& existingCoin = view.AccessCoin(COutPoint(txid, o));
58 // IsSpent doesn't mean the coin is spent, it means the output doesn't exist.
59 // So if the output does exist, then this transaction exists in the chain.
60 if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_UTXO_SET;
61 }
62 63 if (auto mempool_tx = node.mempool->get(txid); mempool_tx) {
64 // There's already a transaction in the mempool with this txid. Don't
65 // try to submit this transaction to the mempool (since it'll be
66 // rejected as a TX_CONFLICT), but do attempt to reannounce the mempool
67 // transaction if broadcast_method is not TxBroadcast::MEMPOOL_NO_BROADCAST.
68 //
69 // The mempool transaction may have the same or different witness (and
70 // wtxid) as this transaction. Use the mempool's wtxid for reannouncement.
71 wtxid = mempool_tx->GetWitnessHash();
72 } else {
73 // Transaction is not already in the mempool.
74 const bool check_max_fee{max_tx_fee > 0};
75 if (check_max_fee || broadcast_method == TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST) {
76 // First, call ATMP with test_accept and check the fee. If ATMP
77 // fails here, return error immediately.
78 const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);
79 if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
80 return HandleATMPError(result.m_state, err_string);
81 } else if (check_max_fee && result.m_base_fees.value() > max_tx_fee) {
82 return TransactionError::MAX_FEE_EXCEEDED;
83 }
84 }
85 86 switch (broadcast_method) {
87 case TxBroadcast::MEMPOOL_NO_BROADCAST:
88 case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
89 // Try to submit the transaction to the mempool.
90 {
91 const MempoolAcceptResult result =
92 node.chainman->ProcessTransaction(tx, /*test_accept=*/false);
93 if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
94 return HandleATMPError(result.m_state, err_string);
95 }
96 }
97 // Transaction was accepted to the mempool.
98 99 if (broadcast_method == TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL) {
100 // the mempool tracks locally submitted transactions to make a
101 // best-effort of initial broadcast
102 node.mempool->AddUnbroadcastTx(txid);
103 }
104 break;
105 case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:
106 break;
107 }
108 109 if (wait_callback && node.validation_signals) {
110 // For transactions broadcast from outside the wallet, make sure
111 // that the wallet has been notified of the transaction before
112 // continuing.
113 //
114 // This prevents a race where a user might call sendrawtransaction
115 // with a transaction to/from their wallet, immediately call some
116 // wallet RPC, and get a stale result because callbacks have not
117 // yet been processed.
118 callback_set = true;
119 }
120 }
121 } // cs_main
122 123 if (callback_set) {
124 // Wait until Validation Interface clients have been notified of the
125 // transaction entering the mempool.
126 node.validation_signals->SyncWithValidationInterfaceQueue();
127 }
128 129 switch (broadcast_method) {
130 case TxBroadcast::MEMPOOL_NO_BROADCAST:
131 break;
132 case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
133 node.peerman->InitiateTxBroadcastToAll(txid, wtxid);
134 break;
135 case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:
136 return node.peerman->InitiateTxBroadcastPrivate(tx);
137 }
138 139 return TransactionError::OK;
140 }
141 142 CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const Txid& hash, const BlockManager& blockman, uint256& hashBlock)
143 {
144 if (mempool && !block_index) {
145 CTransactionRef ptx = mempool->get(hash);
146 if (ptx) return ptx;
147 }
148 if (g_txindex) {
149 CTransactionRef tx;
150 uint256 block_hash;
151 if (g_txindex->FindTx(hash, block_hash, tx)) {
152 if (!block_index || block_index->GetBlockHash() == block_hash) {
153 // Don't return the transaction if the provided block hash doesn't match.
154 // The case where a transaction appears in multiple blocks (e.g. reorgs or
155 // BIP30) is handled by the block lookup below.
156 hashBlock = block_hash;
157 return tx;
158 }
159 }
160 }
161 if (block_index) {
162 CBlock block;
163 if (blockman.ReadBlock(block, *block_index)) {
164 for (const auto& tx : block.vtx) {
165 if (tx->GetHash() == hash) {
166 hashBlock = block_index->GetBlockHash();
167 return tx;
168 }
169 }
170 }
171 }
172 return nullptr;
173 }
174 } // namespace node
175