/root/bitcoin/src/node/transaction.cpp
Line | Count | Source (jump to first uncovered line) |
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, const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback) |
35 | 0 | { |
36 | | // BroadcastTransaction can be called by RPC or by the wallet. |
37 | | // chainman, mempool and peerman are initialized before the RPC server and wallet are started |
38 | | // and reset after the RPC sever and wallet are stopped. |
39 | 0 | assert(node.chainman); |
40 | 0 | assert(node.mempool); |
41 | 0 | assert(node.peerman); |
42 | | |
43 | 0 | std::promise<void> promise; |
44 | 0 | Txid txid = tx->GetHash(); |
45 | 0 | uint256 wtxid = tx->GetWitnessHash(); |
46 | 0 | bool callback_set = false; |
47 | |
|
48 | 0 | { |
49 | 0 | LOCK(cs_main); |
50 | | |
51 | | // If the transaction is already confirmed in the chain, don't do anything |
52 | | // and return early. |
53 | 0 | CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip(); |
54 | 0 | for (size_t o = 0; o < tx->vout.size(); o++) { |
55 | 0 | const Coin& existingCoin = view.AccessCoin(COutPoint(txid, o)); |
56 | | // IsSpent doesn't mean the coin is spent, it means the output doesn't exist. |
57 | | // So if the output does exist, then this transaction exists in the chain. |
58 | 0 | if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_UTXO_SET; |
59 | 0 | } |
60 | | |
61 | 0 | if (auto mempool_tx = node.mempool->get(txid); mempool_tx) { |
62 | | // There's already a transaction in the mempool with this txid. Don't |
63 | | // try to submit this transaction to the mempool (since it'll be |
64 | | // rejected as a TX_CONFLICT), but do attempt to reannounce the mempool |
65 | | // transaction if relay=true. |
66 | | // |
67 | | // The mempool transaction may have the same or different witness (and |
68 | | // wtxid) as this transaction. Use the mempool's wtxid for reannouncement. |
69 | 0 | wtxid = mempool_tx->GetWitnessHash(); |
70 | 0 | } else { |
71 | | // Transaction is not already in the mempool. |
72 | 0 | if (max_tx_fee > 0) { |
73 | | // First, call ATMP with test_accept and check the fee. If ATMP |
74 | | // fails here, return error immediately. |
75 | 0 | const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true); |
76 | 0 | if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { |
77 | 0 | return HandleATMPError(result.m_state, err_string); |
78 | 0 | } else if (result.m_base_fees.value() > max_tx_fee) { |
79 | 0 | return TransactionError::MAX_FEE_EXCEEDED; |
80 | 0 | } |
81 | 0 | } |
82 | | // Try to submit the transaction to the mempool. |
83 | 0 | const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ false); |
84 | 0 | if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { |
85 | 0 | return HandleATMPError(result.m_state, err_string); |
86 | 0 | } |
87 | | |
88 | | // Transaction was accepted to the mempool. |
89 | | |
90 | 0 | if (relay) { |
91 | | // the mempool tracks locally submitted transactions to make a |
92 | | // best-effort of initial broadcast |
93 | 0 | node.mempool->AddUnbroadcastTx(txid); |
94 | 0 | } |
95 | |
|
96 | 0 | if (wait_callback && node.validation_signals) { |
97 | | // For transactions broadcast from outside the wallet, make sure |
98 | | // that the wallet has been notified of the transaction before |
99 | | // continuing. |
100 | | // |
101 | | // This prevents a race where a user might call sendrawtransaction |
102 | | // with a transaction to/from their wallet, immediately call some |
103 | | // wallet RPC, and get a stale result because callbacks have not |
104 | | // yet been processed. |
105 | 0 | node.validation_signals->CallFunctionInValidationInterfaceQueue([&promise] { |
106 | 0 | promise.set_value(); |
107 | 0 | }); |
108 | 0 | callback_set = true; |
109 | 0 | } |
110 | 0 | } |
111 | 0 | } // cs_main |
112 | | |
113 | 0 | if (callback_set) { |
114 | | // Wait until Validation Interface clients have been notified of the |
115 | | // transaction entering the mempool. |
116 | 0 | promise.get_future().wait(); |
117 | 0 | } |
118 | |
|
119 | 0 | if (relay) { |
120 | 0 | node.peerman->RelayTransaction(txid, wtxid); |
121 | 0 | } |
122 | |
|
123 | 0 | return TransactionError::OK; |
124 | 0 | } |
125 | | |
126 | | CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, uint256& hashBlock, const BlockManager& blockman) |
127 | 0 | { |
128 | 0 | if (mempool && !block_index) { |
129 | 0 | CTransactionRef ptx = mempool->get(hash); |
130 | 0 | if (ptx) return ptx; |
131 | 0 | } |
132 | 0 | if (g_txindex) { |
133 | 0 | CTransactionRef tx; |
134 | 0 | uint256 block_hash; |
135 | 0 | if (g_txindex->FindTx(hash, block_hash, tx)) { |
136 | 0 | if (!block_index || block_index->GetBlockHash() == block_hash) { |
137 | | // Don't return the transaction if the provided block hash doesn't match. |
138 | | // The case where a transaction appears in multiple blocks (e.g. reorgs or |
139 | | // BIP30) is handled by the block lookup below. |
140 | 0 | hashBlock = block_hash; |
141 | 0 | return tx; |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } |
145 | 0 | if (block_index) { |
146 | 0 | CBlock block; |
147 | 0 | if (blockman.ReadBlock(block, *block_index)) { |
148 | 0 | for (const auto& tx : block.vtx) { |
149 | 0 | if (tx->GetHash() == hash) { |
150 | 0 | hashBlock = block_index->GetBlockHash(); |
151 | 0 | return tx; |
152 | 0 | } |
153 | 0 | } |
154 | 0 | } |
155 | 0 | } |
156 | 0 | return nullptr; |
157 | 0 | } |
158 | | } // namespace node |