Bitcoin Core Fuzz Coverage Report

Coverage Report

Created: 2026-06-01 16:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/zip/work/bitcoin/src/node/psbt.cpp
Line
Count
Source
1
// Copyright (c) 2009-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#include <coins.h>
6
#include <consensus/amount.h>
7
#include <consensus/tx_verify.h>
8
#include <node/psbt.h>
9
#include <policy/policy.h>
10
#include <policy/settings.h>
11
#include <tinyformat.h>
12
13
#include <numeric>
14
15
namespace node {
16
PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
17
0
{
18
    // Go through each input and build status
19
0
    PSBTAnalysis result;
20
21
0
    std::optional<CMutableTransaction> unsigned_tx = psbtx.GetUnsignedTx();
22
0
    if (!unsigned_tx) {
23
0
        result.SetInvalid("PSBT cannot be made into a valid transaction");
24
0
        return result;
25
0
    }
26
0
    CMutableTransaction& mtx = *unsigned_tx;
27
28
0
    bool calc_fee = true;
29
30
0
    CAmount in_amt = 0;
31
32
0
    result.inputs.resize(psbtx.inputs.size());
33
34
    // PrecomputePSBTData calls GetUnsignedTx() which we checked already works
35
0
    const PrecomputedTransactionData txdata = *PrecomputePSBTData(psbtx);
36
37
0
    for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
38
0
        PSBTInput& input = psbtx.inputs[i];
39
0
        PSBTInputAnalysis& input_analysis = result.inputs[i];
40
41
        // We set next role here and ratchet backwards as required
42
0
        input_analysis.next = PSBTRole::EXTRACTOR;
43
44
        // Check for a UTXO
45
0
        CTxOut utxo;
46
0
        if (input.GetUTXO(utxo)) {
47
0
            if (!MoneyRange(utxo.nValue) || !MoneyRange(in_amt + utxo.nValue)) {
48
0
                result.SetInvalid(strprintf("PSBT is not valid. Input %u has invalid value", i));
Line
Count
Source
1172
0
#define strprintf tfm::format
49
0
                return result;
50
0
            }
51
0
            in_amt += utxo.nValue;
52
0
            input_analysis.has_utxo = true;
53
0
        } else {
54
0
            if (input.non_witness_utxo && input.prev_out >= input.non_witness_utxo->vout.size()) {
55
0
                result.SetInvalid(strprintf("PSBT is not valid. Input %u specifies invalid prevout", i));
Line
Count
Source
1172
0
#define strprintf tfm::format
56
0
                return result;
57
0
            }
58
0
            input_analysis.has_utxo = false;
59
0
            input_analysis.is_final = false;
60
0
            input_analysis.next = PSBTRole::UPDATER;
61
0
            calc_fee = false;
62
0
        }
63
64
0
        if (!utxo.IsNull() && utxo.scriptPubKey.IsUnspendable()) {
65
0
            result.SetInvalid(strprintf("PSBT is not valid. Input %u spends unspendable output", i));
Line
Count
Source
1172
0
#define strprintf tfm::format
66
0
            return result;
67
0
        }
68
69
        // Check if it is final
70
0
        if (!PSBTInputSignedAndVerified(psbtx, i, &txdata)) {
71
0
            input_analysis.is_final = false;
72
73
            // Figure out what is missing
74
0
            SignatureData outdata;
75
0
            bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, /*options=*/{}, &outdata) == PSBTError::OK;
76
77
            // Things are missing
78
0
            if (!complete) {
79
0
                input_analysis.missing_pubkeys = outdata.missing_pubkeys;
80
0
                input_analysis.missing_redeem_script = outdata.missing_redeem_script;
81
0
                input_analysis.missing_witness_script = outdata.missing_witness_script;
82
0
                input_analysis.missing_sigs = outdata.missing_sigs;
83
84
                // If we are only missing signatures and nothing else, then next is signer
85
0
                if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
86
0
                    input_analysis.next = PSBTRole::SIGNER;
87
0
                } else {
88
0
                    input_analysis.next = PSBTRole::UPDATER;
89
0
                }
90
0
            } else {
91
0
                input_analysis.next = PSBTRole::FINALIZER;
92
0
            }
93
0
        } else if (!utxo.IsNull()){
94
0
            input_analysis.is_final = true;
95
0
        }
96
0
    }
97
98
    // Calculate next role for PSBT by grabbing "minimum" PSBTInput next role
99
0
    result.next = PSBTRole::EXTRACTOR;
100
0
    for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
101
0
        PSBTInputAnalysis& input_analysis = result.inputs[i];
102
0
        result.next = std::min(result.next, input_analysis.next);
103
0
    }
104
0
    assert(result.next > PSBTRole::CREATOR);
105
106
0
    if (calc_fee) {
107
        // Get the output amount
108
0
        CAmount out_amt = std::accumulate(psbtx.outputs.begin(), psbtx.outputs.end(), CAmount(0),
109
0
            [](CAmount a, const PSBTOutput& b) {
110
0
                if (!MoneyRange(a) || !MoneyRange(b.amount) || !MoneyRange(a + b.amount)) {
111
0
                    return CAmount(-1);
112
0
                }
113
0
                return a += b.amount;
114
0
            }
115
0
        );
116
0
        if (!MoneyRange(out_amt)) {
117
0
            result.SetInvalid("PSBT is not valid. Output amount invalid");
118
0
            return result;
119
0
        }
120
121
        // Get the fee
122
0
        CAmount fee = in_amt - out_amt;
123
0
        result.fee = fee;
124
125
        // Estimate the size
126
0
        CCoinsViewCache view{&CoinsViewEmpty::Get()};
127
0
        bool success = true;
128
129
0
        for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
130
0
            PSBTInput& input = psbtx.inputs[i];
131
0
            Coin newcoin;
132
133
0
            if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, /*options=*/{}) != PSBTError::OK || !input.GetUTXO(newcoin.out)) {
134
0
                success = false;
135
0
                break;
136
0
            } else {
137
0
                mtx.vin[i].scriptSig = input.final_script_sig;
138
0
                mtx.vin[i].scriptWitness = input.final_script_witness;
139
0
                newcoin.nHeight = 1;
140
0
                view.AddCoin(input.GetOutPoint(), std::move(newcoin), true);
141
0
            }
142
0
        }
143
144
0
        if (success) {
145
0
            CTransaction ctx = CTransaction(mtx);
146
0
            size_t size(GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS), ::nBytesPerSigOp));
147
0
            result.estimated_vsize = size;
148
            // Estimate fee rate
149
0
            CFeeRate feerate(fee, size);
150
0
            result.estimated_feerate = feerate;
151
0
        }
152
153
0
    }
154
155
0
    return result;
156
0
}
157
} // namespace node