Coverage Report

Created: 2026-04-29 19:21

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.34k
{
21
4.34k
    err_string_out = state.ToString();
22
4.34k
    if (state.IsInvalid()) {
23
4.34k
        if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
24
9
            return TransactionError::MISSING_INPUTS;
25
9
        }
26
4.33k
        return TransactionError::MEMPOOL_REJECTED;
27
4.34k
    } else {
28
0
        return TransactionError::MEMPOOL_ERROR;
29
0
    }
30
4.34k
}
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
25.4k
{
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
25.4k
    assert(node.chainman);
43
25.4k
    assert(node.mempool);
44
25.4k
    assert(node.peerman);
45
46
25.4k
    Txid txid = tx->GetHash();
47
25.4k
    Wtxid wtxid = tx->GetWitnessHash();
48
25.4k
    bool callback_set = false;
49
50
25.4k
    {
51
25.4k
        LOCK(cs_main);
52
53
        // If the transaction is already confirmed in the chain, don't do anything
54
        // and return early.
55
25.4k
        CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
56
94.9k
        for (size_t o = 0; o < tx->vout.size(); o++) {
57
69.4k
            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
69.4k
            if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_UTXO_SET;
61
69.4k
        }
62
63
25.4k
        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
9.46k
            wtxid = mempool_tx->GetWitnessHash();
72
16.0k
        } else {
73
            // Transaction is not already in the mempool.
74
16.0k
            const bool check_max_fee{max_tx_fee > 0};
75
16.0k
            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
4.04k
                const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);
79
4.04k
                if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
80
131
                    return HandleATMPError(result.m_state, err_string);
81
3.91k
                } else if (check_max_fee && result.m_base_fees.value() > max_tx_fee) {
82
2
                    return TransactionError::MAX_FEE_EXCEEDED;
83
2
                }
84
4.04k
            }
85
86
15.9k
            switch (broadcast_method) {
87
20
            case TxBroadcast::MEMPOOL_NO_BROADCAST:
88
15.8k
            case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
89
                // Try to submit the transaction to the mempool.
90
15.8k
                {
91
15.8k
                    const MempoolAcceptResult result =
92
15.8k
                        node.chainman->ProcessTransaction(tx, /*test_accept=*/false);
93
15.8k
                    if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
94
4.21k
                        return HandleATMPError(result.m_state, err_string);
95
4.21k
                    }
96
15.8k
                }
97
                // Transaction was accepted to the mempool.
98
99
11.6k
                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
11.6k
                    node.mempool->AddUnbroadcastTx(txid);
103
11.6k
                }
104
11.6k
                break;
105
6
            case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:
106
6
                break;
107
15.9k
            }
108
109
11.6k
            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
10.1k
                callback_set = true;
119
10.1k
            }
120
11.6k
        }
121
25.4k
    } // cs_main
122
123
21.1k
    if (callback_set) {
124
        // Wait until Validation Interface clients have been notified of the
125
        // transaction entering the mempool.
126
10.1k
        node.validation_signals->SyncWithValidationInterfaceQueue();
127
10.1k
    }
128
129
21.1k
    switch (broadcast_method) {
130
107
    case TxBroadcast::MEMPOOL_NO_BROADCAST:
131
107
        break;
132
21.0k
    case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:
133
21.0k
        node.peerman->InitiateTxBroadcastToAll(txid, wtxid);
134
21.0k
        break;
135
7
    case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:
136
7
        node.peerman->InitiateTxBroadcastPrivate(tx);
137
7
        break;
138
21.1k
    }
139
140
21.1k
    return TransactionError::OK;
141
21.1k
}
142
143
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const Txid& hash, const BlockManager& blockman, uint256& hashBlock)
144
3.17k
{
145
3.17k
    if (mempool && !block_index) {
146
3.13k
        CTransactionRef ptx = mempool->get(hash);
147
3.13k
        if (ptx) return ptx;
148
3.13k
    }
149
70
    if (g_txindex) {
150
32
        CTransactionRef tx;
151
32
        uint256 block_hash;
152
32
        if (g_txindex->FindTx(hash, block_hash, tx)) {
153
32
            if (!block_index || block_index->GetBlockHash() == block_hash) {
154
                // Don't return the transaction if the provided block hash doesn't match.
155
                // The case where a transaction appears in multiple blocks (e.g. reorgs or
156
                // BIP30) is handled by the block lookup below.
157
31
                hashBlock = block_hash;
158
31
                return tx;
159
31
            }
160
32
        }
161
32
    }
162
39
    if (block_index) {
163
29
        CBlock block;
164
29
        if (blockman.ReadBlock(block, *block_index)) {
165
56
            for (const auto& tx : block.vtx) {
166
56
                if (tx->GetHash() == hash) {
167
27
                    hashBlock = block_index->GetBlockHash();
168
27
                    return tx;
169
27
                }
170
56
            }
171
29
        }
172
29
    }
173
12
    return nullptr;
174
39
}
175
} // namespace node