Coverage Report

Created: 2026-10-07 20:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/node/transaction.cpp
Line
Count
Source
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 <node/transaction.h>
7
8
#include <consensus/validation.h>
9
#include <index/txindex.h>
10
#include <net.h>
11
#include <net_processing.h>
12
#include <node/blockstorage.h>
13
#include <node/context.h>
14
#include <node/types.h>
15
#include <policy/feerate.h>
16
#include <txmempool.h>
17
#include <validation.h>
18
#include <validationinterface.h>
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()) {
  Branch (24:9): [True: 0, False: 0]
25
0
        if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
  Branch (25:13): [True: 0, False: 0]
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, const CFeeRate& max_tx_fee_rate, TxBroadcast broadcast_method, 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);
  Branch (39:5): [True: 0, False: 0]
40
0
    assert(node.mempool);
  Branch (40:5): [True: 0, False: 0]
41
0
    assert(node.peerman);
  Branch (41:5): [True: 0, False: 0]
42
43
0
    Txid txid = tx->GetHash();
44
0
    Wtxid wtxid = tx->GetWitnessHash();
45
0
    bool callback_set = false;
46
47
0
    {
48
0
        LOCK(cs_main);
49
50
        // If the transaction is already confirmed in the chain, don't do anything
51
        // and return early.
52
0
        CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
53
0
        for (size_t o = 0; o < tx->vout.size(); o++) {
  Branch (53:28): [True: 0, False: 0]
54
0
            const Coin& existingCoin = view.AccessCoin(COutPoint(txid, o));
55
            // IsSpent doesn't mean the coin is spent, it means the output doesn't exist.
56
            // So if the output does exist, then this transaction exists in the chain.
57
0
            if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_UTXO_SET;
  Branch (57:17): [True: 0, False: 0]
58
0
        }
59
60
0
        if (auto mempool_tx = node.mempool->get(txid); mempool_tx) {
  Branch (60:56): [True: 0, False: 0]
61
            // There's already a transaction in the mempool with this txid. Don't
62
            // try to submit this transaction to the mempool (since it'll be
63
            // rejected as a TX_CONFLICT), but do attempt to reannounce the mempool
64
            // transaction if broadcast_method is not TxBroadcast::MEMPOOL_NO_BROADCAST.
65
            //
66
            // The mempool transaction may have the same or different witness (and
67
            // wtxid) as this transaction. Use the mempool's wtxid for reannouncement.
68
0
            wtxid = mempool_tx->GetWitnessHash();
69
0
        } else {
70
            // Transaction is not already in the mempool.
71
0
            const bool check_max_fee{max_tx_fee > 0};
72
0
            const bool check_max_feerate{max_tx_fee_rate > CFeeRate(0)};
73
0
            if (check_max_fee || check_max_feerate || broadcast_method == TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST) {
  Branch (73:17): [True: 0, False: 0]
  Branch (73:34): [True: 0, False: 0]
  Branch (73:55): [True: 0, False: 0]
74
                // First, call ATMP with test_accept and check the fee. If ATMP
75
                // fails here, return error immediately.
76
0
                const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);
77
0
                if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
  Branch (77:21): [True: 0, False: 0]
78
0
                    return HandleATMPError(result.m_state, err_string);
79
0
                } else if (check_max_fee && result.m_base_fees.value() > max_tx_fee) {
  Branch (79:28): [True: 0, False: 0]
  Branch (79:45): [True: 0, False: 0]
80
0
                    return TransactionError::MAX_FEE_EXCEEDED;
81
0
                } else if (check_max_feerate && result.m_base_fees.value() > max_tx_fee_rate.GetFee(result.m_vsize.value())) {
  Branch (81:28): [True: 0, False: 0]
  Branch (81:49): [True: 0, False: 0]
82
0
                    return TransactionError::MAX_FEE_RATE_EXCEEDED;
83
0
                }
84
0
            }
