Bitcoin Core 22.99.0
P2P Digital Currency
psbtoperationsdialog.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2020 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
6
7#include <core_io.h>
8#include <interfaces/node.h>
9#include <key_io.h>
10#include <node/psbt.h>
11#include <policy/policy.h>
12#include <qt/bitcoinunits.h>
14#include <qt/guiutil.h>
15#include <qt/optionsmodel.h>
16#include <util/strencodings.h>
17
18#include <iostream>
19
20
22 QWidget* parent, WalletModel* wallet_model, ClientModel* client_model) : QDialog(parent, GUIUtil::dialog_flags),
23 m_ui(new Ui::PSBTOperationsDialog),
24 m_wallet_model(wallet_model),
25 m_client_model(client_model)
26{
27 m_ui->setupUi(this);
28 setWindowTitle("PSBT Operations");
29
30 connect(m_ui->signTransactionButton, &QPushButton::clicked, this, &PSBTOperationsDialog::signTransaction);
31 connect(m_ui->broadcastTransactionButton, &QPushButton::clicked, this, &PSBTOperationsDialog::broadcastTransaction);
32 connect(m_ui->copyToClipboardButton, &QPushButton::clicked, this, &PSBTOperationsDialog::copyToClipboard);
33 connect(m_ui->saveButton, &QPushButton::clicked, this, &PSBTOperationsDialog::saveTransaction);
34
35 connect(m_ui->closeButton, &QPushButton::clicked, this, &PSBTOperationsDialog::close);
36
37 m_ui->signTransactionButton->setEnabled(false);
38 m_ui->broadcastTransactionButton->setEnabled(false);
39}
40
42{
43 delete m_ui;
44}
45
47{
48 m_transaction_data = psbtx;
49
50 bool complete = FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
51 if (m_wallet_model) {
52 size_t n_could_sign;
53 TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, &n_could_sign, m_transaction_data, complete);
54 if (err != TransactionError::OK) {
55 showStatus(tr("Failed to load transaction: %1")
56 .arg(QString::fromStdString(TransactionErrorString(err).translated)),
58 return;
59 }
60 m_ui->signTransactionButton->setEnabled(!complete && !m_wallet_model->wallet().privateKeysDisabled() && n_could_sign > 0);
61 } else {
62 m_ui->signTransactionButton->setEnabled(false);
63 }
64
65 m_ui->broadcastTransactionButton->setEnabled(complete);
66
68}
69
71{
72 bool complete;
73 size_t n_signed;
74
76
77 TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, &n_signed, m_transaction_data, complete);
78
79 if (err != TransactionError::OK) {
80 showStatus(tr("Failed to sign transaction: %1")
81 .arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
82 return;
83 }
84
86
87 if (!complete && !ctx.isValid()) {
88 showStatus(tr("Cannot sign inputs while wallet is locked."), StatusLevel::WARN);
89 } else if (!complete && n_signed < 1) {
90 showStatus(tr("Could not sign any more inputs."), StatusLevel::WARN);
91 } else if (!complete) {
92 showStatus(tr("Signed %1 inputs, but more signatures are still required.").arg(n_signed),
94 } else {
95 showStatus(tr("Signed transaction successfully. Transaction is ready to broadcast."),
97 m_ui->broadcastTransactionButton->setEnabled(true);
98 }
99}
100
102{
105 // This is never expected to fail unless we were given a malformed PSBT
106 // (e.g. with an invalid signature.)
107 showStatus(tr("Unknown error processing transaction."), StatusLevel::ERR);
108 return;
109 }
110
112 std::string err_string;
114 *m_client_model->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* await_callback */ false);
115
117 showStatus(tr("Transaction broadcast successfully! Transaction ID: %1")
118 .arg(QString::fromStdString(tx->GetHash().GetHex())), StatusLevel::INFO);
119 } else {
120 showStatus(tr("Transaction broadcast failed: %1")
121 .arg(QString::fromStdString(TransactionErrorString(error).translated)), StatusLevel::ERR);
122 }
123}
124
127 ssTx << m_transaction_data;
128 GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
129 showStatus(tr("PSBT copied to clipboard."), StatusLevel::INFO);
130}
131
134 ssTx << m_transaction_data;
135
136 QString selected_filter;
137 QString filename_suggestion = "";
138 bool first = true;
139 for (const CTxOut& out : m_transaction_data.tx->vout) {
140 if (!first) {
141 filename_suggestion.append("-");
142 }
143 CTxDestination address;
144 ExtractDestination(out.scriptPubKey, address);
146 QString address_str = QString::fromStdString(EncodeDestination(address));
147 filename_suggestion.append(address_str + "-" + amount);
148 first = false;
149 }
150 filename_suggestion.append(".psbt");
151 QString filename = GUIUtil::getSaveFileName(this,
152 tr("Save Transaction Data"), filename_suggestion,
153 //: Expanded name of the binary PSBT file format. See: BIP 174.
154 tr("Partially Signed Transaction (Binary)") + QLatin1String(" (*.psbt)"), &selected_filter);
155 if (filename.isEmpty()) {
156 return;
157 }
158 std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary);
159 out << ssTx.str();
160 out.close();
161 showStatus(tr("PSBT saved to disk."), StatusLevel::INFO);
162}
163
165 m_ui->transactionDescription->setText(QString::fromStdString(renderTransaction(m_transaction_data)));
167}
168
170{
171 QString tx_description = "";
172 CAmount totalAmount = 0;
173 for (const CTxOut& out : psbtx.tx->vout) {
174 CTxDestination address;
175 ExtractDestination(out.scriptPubKey, address);
176 totalAmount += out.nValue;
177 tx_description.append(tr(" * Sends %1 to %2")
179 .arg(QString::fromStdString(EncodeDestination(address))));
180 tx_description.append("<br>");
181 }
182
183 PSBTAnalysis analysis = AnalyzePSBT(psbtx);
184 tx_description.append(" * ");
185 if (!*analysis.fee) {
186 // This happens if the transaction is missing input UTXO information.
187 tx_description.append(tr("Unable to calculate transaction fee or total transaction amount."));
188 } else {
189 tx_description.append(tr("Pays transaction fee: "));
190 tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, *analysis.fee));
191
192 // add total amount in all subdivision units
193 tx_description.append("<hr />");
194 QStringList alternativeUnits;
196 {
198 alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
199 }
200 }
201 tx_description.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
203 tx_description.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
204 .arg(alternativeUnits.join(" " + tr("or") + " ")));
205 }
206
207 size_t num_unsigned = CountPSBTUnsignedInputs(psbtx);
208 if (num_unsigned > 0) {
209 tx_description.append("<br><br>");
210 tx_description.append(tr("Transaction has %1 unsigned inputs.").arg(QString::number(num_unsigned)));
211 }
212
213 return tx_description.toStdString();
214}
215
216void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) {
217 m_ui->statusBar->setText(msg);
218 switch (level) {
219 case StatusLevel::INFO: {
220 m_ui->statusBar->setStyleSheet("QLabel { background-color : lightgreen }");
221 break;
222 }
223 case StatusLevel::WARN: {
224 m_ui->statusBar->setStyleSheet("QLabel { background-color : orange }");
225 break;
226 }
227 case StatusLevel::ERR: {
228 m_ui->statusBar->setStyleSheet("QLabel { background-color : red }");
229 break;
230 }
231 }
232 m_ui->statusBar->show();
233}
234
236 if (!m_wallet_model) {
237 return 0;
238 }
239
240 size_t n_signed;
241 bool complete;
242 TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, &n_signed, m_transaction_data, complete);
243
244 if (err != TransactionError::OK) {
245 return 0;
246 }
247 return n_signed;
248}
249
251 PSBTAnalysis analysis = AnalyzePSBT(psbtx);
252 size_t n_could_sign = couldSignInputs(psbtx);
253
254 switch (analysis.next) {
255 case PSBTRole::UPDATER: {
256 showStatus(tr("Transaction is missing some information about inputs."), StatusLevel::WARN);
257 break;
258 }
259 case PSBTRole::SIGNER: {
260 QString need_sig_text = tr("Transaction still needs signature(s).");
262 if (!m_wallet_model) {
263 need_sig_text += " " + tr("(But no wallet is loaded.)");
264 level = StatusLevel::WARN;
265 } else if (m_wallet_model->wallet().privateKeysDisabled()) {
266 need_sig_text += " " + tr("(But this wallet cannot sign transactions.)");
267 level = StatusLevel::WARN;
268 } else if (n_could_sign < 1) {
269 need_sig_text += " " + tr("(But this wallet does not have the right keys.)"); // XXX wording
270 level = StatusLevel::WARN;
271 }
272 showStatus(need_sig_text, level);
273 break;
274 }
276 case PSBTRole::EXTRACTOR: {
277 showStatus(tr("Transaction is fully signed and ready for broadcast."), StatusLevel::INFO);
278 break;
279 }
280 default: {
281 showStatus(tr("Transaction status is unknown."), StatusLevel::ERR);
282 break;
283 }
284 }
285}
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
static QString formatHtmlWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as HTML string (with unit)
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
Unit
Bitcoin units.
Definition: bitcoinunits.h:42
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD, bool justify=false)
Format as string.
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:205
std::string str() const
Definition: streams.h:242
CAmount GetFeePerK() const
Return the fee in satoshis for a size of 1000 bytes.
Definition: feerate.h:57
An output of a transaction.
Definition: transaction.h:129
CScript scriptPubKey
Definition: transaction.h:132
CAmount nValue
Definition: transaction.h:131
Model for Bitcoin network client.
Definition: clientmodel.h:48
OptionsModel * getOptionsModel()
interfaces::Node & node() const
Definition: clientmodel.h:55
int getDisplayUnit() const
Definition: optionsmodel.h:88
Dialog showing transaction details.
PartiallySignedTransaction m_transaction_data
PSBTOperationsDialog(QWidget *parent, WalletModel *walletModel, ClientModel *clientModel)
Ui::PSBTOperationsDialog * m_ui
void openWithPSBT(PartiallySignedTransaction psbtx)
size_t couldSignInputs(const PartiallySignedTransaction &psbtx)
void showTransactionStatus(const PartiallySignedTransaction &psbtx)
void showStatus(const QString &msg, StatusLevel level)
std::string renderTransaction(const PartiallySignedTransaction &psbtx)
void setupUi(QDialog *PSBTOperationsDialog)
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:52
interfaces::Wallet & wallet() const
Definition: walletmodel.h:144
UnlockContext requestUnlock()
virtual NodeContext * context()
Get and set internal node context.
Definition: node.h:228
virtual TransactionError fillPSBT(int sighash_type, bool sign, bool bip32derivs, size_t *n_signed, PartiallySignedTransaction &psbtx, bool &complete)=0
Fill PSBT.
virtual bool privateKeysDisabled()=0
bilingual_str TransactionErrorString(const TransactionError err)
Definition: error.cpp:11
TransactionError
Definition: error.h:22
@ SIGHASH_ALL
Definition: interpreter.h:27
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:256
Utility functions used by the Bitcoin Qt UI.
Definition: bitcoingui.h:59
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut)
Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix when ...
Definition: guiutil.cpp:286
constexpr auto dialog_flags
Definition: guiutil.h:60
void setClipboard(const QString &str)
Definition: guiutil.cpp:645
fs::ofstream ofstream
Definition: fs.h:225
PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
Provides helpful miscellaneous information about where a PSBT is in the signing workflow.
Definition: psbt.cpp:15
TransactionError BroadcastTransaction(NodeContext &node, const CTransactionRef tx, std::string &err_string, const CAmount &max_tx_fee, bool relay, bool wait_callback)
Submit a transaction to the mempool and (optionally) relay it to all P2P peers.
Definition: transaction.cpp:32
static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE
Maximum fee rate for sendrawtransaction and testmempoolaccept RPC calls.
Definition: transaction.h:25
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:387
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:386
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction &psbt)
Counts the unsigned inputs of a PSBT.
Definition: psbt.cpp:202
bool FinalizeAndExtractPSBT(PartiallySignedTransaction &psbtx, CMutableTransaction &result)
Finalizes a PSBT if possible, and extracts it to a CMutableTransaction if it could be finalized.
Definition: psbt.cpp:333
bool FinalizePSBT(PartiallySignedTransaction &psbtx)
Finalizes a PSBT if possible, combining partial signatures.
Definition: psbt.cpp:318
@ SER_NETWORK
Definition: serialize.h:138
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:213
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:157
std::string EncodeBase64(Span< const unsigned char > input)
A mutable version of CTransaction.
Definition: transaction.h:345
Holds the results of AnalyzePSBT (miscellaneous information about a PSBT)
Definition: psbt.h:29
std::optional< CAmount > fee
Amount of fee being paid by the transaction.
Definition: psbt.h:32
PSBTRole next
Which of the BIP 174 roles needs to handle the transaction next.
Definition: psbt.h:34
A version of CTransaction with the PSBT format.
Definition: psbt.h:392
std::optional< CMutableTransaction > tx
Definition: psbt.h:393
bool error(const char *fmt, const Args &... args)
Definition: system.h:49
static secp256k1_context * ctx
Definition: tests.c:42
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:12