forked from ElementsProject/elements
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpsbt.cpp
More file actions
171 lines (147 loc) · 6.27 KB
/
psbt.cpp
File metadata and controls
171 lines (147 loc) · 6.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <coins.h>
#include <consensus/amount.h>
#include <consensus/tx_verify.h>
#include <node/psbt.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <tinyformat.h>
#include <numeric>
namespace node {
PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
{
// Go through each input and build status
PSBTAnalysis result;
// Elements things
bool needs_blinded_outputs = false;
bool has_blinded_outputs = false;
result.inputs.resize(psbtx.inputs.size());
result.next = PSBTRole::EXTRACTOR;
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
PSBTInput& input = psbtx.inputs[i];
PSBTInputAnalysis& input_analysis = result.inputs[i];
// We set next role here and ratchet backwards as required
input_analysis.next = PSBTRole::EXTRACTOR;
// Check for a UTXO
CTxOut utxo;
if (input.GetUTXO(utxo)) {
if (utxo.nValue.IsExplicit()) {
if (!MoneyRange(utxo.nValue.GetAmount())) {
result.SetInvalid(strprintf("PSBT is not valid. Input %u has invalid value", i));
return result;
}
} else {
needs_blinded_outputs = true;
}
input_analysis.has_utxo = true;
} else {
if (input.non_witness_utxo && *input.prev_out >= input.non_witness_utxo->vout.size()) {
result.SetInvalid(strprintf("PSBT is not valid. Input %u specifies invalid prevout", i));
return result;
}
input_analysis.has_utxo = false;
input_analysis.is_final = false;
input_analysis.next = PSBTRole::UPDATER;
}
if (!utxo.IsNull() && utxo.scriptPubKey.IsUnspendable()) {
result.SetInvalid(strprintf("PSBT is not valid. Input %u spends unspendable output", i));
return result;
}
// Check if it is final
if (!utxo.IsNull() && !PSBTInputSigned(input)) {
input_analysis.is_final = false;
// Figure out what is missing
SignatureData outdata;
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, 1, &outdata);
// Things are missing
if (!complete) {
input_analysis.missing_pubkeys = outdata.missing_pubkeys;
input_analysis.missing_redeem_script = outdata.missing_redeem_script;
input_analysis.missing_witness_script = outdata.missing_witness_script;
input_analysis.missing_sigs = outdata.missing_sigs;
// If we are only missing signatures and nothing else, then next is signer
if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
input_analysis.next = PSBTRole::SIGNER;
} else {
input_analysis.next = PSBTRole::UPDATER;
}
} else {
input_analysis.next = PSBTRole::FINALIZER;
}
} else if (!utxo.IsNull()){
input_analysis.is_final = true;
}
}
result.outputs.resize(psbtx.outputs.size());
for (unsigned int i = 0; i < psbtx.outputs.size(); ++i) {
const PSBTOutput& output = psbtx.outputs[i];
PSBTOutputAnalysis& output_analysis = result.outputs[i];
CTxOut txout = output.GetTxOut();
output_analysis.is_blind = output.IsBlinded();
output_analysis.proof_result = VerifyBlindProofs(output);
if (output_analysis.is_blind) {
has_blinded_outputs = true;
if (!output.IsFullyBlinded()) {
result.next = PSBTRole::BLINDER;
}
} else {
// Find the fee output
if (txout.IsFee()) {
if (result.fee != std::nullopt) {
result.SetInvalid("There is more than one fee output");
return result;
}
result.fee = output.amount;
}
}
if (txout.nValue.IsExplicit() && !MoneyRange(txout.nValue.GetAmount())) {
result.SetInvalid("PSBT is not valid. Output amount invalid");
return result;
}
}
if (result.fee == std::nullopt) {
result.SetInvalid("PSBT missing required fee output");
return result;
}
if (needs_blinded_outputs && !has_blinded_outputs) {
result.SetInvalid("PSBT has blinded inputs but no blinded outputs. Must have at least one blinded output to balance with the inputs");
return result;
}
// Estimate the size
CMutableTransaction mtx(psbtx.GetUnsignedTx());
CCoinsView view_dummy;
CCoinsViewCache view(&view_dummy);
bool success = true;
for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
PSBTInput& input = psbtx.inputs[i];
Coin newcoin;
if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, 1) || !input.GetUTXO(newcoin.out)) {
success = false;
break;
} else {
mtx.vin[i].scriptSig = input.final_script_sig;
mtx.witness.vtxinwit[i].scriptWitness = input.final_script_witness;
newcoin.nHeight = 1;
view.AddCoin(input.GetOutPoint(), std::move(newcoin), true);
}
}
if (success) {
CTransaction ctx = CTransaction(mtx);
size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
result.estimated_vsize = size;
// Estimate fee rate
CFeeRate feerate(*result.fee, size);
result.estimated_feerate = feerate;
}
// Calculate next role for PSBT by grabbing "minimum" PSBTInput next role
for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
PSBTInputAnalysis& input_analysis = result.inputs[i];
result.next = std::min(result.next, input_analysis.next);
}
assert(result.next > PSBTRole::CREATOR);
return result;
}
} // namespace node