mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-16 13:01:19 +02:00
Merge c27330897d into merged_master (Bitcoin PR #18027)
This commit is contained in:
commit
4839db857d
24 changed files with 592 additions and 93 deletions
|
|
@ -35,6 +35,7 @@
|
|||
<ClCompile Include="..\..\src\qt\paymentserver.cpp" />
|
||||
<ClCompile Include="..\..\src\qt\peertablemodel.cpp" />
|
||||
<ClCompile Include="..\..\src\qt\platformstyle.cpp" />
|
||||
<ClCompile Include="..\..\src\qt\psbtoperationsdialog.cpp" />
|
||||
<ClCompile Include="..\..\src\qt\qrimagewidget.cpp" />
|
||||
<ClCompile Include="..\..\src\qt\qvalidatedlineedit.cpp" />
|
||||
<ClCompile Include="..\..\src\qt\qvaluecombobox.cpp" />
|
||||
|
|
@ -87,6 +88,7 @@
|
|||
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_paymentserver.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_peertablemodel.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_platformstyle.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_psbtoperationsdialog.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_qrimagewidget.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_qvalidatedlineedit.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_qvaluecombobox.cpp" />
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ QT_FORMS_UI = \
|
|||
qt/forms/openuridialog.ui \
|
||||
qt/forms/optionsdialog.ui \
|
||||
qt/forms/overviewpage.ui \
|
||||
qt/forms/psbtoperationsdialog.ui \
|
||||
qt/forms/receivecoinsdialog.ui \
|
||||
qt/forms/receiverequestdialog.ui \
|
||||
qt/forms/debugwindow.ui \
|
||||
|
|
@ -60,6 +61,7 @@ QT_MOC_CPP = \
|
|||
qt/moc_optionsmodel.cpp \
|
||||
qt/moc_overviewpage.cpp \
|
||||
qt/moc_peertablemodel.cpp \
|
||||
qt/moc_psbtoperationsdialog.cpp \
|
||||
qt/moc_qrimagewidget.cpp \
|
||||
qt/moc_qvalidatedlineedit.cpp \
|
||||
qt/moc_qvaluecombobox.cpp \
|
||||
|
|
@ -131,6 +133,7 @@ BITCOIN_QT_H = \
|
|||
qt/overviewpage.h \
|
||||
qt/peertablemodel.h \
|
||||
qt/platformstyle.h \
|
||||
qt/psbtoperationsdialog.h \
|
||||
qt/qrimagewidget.h \
|
||||
qt/qvalidatedlineedit.h \
|
||||
qt/qvaluecombobox.h \
|
||||
|
|
@ -240,6 +243,7 @@ BITCOIN_QT_WALLET_CPP = \
|
|||
qt/editaddressdialog.cpp \
|
||||
qt/openuridialog.cpp \
|
||||
qt/overviewpage.cpp \
|
||||
qt/psbtoperationsdialog.cpp \
|
||||
qt/qrimagewidget.cpp \
|
||||
qt/receivecoinsdialog.cpp \
|
||||
qt/receiverequestdialog.cpp \
|
||||
|
|
|
|||
|
|
@ -350,9 +350,10 @@ public:
|
|||
bool sign,
|
||||
bool bip32derivs,
|
||||
PartiallySignedTransaction& psbtx,
|
||||
bool& complete) override
|
||||
bool& complete,
|
||||
size_t* n_signed) override
|
||||
{
|
||||
return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs);
|
||||
return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs, n_signed);
|
||||
}
|
||||
WalletBalances getBalances() override
|
||||
{
|
||||
|
|
|
|||
|
|
@ -199,7 +199,8 @@ public:
|
|||
bool sign,
|
||||
bool bip32derivs,
|
||||
PartiallySignedTransaction& psbtx,
|
||||
bool& complete) = 0;
|
||||
bool& complete,
|
||||
size_t* n_signed) = 0;
|
||||
|
||||
//! Get balances.
|
||||
virtual WalletBalances getBalances() = 0;
|
||||
|
|
|
|||
11
src/psbt.cpp
11
src/psbt.cpp
|
|
@ -235,6 +235,17 @@ bool PSBTInputSigned(const PSBTInput& input)
|
|||
return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
|
||||
}
|
||||
|
||||
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt) {
|
||||
size_t count = 0;
|
||||
for (const auto& input : psbt.inputs) {
|
||||
if (!PSBTInputSigned(input)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index)
|
||||
{
|
||||
const CTxOut& out = psbt.tx->vout.at(index);
|
||||
|
|
|
|||
|
|
@ -966,6 +966,9 @@ bool PSBTInputSigned(const PSBTInput& input);
|
|||
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
|
||||
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool use_dummy = false);
|
||||
|
||||
/** Counts the unsigned inputs of a PSBT. */
|
||||
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
|
||||
|
||||
/** Updates a PSBTOutput with information from provider.
|
||||
*
|
||||
* This fills in the redeem_script, witness_script, and hd_keypaths where possible.
|
||||
|
|
|
|||
|
|
@ -321,8 +321,10 @@ void BitcoinGUI::createActions()
|
|||
signMessageAction->setStatusTip(tr("Sign messages with your %1 addresses to prove you own them").arg("Liquid"));
|
||||
verifyMessageAction = new QAction(tr("&Verify message..."), this);
|
||||
verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified %1 addresses").arg("Liquid"));
|
||||
m_load_psbt_action = new QAction(tr("Load PSET..."), this);
|
||||
m_load_psbt_action = new QAction(tr("&Load PSBT from file..."), this);
|
||||
m_load_psbt_action->setStatusTip(tr("Load Partially Signed Elements Transaction"));
|
||||
m_load_psbt_clipboard_action = new QAction(tr("Load PSBT from clipboard..."), this);
|
||||
m_load_psbt_clipboard_action->setStatusTip(tr("Load Partially Signed Elements Transaction from clipboard"));
|
||||
|
||||
openRPCConsoleAction = new QAction(tr("Node window"), this);
|
||||
openRPCConsoleAction->setStatusTip(tr("Open node debugging and diagnostic console"));
|
||||
|
|
@ -378,6 +380,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);
|
||||
|
|
@ -454,6 +457,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);
|
||||
|
|
@ -863,9 +867,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
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,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;
|
||||
|
|
@ -275,8 +276,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);
|
||||
|
||||
#endif // ENABLE_WALLET
|
||||
/** Show configuration dialog */
|
||||
|
|
|
|||
148
src/qt/forms/psbtoperationsdialog.ui
Normal file
148
src/qt/forms/psbtoperationsdialog.ui
Normal file
|
|
@ -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>
|
||||
270
src/qt/psbtoperationsdialog.cpp
Normal file
270
src/qt/psbtoperationsdialog.cpp
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
// 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.GetAmount());
|
||||
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;
|
||||
int unit = m_client_model->getOptionsModel()->getDisplayUnit();
|
||||
for (const CTxOut& out : psbtx.tx->vout) {
|
||||
CTxDestination address;
|
||||
ExtractDestination(out.scriptPubKey, address);
|
||||
totalAmount += out.nValue.GetAmount();
|
||||
tx_description.append(tr(" * Sends %1 to %2")
|
||||
.arg(GUIUtil::formatAssetAmount(out.nAsset.GetAsset(), out.nValue.GetAmount(), unit, BitcoinUnits::SeparatorStyle::ALWAYS))
|
||||
.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 {
|
||||
// FIXME: should we display non-bitcoin fees?
|
||||
tx_description.append(tr("Pays transaction fee: "));
|
||||
tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, (*analysis.fee)[::policyAsset]));
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/qt/psbtoperationsdialog.h
Normal file
54
src/qt/psbtoperationsdialog.h
Normal file
|
|
@ -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
|
||||
|
|
@ -399,7 +399,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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -571,7 +571,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;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#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>
|
||||
|
|
@ -27,6 +28,8 @@
|
|||
|
||||
#include <QAction>
|
||||
#include <QActionGroup>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QFileDialog>
|
||||
#include <QHBoxLayout>
|
||||
#include <QProgressDialog>
|
||||
|
|
@ -203,78 +206,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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ bilingual_str TransactionErrorString(const TransactionError err)
|
|||
case TransactionError::OK:
|
||||
return Untranslated("No error");
|
||||
case TransactionError::MISSING_INPUTS:
|
||||
return Untranslated("Missing inputs");
|
||||
return Untranslated("Inputs missing or spent");
|
||||
case TransactionError::ALREADY_IN_CHAIN:
|
||||
return Untranslated("Transaction already in block chain");
|
||||
case TransactionError::P2P_DISABLED:
|
||||
|
|
@ -24,11 +24,11 @@ bilingual_str TransactionErrorString(const TransactionError err)
|
|||
case TransactionError::MEMPOOL_ERROR:
|
||||
return Untranslated("AcceptToMemoryPool failed");
|
||||
case TransactionError::INVALID_PSBT:
|
||||
return Untranslated("PSBT is not sane");
|
||||
return Untranslated("PSBT is not well-formed");
|
||||
case TransactionError::PSBT_MISMATCH:
|
||||
return Untranslated("PSBTs not compatible (different transactions)");
|
||||
case TransactionError::SIGHASH_MISMATCH:
|
||||
return Untranslated("Specified sighash value does not match existing value");
|
||||
return Untranslated("Specified sighash value does not match value stored in PSBT");
|
||||
case TransactionError::MAX_FEE_EXCEEDED:
|
||||
return Untranslated("Fee exceeds maximum configured by -maxtxfee");
|
||||
case TransactionError::BLINDING_REQUIRED:
|
||||
|
|
|
|||
|
|
@ -591,8 +591,11 @@ SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, con
|
|||
return SigningResult::SIGNING_FAILED;
|
||||
}
|
||||
|
||||
TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs) const
|
||||
TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
|
||||
{
|
||||
if (n_signed) {
|
||||
*n_signed = 0;
|
||||
}
|
||||
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
const CTxIn& txin = psbtx.tx->vin[i];
|
||||
PSBTInput& input = psbtx.inputs.at(i);
|
||||
|
|
@ -623,6 +626,14 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
|
|||
SignatureData sigdata;
|
||||
input.FillSignatureData(sigdata);
|
||||
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type);
|
||||
|
||||
bool signed_one = PSBTInputSigned(input);
|
||||
if (n_signed && (signed_one || !sign)) {
|
||||
// If sign is false, we assume that we _could_ sign if we get here. This
|
||||
// will never have false negatives; it is hard to tell under what i
|
||||
// circumstances it could have false positives.
|
||||
(*n_signed)++;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
|
||||
|
|
@ -2069,8 +2080,11 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
|
|||
return SigningResult::OK;
|
||||
}
|
||||
|
||||
TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs) const
|
||||
TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
|
||||
{
|
||||
if (n_signed) {
|
||||
*n_signed = 0;
|
||||
}
|
||||
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
const CTxIn& txin = psbtx.tx->vin[i];
|
||||
PSBTInput& input = psbtx.inputs.at(i);
|
||||
|
|
@ -2122,6 +2136,14 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
|
|||
}
|
||||
|
||||
SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
|
||||
|
||||
bool signed_one = PSBTInputSigned(input);
|
||||
if (n_signed && (signed_one || !sign)) {
|
||||
// If sign is false, we assume that we _could_ sign if we get here. This
|
||||
// will never have false negatives; it is hard to tell under what i
|
||||
// circumstances it could have false positives.
|
||||
(*n_signed)++;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ public:
|
|||
/** Sign a message with the given script */
|
||||
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
|
||||
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
|
||||
virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const { return TransactionError::INVALID_PSBT; }
|
||||
virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
|
||||
|
||||
virtual uint256 GetID() const { return uint256(); }
|
||||
|
||||
|
|
@ -393,7 +393,7 @@ public:
|
|||
|
||||
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
|
||||
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
|
||||
|
||||
uint256 GetID() const override;
|
||||
|
||||
|
|
@ -602,7 +602,7 @@ public:
|
|||
|
||||
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
|
||||
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
|
||||
|
||||
uint256 GetID() const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -2757,7 +2757,7 @@ TransactionError CWallet::FillPSBTData(PartiallySignedTransaction& psbtx, bool b
|
|||
return TransactionError::OK;
|
||||
}
|
||||
|
||||
TransactionError CWallet::SignPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool imbalance_ok, bool bip32derivs) const
|
||||
TransactionError CWallet::SignPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool imbalance_ok, bool bip32derivs, size_t* n_signed) const
|
||||
{
|
||||
// If we're signing, check that the transaction is not still in need of blinding
|
||||
if (sign) {
|
||||
|
|
@ -2768,6 +2768,10 @@ TransactionError CWallet::SignPSBT(PartiallySignedTransaction& psbtx, bool& comp
|
|||
}
|
||||
}
|
||||
|
||||
if (n_signed) {
|
||||
*n_signed = 0;
|
||||
}
|
||||
|
||||
// Save the original transaction since we need to munge it temporarily, which would violate the PSBT rules
|
||||
CTransaction oldtx = CTransaction(*psbtx.tx);
|
||||
|
||||
|
|
@ -2850,11 +2854,16 @@ TransactionError CWallet::SignPSBT(PartiallySignedTransaction& psbtx, bool& comp
|
|||
}
|
||||
|
||||
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
|
||||
int n_signed_this_spkm = 0;
|
||||
// ELEMENTS: Here we _only_ sign, and do not e.g. fill in key origin data.
|
||||
TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs);
|
||||
TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
|
||||
if (res != TransactionError::OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (n_signed) {
|
||||
(*n_signed) += n_signed_this_spkm;
|
||||
}
|
||||
}
|
||||
|
||||
// Complete if every input is now signed
|
||||
|
|
@ -2869,7 +2878,7 @@ TransactionError CWallet::SignPSBT(PartiallySignedTransaction& psbtx, bool& comp
|
|||
}
|
||||
|
||||
// This function remains for backwards compatibility. It will not succeed in Elements unless everything involved is non-blinded.
|
||||
TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs) const
|
||||
TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs, size_t* n_signed) const
|
||||
{
|
||||
complete = false;
|
||||
TransactionError te;
|
||||
|
|
@ -2878,7 +2887,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
|
|||
return te;
|
||||
}
|
||||
// For backwards compatibility, do not check if amounts balance before signing in this case.
|
||||
te = SignPSBT(psbtx, complete, sighash_type, sign, true, bip32derivs);
|
||||
te = SignPSBT(psbtx, complete, sighash_type, sign, true, bip32derivs, n_signed);
|
||||
if (te != TransactionError::OK) {
|
||||
return te;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1077,11 +1077,12 @@ public:
|
|||
bool& complete,
|
||||
int sighash_type = 1 /* SIGHASH_ALL */,
|
||||
bool sign = true,
|
||||
bool bip32derivs = true) const;
|
||||
bool bip32derivs = true,
|
||||
size_t* n_signed = nullptr) const;
|
||||
|
||||
// ELEMENTS
|
||||
TransactionError FillPSBTData(PartiallySignedTransaction& psbtx, bool bip32derivs = false) const;
|
||||
TransactionError SignPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type = 1, bool sign = true, bool imbalance_ok = false, bool bip32derivs = false) const;
|
||||
TransactionError SignPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type = 1, bool sign = true, bool imbalance_ok = false, bool bip32derivs = false, size_t* n_signed = nullptr) const;
|
||||
// end ELEMENTS
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -588,7 +588,7 @@ class PSBTTest(BitcoinTestFramework):
|
|||
self.nodes[0].generate(200)
|
||||
self.sync_all()
|
||||
|
||||
assert_raises_rpc_error(-25, 'Missing inputs', self.nodes[0].walletprocesspsbt, 'cHNldP8BAOECAAAAAAK2GUhaoOw0nV0BJhZg0a8G7vb9PQvlTds8GA/N+l+peAMAAAAA/////7YZSFqg7DSdXQEmFmDRrwbu9v09C+VN2zwYD836X6l4AQAAAAD/////AgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQK2wAAWABSNJKzjaUb3uOxixsvh1GGE3fW7zQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQL5AAAWABQo3DTHwdFy0CCa+h6+bi7VJs3tcgAAAAAAAQDhAgAAAAACczMa321tVHuN4GKWKRncycI22aX3uXgwSFUKM2orjRsBAAAAAP////9zMxrfbW1Ue43gYpYpGdzJwjbZpfe5eDBIVQozaiuNGwAAAAAA/////wIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAJUC+QAAFgAU/c11FQPC7OYlaspyx3uf70ntV8kBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAJUCzUAAFgAUZlkFHI0o4IUPP7h+AZ/85GpMC7QAAAAAAAEBQgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQLNQAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAA==')
|
||||
assert_raises_rpc_error(-25, 'Inputs missing or spent', self.nodes[0].walletprocesspsbt, 'cHNldP8BAOECAAAAAAK2GUhaoOw0nV0BJhZg0a8G7vb9PQvlTds8GA/N+l+peAMAAAAA/////7YZSFqg7DSdXQEmFmDRrwbu9v09C+VN2zwYD836X6l4AQAAAAD/////AgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQK2wAAWABSNJKzjaUb3uOxixsvh1GGE3fW7zQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQL5AAAWABQo3DTHwdFy0CCa+h6+bi7VJs3tcgAAAAAAAQDhAgAAAAACczMa321tVHuN4GKWKRncycI22aX3uXgwSFUKM2orjRsBAAAAAP////9zMxrfbW1Ue43gYpYpGdzJwjbZpfe5eDBIVQozaiuNGwAAAAAA/////wIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAJUC+QAAFgAU/c11FQPC7OYlaspyx3uf70ntV8kBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAJUCzUAAFgAUZlkFHI0o4IUPP7h+AZ/85GpMC7QAAAAAAAEBQgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQLNQAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAA==')
|
||||
|
||||
# Run all the pre-Elements, tests first with non-confidential addresses, then again with confidential addresses
|
||||
self.run_basic_tests(False)
|
||||
|
|
@ -726,7 +726,7 @@ class PSBTTest(BitcoinTestFramework):
|
|||
assert_equal(analysis['next'], 'creator')
|
||||
assert_equal(analysis['error'], 'PSBT is not valid. Input 0 specifies invalid prevout')
|
||||
|
||||
assert_raises_rpc_error(-25, 'Missing inputs', self.nodes[0].walletprocesspsbt, 'cHNldP8BAOECAAAAAAK2GUhaoOw0nV0BJhZg0a8G7vb9PQvlTds8GA/N+l+peAMAAAAA/////7YZSFqg7DSdXQEmFmDRrwbu9v09C+VN2zwYD836X6l4AQAAAAD/////AgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQK2wAAWABSNJKzjaUb3uOxixsvh1GGE3fW7zQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQL5AAAWABQo3DTHwdFy0CCa+h6+bi7VJs3tcgAAAAAAAQDhAgAAAAACczMa321tVHuN4GKWKRncycI22aX3uXgwSFUKM2orjRsBAAAAAP////9zMxrfbW1Ue43gYpYpGdzJwjbZpfe5eDBIVQozaiuNGwAAAAAA/////wIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAJUC+QAAFgAU/c11FQPC7OYlaspyx3uf70ntV8kBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAJUCzUAAFgAUZlkFHI0o4IUPP7h+AZ/85GpMC7QAAAAAAAEBQgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQLNQAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAA==')
|
||||
assert_raises_rpc_error(-25, 'Inputs missing or spent', self.nodes[0].walletprocesspsbt, 'cHNldP8BAOECAAAAAAK2GUhaoOw0nV0BJhZg0a8G7vb9PQvlTds8GA/N+l+peAMAAAAA/////7YZSFqg7DSdXQEmFmDRrwbu9v09C+VN2zwYD836X6l4AQAAAAD/////AgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQK2wAAWABSNJKzjaUb3uOxixsvh1GGE3fW7zQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQL5AAAWABQo3DTHwdFy0CCa+h6+bi7VJs3tcgAAAAAAAQDhAgAAAAACczMa321tVHuN4GKWKRncycI22aX3uXgwSFUKM2orjRsBAAAAAP////9zMxrfbW1Ue43gYpYpGdzJwjbZpfe5eDBIVQozaiuNGwAAAAAA/////wIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAJUC+QAAFgAU/c11FQPC7OYlaspyx3uf70ntV8kBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAJUCzUAAFgAUZlkFHI0o4IUPP7h+AZ/85GpMC7QAAAAAAAEBQgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAlQLNQAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAA==')
|
||||
|
||||
if __name__ == '__main__':
|
||||
PSBTTest().main()
|
||||
|
|
|
|||
|
|
@ -16,3 +16,4 @@ setban
|
|||
hist
|
||||
ser
|
||||
unselect
|
||||
lowercased
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue