Coverage Report

Created: 2026-09-01 13:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/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
8.30k
{
18
    // Go through each input and build status
19
8.30k
    PSBTAnalysis result;
20
21
8.30k
    std::optional<CMutableTransaction> unsigned_tx = psbtx.GetUnsignedTx();
22
8.30k
    if (!unsigned_tx) {
  Branch (22:9): [True: 0, False: 8.30k]
23
0
        result.SetInvalid("PSBT cannot be made into a valid transaction");
24
0
        return result;
25
0
    }
26
8.30k
    CMutableTransaction& mtx = *unsigned_tx;
27
28
8.30k
    bool calc_fee = true;
29
30
8.30k
    CAmount in_amt = 0;
31
32
8.30k
    result.inputs.resize(psbtx.inputs.size());
33
34
    // PrecomputePSBTData calls GetUnsignedTx() which we checked already works
35
8.30k
    const PrecomputedTransactionData txdata = *PrecomputePSBTData(psbtx);
36
37
19.9k
    for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
  Branch (37:30): [True: 13.3k, False: 6.56k]
38
13.3k
        PSBTInput& input = psbtx.inputs[i];
39
13.3k
        PSBTInputAnalysis& input_analysis = result.inputs[i];
40
41
        // We set next role here and ratchet backwards as required
42
13.3k
        input_analysis.next = PSBTRole::EXTRACTOR;
43
44
        // Check for a UTXO
45
13.3k
        CTxOut utxo;
46
13.3k
        if (input.GetUTXO(utxo)) {
  Branch (46:13): [True: 8.16k, False: 5.20k]
47
8.16k
            if (!MoneyRange(utxo.nValue) || !MoneyRange(in_amt + utxo.nValue)) {
  Branch (47:17): [True: 1.68k, False: 6.47k]
  Branch (47:17): [True: 1.68k, False: 6.47k]
  Branch (47:45): [True: 4, False: 6.47k]
48
1.68k
                result.SetInvalid(strprintf("PSBT is not valid. Input %u has invalid value", i));
49
1.68k
                return result;
50
1.68k
            }
51
6.47k
            in_amt += utxo.nValue;
52
6.47k
            input_analysis.has_utxo = true;
53
6.47k
        } else {
54
5.20k
            if (input.non_witness_utxo && input.prev_out >= input.non_witness_utxo->vout.size()) {
  Branch (54:17): [True: 0, False: 5.20k]
  Branch (54:43): [True: 0, False: 0]
55
0
                result.SetInvalid(strprintf("PSBT is not valid. Input %u specifies invalid prevout", i));
56
0
                return result;
57
0
            }
58
5.20k
            input_analysis.has_utxo = false;
59
5.20k
            input_analysis.is_final = false;
60
5.20k
            input_analysis.next = PSBTRole::UPDATER;
61
5.20k
            calc_fee = false;
62
5.20k
        }
63
64
11.6k
        if (!utxo.IsNull() && utxo.scriptPubKey.IsUnspendable()) {
  Branch (64:13): [True: 6.47k, False: 5.20k]
  Branch (64:31): [True: 51, False: 6.42k]
65
51
            result.SetInvalid(strprintf("PSBT is not valid. Input %u spends unspendable output", i));
66
51
            return result;
67
51
        }
68
69
        // Check if it is final
70
11.6k
        if (!PSBTInputSignedAndVerified(psbtx, i, &txdata)) {
  Branch (70:13): [True: 10.8k, False: 790]
71
10.8k
            input_analysis.is_final = false;
72
73
            // Figure out what is missing
74
10.8k
            SignatureData outdata;
75
10.8k
            const auto sign_result = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, /*options*/{}, &outdata);
76
10.8k
            bool complete = sign_result.has_value();
77
78
            // Things are missing
79
10.8k
            if (!complete) {
  Branch (79:17): [True: 10.6k, False: 150]
80
10.6k
                input_analysis.missing_pubkeys = outdata.missing_pubkeys;
81
10.6k
                input_analysis.missing_redeem_script = outdata.missing_redeem_script;
82
10.6k
                input_analysis.missing_witness_script = outdata.missing_witness_script;
83
10.6k
                input_analysis.missing_sigs = outdata.missing_sigs;
84
85
                // If we are only missing signatures and nothing else, then next is signer
86
10.6k
                if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
  Branch (86:21): [True: 10.0k, False: 627]
  Branch (86:56): [True: 10.0k, False: 16]
  Branch (86:98): [True: 10.0k, False: 0]
  Branch (86:141): [True: 462, False: 9.58k]
87
462
                    input_analysis.next = PSBTRole::SIGNER;
88
10.2k
                } else {
89
10.2k
                    input_analysis.next = PSBTRole::UPDATER;
90
10.2k
                }
91
10.6k
            } else {
92
150
                input_analysis.next = PSBTRole::FINALIZER;
93
150
            }
