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