85
86
0
            switch (broadcast_method) {
  Branch (86:21): [True: 0, False: 0]
87
0
            case TxBroadcast::MEMPOOL_NO_BROADCAST:
  Branch (87:13): [True: 0, False: 0]
88
0
            case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
  Branch (88:13): [True: 0, False: 0]
89
                // Try to submit the transaction to the mempool.
90
0
                {
91
0
                    const MempoolAcceptResult result =
92
0
                        node.chainman->ProcessTransaction(tx, /*test_accept=*/false);
93
0
                    if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
  Branch (93:25): [True: 0, False: 0]
94
0
                        return HandleATMPError(result.m_state, err_string);
95
0
                    }
96
0
                }
97
                // Transaction was accepted to the mempool.
98
99
0
                if (broadcast_method == TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL) {
  Branch (99:21): [True: 0, False: 0]
100
                    // the mempool tracks locally submitted transactions to make a
101
                    // best-effort of initial broadcast
102
0
                    node.mempool->AddUnbroadcastTx(txid);
103
0
                }
104
0
                break;
105
0
            case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:
  Branch (105:13): [True: 0, False: 0]
106
0
                break;
107
0
            }
108
109
0
            if (wait_callback && node.validation_signals) {
  Branch (109:17): [True: 0, False: 0]
  Branch (109:34): [True: 0, False: 0]
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
                callback_set = true;
119
0
            }
120
0
        }
121
0
    } // cs_main
122
123
0
    if (callback_set) {
  Branch (123:9): [True: 0, False: 0]
124
        // Wait until Validation Interface clients have been notified of the
125
        // transaction entering the mempool.
126
0
        node.validation_signals->SyncWithValidationInterfaceQueue();
127
0
    }
128
129
0
    switch (broadcast_method) {
  Branch (129:13): [True: 0, False: 0]
130
0
    case TxBroadcast::MEMPOOL_NO_BROADCAST:
  Branch (130:5): [True: 0, False: 0]
131
0
        break;
132
0
    case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
  Branch (132:5): [True: 0, False: 0]
133
0
        node.peerman->InitiateTxBroadcastToAll(wtxid);
134
0
        break;
135
0
    case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:
  Branch (135:5): [True: 0, False: 0]
136
0
        return node.peerman->InitiateTxBroadcastPrivate(tx);
137
0
    }
138
139
0
    return TransactionError::OK;
140
0
}
141
142
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const Txid& hash, const BlockManager& blockman, uint256& hashBlock)
143
0
{
144
0
    if (mempool && !block_index) {
  Branch (144:9): [True: 0, False: 0]
  Branch (144:20): [True: 0, False: 0]
145
0
        CTransactionRef ptx = mempool->get(hash);
146
0
        if (ptx) return ptx;
  Branch (146:13): [True: 0, False: 0]
147
0
    }
148
0
    if (g_txindex) {
  Branch (148:9): [True: 0, False: 0]
149
0
        if (auto result{g_txindex->FindTx(hash)}) {
  Branch (149:18): [True: 0, False: 0]
150
0
            if (!block_index || block_index->GetBlockHash() == result->block_hash) {
  Branch (150:17): [True: 0, False: 0]
  Branch (150:17): [True: 0, False: 0]
  Branch (150:33): [True: 0, False: 0]
151
                // Don't return the transaction if the provided block hash doesn't match.
152
                // The case where a transaction appears in multiple blocks (e.g. reorgs or
153
                // BIP30) is handled by the block lookup below.
154
0
                hashBlock = result->block_hash;
155
0
                return result->tx;
156
0
            }
157
0
        }
158
0
    }
159
0
    if (block_index) {
  Branch (159:9): [True: 0, False: 0]
160
0
        CBlock block;
161
0
        if (blockman.ReadBlock(block, *block_index)) {
  Branch (161:13): [True: 0, False: 0]
162
0
            for (const auto& tx : block.vtx) {
  Branch (162:33): [True: 0, False: 0]
163
0
                if (tx->GetHash() == hash) {
  Branch (163:21): [True: 0, False: 0]
164
0
                    hashBlock = block_index->GetBlockHash();
165
0
                    return tx;
166
0
                }
167
0
            }
168
0
        }
169
0
    }
170
0
    return nullptr;
171
0
}
172
} // namespace node