aboutsummaryrefslogtreecommitdiff
path: root/src/qt
diff options
context:
space:
mode:
Diffstat (limited to 'src/qt')
-rw-r--r--src/qt/bitcoingui.cpp10
-rw-r--r--src/qt/bitcoingui.h5
-rw-r--r--src/qt/coincontroldialog.cpp2
-rw-r--r--src/qt/forms/psbtoperationsdialog.ui148
-rw-r--r--src/qt/psbtoperationsdialog.cpp268
-rw-r--r--src/qt/psbtoperationsdialog.h54
-rw-r--r--src/qt/sendcoinsdialog.cpp2
-rw-r--r--src/qt/walletframe.cpp4
-rw-r--r--src/qt/walletframe.h2
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/qt/walletview.cpp97
-rw-r--r--src/qt/walletview.h2
12 files changed, 518 insertions, 78 deletions
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 1092cdd754..b4182e8524 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -321,8 +321,10 @@ void BitcoinGUI::createActions()
signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
verifyMessageAction = new QAction(tr("&Verify message..."), this);
verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
- m_load_psbt_action = new QAction(tr("Load PSBT..."), this);
+ m_load_psbt_action = new QAction(tr("&Load PSBT from file..."), this);
m_load_psbt_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction"));
+ m_load_psbt_clipboard_action = new QAction(tr("Load PSBT from clipboard..."), this);
+ m_load_psbt_clipboard_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction from clipboard"));
openRPCConsoleAction = new QAction(tr("Node window"), this);
openRPCConsoleAction->setStatusTip(tr("Open node debugging and diagnostic console"));
@@ -381,6 +383,7 @@ void BitcoinGUI::createActions()
connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(signMessageAction, &QAction::triggered, [this]{ gotoSignMessageTab(); });
connect(m_load_psbt_action, &QAction::triggered, [this]{ gotoLoadPSBT(); });
+ connect(m_load_psbt_clipboard_action, &QAction::triggered, [this]{ gotoLoadPSBT(true); });
connect(verifyMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(verifyMessageAction, &QAction::triggered, [this]{ gotoVerifyMessageTab(); });
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
@@ -459,6 +462,7 @@ void BitcoinGUI::createMenuBar()
file->addAction(signMessageAction);
file->addAction(verifyMessageAction);
file->addAction(m_load_psbt_action);
+ file->addAction(m_load_psbt_clipboard_action);
file->addSeparator();
}
file->addAction(quitAction);
@@ -878,9 +882,9 @@ void BitcoinGUI::gotoVerifyMessageTab(QString addr)
{
if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
}
-void BitcoinGUI::gotoLoadPSBT()
+void BitcoinGUI::gotoLoadPSBT(bool from_clipboard)
{
- if (walletFrame) walletFrame->gotoLoadPSBT();
+ if (walletFrame) walletFrame->gotoLoadPSBT(from_clipboard);
}
#endif // ENABLE_WALLET
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index b009e279b6..697e83e772 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -139,6 +139,7 @@ private:
QAction* signMessageAction = nullptr;
QAction* verifyMessageAction = nullptr;
QAction* m_load_psbt_action = nullptr;
+ QAction* m_load_psbt_clipboard_action = nullptr;
QAction* aboutAction = nullptr;
QAction* receiveCoinsAction = nullptr;
QAction* receiveCoinsMenuAction = nullptr;
@@ -278,8 +279,8 @@ public Q_SLOTS:
void gotoSignMessageTab(QString addr = "");
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
- /** Show load Partially Signed Bitcoin Transaction dialog */
- void gotoLoadPSBT();
+ /** Load Partially Signed Bitcoin Transaction from file or clipboard */
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Show open dialog */
void openClicked();
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index f44a9f285a..7c72858501 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -456,7 +456,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
{
CPubKey pubkey;
PKHash *pkhash = boost::get<PKHash>(&address);
- if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, CKeyID(*pkhash), pubkey))
+ if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, ToKeyID(*pkhash), pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
}
diff --git a/src/qt/forms/psbtoperationsdialog.ui b/src/qt/forms/psbtoperationsdialog.ui
new file mode 100644
index 0000000000..c2e2f5035b
--- /dev/null
+++ b/src/qt/forms/psbtoperationsdialog.ui
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PSBTOperationsDialog</class>
+ <widget class="QDialog" name="PSBTOperationsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>585</width>
+ <height>327</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="bottomMargin">
+ <number>12</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="mainDialogLayout">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="statusBar">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTextEdit" name="transactionDescription">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonRowLayout">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="signTransactionButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <weight>50</weight>
+ <bold>false</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Sign Tx</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>false</bool>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="broadcastTransactionButton">
+ <property name="text">
+ <string>Broadcast Tx</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="copyToClipboardButton">
+ <property name="text">
+ <string>Copy to Clipboard</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="saveButton">
+ <property name="text">
+ <string>Save...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeButton">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
new file mode 100644
index 0000000000..58167d4bb4
--- /dev/null
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -0,0 +1,268 @@
+// Copyright (c) 2011-2020 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 <qt/psbtoperationsdialog.h>
+
+#include <core_io.h>
+#include <interfaces/node.h>
+#include <key_io.h>
+#include <node/psbt.h>
+#include <policy/policy.h>
+#include <qt/bitcoinunits.h>
+#include <qt/forms/ui_psbtoperationsdialog.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <util/strencodings.h>
+
+#include <iostream>
+
+
+PSBTOperationsDialog::PSBTOperationsDialog(
+ QWidget* parent, WalletModel* wallet_model, ClientModel* client_model) : QDialog(parent),
+ m_ui(new Ui::PSBTOperationsDialog),
+ m_wallet_model(wallet_model),
+ m_client_model(client_model)
+{
+ m_ui->setupUi(this);
+ setWindowTitle("PSBT Operations");
+
+ connect(m_ui->signTransactionButton, &QPushButton::clicked, this, &PSBTOperationsDialog::signTransaction);
+ connect(m_ui->broadcastTransactionButton, &QPushButton::clicked, this, &PSBTOperationsDialog::broadcastTransaction);
+ connect(m_ui->copyToClipboardButton, &QPushButton::clicked, this, &PSBTOperationsDialog::copyToClipboard);
+ connect(m_ui->saveButton, &QPushButton::clicked, this, &PSBTOperationsDialog::saveTransaction);
+
+ connect(m_ui->closeButton, &QPushButton::clicked, this, &PSBTOperationsDialog::close);
+
+ m_ui->signTransactionButton->setEnabled(false);
+ m_ui->broadcastTransactionButton->setEnabled(false);
+}
+
+PSBTOperationsDialog::~PSBTOperationsDialog()
+{
+ delete m_ui;
+}
+
+void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
+{
+ m_transaction_data = psbtx;
+
+ bool complete;
+ size_t n_could_sign;
+ FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_could_sign);
+ if (err != TransactionError::OK) {
+ showStatus(tr("Failed to load transaction: %1")
+ .arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
+ return;
+ }
+
+ m_ui->broadcastTransactionButton->setEnabled(complete);
+ m_ui->signTransactionButton->setEnabled(!complete && !m_wallet_model->wallet().privateKeysDisabled() && n_could_sign > 0);
+
+ updateTransactionDisplay();
+}
+
+void PSBTOperationsDialog::signTransaction()
+{
+ bool complete;
+ size_t n_signed;
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_signed);
+
+ if (err != TransactionError::OK) {
+ showStatus(tr("Failed to sign transaction: %1")
+ .arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
+ return;
+ }
+
+ updateTransactionDisplay();
+
+ if (!complete && n_signed < 1) {
+ showStatus(tr("Could not sign any more inputs."), StatusLevel::WARN);
+ } else if (!complete) {
+ showStatus(tr("Signed %1 inputs, but more signatures are still required.").arg(n_signed),
+ StatusLevel::INFO);
+ } else {
+ showStatus(tr("Signed transaction successfully. Transaction is ready to broadcast."),
+ StatusLevel::INFO);
+ m_ui->broadcastTransactionButton->setEnabled(true);
+ }
+}
+
+void PSBTOperationsDialog::broadcastTransaction()
+{
+ CMutableTransaction mtx;
+ if (!FinalizeAndExtractPSBT(m_transaction_data, mtx)) {
+ // This is never expected to fail unless we were given a malformed PSBT
+ // (e.g. with an invalid signature.)
+ showStatus(tr("Unknown error processing transaction."), StatusLevel::ERR);
+ return;
+ }
+
+ CTransactionRef tx = MakeTransactionRef(mtx);
+ std::string err_string;
+ TransactionError error = BroadcastTransaction(
+ *m_client_model->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* await_callback */ false);
+
+ if (error == TransactionError::OK) {
+ showStatus(tr("Transaction broadcast successfully! Transaction ID: %1")
+ .arg(QString::fromStdString(tx->GetHash().GetHex())), StatusLevel::INFO);
+ } else {
+ showStatus(tr("Transaction broadcast failed: %1")
+ .arg(QString::fromStdString(TransactionErrorString(error).translated)), StatusLevel::ERR);
+ }
+}
+
+void PSBTOperationsDialog::copyToClipboard() {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << m_transaction_data;
+ GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
+ showStatus(tr("PSBT copied to clipboard."), StatusLevel::INFO);
+}
+
+void PSBTOperationsDialog::saveTransaction() {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << m_transaction_data;
+
+ QString selected_filter;
+ QString filename_suggestion = "";
+ bool first = true;
+ for (const CTxOut& out : m_transaction_data.tx->vout) {
+ if (!first) {
+ filename_suggestion.append("-");
+ }
+ CTxDestination address;
+ ExtractDestination(out.scriptPubKey, address);
+ QString amount = BitcoinUnits::format(m_wallet_model->getOptionsModel()->getDisplayUnit(), out.nValue);
+ QString address_str = QString::fromStdString(EncodeDestination(address));
+ filename_suggestion.append(address_str + "-" + amount);
+ first = false;
+ }
+ filename_suggestion.append(".psbt");
+ QString filename = GUIUtil::getSaveFileName(this,
+ tr("Save Transaction Data"), filename_suggestion,
+ tr("Partially Signed Transaction (Binary) (*.psbt)"), &selected_filter);
+ if (filename.isEmpty()) {
+ return;
+ }
+ std::ofstream out(filename.toLocal8Bit().data());
+ out << ssTx.str();
+ out.close();
+ showStatus(tr("PSBT saved to disk."), StatusLevel::INFO);
+}
+
+void PSBTOperationsDialog::updateTransactionDisplay() {
+ m_ui->transactionDescription->setText(QString::fromStdString(renderTransaction(m_transaction_data)));
+ showTransactionStatus(m_transaction_data);
+}
+
+std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransaction &psbtx)
+{
+ QString tx_description = "";
+ CAmount totalAmount = 0;
+ for (const CTxOut& out : psbtx.tx->vout) {
+ CTxDestination address;
+ ExtractDestination(out.scriptPubKey, address);
+ totalAmount += out.nValue;
+ tx_description.append(tr(" * Sends %1 to %2")
+ .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, out.nValue))
+ .arg(QString::fromStdString(EncodeDestination(address))));
+ tx_description.append("<br>");
+ }
+
+ PSBTAnalysis analysis = AnalyzePSBT(psbtx);
+ tx_description.append(" * ");
+ if (!*analysis.fee) {
+ // This happens if the transaction is missing input UTXO information.
+ tx_description.append(tr("Unable to calculate transaction fee or total transaction amount."));
+ } else {
+ tx_description.append(tr("Pays transaction fee: "));
+ tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, *analysis.fee));
+
+ // add total amount in all subdivision units
+ tx_description.append("<hr />");
+ QStringList alternativeUnits;
+ for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits())
+ {
+ if(u != m_client_model->getOptionsModel()->getDisplayUnit()) {
+ alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
+ }
+ }
+ tx_description.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
+ .arg(BitcoinUnits::formatHtmlWithUnit(m_client_model->getOptionsModel()->getDisplayUnit(), totalAmount)));
+ tx_description.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
+ .arg(alternativeUnits.join(" " + tr("or") + " ")));
+ }
+
+ size_t num_unsigned = CountPSBTUnsignedInputs(psbtx);
+ if (num_unsigned > 0) {
+ tx_description.append("<br><br>");
+ tx_description.append(tr("Transaction has %1 unsigned inputs.").arg(QString::number(num_unsigned)));
+ }
+
+ return tx_description.toStdString();
+}
+
+void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) {
+ m_ui->statusBar->setText(msg);
+ switch (level) {
+ case StatusLevel::INFO: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : lightgreen }");
+ break;
+ }
+ case StatusLevel::WARN: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : orange }");
+ break;
+ }
+ case StatusLevel::ERR: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : red }");
+ break;
+ }
+ }
+ m_ui->statusBar->show();
+}
+
+size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &psbtx) {
+ size_t n_signed;
+ bool complete;
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, m_transaction_data, complete, &n_signed);
+
+ if (err != TransactionError::OK) {
+ return 0;
+ }
+ return n_signed;
+}
+
+void PSBTOperationsDialog::showTransactionStatus(const PartiallySignedTransaction &psbtx) {
+ PSBTAnalysis analysis = AnalyzePSBT(psbtx);
+ size_t n_could_sign = couldSignInputs(psbtx);
+
+ switch (analysis.next) {
+ case PSBTRole::UPDATER: {
+ showStatus(tr("Transaction is missing some information about inputs."), StatusLevel::WARN);
+ break;
+ }
+ case PSBTRole::SIGNER: {
+ QString need_sig_text = tr("Transaction still needs signature(s).");
+ StatusLevel level = StatusLevel::INFO;
+ if (m_wallet_model->wallet().privateKeysDisabled()) {
+ need_sig_text += " " + tr("(But this wallet cannot sign transactions.)");
+ level = StatusLevel::WARN;
+ } else if (n_could_sign < 1) {
+ need_sig_text += " " + tr("(But this wallet does not have the right keys.)"); // XXX wording
+ level = StatusLevel::WARN;
+ }
+ showStatus(need_sig_text, level);
+ break;
+ }
+ case PSBTRole::FINALIZER:
+ case PSBTRole::EXTRACTOR: {
+ showStatus(tr("Transaction is fully signed and ready for broadcast."), StatusLevel::INFO);
+ break;
+ }
+ default: {
+ showStatus(tr("Transaction status is unknown."), StatusLevel::ERR);
+ break;
+ }
+ }
+}
diff --git a/src/qt/psbtoperationsdialog.h b/src/qt/psbtoperationsdialog.h
new file mode 100644
index 0000000000..f37bdbe39a
--- /dev/null
+++ b/src/qt/psbtoperationsdialog.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_PSBTOPERATIONSDIALOG_H
+#define BITCOIN_QT_PSBTOPERATIONSDIALOG_H
+
+#include <QDialog>
+
+#include <psbt.h>
+#include <qt/clientmodel.h>
+#include <qt/walletmodel.h>
+
+namespace Ui {
+class PSBTOperationsDialog;
+}
+
+/** Dialog showing transaction details. */
+class PSBTOperationsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit PSBTOperationsDialog(QWidget* parent, WalletModel* walletModel, ClientModel* clientModel);
+ ~PSBTOperationsDialog();
+
+ void openWithPSBT(PartiallySignedTransaction psbtx);
+
+public Q_SLOTS:
+ void signTransaction();
+ void broadcastTransaction();
+ void copyToClipboard();
+ void saveTransaction();
+
+private:
+ Ui::PSBTOperationsDialog* m_ui;
+ PartiallySignedTransaction m_transaction_data;
+ WalletModel* m_wallet_model;
+ ClientModel* m_client_model;
+
+ enum class StatusLevel {
+ INFO,
+ WARN,
+ ERR
+ };
+
+ size_t couldSignInputs(const PartiallySignedTransaction &psbtx);
+ void updateTransactionDisplay();
+ std::string renderTransaction(const PartiallySignedTransaction &psbtx);
+ void showStatus(const QString &msg, StatusLevel level);
+ void showTransactionStatus(const PartiallySignedTransaction &psbtx);
+};
+
+#endif // BITCOIN_QT_PSBTOPERATIONSDIALOG_H
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 9e23fe78d8..0ac61f3adc 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -392,7 +392,7 @@ void SendCoinsDialog::on_sendButton_clicked()
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete);
+ const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
assert(!complete);
assert(err == TransactionError::OK);
// Serialize the PSBT
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 5e68ee4f93..ec56f2755f 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -165,11 +165,11 @@ void WalletFrame::gotoVerifyMessageTab(QString addr)
walletView->gotoVerifyMessageTab(addr);
}
-void WalletFrame::gotoLoadPSBT()
+void WalletFrame::gotoLoadPSBT(bool from_clipboard)
{
WalletView *walletView = currentWalletView();
if (walletView) {
- walletView->gotoLoadPSBT();
+ walletView->gotoLoadPSBT(from_clipboard);
}
}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index d90ade5005..2b5f263468 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -79,7 +79,7 @@ public Q_SLOTS:
void gotoVerifyMessageTab(QString addr = "");
/** Load Partially Signed Bitcoin Transaction */
- void gotoLoadPSBT();
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Encrypt the wallet */
void encryptWallet(bool status);
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 671b5e1ce6..72c75f7be0 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -536,7 +536,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
if (create_psbt) {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete);
+ const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
if (err != TransactionError::OK || complete) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction."));
return false;
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 861d1c5f4a..cec9b0eeb8 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -4,13 +4,11 @@
#include <qt/walletview.h>
-#include <node/psbt.h>
-#include <node/transaction.h>
-#include <policy/policy.h>
#include <qt/addressbookpage.h>
#include <qt/askpassphrasedialog.h>
#include <qt/clientmodel.h>
#include <qt/guiutil.h>
+#include <qt/psbtoperationsdialog.h>
#include <qt/optionsmodel.h>
#include <qt/overviewpage.h>
#include <qt/platformstyle.h>
@@ -22,11 +20,14 @@
#include <qt/walletmodel.h>
#include <interfaces/node.h>
+#include <psbt.h>
#include <ui_interface.h>
#include <util/strencodings.h>
#include <QAction>
#include <QActionGroup>
+#include <QApplication>
+#include <QClipboard>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QProgressDialog>
@@ -204,78 +205,42 @@ void WalletView::gotoVerifyMessageTab(QString addr)
signVerifyMessageDialog->setAddress_VM(addr);
}
-void WalletView::gotoLoadPSBT()
+void WalletView::gotoLoadPSBT(bool from_clipboard)
{
- QString filename = GUIUtil::getOpenFileName(this,
- tr("Load Transaction Data"), QString(),
- tr("Partially Signed Transaction (*.psbt)"), nullptr);
- if (filename.isEmpty()) return;
- if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
- Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
- return;
+ std::string data;
+
+ if (from_clipboard) {
+ std::string raw = QApplication::clipboard()->text().toStdString();
+ bool invalid;
+ data = DecodeBase64(raw, &invalid);
+ if (invalid) {
+ Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+ } else {
+ QString filename = GUIUtil::getOpenFileName(this,
+ tr("Load Transaction Data"), QString(),
+ tr("Partially Signed Transaction (*.psbt)"), nullptr);
+ if (filename.isEmpty()) return;
+ if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
+ Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+ std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
+ data = std::string(std::istreambuf_iterator<char>{in}, {});
}
- std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
- std::string data(std::istreambuf_iterator<char>{in}, {});
std::string error;
PartiallySignedTransaction psbtx;
if (!DecodeRawPSBT(psbtx, data, error)) {
- Q_EMIT message(tr("Error"), tr("Unable to decode PSBT file") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
+ Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
return;
}
- CMutableTransaction mtx;
- bool complete = false;
- PSBTAnalysis analysis = AnalyzePSBT(psbtx);
- QMessageBox msgBox;
- msgBox.setText("PSBT");
- switch (analysis.next) {
- case PSBTRole::CREATOR:
- case PSBTRole::UPDATER:
- msgBox.setInformativeText("PSBT is incomplete. Copy to clipboard for manual inspection?");
- break;
- case PSBTRole::SIGNER:
- msgBox.setInformativeText("Transaction needs more signatures. Copy to clipboard?");
- break;
- case PSBTRole::FINALIZER:
- case PSBTRole::EXTRACTOR:
- complete = FinalizeAndExtractPSBT(psbtx, mtx);
- if (complete) {
- msgBox.setInformativeText(tr("Would you like to send this transaction?"));
- } else {
- // The analyzer missed something, e.g. if there are final_scriptSig/final_scriptWitness
- // but with invalid signatures.
- msgBox.setInformativeText(tr("There was an unexpected problem processing the PSBT. Copy to clipboard for manual inspection?"));
- }
- }
-
- msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
- switch (msgBox.exec()) {
- case QMessageBox::Yes: {
- if (complete) {
- std::string err_string;
- CTransactionRef tx = MakeTransactionRef(mtx);
-
- TransactionError result = BroadcastTransaction(*clientModel->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* wait_callback */ false);
- if (result == TransactionError::OK) {
- Q_EMIT message(tr("Success"), tr("Broadcasted transaction successfully."), CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL);
- } else {
- Q_EMIT message(tr("Error"), QString::fromStdString(err_string), CClientUIInterface::MSG_ERROR);
- }
- } else {
- // Serialize the PSBT
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << psbtx;
- GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
- Q_EMIT message(tr("PSBT copied"), "Copied to clipboard", CClientUIInterface::MSG_INFORMATION);
- return;
- }
- }
- case QMessageBox::Cancel:
- break;
- default:
- assert(false);
- }
+ PSBTOperationsDialog* dlg = new PSBTOperationsDialog(this, walletModel, clientModel);
+ dlg->openWithPSBT(psbtx);
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
+ dlg->exec();
}
bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index fd09456baa..f186554758 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -84,7 +84,7 @@ public Q_SLOTS:
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
/** Load Partially Signed Bitcoin Transaction */
- void gotoLoadPSBT();
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Show incoming transaction notification for new transactions.