mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +02:00
Merge 4f802e59a0 into merged_master (Bitcoin PR #17509)
This commit is contained in:
commit
1ea940a2e2
13 changed files with 223 additions and 53 deletions
|
|
@ -6,11 +6,19 @@
|
|||
#define BITCOIN_NODE_TRANSACTION_H
|
||||
|
||||
#include <attributes.h>
|
||||
#include <policy/feerate.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <util/error.h>
|
||||
|
||||
struct NodeContext;
|
||||
|
||||
/** Maximum fee rate for sendrawtransaction and testmempoolaccept RPC calls.
|
||||
* Also used by the GUI when broadcasting a completed PSBT.
|
||||
* By default, a transaction with a fee rate higher than this will be rejected
|
||||
* by these RPCs and the GUI. This can be overridden with the maxfeerate argument.
|
||||
*/
|
||||
static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10};
|
||||
|
||||
/**
|
||||
* Submit a transaction to the mempool and (optionally) relay it to all P2P peers.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ static const std::string PSBT_ELEMENTS_ID("elements");
|
|||
// as a 0 length key which indicates that this is the separator. The separator has no value.
|
||||
static constexpr uint8_t PSBT_SEPARATOR = 0x00;
|
||||
|
||||
// BIP 174 does not specify a maximum file size, but we set a limit anyway
|
||||
// to prevent reading a stream indefinately and running out of memory.
|
||||
const std::streamsize MAX_FILE_SIZE_PSBT = 100000000; // 100 MiB
|
||||
|
||||
/** A structure for PSBTs which contain per-input information */
|
||||
struct PSBTInput
|
||||
{
|
||||
|
|
|
|||
|
|
@ -317,6 +317,8 @@ 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->setStatusTip(tr("Load Partially Signed Elements Transaction"));
|
||||
|
||||
openRPCConsoleAction = new QAction(tr("Node window"), this);
|
||||
openRPCConsoleAction->setStatusTip(tr("Open node debugging and diagnostic console"));
|
||||
|
|
@ -363,6 +365,7 @@ void BitcoinGUI::createActions()
|
|||
connect(changePassphraseAction, &QAction::triggered, walletFrame, &WalletFrame::changePassphrase);
|
||||
connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
|
||||
connect(signMessageAction, &QAction::triggered, [this]{ gotoSignMessageTab(); });
|
||||
connect(m_load_psbt_action, &QAction::triggered, [this]{ gotoLoadPSBT(); });
|
||||
connect(verifyMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
|
||||
connect(verifyMessageAction, &QAction::triggered, [this]{ gotoVerifyMessageTab(); });
|
||||
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
|
||||
|
|
@ -433,6 +436,7 @@ void BitcoinGUI::createMenuBar()
|
|||
file->addAction(backupWalletAction);
|
||||
file->addAction(signMessageAction);
|
||||
file->addAction(verifyMessageAction);
|
||||
file->addAction(m_load_psbt_action);
|
||||
file->addSeparator();
|
||||
}
|
||||
file->addAction(quitAction);
|
||||
|
|
@ -839,6 +843,10 @@ void BitcoinGUI::gotoVerifyMessageTab(QString addr)
|
|||
{
|
||||
if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
|
||||
}
|
||||
void BitcoinGUI::gotoLoadPSBT()
|
||||
{
|
||||
if (walletFrame) walletFrame->gotoLoadPSBT();
|
||||
}
|
||||
#endif // ENABLE_WALLET
|
||||
|
||||
void BitcoinGUI::updateNetworkState()
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ private:
|
|||
QAction* usedReceivingAddressesAction = nullptr;
|
||||
QAction* signMessageAction = nullptr;
|
||||
QAction* verifyMessageAction = nullptr;
|
||||
QAction* m_load_psbt_action = nullptr;
|
||||
QAction* aboutAction = nullptr;
|
||||
QAction* receiveCoinsAction = nullptr;
|
||||
QAction* receiveCoinsMenuAction = nullptr;
|
||||
|
|
@ -267,6 +268,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();
|
||||
|
||||
#endif // ENABLE_WALLET
|
||||
/** Show configuration dialog */
|
||||
|
|
|
|||
|
|
@ -220,11 +220,8 @@ SendCoinsDialog::~SendCoinsDialog()
|
|||
delete ui;
|
||||
}
|
||||
|
||||
void SendCoinsDialog::on_sendButton_clicked()
|
||||
bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informative_text, QString& detailed_text)
|
||||
{
|
||||
if(!model || !model->getOptionsModel())
|
||||
return;
|
||||
|
||||
QList<SendAssetsRecipient> recipients;
|
||||
bool valid = true;
|
||||
|
||||
|
|
@ -247,7 +244,7 @@ void SendCoinsDialog::on_sendButton_clicked()
|
|||
|
||||
if(!valid || recipients.isEmpty())
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
fNewRecipientAllowed = false;
|
||||
|
|
@ -256,11 +253,11 @@ void SendCoinsDialog::on_sendButton_clicked()
|
|||
{
|
||||
// Unlock wallet was cancelled
|
||||
fNewRecipientAllowed = true;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// prepare transaction for getting txFee earlier
|
||||
WalletModelTransaction currentTransaction(recipients);
|
||||
m_current_transaction = MakeUnique<WalletModelTransaction>(recipients);
|
||||
WalletModel::SendCoinsReturn prepareStatus;
|
||||
|
||||
// Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled
|
||||
|
|
@ -270,23 +267,21 @@ void SendCoinsDialog::on_sendButton_clicked()
|
|||
|
||||
updateCoinControlState(ctrl);
|
||||
|
||||
prepareStatus = model->prepareTransaction(currentTransaction, ctrl);
|
||||
prepareStatus = model->prepareTransaction(*m_current_transaction, ctrl);
|
||||
|
||||
// process prepareStatus and on error generate message shown to user
|
||||
processSendCoinsReturn(prepareStatus,
|
||||
BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee()));
|
||||
BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), m_current_transaction->getTransactionFee()));
|
||||
|
||||
if(prepareStatus.status != WalletModel::OK) {
|
||||
fNewRecipientAllowed = true;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
CAmount txFee = currentTransaction.getTransactionFee();
|
||||
|
||||
// Format confirmation message
|
||||
CAmount txFee = m_current_transaction->getTransactionFee();
|
||||
int bitcoin_unit = model->getOptionsModel()->getDisplayUnit();
|
||||
QStringList formatted;
|
||||
for (const SendAssetsRecipient &rcp : currentTransaction.getRecipients())
|
||||
for (const SendAssetsRecipient &rcp : m_current_transaction->getRecipients())
|
||||
{
|
||||
// generate amount string with wallet name in case of multiwallet
|
||||
QString amount = GUIUtil::formatAssetAmount(rcp.asset, rcp.asset_amount, bitcoin_unit, BitcoinUnits::separatorStandard, true);
|
||||
|
|
@ -313,49 +308,48 @@ void SendCoinsDialog::on_sendButton_clicked()
|
|||
formatted.append(recipientElement);
|
||||
}
|
||||
|
||||
QString questionString;
|
||||
if (model->wallet().privateKeysDisabled()) {
|
||||
questionString.append(tr("Do you want to draft this transaction?"));
|
||||
question_string.append(tr("Do you want to draft this transaction?"));
|
||||
} else {
|
||||
questionString.append(tr("Are you sure you want to send?"));
|
||||
question_string.append(tr("Are you sure you want to send?"));
|
||||
}
|
||||
|
||||
questionString.append("<br /><span style='font-size:10pt;'>");
|
||||
question_string.append("<br /><span style='font-size:10pt;'>");
|
||||
if (model->wallet().privateKeysDisabled()) {
|
||||
questionString.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
|
||||
question_string.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
|
||||
} else {
|
||||
questionString.append(tr("Please, review your transaction."));
|
||||
question_string.append(tr("Please, review your transaction."));
|
||||
}
|
||||
questionString.append("</span>%1");
|
||||
question_string.append("</span>%1");
|
||||
|
||||
if(txFee > 0)
|
||||
{
|
||||
// append fee string if a fee is required
|
||||
questionString.append("<hr /><b>");
|
||||
questionString.append(tr("Transaction fee"));
|
||||
questionString.append("</b>");
|
||||
question_string.append("<hr /><b>");
|
||||
question_string.append(tr("Transaction fee"));
|
||||
question_string.append("</b>");
|
||||
|
||||
// append transaction size
|
||||
questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB): ");
|
||||
question_string.append(" (" + QString::number((double)m_current_transaction->getTransactionSize() / 1000) + " kB): ");
|
||||
|
||||
// append transaction fee value
|
||||
questionString.append("<span style='color:#aa0000; font-weight:bold;'>");
|
||||
questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
|
||||
questionString.append("</span><br />");
|
||||
question_string.append("<span style='color:#aa0000; font-weight:bold;'>");
|
||||
question_string.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
|
||||
question_string.append("</span><br />");
|
||||
|
||||
// append RBF message according to transaction's signalling
|
||||
questionString.append("<span style='font-size:10pt; font-weight:normal;'>");
|
||||
question_string.append("<span style='font-size:10pt; font-weight:normal;'>");
|
||||
if (ui->optInRBF->isChecked()) {
|
||||
questionString.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125)."));
|
||||
question_string.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125)."));
|
||||
} else {
|
||||
questionString.append(tr("Not signalling Replace-By-Fee, BIP-125."));
|
||||
question_string.append(tr("Not signalling Replace-By-Fee, BIP-125."));
|
||||
}
|
||||
questionString.append("</span>");
|
||||
question_string.append("</span>");
|
||||
}
|
||||
|
||||
// add total amount in all subdivision units
|
||||
questionString.append("<hr />");
|
||||
CAmountMap totalAmount = currentTransaction.getTotalTransactionAmount();
|
||||
question_string.append("<hr />");
|
||||
CAmountMap totalAmount = m_current_transaction->getTotalTransactionAmount();
|
||||
totalAmount[Params().GetConsensus().pegged_asset] += txFee;
|
||||
QStringList alternativeUnits;
|
||||
for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits())
|
||||
|
|
@ -363,27 +357,38 @@ void SendCoinsDialog::on_sendButton_clicked()
|
|||
if(u != model->getOptionsModel()->getDisplayUnit())
|
||||
alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount[Params().GetConsensus().pegged_asset]));
|
||||
}
|
||||
questionString.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
|
||||
question_string.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
|
||||
.arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount[Params().GetConsensus().pegged_asset])));
|
||||
questionString.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
|
||||
question_string.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
|
||||
.arg(alternativeUnits.join(" " + tr("or") + " ")));
|
||||
totalAmount.erase(Params().GetConsensus().pegged_asset);
|
||||
if (!!totalAmount) {
|
||||
questionString.append(" " + tr("and") + "<br />" + GUIUtil::formatMultiAssetAmount(totalAmount, -1 /*bitcoin unit, hide*/, BitcoinUnits::separatorStandard, ";<br />"));
|
||||
question_string.append(" " + tr("and") + "<br />" + GUIUtil::formatMultiAssetAmount(totalAmount, -1 /*bitcoin unit, hide*/, BitcoinUnits::separatorStandard, ";<br />"));
|
||||
}
|
||||
|
||||
QString informative_text;
|
||||
QString detailed_text;
|
||||
if (formatted.size() > 1) {
|
||||
questionString = questionString.arg("");
|
||||
question_string = question_string.arg("");
|
||||
informative_text = tr("To review recipient list click \"Show Details...\"");
|
||||
detailed_text = formatted.join("\n\n");
|
||||
} else {
|
||||
questionString = questionString.arg("<br /><br />" + formatted.at(0));
|
||||
question_string = question_string.arg("<br /><br />" + formatted.at(0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SendCoinsDialog::on_sendButton_clicked()
|
||||
{
|
||||
if(!model || !model->getOptionsModel())
|
||||
return;
|
||||
|
||||
QString question_string, informative_text, detailed_text;
|
||||
if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
|
||||
assert(m_current_transaction);
|
||||
|
||||
const QString confirmation = model->wallet().privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
|
||||
const QString confirmButtonText = model->wallet().privateKeysDisabled() ? tr("Copy PSBT to clipboard") : tr("Send");
|
||||
SendConfirmationDialog confirmationDialog(confirmation, questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
|
||||
const QString confirmButtonText = model->wallet().privateKeysDisabled() ? tr("Create Unsigned") : tr("Send");
|
||||
SendConfirmationDialog confirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
|
||||
confirmationDialog.exec();
|
||||
QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
|
||||
|
||||
|
|
@ -395,7 +400,7 @@ void SendCoinsDialog::on_sendButton_clicked()
|
|||
|
||||
bool send_failure = false;
|
||||
if (model->wallet().privateKeysDisabled()) {
|
||||
CMutableTransaction mtx = CMutableTransaction{*(currentTransaction.getWtx())};
|
||||
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);
|
||||
|
|
@ -405,15 +410,52 @@ void SendCoinsDialog::on_sendButton_clicked()
|
|||
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);
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText("Unsigned Transaction");
|
||||
msgBox.setInformativeText("The PSBT has been copied to the clipboard. You can also save it.");
|
||||
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
|
||||
msgBox.setDefaultButton(QMessageBox::Discard);
|
||||
switch (msgBox.exec()) {
|
||||
case QMessageBox::Save: {
|
||||
int bitcoin_unit = model->getOptionsModel()->getDisplayUnit();
|
||||
QString selectedFilter;
|
||||
QString fileNameSuggestion = "";
|
||||
bool first = true;
|
||||
for (const SendAssetsRecipient &rcp : m_current_transaction->getRecipients()) {
|
||||
if (!first) {
|
||||
fileNameSuggestion.append(" - ");
|
||||
}
|
||||
QString labelOrAddress = rcp.label.isEmpty() ? rcp.address : rcp.label;
|
||||
QString amount = GUIUtil::formatAssetAmount(rcp.asset, rcp.asset_amount, bitcoin_unit, BitcoinUnits::separatorStandard, true);
|
||||
fileNameSuggestion.append(labelOrAddress + "-" + amount);
|
||||
first = false;
|
||||
}
|
||||
fileNameSuggestion.append(".psbt");
|
||||
QString filename = GUIUtil::getSaveFileName(this,
|
||||
tr("Save Transaction Data"), fileNameSuggestion,
|
||||
tr("Partially Signed Transaction (Binary) (*.psbt)"), &selectedFilter);
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
std::ofstream out(filename.toLocal8Bit().data());
|
||||
out << ssTx.str();
|
||||
out.close();
|
||||
Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION);
|
||||
break;
|
||||
}
|
||||
case QMessageBox::Discard:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
} else {
|
||||
// now send the prepared transaction
|
||||
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
|
||||
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
|
||||
// process sendStatus and on error generate message shown to user
|
||||
processSendCoinsReturn(sendStatus);
|
||||
|
||||
if (sendStatus.status == WalletModel::OK) {
|
||||
Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash());
|
||||
Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
|
||||
} else {
|
||||
send_failure = true;
|
||||
}
|
||||
|
|
@ -424,10 +466,13 @@ void SendCoinsDialog::on_sendButton_clicked()
|
|||
coinControlUpdateLabels();
|
||||
}
|
||||
fNewRecipientAllowed = true;
|
||||
m_current_transaction.reset();
|
||||
}
|
||||
|
||||
void SendCoinsDialog::clear()
|
||||
{
|
||||
m_current_transaction.reset();
|
||||
|
||||
// Clear coin control settings
|
||||
CoinControlDialog::coinControl()->UnSelectAll();
|
||||
ui->checkBoxCoinControlChange->setChecked(false);
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ private:
|
|||
Ui::SendCoinsDialog *ui;
|
||||
ClientModel *clientModel;
|
||||
WalletModel *model;
|
||||
std::unique_ptr<WalletModelTransaction> m_current_transaction;
|
||||
bool fNewRecipientAllowed;
|
||||
bool fFeeMinimized;
|
||||
const PlatformStyle *platformStyle;
|
||||
|
|
@ -69,6 +70,8 @@ private:
|
|||
// Additional parameter msgArg can be used via .arg(msgArg).
|
||||
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg = QString());
|
||||
void minimizeFeeSection(bool fMinimize);
|
||||
// Format confirmation message
|
||||
bool PrepareSendText(QString& question_string, QString& informative_text, QString& detailed_text);
|
||||
void updateFeeMinimizedLabel();
|
||||
// Update the passed in CCoinControl with state from the GUI
|
||||
void updateCoinControlState(CCoinControl& ctrl);
|
||||
|
|
|
|||
|
|
@ -163,6 +163,14 @@ void WalletFrame::gotoVerifyMessageTab(QString addr)
|
|||
walletView->gotoVerifyMessageTab(addr);
|
||||
}
|
||||
|
||||
void WalletFrame::gotoLoadPSBT()
|
||||
{
|
||||
WalletView *walletView = currentWalletView();
|
||||
if (walletView) {
|
||||
walletView->gotoLoadPSBT();
|
||||
}
|
||||
}
|
||||
|
||||
void WalletFrame::encryptWallet(bool status)
|
||||
{
|
||||
WalletView *walletView = currentWalletView();
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@ 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();
|
||||
|
||||
/** Encrypt the wallet */
|
||||
void encryptWallet(bool status);
|
||||
/** Backup the wallet */
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
#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>
|
||||
|
|
@ -20,6 +23,7 @@
|
|||
|
||||
#include <interfaces/node.h>
|
||||
#include <ui_interface.h>
|
||||
#include <util/strencodings.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QActionGroup>
|
||||
|
|
@ -196,6 +200,80 @@ void WalletView::gotoVerifyMessageTab(QString addr)
|
|||
signVerifyMessageDialog->setAddress_VM(addr);
|
||||
}
|
||||
|
||||
void WalletView::gotoLoadPSBT()
|
||||
{
|
||||
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);
|
||||
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);
|
||||
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 sucessfully."), 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);
|
||||
}
|
||||
}
|
||||
|
||||
bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
|
||||
{
|
||||
return sendCoinsPage->handlePaymentRequest(recipient);
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ public Q_SLOTS:
|
|||
void gotoSignMessageTab(QString addr = "");
|
||||
/** Show Sign/Verify Message dialog and switch to verify message tab */
|
||||
void gotoVerifyMessageTab(QString addr = "");
|
||||
/** Load Partially Signed Bitcoin Transaction */
|
||||
void gotoLoadPSBT();
|
||||
|
||||
/** Show incoming transaction notification for new transactions.
|
||||
|
||||
|
|
|
|||
|
|
@ -51,12 +51,6 @@
|
|||
|
||||
#include <univalue.h>
|
||||
|
||||
/** Maximum fee rate for sendrawtransaction and testmempoolaccept.
|
||||
* By default, a transaction with a fee rate higher than this will be rejected
|
||||
* by the RPCs. This can be overridden with the maxfeerate argument.
|
||||
*/
|
||||
static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10};
|
||||
|
||||
static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
|
||||
{
|
||||
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
|
||||
|
|
|
|||
|
|
@ -147,6 +147,12 @@ bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
|
|||
return free_bytes_available >= min_disk_space + additional_bytes;
|
||||
}
|
||||
|
||||
std::streampos GetFileSize(const char* path, std::streamsize max) {
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
file.ignore(max);
|
||||
return file.gcount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret a string argument as a boolean.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -63,6 +63,14 @@ void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name
|
|||
bool DirIsWritable(const fs::path& directory);
|
||||
bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes = 0);
|
||||
|
||||
/** Get the size of a file by scanning it.
|
||||
*
|
||||
* @param[in] path The file path
|
||||
* @param[in] max Stop seeking beyond this limit
|
||||
* @return The file size or max
|
||||
*/
|
||||
std::streampos GetFileSize(const char* path, std::streamsize max = std::numeric_limits<std::streamsize>::max());
|
||||
|
||||
/** Release all directory locks. This is used for unit testing only, at runtime
|
||||
* the global destructor will take care of the locks.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue