mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 10:38:42 +01:00
gui: send using external signer
This commit is contained in:
parent
24815c6309
commit
1c4b456e1a
@ -199,7 +199,16 @@ void SendCoinsDialog::setModel(WalletModel *_model)
|
||||
// set default rbf checkbox state
|
||||
ui->optInRBF->setCheckState(Qt::Checked);
|
||||
|
||||
if (model->wallet().privateKeysDisabled()) {
|
||||
if (model->wallet().hasExternalSigner()) {
|
||||
ui->sendButton->setText(tr("Sign on device"));
|
||||
if (gArgs.GetArg("-signer", "") != "") {
|
||||
ui->sendButton->setEnabled(true);
|
||||
ui->sendButton->setToolTip(tr("Connect your hardware wallet first."));
|
||||
} else {
|
||||
ui->sendButton->setEnabled(false);
|
||||
ui->sendButton->setToolTip(tr("Set external signer script path in Options -> Wallet"));
|
||||
}
|
||||
} else if (model->wallet().privateKeysDisabled()) {
|
||||
ui->sendButton->setText(tr("Cr&eate Unsigned"));
|
||||
ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
|
||||
}
|
||||
@ -313,14 +322,14 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
|
||||
formatted.append(recipientElement);
|
||||
}
|
||||
|
||||
if (model->wallet().privateKeysDisabled()) {
|
||||
if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
|
||||
question_string.append(tr("Do you want to draft this transaction?"));
|
||||
} else {
|
||||
question_string.append(tr("Are you sure you want to send?"));
|
||||
}
|
||||
|
||||
question_string.append("<br /><span style='font-size:10pt;'>");
|
||||
if (model->wallet().privateKeysDisabled()) {
|
||||
if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
|
||||
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 {
|
||||
question_string.append(tr("Please, review your transaction."));
|
||||
@ -386,8 +395,8 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
||||
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("Create Unsigned") : tr("Send");
|
||||
const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
|
||||
const QString confirmButtonText = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Create Unsigned") : tr("Sign and 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());
|
||||
@ -403,9 +412,58 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
||||
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, nullptr);
|
||||
// Always fill without signing first. This prevents an external signer
|
||||
// from being called prematurely and is not expensive.
|
||||
TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
|
||||
assert(!complete);
|
||||
assert(err == TransactionError::OK);
|
||||
if (model->wallet().hasExternalSigner()) {
|
||||
try {
|
||||
err = model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
|
||||
} catch (const std::runtime_error& e) {
|
||||
QMessageBox::critical(nullptr, tr("Sign failed"), e.what());
|
||||
send_failure = true;
|
||||
return;
|
||||
}
|
||||
if (err == TransactionError::EXTERNAL_SIGNER_NOT_FOUND) {
|
||||
QMessageBox::critical(nullptr, tr("External signer not found"), "External signer not found");
|
||||
send_failure = true;
|
||||
return;
|
||||
}
|
||||
if (err == TransactionError::EXTERNAL_SIGNER_FAILED) {
|
||||
QMessageBox::critical(nullptr, tr("External signer failure"), "External signer failure");
|
||||
send_failure = true;
|
||||
return;
|
||||
}
|
||||
if (err != TransactionError::OK) {
|
||||
tfm::format(std::cerr, "Failed to sign PSBT");
|
||||
processSendCoinsReturn(WalletModel::TransactionCreationFailed);
|
||||
send_failure = true;
|
||||
return;
|
||||
}
|
||||
// fillPSBT does not always properly finalize
|
||||
complete = FinalizeAndExtractPSBT(psbtx, mtx);
|
||||
}
|
||||
|
||||
// Broadcast transaction if complete (even with an external signer this
|
||||
// is not always the case, e.g. in a multisig wallet).
|
||||
if (complete) {
|
||||
const CTransactionRef tx = MakeTransactionRef(mtx);
|
||||
m_current_transaction->setWtx(tx);
|
||||
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(m_current_transaction->getWtx()->GetHash());
|
||||
} else {
|
||||
send_failure = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy PSBT to clipboard and offer to save
|
||||
assert(!complete);
|
||||
// Serialize the PSBT
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << psbtx;
|
||||
@ -447,7 +505,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
} // msgBox.exec()
|
||||
} else {
|
||||
// now send the prepared transaction
|
||||
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
|
||||
@ -614,7 +672,9 @@ void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances)
|
||||
if(model && model->getOptionsModel())
|
||||
{
|
||||
CAmount balance = balances.balance;
|
||||
if (model->wallet().privateKeysDisabled()) {
|
||||
if (model->wallet().hasExternalSigner()) {
|
||||
ui->labelBalanceName->setText(tr("External balance:"));
|
||||
} else if (model->wallet().privateKeysDisabled()) {
|
||||
balance = balances.watch_only_balance;
|
||||
ui->labelBalanceName->setText(tr("Watch-only balance:"));
|
||||
}
|
||||
@ -698,7 +758,7 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked()
|
||||
void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
|
||||
{
|
||||
// Include watch-only for wallets without private key
|
||||
m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled();
|
||||
m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner();
|
||||
|
||||
// Calculate available amount to send.
|
||||
CAmount amount = model->wallet().getAvailableBalance(*m_coin_control);
|
||||
@ -753,7 +813,7 @@ void SendCoinsDialog::updateCoinControlState()
|
||||
m_coin_control->m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
|
||||
m_coin_control->m_signal_bip125_rbf = ui->optInRBF->isChecked();
|
||||
// Include watch-only for wallets without private key
|
||||
m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled();
|
||||
m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner();
|
||||
}
|
||||
|
||||
void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state) {
|
||||
|
@ -26,6 +26,11 @@ CTransactionRef& WalletModelTransaction::getWtx()
|
||||
return wtx;
|
||||
}
|
||||
|
||||
void WalletModelTransaction::setWtx(const CTransactionRef& newTx)
|
||||
{
|
||||
wtx = newTx;
|
||||
}
|
||||
|
||||
unsigned int WalletModelTransaction::getTransactionSize()
|
||||
{
|
||||
return wtx ? GetVirtualTransactionSize(*wtx) : 0;
|
||||
|
@ -27,6 +27,8 @@ public:
|
||||
QList<SendCoinsRecipient> getRecipients() const;
|
||||
|
||||
CTransactionRef& getWtx();
|
||||
void setWtx(const CTransactionRef&);
|
||||
|
||||
unsigned int getTransactionSize();
|
||||
|
||||
void setTransactionFee(const CAmount& newFee);
|
||||
|
Loading…
Reference in New Issue
Block a user