Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
501assert(!model->wallet().privateKeysDisabled() || model->wallet().hasExternalSigner());502bool broadcast = true;503if (model->wallet().hasExternalSigner()) {504CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};505PartiallySignedTransaction psbtx(mtx);506bool complete = false;507// Always fill without signing first. This prevents an external signer508// from being called prematurely and is not expensive.509const auto err{model->wallet().fillPSBT({.sign = false, .bip32_derivs = true}, /*n_signed=*/nullptr, psbtx, complete)};510assert(!complete);511assert(!err);512send_failure = !signWithExternalSigner(psbtx, mtx, complete);513// Don't broadcast when user rejects it on the device or there's a failure:514broadcast = complete && !send_failure;515if (!send_failure) {516// A transaction signed with an external signer is not always complete,517// e.g. in a multisig wallet.518if (complete) {519// Prepare transaction for broadcast transaction if complete520const CTransactionRef tx = MakeTransactionRef(mtx);521m_current_transaction->setWtx(tx);522} else {523presentPSBT(psbtx);524}525}526}528// Broadcast the transaction, unless an external signer was used and it529// failed, or more signatures are needed.530if (broadcast) {531// now send the prepared transaction532model->sendCoins(*m_current_transaction);533Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());534}535}536if (!send_failure) {537accept();538m_coin_control->UnSelectAll();539coinControlUpdateLabels();540}541fNewRecipientAllowed = true;542m_current_transaction.reset();543}545void SendCoinsDialog::clear()546{547m_current_transaction.reset();549// Clear coin control settings550m_coin_control->UnSelectAll();551ui->checkBoxCoinControlChange->setChecked(false);552ui->lineEditCoinControlChange->clear();553coinControlUpdateLabels();555// Remove entries until only one left556while(ui->entries->count())557{558ui->entries->takeAt(0)->widget()->deleteLater();559}560addEntry();562updateTabsAndLabels();563}565void SendCoinsDialog::reject()566{567clear();568}570void SendCoinsDialog::accept()571{572clear();573}575SendCoinsEntry *SendCoinsDialog::addEntry()576{577SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);578entry->setModel(model);579ui->entries->addWidget(entry);580connect(entry, &SendCoinsEntry::removeEntry, this, &SendCoinsDialog::removeEntry);581connect(entry, &SendCoinsEntry::useAvailableBalance, this, &SendCoinsDialog::useAvailableBalance);582connect(entry, &SendCoinsEntry::payAmountChanged, this, &SendCoinsDialog::coinControlUpdateLabels);583connect(entry, &SendCoinsEntry::subtractFeeFromAmountChanged, this, &SendCoinsDialog::coinControlUpdateLabels);585// Focus the field, so that entry can start immediately586entry->clear();587entry->setFocus();588ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());590// Scroll to the newly added entry on a QueuedConnection because Qt doesn't591// adjust the scroll area and scrollbar immediately when the widget is added.592// Invoking on a DirectConnection will only scroll to the second-to-last entry.593QMetaObject::invokeMethod(ui->scrollArea, [this] {594if (ui->scrollArea->verticalScrollBar()) {595ui->scrollArea->verticalScrollBar()->setValue(ui->scrollArea->verticalScrollBar()->maximum());596}597}, Qt::QueuedConnection);599updateTabsAndLabels();600return entry;601}603void SendCoinsDialog::updateTabsAndLabels()604{605setupTabChain(nullptr);606coinControlUpdateLabels();607}609void SendCoinsDialog::removeEntry(SendCoinsEntry* entry)610{611entry->hide();613// If the last entry is about to be removed add an empty one614if (ui->entries->count() == 1)615addEntry();617entry->deleteLater();619updateTabsAndLabels();620}622QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)623{624for(int i = 0; i < ui->entries->count(); ++i)625{626SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());627if(entry)628{629prev = entry->setupTabChain(prev);630}631}632QWidget::setTabOrder(prev, ui->sendButton);633QWidget::setTabOrder(ui->sendButton, ui->clearButton);634QWidget::setTabOrder(ui->clearButton, ui->addButton);635return ui->addButton;636}638void SendCoinsDialog::setAddress(const QString &address)639{640SendCoinsEntry *entry = nullptr;641// Replace the first entry if it is still unused642if(ui->entries->count() == 1)643{644SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());645if(first->isClear())646{647entry = first;648}649}650if(!entry)651{652entry = addEntry();653}655entry->setAddress(address);656}658void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)659{660if(!fNewRecipientAllowed)661return;663SendCoinsEntry *entry = nullptr;664// Replace the first entry if it is still unused665if(ui->entries->count() == 1)666{667SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());668if(first->isClear())669{670entry = first;671}672}673if(!entry)674{675entry = addEntry();676}678entry->setValue(rv);679updateTabsAndLabels();680}682bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv)683{684// Just paste the entry, all pre-checks685// are done in paymentserver.cpp.686pasteEntry(rv);687return true;688}690void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances)691{692if(model && model->getOptionsModel())693{694CAmount balance = balances.balance;695if (model->wallet().hasExternalSigner()) {696ui->labelBalanceName->setText(tr("External balance:"));697}698ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));699}700}702void SendCoinsDialog::refreshBalance()703{704setBalance(model->getCachedBalance());705ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());706updateSmartFeeLabel();707}709void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)710{711QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;712// Default to a warning message, override if error message is needed713msgParams.second = CClientUIInterface::MSG_WARNING;715// This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.716// All status values are used only in WalletModel::prepareTransaction()717switch(sendCoinsReturn.status)718{719case WalletModel::InvalidAddress:720msgParams.first = tr("The recipient address is not valid. Please recheck.");721break;722case WalletModel::InvalidAmount:723msgParams.first = tr("The amount to pay must be larger than 0.");724break;725case WalletModel::AmountExceedsBalance:726msgParams.first = tr("The amount exceeds your balance.");727break;728case WalletModel::DuplicateAddress:729msgParams.first = tr("Duplicate address found: addresses should only be used once each.");730break;731case WalletModel::TransactionCreationFailed:732msgParams.first = tr("Transaction creation failed!");733msgParams.second = CClientUIInterface::MSG_ERROR;734break;735case WalletModel::AbsurdFee:736msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));737break;738case WalletModel::OK:739return;740} // no default case, so the compiler can warn about missing cases741Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);742}744void SendCoinsDialog::minimizeFeeSection(bool fMinimize)745{746ui->labelFeeMinimized->setVisible(fMinimize);747ui->buttonChooseFee ->setVisible(fMinimize);748ui->buttonMinimizeFee->setVisible(!fMinimize);749ui->frameFeeSelection->setVisible(!fMinimize);750ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.