/Users/brunogarcia/projects/bitcoin-core-dev/src/node/transaction.cpp
Line | Count | Source |
1 | | // Copyright (c) 2010 Satoshi Nakamoto |
2 | | // Copyright (c) 2009-2021 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 | | #include <future> |
19 | | |
20 | | namespace node { |
21 | | static TransactionError HandleATMPError(const TxValidationState& state, std::string& err_string_out) |
22 | 0 | { |
23 | 0 | err_string_out = state.ToString(); |
24 | 0 | if (state.IsInvalid()) { |
25 | 0 | if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { |
26 | 0 | return TransactionError::MISSING_INPUTS; |
27 | 0 | } |
28 | 0 | return TransactionError::MEMPOOL_REJECTED; |
29 | 0 | } else { |
30 | 0 | return TransactionError::MEMPOOL_ERROR; |
31 | 0 | } |
32 | 0 | } |
33 | | |
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) |
40 | 0 | { |
41 | | // BroadcastTransaction can be called by RPC or by the wallet. |
42 | | // chainman, mempool and peerman are initialized before the RPC server and wallet are started |
43 | | // and reset after the RPC sever and wallet are stopped. |
44 | 0 | assert(node.chainman); |
45 | 0 | assert(node.mempool); |
46 | 0 | assert(node.peerman); |
47 | | |
48 | 0 | std::promise<void> promise; |
49 | 0 | Txid txid = tx->GetHash(); |
50 | 0 | Wtxid wtxid = tx->GetWitnessHash(); |
51 | 0 | bool callback_set = false; |
52 | |
|
53 | 0 | { |
54 | 0 | LOCK(cs_main); Line | Count | Source | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
55 | | |
56 | | // If the transaction is already confirmed in the chain, don't do anything |
57 | | // and return early. |
58 | 0 | CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip(); |
59 | 0 | for (size_t o = 0; o < tx->vout.size(); o++) { |
60 | 0 | const Coin& existingCoin = view.AccessCoin(COutPoint(txid, o)); |
61 | | // IsSpent doesn't mean the coin is spent, it means the output doesn't exist. |
62 | | // So if the output does exist, then this transaction exists in the chain. |
63 | 0 | if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_UTXO_SET; |
64 | 0 | } |
65 | | |
66 | 0 | if (auto mempool_tx = node.mempool->get(txid); mempool_tx) { |
67 | | // There's already a transaction in the mempool with this txid. Don't |
68 | | // try to submit this transaction to the mempool (since it'll be |
69 | | // rejected as a TX_CONFLICT), but do attempt to reannounce the mempool |
70 | | // transaction if broadcast_method is not TxBroadcast::MEMPOOL_NO_BROADCAST. |
71 | | // |
72 | | // The mempool transaction may have the same or different witness (and |
73 | | // wtxid) as this transaction. Use the mempool's wtxid for reannouncement. |
74 | 0 | wtxid = mempool_tx->GetWitnessHash(); |
75 | 0 | } else { |
76 | | // Transaction is not already in the mempool. |
77 | 0 | if (max_tx_fee > 0) { |
78 | | // First, call ATMP with test_accept and check the fee. If ATMP |
79 | | // fails here, return error immediately. |
80 | 0 | const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true); |
81 | 0 | if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { |
82 | 0 | return HandleATMPError(result.m_state, err_string); |
83 | 0 | } else if (result.m_base_fees.value() > max_tx_fee) { |
84 | 0 | return TransactionError::MAX_FEE_EXCEEDED; |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | 0 | switch (broadcast_method) { |
89 | 0 | case TxBroadcast::MEMPOOL_NO_BROADCAST: |
90 | 0 | case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL: |
91 | | // Try to submit the transaction to the mempool. |
92 | 0 | { |
93 | 0 | const MempoolAcceptResult result = |
94 | 0 | node.chainman->ProcessTransaction(tx, /*test_accept=*/false); |
95 | 0 | if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { |
96 | 0 | return HandleATMPError(result.m_state, err_string); |
97 | 0 | } |
98 | 0 | } |
99 | | // Transaction was accepted to the mempool. |
100 | | |
101 | 0 | 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 | 0 | node.mempool->AddUnbroadcastTx(txid); |
105 | 0 | } |
106 | 0 | break; |
107 | 0 | } |
108 | | |
109 | 0 | 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 | 0 | node.validation_signals->CallFunctionInValidationInterfaceQueue([&promise] { |
119 | 0 | promise.set_value(); |
120 | 0 | }); |
121 | 0 | callback_set = true; |
122 | 0 | } |
123 | 0 | } |
124 | 0 | } // cs_main |
125 | | |
126 | 0 | if (callback_set) { |
127 | | // Wait until Validation Interface clients have been notified of the |
128 | | // transaction entering the mempool. |
129 | 0 | promise.get_future().wait(); |
130 | 0 | } |
131 | |
|
132 | 0 | switch (broadcast_method) { |
133 | 0 | case TxBroadcast::MEMPOOL_NO_BROADCAST: |
134 | 0 | break; |
135 | 0 | case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL: |
136 | 0 | node.peerman->RelayTransaction(txid, wtxid); |
137 | 0 | break; |
138 | 0 | } |
139 | | |
140 | 0 | return TransactionError::OK; |
141 | 0 | } |
142 | | |
143 | | CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const Txid& hash, const BlockManager& blockman, uint256& hashBlock) |
144 | 0 | { |
145 | 0 | if (mempool && !block_index) { |
146 | 0 | CTransactionRef ptx = mempool->get(hash); |
147 | 0 | if (ptx) return ptx; |
148 | 0 | } |
149 | 0 | if (g_txindex) { |
150 | 0 | CTransactionRef tx; |
151 | 0 | uint256 block_hash; |
152 | 0 | if (g_txindex->FindTx(hash, block_hash, tx)) { |
153 | 0 | if (!block_index || block_index->GetBlockHash() == block_hash) { |
154 | | // Don't return the transaction if the provided block hash doesn't match. |
155 | | // The case where a transaction appears in multiple blocks (e.g. reorgs or |
156 | | // BIP30) is handled by the block lookup below. |
157 | 0 | hashBlock = block_hash; |
158 | 0 | return tx; |
159 | 0 | } |
160 | 0 | } |
161 | 0 | } |
162 | 0 | if (block_index) { |
163 | 0 | CBlock block; |
164 | 0 | if (blockman.ReadBlock(block, *block_index)) { |
165 | 0 | for (const auto& tx : block.vtx) { |
166 | 0 | if (tx->GetHash() == hash) { |
167 | 0 | hashBlock = block_index->GetBlockHash(); |
168 | 0 | return tx; |
169 | 0 | } |
170 | 0 | } |
171 | 0 | } |
172 | 0 | } |
173 | 0 | return nullptr; |
174 | 0 | } |
175 | | } // namespace node |