LCOV - code coverage report
Current view: top level - src/qt - addresstablemodel.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 2 234 0.9 %
Date: 2020-09-26 01:30:44 Functions: 2 44 4.5 %

          Line data    Source code
       1             : // Copyright (c) 2011-2020 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <qt/addresstablemodel.h>
       6             : 
       7             : #include <qt/guiutil.h>
       8             : #include <qt/walletmodel.h>
       9             : 
      10             : #include <key_io.h>
      11             : #include <wallet/wallet.h>
      12             : 
      13             : #include <algorithm>
      14             : #include <typeinfo>
      15             : 
      16             : #include <QFont>
      17             : #include <QDebug>
      18             : 
      19           1 : const QString AddressTableModel::Send = "S";
      20           1 : const QString AddressTableModel::Receive = "R";
      21             : 
      22           0 : struct AddressTableEntry
      23             : {
      24             :     enum Type {
      25             :         Sending,
      26             :         Receiving,
      27             :         Hidden /* QSortFilterProxyModel will filter these out */
      28             :     };
      29             : 
      30             :     Type type;
      31             :     QString label;
      32             :     QString address;
      33             : 
      34             :     AddressTableEntry() {}
      35           0 :     AddressTableEntry(Type _type, const QString &_label, const QString &_address):
      36           0 :         type(_type), label(_label), address(_address) {}
      37             : };
      38             : 
      39             : struct AddressTableEntryLessThan
      40             : {
      41           0 :     bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
      42             :     {
      43           0 :         return a.address < b.address;
      44             :     }
      45           0 :     bool operator()(const AddressTableEntry &a, const QString &b) const
      46             :     {
      47           0 :         return a.address < b;
      48             :     }
      49           0 :     bool operator()(const QString &a, const AddressTableEntry &b) const
      50             :     {
      51           0 :         return a < b.address;
      52             :     }
      53             : };
      54             : 
      55             : /* Determine address type from address purpose */
      56           0 : static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
      57             : {
      58             :     AddressTableEntry::Type addressType = AddressTableEntry::Hidden;
      59             :     // "refund" addresses aren't shown, and change addresses aren't returned by getAddresses at all.
      60           0 :     if (strPurpose == "send")
      61           0 :         addressType = AddressTableEntry::Sending;
      62           0 :     else if (strPurpose == "receive")
      63           0 :         addressType = AddressTableEntry::Receiving;
      64           0 :     else if (strPurpose == "unknown" || strPurpose == "") // if purpose not set, guess
      65           0 :         addressType = (isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending);
      66           0 :     return addressType;
      67             : }
      68             : 
      69             : // Private implementation
      70           0 : class AddressTablePriv
      71             : {
      72             : public:
      73             :     QList<AddressTableEntry> cachedAddressTable;
      74             :     AddressTableModel *parent;
      75             : 
      76           0 :     explicit AddressTablePriv(AddressTableModel *_parent):
      77           0 :         parent(_parent) {}
      78             : 
      79           0 :     void refreshAddressTable(interfaces::Wallet& wallet, bool pk_hash_only = false)
      80             :     {
      81           0 :         cachedAddressTable.clear();
      82             :         {
      83           0 :             for (const auto& address : wallet.getAddresses())
      84             :             {
      85           0 :                 if (pk_hash_only && address.dest.type() != typeid(PKHash))
      86           0 :                     continue;
      87           0 :                 AddressTableEntry::Type addressType = translateTransactionType(
      88           0 :                         QString::fromStdString(address.purpose), address.is_mine);
      89           0 :                 cachedAddressTable.append(AddressTableEntry(addressType,
      90           0 :                                   QString::fromStdString(address.name),
      91           0 :                                   QString::fromStdString(EncodeDestination(address.dest))));
      92           0 :             }
      93             :         }
      94             :         // std::lower_bound() and std::upper_bound() require our cachedAddressTable list to be sorted in asc order
      95             :         // Even though the map is already sorted this re-sorting step is needed because the originating map
      96             :         // is sorted by binary address, not by base58() address.
      97           0 :         std::sort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
      98           0 :     }
      99             : 
     100           0 :     void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
     101             :     {
     102             :         // Find address / label in model
     103           0 :         QList<AddressTableEntry>::iterator lower = std::lower_bound(
     104           0 :             cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
     105           0 :         QList<AddressTableEntry>::iterator upper = std::upper_bound(
     106           0 :             cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
     107           0 :         int lowerIndex = (lower - cachedAddressTable.begin());
     108           0 :         int upperIndex = (upper - cachedAddressTable.begin());
     109           0 :         bool inModel = (lower != upper);
     110           0 :         AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine);
     111             : 
     112           0 :         switch(status)
     113             :         {
     114             :         case CT_NEW:
     115           0 :             if(inModel)
     116             :             {
     117           0 :                 qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_NEW, but entry is already in model";
     118           0 :                 break;
     119             :             }
     120           0 :             parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
     121           0 :             cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
     122           0 :             parent->endInsertRows();
     123           0 :             break;
     124             :         case CT_UPDATED:
     125           0 :             if(!inModel)
     126             :             {
     127           0 :                 qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model";
     128           0 :                 break;
     129             :             }
     130           0 :             lower->type = newEntryType;
     131           0 :             lower->label = label;
     132           0 :             parent->emitDataChanged(lowerIndex);
     133           0 :             break;
     134             :         case CT_DELETED:
     135           0 :             if(!inModel)
     136             :             {
     137           0 :                 qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model";
     138           0 :                 break;
     139             :             }
     140           0 :             parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
     141           0 :             cachedAddressTable.erase(lower, upper);
     142           0 :             parent->endRemoveRows();
     143           0 :             break;
     144             :         }
     145           0 :     }
     146             : 
     147           0 :     int size()
     148             :     {
     149           0 :         return cachedAddressTable.size();
     150             :     }
     151             : 
     152           0 :     AddressTableEntry *index(int idx)
     153             :     {
     154           0 :         if(idx >= 0 && idx < cachedAddressTable.size())
     155             :         {
     156           0 :             return &cachedAddressTable[idx];
     157             :         }
     158             :         else
     159             :         {
     160           0 :             return nullptr;
     161             :         }
     162           0 :     }
     163             : };
     164             : 
     165           0 : AddressTableModel::AddressTableModel(WalletModel *parent, bool pk_hash_only) :
     166           0 :     QAbstractTableModel(parent), walletModel(parent)
     167           0 : {
     168           0 :     columns << tr("Label") << tr("Address");
     169           0 :     priv = new AddressTablePriv(this);
     170           0 :     priv->refreshAddressTable(parent->wallet(), pk_hash_only);
     171           0 : }
     172             : 
     173           0 : AddressTableModel::~AddressTableModel()
     174           0 : {
     175           0 :     delete priv;
     176           0 : }
     177             : 
     178           0 : int AddressTableModel::rowCount(const QModelIndex &parent) const
     179             : {
     180             :     Q_UNUSED(parent);
     181           0 :     return priv->size();
     182             : }
     183             : 
     184           0 : int AddressTableModel::columnCount(const QModelIndex &parent) const
     185             : {
     186             :     Q_UNUSED(parent);
     187           0 :     return columns.length();
     188             : }
     189             : 
     190           0 : QVariant AddressTableModel::data(const QModelIndex &index, int role) const
     191             : {
     192           0 :     if(!index.isValid())
     193           0 :         return QVariant();
     194             : 
     195           0 :     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
     196             : 
     197           0 :     if(role == Qt::DisplayRole || role == Qt::EditRole)
     198             :     {
     199           0 :         switch(index.column())
     200             :         {
     201             :         case Label:
     202           0 :             if(rec->label.isEmpty() && role == Qt::DisplayRole)
     203             :             {
     204           0 :                 return tr("(no label)");
     205             :             }
     206             :             else
     207             :             {
     208           0 :                 return rec->label;
     209             :             }
     210             :         case Address:
     211           0 :             return rec->address;
     212             :         }
     213             :     }
     214           0 :     else if (role == Qt::FontRole)
     215             :     {
     216           0 :         QFont font;
     217           0 :         if(index.column() == Address)
     218             :         {
     219           0 :             font = GUIUtil::fixedPitchFont();
     220           0 :         }
     221           0 :         return font;
     222           0 :     }
     223           0 :     else if (role == TypeRole)
     224             :     {
     225           0 :         switch(rec->type)
     226             :         {
     227             :         case AddressTableEntry::Sending:
     228           0 :             return Send;
     229             :         case AddressTableEntry::Receiving:
     230           0 :             return Receive;
     231             :         default: break;
     232             :         }
     233             :     }
     234           0 :     return QVariant();
     235           0 : }
     236             : 
     237           0 : bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
     238             : {
     239           0 :     if(!index.isValid())
     240           0 :         return false;
     241           0 :     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
     242           0 :     std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
     243           0 :     editStatus = OK;
     244             : 
     245           0 :     if(role == Qt::EditRole)
     246             :     {
     247           0 :         CTxDestination curAddress = DecodeDestination(rec->address.toStdString());
     248           0 :         if(index.column() == Label)
     249             :         {
     250             :             // Do nothing, if old label == new label
     251           0 :             if(rec->label == value.toString())
     252             :             {
     253           0 :                 editStatus = NO_CHANGES;
     254           0 :                 return false;
     255             :             }
     256           0 :             walletModel->wallet().setAddressBook(curAddress, value.toString().toStdString(), strPurpose);
     257           0 :         } else if(index.column() == Address) {
     258           0 :             CTxDestination newAddress = DecodeDestination(value.toString().toStdString());
     259             :             // Refuse to set invalid address, set error status and return false
     260           0 :             if(boost::get<CNoDestination>(&newAddress))
     261             :             {
     262           0 :                 editStatus = INVALID_ADDRESS;
     263           0 :                 return false;
     264             :             }
     265             :             // Do nothing, if old address == new address
     266           0 :             else if(newAddress == curAddress)
     267             :             {
     268           0 :                 editStatus = NO_CHANGES;
     269           0 :                 return false;
     270             :             }
     271             :             // Check for duplicate addresses to prevent accidental deletion of addresses, if you try
     272             :             // to paste an existing address over another address (with a different label)
     273           0 :             if (walletModel->wallet().getAddress(
     274             :                     newAddress, /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr))
     275             :             {
     276           0 :                 editStatus = DUPLICATE_ADDRESS;
     277           0 :                 return false;
     278             :             }
     279             :             // Double-check that we're not overwriting a receiving address
     280           0 :             else if(rec->type == AddressTableEntry::Sending)
     281             :             {
     282             :                 // Remove old entry
     283           0 :                 walletModel->wallet().delAddressBook(curAddress);
     284             :                 // Add new entry with new address
     285           0 :                 walletModel->wallet().setAddressBook(newAddress, value.toString().toStdString(), strPurpose);
     286           0 :             }
     287           0 :         }
     288           0 :         return true;
     289           0 :     }
     290           0 :     return false;
     291           0 : }
     292             : 
     293           0 : QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
     294             : {
     295           0 :     if(orientation == Qt::Horizontal)
     296             :     {
     297           0 :         if(role == Qt::DisplayRole && section < columns.size())
     298             :         {
     299           0 :             return columns[section];
     300             :         }
     301             :     }
     302           0 :     return QVariant();
     303           0 : }
     304             : 
     305           0 : Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
     306             : {
     307           0 :     if (!index.isValid()) return Qt::NoItemFlags;
     308             : 
     309           0 :     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
     310             : 
     311           0 :     Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
     312             :     // Can edit address and label for sending addresses,
     313             :     // and only label for receiving addresses.
     314           0 :     if(rec->type == AddressTableEntry::Sending ||
     315           0 :       (rec->type == AddressTableEntry::Receiving && index.column()==Label))
     316             :     {
     317           0 :         retval |= Qt::ItemIsEditable;
     318           0 :     }
     319           0 :     return retval;
     320           0 : }
     321             : 
     322           0 : QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
     323             : {
     324             :     Q_UNUSED(parent);
     325           0 :     AddressTableEntry *data = priv->index(row);
     326           0 :     if(data)
     327             :     {
     328           0 :         return createIndex(row, column, priv->index(row));
     329             :     }
     330             :     else
     331             :     {
     332           0 :         return QModelIndex();
     333             :     }
     334           0 : }
     335             : 
     336           0 : void AddressTableModel::updateEntry(const QString &address,
     337             :         const QString &label, bool isMine, const QString &purpose, int status)
     338             : {
     339             :     // Update address book model from Bitcoin core
     340           0 :     priv->updateEntry(address, label, isMine, purpose, status);
     341           0 : }
     342             : 
     343           0 : QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address, const OutputType address_type)
     344             : {
     345           0 :     std::string strLabel = label.toStdString();
     346           0 :     std::string strAddress = address.toStdString();
     347             : 
     348           0 :     editStatus = OK;
     349             : 
     350           0 :     if(type == Send)
     351             :     {
     352           0 :         if(!walletModel->validateAddress(address))
     353             :         {
     354           0 :             editStatus = INVALID_ADDRESS;
     355           0 :             return QString();
     356             :         }
     357             :         // Check for duplicate addresses
     358             :         {
     359           0 :             if (walletModel->wallet().getAddress(
     360           0 :                     DecodeDestination(strAddress), /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr))
     361             :             {
     362           0 :                 editStatus = DUPLICATE_ADDRESS;
     363           0 :                 return QString();
     364             :             }
     365             :         }
     366             : 
     367             :         // Add entry
     368           0 :         walletModel->wallet().setAddressBook(DecodeDestination(strAddress), strLabel, "send");
     369           0 :     }
     370           0 :     else if(type == Receive)
     371             :     {
     372             :         // Generate a new address to associate with given label
     373           0 :         CTxDestination dest;
     374           0 :         if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
     375             :         {
     376           0 :             WalletModel::UnlockContext ctx(walletModel->requestUnlock());
     377           0 :             if(!ctx.isValid())
     378             :             {
     379             :                 // Unlock wallet failed or was cancelled
     380           0 :                 editStatus = WALLET_UNLOCK_FAILURE;
     381           0 :                 return QString();
     382             :             }
     383           0 :             if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
     384             :             {
     385           0 :                 editStatus = KEY_GENERATION_FAILURE;
     386           0 :                 return QString();
     387             :             }
     388           0 :         }
     389           0 :         strAddress = EncodeDestination(dest);
     390           0 :     }
     391             :     else
     392             :     {
     393           0 :         return QString();
     394             :     }
     395           0 :     return QString::fromStdString(strAddress);
     396           0 : }
     397             : 
     398           0 : bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
     399             : {
     400             :     Q_UNUSED(parent);
     401           0 :     AddressTableEntry *rec = priv->index(row);
     402           0 :     if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
     403             :     {
     404             :         // Can only remove one row at a time, and cannot remove rows not in model.
     405             :         // Also refuse to remove receiving addresses.
     406           0 :         return false;
     407             :     }
     408           0 :     walletModel->wallet().delAddressBook(DecodeDestination(rec->address.toStdString()));
     409           0 :     return true;
     410           0 : }
     411             : 
     412           0 : QString AddressTableModel::labelForAddress(const QString &address) const
     413             : {
     414           0 :     std::string name;
     415           0 :     if (getAddressData(address, &name, /* purpose= */ nullptr)) {
     416           0 :         return QString::fromStdString(name);
     417             :     }
     418           0 :     return QString();
     419           0 : }
     420             : 
     421           0 : QString AddressTableModel::purposeForAddress(const QString &address) const
     422             : {
     423           0 :     std::string purpose;
     424           0 :     if (getAddressData(address, /* name= */ nullptr, &purpose)) {
     425           0 :         return QString::fromStdString(purpose);
     426             :     }
     427           0 :     return QString();
     428           0 : }
     429             : 
     430           0 : bool AddressTableModel::getAddressData(const QString &address,
     431             :         std::string* name,
     432             :         std::string* purpose) const {
     433           0 :     CTxDestination destination = DecodeDestination(address.toStdString());
     434           0 :     return walletModel->wallet().getAddress(destination, name, /* is_mine= */ nullptr, purpose);
     435           0 : }
     436             : 
     437           0 : int AddressTableModel::lookupAddress(const QString &address) const
     438             : {
     439           0 :     QModelIndexList lst = match(index(0, Address, QModelIndex()),
     440           0 :                                 Qt::EditRole, address, 1, Qt::MatchExactly);
     441           0 :     if(lst.isEmpty())
     442             :     {
     443           0 :         return -1;
     444             :     }
     445             :     else
     446             :     {
     447           0 :         return lst.at(0).row();
     448             :     }
     449           0 : }
     450             : 
     451           0 : OutputType AddressTableModel::GetDefaultAddressType() const { return walletModel->wallet().getDefaultAddressType(); };
     452             : 
     453           0 : void AddressTableModel::emitDataChanged(int idx)
     454             : {
     455           0 :     Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
     456           0 : }

Generated by: LCOV version 1.15