94
10.8k
        } else if (!utxo.IsNull()){
  Branch (94:20): [True: 790, False: 0]
95
790
            input_analysis.is_final = true;
96
790
        }
97
11.6k
    }
98
99
    // Calculate next role for PSBT by grabbing "minimum" PSBTInput next role
100
6.56k
    result.next = PSBTRole::EXTRACTOR;
101
17.6k
    for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
  Branch (101:30): [True: 11.1k, False: 6.56k]
102
11.1k
        PSBTInputAnalysis& input_analysis = result.inputs[i];
103
11.1k
        result.next = std::min(result.next, input_analysis.next);
104
11.1k
    }
105
6.56k
    assert(result.next > PSBTRole::CREATOR);
  Branch (105:5): [True: 6.56k, False: 0]
106
107
6.56k
    if (calc_fee) {
  Branch (107:9): [True: 4.71k, False: 1.85k]
108
        // Get the output amount
109
4.71k
        CAmount out_amt = std::accumulate(psbtx.outputs.begin(), psbtx.outputs.end(), CAmount(0),
110
10.3k
            [](CAmount a, const PSBTOutput& b) {
111
10.3k
                if (!MoneyRange(a) || !MoneyRange(b.amount) || !MoneyRange(a + b.amount)) {
  Branch (111:21): [True: 5.64k, False: 4.69k]
  Branch (111:21): [True: 7.57k, False: 2.76k]
  Branch (111:39): [True: 1.92k, False: 2.76k]
  Branch (111:64): [True: 4, False: 2.76k]
112
7.57k
                    return CAmount(-1);
113
7.57k
                }
114
2.76k
                return a += b.amount;
115
10.3k
            }
116
4.71k
        );
117
4.71k
        if (!MoneyRange(out_amt)) {
  Branch (117:13): [True: 1.93k, False: 2.78k]
118
1.93k
            result.SetInvalid("PSBT is not valid. Output amount invalid");
119
1.93k
            return result;
120
1.93k
        }
121
122
        // Get the fee
123
2.78k
        CAmount fee = in_amt - out_amt;
124
2.78k
        result.fee = fee;
125
126
        // Estimate the size
127
2.78k
        CCoinsViewCache view{&CoinsViewEmpty::Get()};
128
2.78k
        bool success = true;
129
130
3.99k
        for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
  Branch (130:34): [True: 3.13k, False: 858]
131
3.13k
            PSBTInput& input = psbtx.inputs[i];
132
3.13k
            Coin newcoin;
133
134
3.13k
            const auto sign_result = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, /*options=*/{});
135
3.13k
            if (!sign_result.has_value() || !input.GetUTXO(newcoin.out)) {
  Branch (135:17): [True: 1.92k, False: 1.20k]
  Branch (135:45): [True: 0, False: 1.20k]
136
1.92k
                success = false;
137
1.92k
                break;
138
1.92k
            } else {
139
1.20k
                mtx.vin[i].scriptSig = input.final_script_sig;
140
1.20k
                mtx.vin[i].scriptWitness = input.final_script_witness;
141
1.20k
                newcoin.nHeight = 1;
142
1.20k
                view.AddCoin(input.GetOutPoint(), std::move(newcoin), true);
143
1.20k
            }
144
3.13k
        }
145
146
2.78k
        if (success) {
  Branch (146:13): [True: 858, False: 1.92k]
147
858
            CTransaction ctx = CTransaction(mtx);
148
858
            size_t size(GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS), ::nBytesPerSigOp));
149
858
            result.estimated_vsize = size;
150
            // Estimate fee rate
151
858
            CFeeRate feerate(fee, size);
152
858
            result.estimated_feerate = feerate;
153
858
        }
154
155
2.78k
    }
156
157
4.63k
    return result;
158
6.56k
}
159
} // namespace node