Coverage Report

Created: 2026-09-14 20:36

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 <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
namespace node {
19
static TransactionError HandleATMPError(const TxValidationState& state, std::string& err_string_out)
20
4.48k
{
21
4.48k
    err_string_out = state.ToString();
22
4.48k
    if (state.IsInvalid()) {
23
4.48k
        if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
24
9
            return TransactionError::MISSING_INPUTS;
25
9
        }
26
4.47k
        return TransactionError::MEMPOOL_REJECTED;
27
4.48k
    } else {
28
0
        return TransactionError::MEMPOOL_ERROR;
29
0
    }
30
4.48k
}
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
38.1k
{
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
38.1k
    assert(node.chainman);
43
38.1k
    assert(node.mempool);
44
38.1k
    assert(node.peerman);
45
46
38.1k
    Txid txid = tx->GetHash();
47
38.1k
    Wtxid wtxid = tx->GetWitnessHash();
48
38.1k
    bool callback_set = false;
49
50
38.1k
    {
51
38.1k
        LOCK(cs_main);
52
53
        // If the transaction is already confirmed in the chain, don't do anything
54
        // and return early.
55
38.1k
        CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
56
120k
        for (size_t o = 0; o < tx->vout.size(); o++) {
57
81.9k
            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
81.9k
            if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_UTXO_SET;
61
81.9k
        }
62
63
38.1k
        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
10.1k
            wtxid = mempool_tx->GetWitnessHash();
72
27.9k
        } else {
73
            // Transaction is not already in the mempool.
74
27.9k
            const bool check_max_fee{max_tx_fee > 0};
75
27.9k
            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
15.5k
                const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);
79
15.5k
                if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
80
141
                    return HandleATMPError(result.m_state, err_string);
81
15.4k
                } else if (check_max_fee && result.m_base_fees.value() > max_tx_fee) {
82
2
                    return TransactionError::MAX_FEE_EXCEEDED;
83
2
                }
84
15.5k
            }
85
86
27.8k
            switch (broadcast_method) {
87
25
            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.4k
                    node.mempool->AddUnbroadcastTx(txid);
103
13.4k
                }
104
13.4k
                break;
105
10.0k
            case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:
106
10.0k
                break;
107
27.8k
            }
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.8k
                callback_set = true;
119
21.8k
            }
120
23.4k
        }
121
38.1k
    } // cs_main
122
123
33.6k
    if (callback_set) {
124
        // Wait until Validation Interface clients have been notified of the
125
        // transaction entering the mempool.
126
21.8k
        node.validation_signals->SyncWithValidationInterfaceQueue();
127
21.8k
    }
128
129
33.6k
    switch (broadcast_method) {
130
126
    case TxBroadcast::MEMPOOL_NO_BROADCAST:
131
126
        break;
132
23.5k
    case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
133
23.5k
        node.peerman->InitiateTxBroadcastToAll(wtxid);
134
23.5k
        break;
135
10.0k
    case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:
136
10.0k
        return node.peerman->InitiateTxBroadcastPrivate(tx);
137
33.6k
    }
138
139
23.6k
    return TransactionError::OK;
140
33.6k
}
141
142
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const Txid& hash, const BlockManager& blockman, uint256& hashBlock)
143
3.77k
{
144
3.77k
    if (mempool && !block_index) {
145
3.74k
        CTransactionRef ptx = mempool->get(hash);
146
3.74k
        if (ptx) return ptx;
147
3.74k
    }
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