Bitcoin Core  22.99.0
P2P Digital Currency
addresstablemodel.cpp
Go to the documentation of this file.
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 
15 #include <QFont>
16 #include <QDebug>
17 
18 const QString AddressTableModel::Send = "S";
19 const QString AddressTableModel::Receive = "R";
20 
22 {
23  enum Type {
26  Hidden /* QSortFilterProxyModel will filter these out */
27  };
28 
30  QString label;
31  QString address;
32 
34  AddressTableEntry(Type _type, const QString &_label, const QString &_address):
35  type(_type), label(_label), address(_address) {}
36 };
37 
39 {
40  bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
41  {
42  return a.address < b.address;
43  }
44  bool operator()(const AddressTableEntry &a, const QString &b) const
45  {
46  return a.address < b;
47  }
48  bool operator()(const QString &a, const AddressTableEntry &b) const
49  {
50  return a < b.address;
51  }
52 };
53 
54 /* Determine address type from address purpose */
55 static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
56 {
58  // "refund" addresses aren't shown, and change addresses aren't returned by getAddresses at all.
59  if (strPurpose == "send")
60  addressType = AddressTableEntry::Sending;
61  else if (strPurpose == "receive")
62  addressType = AddressTableEntry::Receiving;
63  else if (strPurpose == "unknown" || strPurpose == "") // if purpose not set, guess
65  return addressType;
66 }
67 
68 // Private implementation
70 {
71 public:
72  QList<AddressTableEntry> cachedAddressTable;
74 
75  explicit AddressTablePriv(AddressTableModel *_parent):
76  parent(_parent) {}
77 
78  void refreshAddressTable(interfaces::Wallet& wallet, bool pk_hash_only = false)
79  {
80  cachedAddressTable.clear();
81  {
82  for (const auto& address : wallet.getAddresses())
83  {
84  if (pk_hash_only && !std::holds_alternative<PKHash>(address.dest)) {
85  continue;
86  }
88  QString::fromStdString(address.purpose), address.is_mine);
89  cachedAddressTable.append(AddressTableEntry(addressType,
90  QString::fromStdString(address.name),
91  QString::fromStdString(EncodeDestination(address.dest))));
92  }
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.
98  }
99 
100  void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
101  {
102  // Find address / label in model
103  QList<AddressTableEntry>::iterator lower = std::lower_bound(
105  QList<AddressTableEntry>::iterator upper = std::upper_bound(
107  int lowerIndex = (lower - cachedAddressTable.begin());
108  int upperIndex = (upper - cachedAddressTable.begin());
109  bool inModel = (lower != upper);
110  AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine);
111 
112  switch(status)
113  {
114  case CT_NEW:
115  if(inModel)
116  {
117  qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_NEW, but entry is already in model";
118  break;
119  }
120  parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
121  cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
122  parent->endInsertRows();
123  break;
124  case CT_UPDATED:
125  if(!inModel)
126  {
127  qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model";
128  break;
129  }
130  lower->type = newEntryType;
131  lower->label = label;
132  parent->emitDataChanged(lowerIndex);
133  break;
134  case CT_DELETED:
135  if(!inModel)
136  {
137  qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model";
138  break;
139  }
140  parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
141  cachedAddressTable.erase(lower, upper);
142  parent->endRemoveRows();
143  break;
144  }
145  }
146 
147  int size()
148  {
149  return cachedAddressTable.size();
150  }
151 
153  {
154  if(idx >= 0 && idx < cachedAddressTable.size())
155  {
156  return &cachedAddressTable[idx];
157  }
158  else
159  {
160  return nullptr;
161  }
162  }
163 };
164 
166  QAbstractTableModel(parent), walletModel(parent)
167 {
168  columns << tr("Label") << tr("Address");
169  priv = new AddressTablePriv(this);
170  priv->refreshAddressTable(parent->wallet(), pk_hash_only);
171 }
172 
174 {
175  delete priv;
176 }
177 
178 int AddressTableModel::rowCount(const QModelIndex &parent) const
179 {
180  if (parent.isValid()) {
181  return 0;
182  }
183  return priv->size();
184 }
185 
186 int AddressTableModel::columnCount(const QModelIndex &parent) const
187 {
188  if (parent.isValid()) {
189  return 0;
190  }
191  return columns.length();
192 }
193 
194 QVariant AddressTableModel::data(const QModelIndex &index, int role) const
195 {
196  if(!index.isValid())
197  return QVariant();
198 
199  AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
200 
201  const auto column = static_cast<ColumnIndex>(index.column());
202  if (role == Qt::DisplayRole || role == Qt::EditRole) {
203  switch (column) {
204  case Label:
205  if (rec->label.isEmpty() && role == Qt::DisplayRole) {
206  return tr("(no label)");
207  } else {
208  return rec->label;
209  }
210  case Address:
211  return rec->address;
212  } // no default case, so the compiler can warn about missing cases
213  assert(false);
214  } else if (role == Qt::FontRole) {
215  switch (column) {
216  case Label:
217  return QFont();
218  case Address:
219  return GUIUtil::fixedPitchFont();
220  } // no default case, so the compiler can warn about missing cases
221  assert(false);
222  } else if (role == TypeRole) {
223  switch(rec->type)
224  {
226  return Send;
228  return Receive;
230  return {};
231  } // no default case, so the compiler can warn about missing cases
232  assert(false);
233  }
234  return QVariant();
235 }
236 
237 bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
238 {
239  if(!index.isValid())
240  return false;
241  AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
242  std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
243  editStatus = OK;
244 
245  if(role == Qt::EditRole)
246  {
247  CTxDestination curAddress = DecodeDestination(rec->address.toStdString());
248  if(index.column() == Label)
249  {
250  // Do nothing, if old label == new label
251  if(rec->label == value.toString())
252  {
254  return false;
255  }
256  walletModel->wallet().setAddressBook(curAddress, value.toString().toStdString(), strPurpose);
257  } else if(index.column() == Address) {
258  CTxDestination newAddress = DecodeDestination(value.toString().toStdString());
259  // Refuse to set invalid address, set error status and return false
260  if(std::get_if<CNoDestination>(&newAddress))
261  {
263  return false;
264  }
265  // Do nothing, if old address == new address
266  else if(newAddress == curAddress)
267  {
269  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)
274  newAddress, /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr))
275  {
277  return false;
278  }
279  // Double-check that we're not overwriting a receiving address
280  else if(rec->type == AddressTableEntry::Sending)
281  {
282  // Remove old entry
283  walletModel->wallet().delAddressBook(curAddress);
284  // Add new entry with new address
285  walletModel->wallet().setAddressBook(newAddress, value.toString().toStdString(), strPurpose);
286  }
287  }
288  return true;
289  }
290  return false;
291 }
292 
293 QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
294 {
295  if(orientation == Qt::Horizontal)
296  {
297  if(role == Qt::DisplayRole && section < columns.size())
298  {
299  return columns[section];
300  }
301  }
302  return QVariant();
303 }
304 
305 Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
306 {
307  if (!index.isValid()) return Qt::NoItemFlags;
308 
309  AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
310 
311  Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
312  // Can edit address and label for sending addresses,
313  // and only label for receiving addresses.
314  if(rec->type == AddressTableEntry::Sending ||
315  (rec->type == AddressTableEntry::Receiving && index.column()==Label))
316  {
317  retval |= Qt::ItemIsEditable;
318  }
319  return retval;
320 }
321 
322 QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
323 {
324  Q_UNUSED(parent);
325  AddressTableEntry *data = priv->index(row);
326  if(data)
327  {
328  return createIndex(row, column, priv->index(row));
329  }
330  else
331  {
332  return QModelIndex();
333  }
334 }
335 
336 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  priv->updateEntry(address, label, isMine, purpose, status);
341 }
342 
343 QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address, const OutputType address_type)
344 {
345  std::string strLabel = label.toStdString();
346  std::string strAddress = address.toStdString();
347 
348  editStatus = OK;
349 
350  if(type == Send)
351  {
352  if(!walletModel->validateAddress(address))
353  {
355  return QString();
356  }
357  // Check for duplicate addresses
358  {
360  DecodeDestination(strAddress), /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr))
361  {
363  return QString();
364  }
365  }
366 
367  // Add entry
368  walletModel->wallet().setAddressBook(DecodeDestination(strAddress), strLabel, "send");
369  }
370  else if(type == Receive)
371  {
372  // Generate a new address to associate with given label
373  CTxDestination dest;
374  if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
375  {
377  if(!ctx.isValid())
378  {
379  // Unlock wallet failed or was cancelled
381  return QString();
382  }
383  if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
384  {
386  return QString();
387  }
388  }
389  strAddress = EncodeDestination(dest);
390  }
391  else
392  {
393  return QString();
394  }
395  return QString::fromStdString(strAddress);
396 }
397 
398 bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
399 {
400  Q_UNUSED(parent);
401  AddressTableEntry *rec = priv->index(row);
402  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  return false;
407  }
409  return true;
410 }
411 
412 QString AddressTableModel::labelForAddress(const QString &address) const
413 {
414  std::string name;
415  if (getAddressData(address, &name, /* purpose= */ nullptr)) {
416  return QString::fromStdString(name);
417  }
418  return QString();
419 }
420 
421 QString AddressTableModel::purposeForAddress(const QString &address) const
422 {
423  std::string purpose;
424  if (getAddressData(address, /* name= */ nullptr, &purpose)) {
425  return QString::fromStdString(purpose);
426  }
427  return QString();
428 }
429 
430 bool AddressTableModel::getAddressData(const QString &address,
431  std::string* name,
432  std::string* purpose) const {
433  CTxDestination destination = DecodeDestination(address.toStdString());
434  return walletModel->wallet().getAddress(destination, name, /* is_mine= */ nullptr, purpose);
435 }
436 
437 int AddressTableModel::lookupAddress(const QString &address) const
438 {
439  QModelIndexList lst = match(index(0, Address, QModelIndex()),
440  Qt::EditRole, address, 1, Qt::MatchExactly);
441  if(lst.isEmpty())
442  {
443  return -1;
444  }
445  else
446  {
447  return lst.at(0).row();
448  }
449 }
450 
452 
454 {
455  Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
456 }
AddressTableEntry::Receiving
@ Receiving
Definition: addresstablemodel.cpp:25
OutputType
OutputType
Definition: outputtype.h:18
count
static int count
Definition: tests.c:41
assert
assert(!tx.IsCoinBase())
AddressTableEntry::Sending
@ Sending
Definition: addresstablemodel.cpp:24
wallet.h
WalletModel
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:51
AddressTablePriv::AddressTablePriv
AddressTablePriv(AddressTableModel *_parent)
Definition: addresstablemodel.cpp:75
key_io.h
AddressTableModel::walletModel
WalletModel *const walletModel
Definition: addresstablemodel.h:86
AddressTableEntry::label
QString label
Definition: addresstablemodel.cpp:30
walletmodel.h
AddressTableModel::AddressTableModel
AddressTableModel(WalletModel *parent=nullptr, bool pk_hash_only=false)
Definition: addresstablemodel.cpp:165
AddressTableModel
Qt model of the address book in the core.
Definition: addresstablemodel.h:23
AddressTableModel::~AddressTableModel
~AddressTableModel()
Definition: addresstablemodel.cpp:173
AddressTableModel::labelForAddress
QString labelForAddress(const QString &address) const
Look up label for address in address book, if not found return empty string.
Definition: addresstablemodel.cpp:412
AddressTableModel::KEY_GENERATION_FAILURE
@ KEY_GENERATION_FAILURE
Generating a new public key for a receiving address failed.
Definition: addresstablemodel.h:47
CT_DELETED
@ CT_DELETED
Definition: ui_change_type.h:12
AddressTableModel::Receive
static const QString Receive
Specifies receive address.
Definition: addresstablemodel.h:51
AddressTableModel::data
QVariant data(const QModelIndex &index, int role) const override
Definition: addresstablemodel.cpp:194
AddressTableModel::columns
QStringList columns
Definition: addresstablemodel.h:88
AddressTableEntry::address
QString address
Definition: addresstablemodel.cpp:31
CT_UPDATED
@ CT_UPDATED
Definition: ui_change_type.h:11
wallet
Definition: interfaces.cpp:50
AddressTableEntryLessThan
Definition: addresstablemodel.cpp:38
WalletModel::requestUnlock
UnlockContext requestUnlock()
Definition: walletmodel.cpp:433
AddressTableEntryLessThan::operator()
bool operator()(const AddressTableEntry &a, const QString &b) const
Definition: addresstablemodel.cpp:44
AddressTableModel::emitDataChanged
void emitDataChanged(int index)
Notify listeners that data changed.
Definition: addresstablemodel.cpp:453
interfaces::Wallet::getAddress
virtual bool getAddress(const CTxDestination &dest, std::string *name, isminetype *is_mine, std::string *purpose)=0
Look up address in wallet, return whether exists.
AddressTableModel::ColumnIndex
ColumnIndex
Definition: addresstablemodel.h:31
AddressTableEntryLessThan::operator()
bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
Definition: addresstablemodel.cpp:40
WalletModel::wallet
interfaces::Wallet & wallet() const
Definition: walletmodel.h:144
CTxDestination
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:157
WalletModel::validateAddress
bool validateAddress(const QString &address)
Definition: walletmodel.cpp:145
AddressTableModel::priv
AddressTablePriv * priv
Definition: addresstablemodel.h:87
AddressTableModel::addRow
QString addRow(const QString &type, const QString &label, const QString &address, const OutputType address_type)
Definition: addresstablemodel.cpp:343
AddressTablePriv::index
AddressTableEntry * index(int idx)
Definition: addresstablemodel.cpp:152
AddressTableEntry::AddressTableEntry
AddressTableEntry()
Definition: addresstablemodel.cpp:33
AddressTableModel::index
QModelIndex index(int row, int column, const QModelIndex &parent) const override
Definition: addresstablemodel.cpp:322
AddressTableModel::rowCount
int rowCount(const QModelIndex &parent) const override
Definition: addresstablemodel.cpp:178
guiutil.h
AddressTableEntry::Hidden
@ Hidden
Definition: addresstablemodel.cpp:26
AddressTableModel::flags
Qt::ItemFlags flags(const QModelIndex &index) const override
Definition: addresstablemodel.cpp:305
AddressTableModel::Label
@ Label
User specified label.
Definition: addresstablemodel.h:32
AddressTablePriv
Definition: addresstablemodel.cpp:69
DecodeDestination
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg)
Definition: key_io.cpp:261
interfaces::Wallet::delAddressBook
virtual bool delAddressBook(const CTxDestination &dest)=0
interfaces::Wallet
Interface for accessing a wallet.
Definition: wallet.h:52
AddressTableModel::columnCount
int columnCount(const QModelIndex &parent) const override
Definition: addresstablemodel.cpp:186
interfaces::Wallet::getNewDestination
virtual bool getNewDestination(const OutputType type, const std::string label, CTxDestination &dest)=0
AddressTableModel::setData
bool setData(const QModelIndex &index, const QVariant &value, int role) override
Definition: addresstablemodel.cpp:237
GUIUtil::fixedPitchFont
QFont fixedPitchFont(bool use_embedded_font)
Definition: guiutil.cpp:88
AddressTableModel::lookupAddress
int lookupAddress(const QString &address) const
Definition: addresstablemodel.cpp:437
name
const char * name
Definition: rest.cpp:43
AddressTableEntry::AddressTableEntry
AddressTableEntry(Type _type, const QString &_label, const QString &_address)
Definition: addresstablemodel.cpp:34
interfaces::Wallet::getDefaultAddressType
virtual OutputType getDefaultAddressType()=0
AddressTableModel::WALLET_UNLOCK_FAILURE
@ WALLET_UNLOCK_FAILURE
Wallet could not be unlocked to create new receiving address.
Definition: addresstablemodel.h:46
AddressTableModel::TypeRole
@ TypeRole
Type of address (Send or Receive)
Definition: addresstablemodel.h:37
WalletModel::UnlockContext
Definition: walletmodel.h:114
AddressTablePriv::cachedAddressTable
QList< AddressTableEntry > cachedAddressTable
Definition: addresstablemodel.cpp:72
AddressTablePriv::refreshAddressTable
void refreshAddressTable(interfaces::Wallet &wallet, bool pk_hash_only=false)
Definition: addresstablemodel.cpp:78
AddressTableEntry
Definition: addresstablemodel.cpp:21
AddressTableModel::INVALID_ADDRESS
@ INVALID_ADDRESS
Unparseable address.
Definition: addresstablemodel.h:44
AddressTableModel::OK
@ OK
Everything ok.
Definition: addresstablemodel.h:42
AddressTableModel::NO_CHANGES
@ NO_CHANGES
No changes were made during edit operation.
Definition: addresstablemodel.h:43
AddressTableModel::Send
static const QString Send
Specifies send address.
Definition: addresstablemodel.h:50
AddressTablePriv::updateEntry
void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
Definition: addresstablemodel.cpp:100
CT_NEW
@ CT_NEW
Definition: ui_change_type.h:10
interfaces::Wallet::setAddressBook
virtual bool setAddressBook(const CTxDestination &dest, const std::string &name, const std::string &purpose)=0
Add or update address.
AddressTablePriv::size
int size()
Definition: addresstablemodel.cpp:147
AddressTableModel::updateEntry
void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
Definition: addresstablemodel.cpp:336
AddressTablePriv::parent
AddressTableModel * parent
Definition: addresstablemodel.cpp:73
AddressTableModel::editStatus
EditStatus editStatus
Definition: addresstablemodel.h:89
AddressTableEntry::type
Type type
Definition: addresstablemodel.cpp:29
AddressTableModel::GetDefaultAddressType
OutputType GetDefaultAddressType() const
Definition: addresstablemodel.cpp:451
AddressTableModel::getAddressData
bool getAddressData(const QString &address, std::string *name, std::string *purpose) const
Look up address book data given an address string.
Definition: addresstablemodel.cpp:430
AddressTableModel::removeRows
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
Definition: addresstablemodel.cpp:398
AddressTableEntryLessThan::operator()
bool operator()(const QString &a, const AddressTableEntry &b) const
Definition: addresstablemodel.cpp:48
AddressTableModel::purposeForAddress
QString purposeForAddress(const QString &address) const
Look up purpose for address in address book, if not found return empty string.
Definition: addresstablemodel.cpp:421
AddressTableModel::DUPLICATE_ADDRESS
@ DUPLICATE_ADDRESS
Address already in address book.
Definition: addresstablemodel.h:45
AddressTableEntry::Type
Type
Definition: addresstablemodel.cpp:23
addresstablemodel.h
AddressTableModel::headerData
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
Definition: addresstablemodel.cpp:293
AddressTableModel::AddressTablePriv
friend class AddressTablePriv
Definition: addresstablemodel.h:102
AddressTableModel::Address
@ Address
Bitcoin address.
Definition: addresstablemodel.h:33
EncodeDestination
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:256
ctx
static secp256k1_context * ctx
Definition: tests.c:42
translateTransactionType
static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
Definition: addresstablemodel.cpp:55