Coverage Report

Created: 2026-10-07 13:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/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
4.47k
{
23
4.47k
    err_string_out = state.ToString();
24
4.47k
    if (state.IsInvalid()) {
25
4.47k
        if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
26
9
            return TransactionError::MISSING_INPUTS;
27
9
        }
28
4.46k
        return TransactionError::MEMPOOL_REJECTED;
29
4.47k
    } else {
30
0
        return TransactionError::MEMPOOL_ERROR;
31
0
    }
32
4.47k
}
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
38.9k
{
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
38.9k
    assert(node.chainman);
40
38.9k
    assert(node.mempool);
41
38.9k
    assert(node.peerman);
42
43
38.9k
    Txid txid = tx->GetHash();
44
38.9k
    Wtxid wtxid = tx->GetWitnessHash();
45
38.9k
    bool callback_set = false;
46
47
38.9k
    {
48
38.9k
        LOCK(cs_main);
49
50
        // If the transaction is already confirmed in the chain, don't do anything
51
        // and return early.
52
38.9k
        CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
53
121k
        for (size_t o = 0; o < tx->vout.size(); o++) {
54
82.9k
            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
82.9k
            if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_UTXO_SET;
58
82.9k
        }
59
60
38.9k
        if (auto mempool_tx = node.mempool->get(txid); mempool_tx) {
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
11.0k
            wtxid = mempool_tx->GetWitnessHash();
69
27.9k
        } else {
70
            // Transaction is not already in the mempool.
71
27.9k
            const bool check_max_fee{max_tx_fee > 0};
72
27.9k
            const bool check_max_feerate{max_tx_fee_rate > CFeeRate(0)};
73
27.9k
            if (check_max_fee || check_max_feerate || broadcast_method == TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST) {
74
                // First, call ATMP with test_accept and check the fee. If ATMP
75
                // fails here, return error immediately.
76
15.7k
                const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);
77
15.7k
                if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
78
144
                    return HandleATMPError(result.m_state, err_string);
79
15.5k
                } else if (check_max_fee && result.m_base_fees.value() > max_tx_fee) {
80
0
                    return TransactionError::MAX_FEE_EXCEEDED;
81
15.5k
                } else if (check_max_feerate && result.m_base_fees.value() > max_tx_fee_rate.GetFee(result.m_vsize.value())) {
82
4
                    return TransactionError::MAX_FEE_RATE_EXCEEDED;
83
4
                }
84
15.7k
            }
85
86
27.7k
            switch (broadcast_method) {
87
23
            case TxBroadcast::MEMPOOL_NO_BROADCAST:
88
17.7k
            case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
89
                // Try to submit the transaction to the mempool.
90
17.7k
                {
91
17.7k
                    const MempoolAcceptResult result =
92
17.7k
                        node.chainman->ProcessTransaction(tx, /*test_accept=*/false);
93
17.7k
                    if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
94
4.33k
                        return HandleATMPError(result.m_state, err_string);
95
4.33k
                    }
96
17.7k
                }
97
                // Transaction was accepted to the mempool.
98
99
13.4k
                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
13.3k
                    node.mempool->AddUnbroadcastTx(txid);
103
13.3k
                }
104
13.4k
                break;
105
10.0k
            case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:
106
10.0k
                break;
107
27.7k
            }
108
109
23.4k
            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
21.7k
                callback_set = true;
119
21.7k
            }
120
23.4k
        }
121
38.9k
    } // cs_main
122
123
34.4k
    if (callback_set) {
124
        // Wait until Validation Interface clients have been notified of the
125
        // transaction entering the mempool.
126
21.7k
        node.validation_signals->SyncWithValidationInterfaceQueue();
127
21.7k
    }
128
129
34.4k
    switch (broadcast_method) {
130
133
    case TxBroadcast::MEMPOOL_NO_BROADCAST:
131
133
        break;
132
24.3k
    case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
133
24.3k
        node.peerman->InitiateTxBroadcastToAll(wtxid);
134
24.3k
        break;
135
10.0k
    case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:
136
10.0k
        return node.peerman->InitiateTxBroadcastPrivate(tx);
137
34.4k
    }
138
139
24.4k
    return TransactionError::OK;
140
34.4k
}
141
142
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const Txid& hash, const BlockManager& blockman, uint256& hashBlock)
143
3.34k
{
144
3.34k
    if (mempool && !block_index) {
145
3.30k
        CTransactionRef ptx = mempool->get(hash);
146
3.30k
        if (ptx) return ptx;
147
3.30k
    }
148
81
    if (g_txindex) {
149
42
        if (auto result{g_txindex->FindTx(hash)}) {
150
40
            if (!block_index || block_index->GetBlockHash() == result->block_hash) {
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
39
                hashBlock = result->block_hash;
155
39
                return result->tx;
156
39
            }
157
40
        }
158
42
    }
159
42
    if (block_index) {
160
30
        CBlock block;
161
30
        if (blockman.ReadBlock(block, *block_index)) {
162
58
            for (const auto& tx : block.vtx) {
163
58
                if (tx->GetHash() == hash) {
164
28
                    hashBlock = block_index->GetBlockHash();
165
28
                    return tx;
166
28
                }
167
58
            }
168
30
        }
169
30
    }
170
14
    return nullptr;
171
42
}
172
} // namespace node