Coverage Report

Created: 2026-04-29 19:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/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
{
18
    // Go through each input and build status
19
8
    PSBTAnalysis result;
20
21
8
    bool calc_fee = true;
22
23
8
    CAmount in_amt = 0;
24
25
8
    result.inputs.resize(psbtx.tx->vin.size());
26
27
8
    const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
28
29
15
    for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
30
9
        PSBTInput& input = psbtx.inputs[i];
31
9
        PSBTInputAnalysis& input_analysis = result.inputs[i];
32
33
        // We set next role here and ratchet backwards as required
34
9
        input_analysis.next = PSBTRole::EXTRACTOR;
35
36
        // Check for a UTXO
37
9
        CTxOut utxo;
38
9
        if (psbtx.GetInputUTXO(utxo, i)) {
39
6
            if (!MoneyRange(utxo.nValue) || !MoneyRange(in_amt + utxo.nValue)) {
40
1
                result.SetInvalid(strprintf("PSBT is not valid. Input %u has invalid value", i));
41
1
                return result;
42
1
            }
43
5
            in_amt += utxo.nValue;
44
5
            input_analysis.has_utxo = true;
45
5
        } else {
46
3
            if (input.non_witness_utxo && psbtx.tx->vin[i].prevout.n >= input.non_witness_utxo->vout.size()) {
47
0
                result.SetInvalid(strprintf("PSBT is not valid. Input %u specifies invalid prevout", i));
48
0
                return result;
49
0
            }
50
3
            input_analysis.has_utxo = false;
51
3
            input_analysis.is_final = false;
52
3
            input_analysis.next = PSBTRole::UPDATER;
53
3
            calc_fee = false;
54
3
        }
55
56
8
        if (!utxo.IsNull() && utxo.scriptPubKey.IsUnspendable()) {
57
1
            result.SetInvalid(strprintf("PSBT is not valid. Input %u spends unspendable output", i));
58
1
            return result;
59
1
        }
60
61
        // Check if it is final
62
7
        if (!PSBTInputSignedAndVerified(psbtx, i, &txdata)) {
63
6
            input_analysis.is_final = false;
64
65
            // Figure out what is missing
66
6
            SignatureData outdata;
67
6
            bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, /*options=*/{}, &outdata) == PSBTError::OK;
68
69
            // Things are missing
70
6
            if (!complete) {
71
5
                input_analysis.missing_pubkeys = outdata.missing_pubkeys;
72
5
                input_analysis.missing_redeem_script = outdata.missing_redeem_script;
73
5
                input_analysis.missing_witness_script = outdata.missing_witness_script;
74
5
                input_analysis.missing_sigs = outdata.missing_sigs;
75
76
                // If we are only missing signatures and nothing else, then next is signer
77
5
                if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
78
1
                    input_analysis.next = PSBTRole::SIGNER;
79
4
                } else {
80
4
                    input_analysis.next = PSBTRole::UPDATER;
81
4
                }
82
5
            } else {
83
1
                input_analysis.next = PSBTRole::FINALIZER;
84
1
            }
85
6
        } else if (!utxo.IsNull()){
86
1
            input_analysis.is_final = true;
87
1
        }
88
7
    }
89
90
    // Calculate next role for PSBT by grabbing "minimum" PSBTInput next role
91
6
    result.next = PSBTRole::EXTRACTOR;
92
13
    for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
93
7
        PSBTInputAnalysis& input_analysis = result.inputs[i];
94
7
        result.next = std::min(result.next, input_analysis.next);
95
7
    }
96
6
    assert(result.next > PSBTRole::CREATOR);
97
98
6
    if (calc_fee) {
99
        // Get the output amount
100
4
        CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0),
101
6
            [](CAmount a, const CTxOut& b) {
102
6
                if (!MoneyRange(a) || !MoneyRange(b.nValue) || !MoneyRange(a + b.nValue)) {
103
2
                    return CAmount(-1);
104
2
                }
105
4
                return a += b.nValue;
106
6
            }
107
4
        );
108
4
        if (!MoneyRange(out_amt)) {
109
1
            result.SetInvalid("PSBT is not valid. Output amount invalid");
110
1
            return result;
111
1
        }
112
113
        // Get the fee
114
3
        CAmount fee = in_amt - out_amt;
115
3
        result.fee = fee;
116
117
        // Estimate the size
118
3
        CMutableTransaction mtx(*psbtx.tx);
119
3
        CCoinsViewCache view{&CoinsViewEmpty::Get()};
120
3
        bool success = true;
121
122
6
        for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
123
3
            PSBTInput& input = psbtx.inputs[i];
124
3
            Coin newcoin;
125
126
3
            if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, /*options=*/{}) != PSBTError::OK || !psbtx.GetInputUTXO(newcoin.out, i)) {
127
0
                success = false;
128
0
                break;
129
3
            } else {
130
3
                mtx.vin[i].scriptSig = input.final_script_sig;
131
3
                mtx.vin[i].scriptWitness = input.final_script_witness;
132
3
                newcoin.nHeight = 1;
133
3
                view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
134
3
            }
135
3
        }
136
137
3
        if (success) {
138
3
            CTransaction ctx = CTransaction(mtx);
139
3
            size_t size(GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS), ::nBytesPerSigOp));
140
3
            result.estimated_vsize = size;
141
            // Estimate fee rate
142
3
            CFeeRate feerate(fee, size);
143
3
            result.estimated_feerate = feerate;
144
3
        }
145
146
3
    }
147
148
5
    return result;
149
6
}
150
} // namespace node