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.
1// Copyright (c) 2011-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <qt/transactiontablemodel.h>7#include <qt/addresstablemodel.h>8#include <qt/bitcoinunits.h>9#include <qt/clientmodel.h>10#include <qt/guiconstants.h>11#include <qt/guiutil.h>12#include <qt/optionsmodel.h>13#include <qt/platformstyle.h>14#include <qt/transactiondesc.h>15#include <qt/transactionrecord.h>16#include <qt/walletmodel.h>18#include <core_io.h>19#include <interfaces/handler.h>20#include <tinyformat.h>21#include <uint256.h>23#include <algorithm>24#include <functional>26#include <QColor>27#include <QDateTime>28#include <QDebug>29#include <QIcon>30#include <QLatin1Char>31#include <QLatin1String>32#include <QList>35// Amount column is right-aligned it contains numbers36static int column_alignments[] = {37Qt::AlignLeft|Qt::AlignVCenter, /*status=*/38Qt::AlignLeft|Qt::AlignVCenter, /*date=*/39Qt::AlignLeft|Qt::AlignVCenter, /*type=*/40Qt::AlignLeft|Qt::AlignVCenter, /*address=*/41Qt::AlignRight|Qt::AlignVCenter /* amount */42};44// Comparison operator for sort/binary search of model tx list45struct TxLessThan46{47bool operator()(const TransactionRecord &a, const TransactionRecord &b) const48{49return a.hash < b.hash;50}51bool operator()(const TransactionRecord &a, const Txid &b) const52{53return a.hash < b;54}55bool operator()(const Txid &a, const TransactionRecord &b) const56{57return a < b.hash;58}59};61// queue notifications to show a non freezing progress dialog e.g. for rescan62struct TransactionNotification63{64public:65TransactionNotification() = default;66TransactionNotification(Txid _hash, ChangeType _status, bool _showTransaction):67hash(_hash), status(_status), showTransaction(_showTransaction) {}69void invoke(QObject *ttm)70{71QString strHash = QString::fromStdString(hash.GetHex());72qDebug() << "NotifyTransactionChanged: " + strHash + " status= " + QString::number(status);73bool invoked = QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection,74Q_ARG(QString, strHash),75Q_ARG(int, status),76Q_ARG(bool, showTransaction));77assert(invoked);78}79private:80Txid hash;81ChangeType status;82bool showTransaction;83};85// Private implementation86class TransactionTablePriv87{88public:89explicit TransactionTablePriv(TransactionTableModel *_parent) :90parent(_parent)91{92}94TransactionTableModel *parent;96//! Local cache of wallet sorted by transaction hash97QList<TransactionRecord> cachedWallet;99/** True when model finishes loading all wallet transactions on start */100bool m_loaded = false;101/** True when transactions are being notified, for instance when scanning */102bool m_loading = false;103std::vector< TransactionNotification > vQueueNotifications;105void NotifyTransactionChanged(const Txid& hash, ChangeType status);106void DispatchNotifications();108/* Query entire wallet anew from core.109*/110void refreshWallet(interfaces::Wallet& wallet)111{112assert(!m_loaded);113{114for (const auto& wtx : wallet.getWalletTxs()) {115if (TransactionRecord::showTransaction()) {116cachedWallet.append(TransactionRecord::decomposeTransaction(wtx));117}118}119}120m_loaded = true;121DispatchNotifications();122}124/* Update our model of the wallet incrementally, to synchronize our model of the wallet125with that of the core.127Call with transaction that was added, removed or changed.128*/129void updateWallet(interfaces::Wallet& wallet, const Txid& hash, int status, bool showTransaction)130{131qDebug() << "TransactionTablePriv::updateWallet: " + QString::fromStdString(hash.ToString()) + " " + QString::number(status);133// Find bounds of this transaction in model134QList<TransactionRecord>::iterator lower = std::lower_bound(135cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());136QList<TransactionRecord>::iterator upper = std::upper_bound(137cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());138int lowerIndex = (lower - cachedWallet.begin());139int upperIndex = (upper - cachedWallet.begin());140bool inModel = (lower != upper);142if(status == CT_UPDATED)143{144if(showTransaction && !inModel)145status = CT_NEW; /* Not in model, but want to show, treat as new */146if(!showTransaction && inModel)147status = CT_DELETED; /* In model, but want to hide, treat as deleted */148}150qDebug() << " inModel=" + QString::number(inModel) +151" Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) +152" showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status);154switch(status)155{156case CT_NEW:157if(inModel)158{159qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is already in model";160break;161}162if(showTransaction)163{164// Find transaction in wallet165interfaces::WalletTx wtx = wallet.getWalletTx(hash);166if(!wtx.tx)167{168qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is not in wallet";169break;170}171// Added -- insert at the right position172QList<TransactionRecord> toInsert =173TransactionRecord::decomposeTransaction(wtx);174if(!toInsert.isEmpty()) /* only if something to insert */175{176parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);177int insert_idx = lowerIndex;178for (const TransactionRecord &rec : toInsert)179{180cachedWallet.insert(insert_idx, rec);181insert_idx += 1;182}183parent->endInsertRows();184}185}186break;187case CT_DELETED:188if(!inModel)189{190qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_DELETED, but transaction is not in model";191break;192}193// Removed -- remove entire transaction from table194parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);195cachedWallet.erase(lower, upper);196parent->endRemoveRows();197break;198case CT_UPDATED:199// Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for200// visible transactions.201for (int i = lowerIndex; i < upperIndex; i++) {202TransactionRecord *rec = &cachedWallet[i];203rec->status.needsUpdate = true;204}205break;206}207}209int size()210{211return cachedWallet.size();212}214TransactionRecord* index(interfaces::Wallet& wallet, const uint256& cur_block_hash, const int idx)215{216if (idx >= 0 && idx < cachedWallet.size()) {217TransactionRecord *rec = &cachedWallet[idx];219// If a status update is needed (blocks came in since last check),220// try to update the status of this transaction from the wallet.221// Otherwise, simply reuse the cached status.222interfaces::WalletTxStatus wtx;223int numBlocks;224int64_t block_time;225if (!cur_block_hash.IsNull() && rec->statusUpdateNeeded(cur_block_hash) && wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, block_time)) {226rec->updateStatus(wtx, cur_block_hash, numBlocks, block_time);227}228return rec;229}230return nullptr;231}233QString describe(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord* rec, BitcoinUnit unit)234{235return TransactionDesc::toHTML(node, wallet, rec, unit);236}238QString getTxHex(interfaces::Wallet& wallet, TransactionRecord *rec)239{240auto tx = wallet.getTx(rec->hash);241if (tx) {242std::string strHex = EncodeHexTx(*tx);243return QString::fromStdString(strHex);244}245return QString();246}247};249TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, WalletModel *parent):250QAbstractTableModel(parent),
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.