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/addresstablemodel.h>7#include <qt/guiutil.h>8#include <qt/walletmodel.h>10#include <key_io.h>11#include <wallet/types.h>12#include <wallet/wallet.h>14#include <algorithm>16#include <QFont>17#include <QDebug>19const QString AddressTableModel::Send = "S";20const QString AddressTableModel::Receive = "R";22struct AddressTableEntry23{24enum Type {25Sending,26Receiving,27Hidden /* QSortFilterProxyModel will filter these out */28};30Type type;31QString label;32QString address;34AddressTableEntry() = default;35AddressTableEntry(Type _type, const QString &_label, const QString &_address):36type(_type), label(_label), address(_address) {}37};39struct AddressTableEntryLessThan40{41bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const42{43return a.address < b.address;44}45bool operator()(const AddressTableEntry &a, const QString &b) const46{47return a.address < b;48}49bool operator()(const QString &a, const AddressTableEntry &b) const50{51return a < b.address;52}53};55/* Determine address type from address purpose */56constexpr AddressTableEntry::Type translateTransactionType(wallet::AddressPurpose purpose, bool isMine)57{58// "refund" addresses aren't shown, and change addresses aren't returned by getAddresses at all.59switch (purpose) {60case wallet::AddressPurpose::SEND: return AddressTableEntry::Sending;61case wallet::AddressPurpose::RECEIVE: return AddressTableEntry::Receiving;62case wallet::AddressPurpose::REFUND: return AddressTableEntry::Hidden;63} // no default case, so the compiler can warn about missing cases64assert(false);65}67// Private implementation68class AddressTablePriv69{70public:71QList<AddressTableEntry> cachedAddressTable;72AddressTableModel *parent;74explicit AddressTablePriv(AddressTableModel *_parent):75parent(_parent) {}77void refreshAddressTable(interfaces::Wallet& wallet, bool pk_hash_only = false)78{79cachedAddressTable.clear();80{81for (const auto& address : wallet.getAddresses())82{83if (pk_hash_only && !std::holds_alternative<PKHash>(address.dest)) {84continue;85}86AddressTableEntry::Type addressType = translateTransactionType(87address.purpose, address.is_mine);88cachedAddressTable.append(AddressTableEntry(addressType,89QString::fromStdString(address.name),90QString::fromStdString(EncodeDestination(address.dest))));91}92}93// std::lower_bound() and std::upper_bound() require our cachedAddressTable list to be sorted in asc order94// Even though the map is already sorted this re-sorting step is needed because the originating map95// is sorted by binary address, not by base58() address.96std::sort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());97}99void updateEntry(const QString &address, const QString &label, bool isMine, wallet::AddressPurpose purpose, int status)100{101// Find address / label in model102QList<AddressTableEntry>::iterator lower = std::lower_bound(103cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());104QList<AddressTableEntry>::iterator upper = std::upper_bound(105cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());106int lowerIndex = (lower - cachedAddressTable.begin());107int upperIndex = (upper - cachedAddressTable.begin());108bool inModel = (lower != upper);109AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine);111switch(status)112{113case CT_NEW:114if(inModel)115{116qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_NEW, but entry is already in model";117break;118}119parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);120cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));121parent->endInsertRows();122break;123case CT_UPDATED:124if(!inModel)125{126qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model";127break;128}129lower->type = newEntryType;130lower->label = label;131parent->emitDataChanged(lowerIndex);132break;133case CT_DELETED:134if(!inModel)135{136qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model";137break;138}139parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);140cachedAddressTable.erase(lower, upper);141parent->endRemoveRows();142break;143}144}146int size()147{148return cachedAddressTable.size();149}151AddressTableEntry *index(int idx)152{153if(idx >= 0 && idx < cachedAddressTable.size())154{155return &cachedAddressTable[idx];156}157else158{159return nullptr;160}161}162};164AddressTableModel::AddressTableModel(WalletModel *parent, bool pk_hash_only) :165QAbstractTableModel(parent), walletModel(parent)166{167columns << tr("Label") << tr("Address");168priv = new AddressTablePriv(this);169priv->refreshAddressTable(parent->wallet(), pk_hash_only);170}172AddressTableModel::~AddressTableModel()173{174delete priv;175}177int AddressTableModel::rowCount(const QModelIndex &parent) const178{179if (parent.isValid()) {180return 0;181}182return priv->size();183}185int AddressTableModel::columnCount(const QModelIndex &parent) const186{187if (parent.isValid()) {188return 0;189}190return columns.length();191}193QVariant AddressTableModel::data(const QModelIndex &index, int role) const194{195if(!index.isValid())196return QVariant();198AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());200const auto column = static_cast<ColumnIndex>(index.column());201if (role == Qt::DisplayRole || role == Qt::EditRole) {202switch (column) {203case Label:204if (rec->label.isEmpty() && role == Qt::DisplayRole) {205return tr("(no label)");206} else {207return rec->label;208}209case Address:210return rec->address;211} // no default case, so the compiler can warn about missing cases212assert(false);213} else if (role == Qt::FontRole) {214switch (column) {215case Label:216return QFont();217case Address:218return GUIUtil::fixedPitchFont();219} // no default case, so the compiler can warn about missing cases220assert(false);221} else if (role == TypeRole) {222switch(rec->type)223{224case AddressTableEntry::Sending:225return Send;226case AddressTableEntry::Receiving:227return Receive;228case AddressTableEntry::Hidden:229return {};230} // no default case, so the compiler can warn about missing cases231assert(false);232}233return QVariant();234}236bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)237{238if(!index.isValid())239return false;240AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());241wallet::AddressPurpose purpose = rec->type == AddressTableEntry::Sending ? wallet::AddressPurpose::SEND : wallet::AddressPurpose::RECEIVE;242editStatus = OK;244if(role == Qt::EditRole)245{246CTxDestination curAddress = DecodeDestination(rec->address.toStdString());247if(index.column() == Label)248{249// Do nothing, if old label == new label250if(rec->label == value.toString())
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.