| 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. |
no test coverage detected