Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

addresstablemodel.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Copyright (c) 2014-2020 The Dash Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <qt/addresstablemodel.h>
7 
8 #include <qt/guiutil.h>
9 #include <qt/walletmodel.h>
10 
11 #include <base58.h>
12 #include <wallet/wallet.h>
13 
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 in mapAddressBook 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:
73  QList<AddressTableEntry> cachedAddressTable;
75 
77  wallet(_wallet), parent(_parent) {}
78 
80  {
81  cachedAddressTable.clear();
82  {
84  for (const std::pair<CTxDestination, CAddressBookData>& item : wallet->mapAddressBook)
85  {
86  const CTxDestination& address = item.first;
87  bool fMine = IsMine(*wallet, address);
89  QString::fromStdString(item.second.purpose), fMine);
90  const std::string& strName = item.second.name;
91  cachedAddressTable.append(AddressTableEntry(addressType,
92  QString::fromStdString(strName),
93  QString::fromStdString(EncodeDestination(address))));
94  }
95  }
96  // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
97  // Even though the map is already sorted this re-sorting step is needed because the originating map
98  // is sorted by binary address, not by base58() address.
100  }
101 
102  void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
103  {
104  // Find address / label in model
105  QList<AddressTableEntry>::iterator lower = qLowerBound(
107  QList<AddressTableEntry>::iterator upper = qUpperBound(
109  int lowerIndex = (lower - cachedAddressTable.begin());
110  int upperIndex = (upper - cachedAddressTable.begin());
111  bool inModel = (lower != upper);
112  AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine);
113 
114  switch(status)
115  {
116  case CT_NEW:
117  if(inModel)
118  {
119  qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_NEW, but entry is already in model";
120  break;
121  }
122  parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
123  cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
124  parent->endInsertRows();
125  break;
126  case CT_UPDATED:
127  if(!inModel)
128  {
129  qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model";
130  break;
131  }
132  lower->type = newEntryType;
133  lower->label = label;
134  parent->emitDataChanged(lowerIndex);
135  break;
136  case CT_DELETED:
137  if(!inModel)
138  {
139  qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model";
140  break;
141  }
142  parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
143  cachedAddressTable.erase(lower, upper);
144  parent->endRemoveRows();
145  break;
146  }
147  }
148 
149  int size()
150  {
151  return cachedAddressTable.size();
152  }
153 
155  {
156  if(idx >= 0 && idx < cachedAddressTable.size())
157  {
158  return &cachedAddressTable[idx];
159  }
160  else
161  {
162  return 0;
163  }
164  }
165 };
166 
168  QAbstractTableModel(parent),walletModel(parent),wallet(_wallet),priv(0)
169 {
170  columns << tr("Label") << tr("Address");
171  priv = new AddressTablePriv(wallet, this);
173 }
174 
176 {
177  delete priv;
178 }
179 
180 int AddressTableModel::rowCount(const QModelIndex &parent) const
181 {
182  Q_UNUSED(parent);
183  return priv->size();
184 }
185 
186 int AddressTableModel::columnCount(const QModelIndex &parent) const
187 {
188  Q_UNUSED(parent);
189  return columns.length();
190 }
191 
192 QVariant AddressTableModel::data(const QModelIndex &index, int role) const
193 {
194  if(!index.isValid())
195  return QVariant();
196 
197  AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
198 
199  if(role == Qt::DisplayRole || role == Qt::EditRole)
200  {
201  switch(index.column())
202  {
203  case Label:
204  if(rec->label.isEmpty() && role == Qt::DisplayRole)
205  {
206  return tr("(no label)");
207  }
208  else
209  {
210  return rec->label;
211  }
212  case Address:
213  return rec->address;
214  }
215  }
216  else if (role == Qt::FontRole)
217  {
218  QFont font;
219  if(index.column() == Address)
220  {
221  font = GUIUtil::getFontNormal();
222  }
223  return font;
224  }
225  else if (role == TypeRole)
226  {
227  switch(rec->type)
228  {
230  return Send;
232  return Receive;
233  default: break;
234  }
235  }
236  return QVariant();
237 }
238 
239 bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
240 {
241  if(!index.isValid())
242  return false;
243  AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
244  std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
245  editStatus = OK;
246 
247  if(role == Qt::EditRole)
248  {
249  LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
250  CTxDestination curAddress = DecodeDestination(rec->address.toStdString());
251  if(index.column() == Label)
252  {
253  // Do nothing, if old label == new label
254  if(rec->label == value.toString())
255  {
257  return false;
258  }
259  wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
260  } else if(index.column() == Address) {
261  CTxDestination newAddress = DecodeDestination(value.toString().toStdString());
262  // Refuse to set invalid address, set error status and return false
263  if(boost::get<CNoDestination>(&newAddress))
264  {
266  return false;
267  }
268  // Do nothing, if old address == new address
269  else if(newAddress == curAddress)
270  {
272  return false;
273  }
274  // Check for duplicate addresses to prevent accidental deletion of addresses, if you try
275  // to paste an existing address over another address (with a different label)
276  else if(wallet->mapAddressBook.count(newAddress))
277  {
279  return false;
280  }
281  // Double-check that we're not overwriting a receiving address
282  else if(rec->type == AddressTableEntry::Sending)
283  {
284  // Remove old entry
285  wallet->DelAddressBook(curAddress);
286  // Add new entry with new address
287  wallet->SetAddressBook(newAddress, rec->label.toStdString(), strPurpose);
288  }
289  }
290  return true;
291  }
292  return false;
293 }
294 
295 QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
296 {
297  if(orientation == Qt::Horizontal)
298  {
299  if(role == Qt::DisplayRole && section < columns.size())
300  {
301  return columns[section];
302  }
303  }
304  return QVariant();
305 }
306 
307 Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
308 {
309  if(!index.isValid())
310  return 0;
311  AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
312 
313  Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
314  // Can edit address and label for sending addresses,
315  // and only label for receiving addresses.
316  if(rec->type == AddressTableEntry::Sending ||
317  (rec->type == AddressTableEntry::Receiving && index.column()==Label))
318  {
319  retval |= Qt::ItemIsEditable;
320  }
321  return retval;
322 }
323 
324 QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
325 {
326  Q_UNUSED(parent);
327  AddressTableEntry *data = priv->index(row);
328  if(data)
329  {
330  return createIndex(row, column, priv->index(row));
331  }
332  else
333  {
334  return QModelIndex();
335  }
336 }
337 
338 void AddressTableModel::updateEntry(const QString &address,
339  const QString &label, bool isMine, const QString &purpose, int status)
340 {
341  // Update address book model from Dash core
342  priv->updateEntry(address, label, isMine, purpose, status);
343 }
344 
345 QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
346 {
347  std::string strLabel = label.toStdString();
348  std::string strAddress = address.toStdString();
349 
350  editStatus = OK;
351 
352  if(type == Send)
353  {
354  if(!walletModel->validateAddress(address))
355  {
357  return QString();
358  }
359  // Check for duplicate addresses
360  {
362  if(wallet->mapAddressBook.count(DecodeDestination(strAddress)))
363  {
365  return QString();
366  }
367  }
368  }
369  else if(type == Receive)
370  {
371  // Generate a new address to associate with given label
372  CPubKey newKey;
373  if(!wallet->GetKeyFromPool(newKey, false))
374  {
376  if(!ctx.isValid())
377  {
378  // Unlock wallet failed or was cancelled
380  return QString();
381  }
382  if(!wallet->GetKeyFromPool(newKey, false))
383  {
385  return QString();
386  }
387  }
388  strAddress = EncodeDestination(newKey.GetID());
389  }
390  else
391  {
392  return QString();
393  }
394 
395  // Add entry
396  {
398  wallet->SetAddressBook(DecodeDestination(strAddress), strLabel,
399  (type == Send ? "send" : "receive"));
400  }
401  return QString::fromStdString(strAddress);
402 }
403 
404 bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
405 {
406  Q_UNUSED(parent);
407  AddressTableEntry *rec = priv->index(row);
408  if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
409  {
410  // Can only remove one row at a time, and cannot remove rows not in model.
411  // Also refuse to remove receiving addresses.
412  return false;
413  }
414  {
416  wallet->DelAddressBook(DecodeDestination(rec->address.toStdString()));
417  }
418  return true;
419 }
420 
421 /* Look up label for address in address book, if not found return empty string.
422  */
423 QString AddressTableModel::labelForAddress(const QString &address) const
424 {
425  CTxDestination dest = DecodeDestination(address.toStdString());
426  return labelForDestination(dest);
427 }
428 
429 
431 {
432  {
434  std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest);
435  if (mi != wallet->mapAddressBook.end())
436  {
437  return QString::fromStdString(mi->second.name);
438  }
439  }
440  return QString();
441 }
442 
443 int AddressTableModel::lookupAddress(const QString &address) const
444 {
445  QModelIndexList lst = match(index(0, Address, QModelIndex()),
446  Qt::EditRole, address, 1, Qt::MatchExactly);
447  if(lst.isEmpty())
448  {
449  return -1;
450  }
451  else
452  {
453  return lst.at(0).row();
454  }
455 }
456 
458 {
459  Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
460 }
AddressTableModel(CWallet *wallet, WalletModel *parent=0)
Generating a new public key for a receiving address failed.
QString address
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:80
bool operator()(const QString &a, const AddressTableEntry &b) const
QModelIndex index(int row, int column, const QModelIndex &parent) const
AddressTablePriv(CWallet *_wallet, AddressTableModel *_parent)
int lookupAddress(const QString &address) const
Qt::ItemFlags flags(const QModelIndex &index) const
WalletModel * walletModel
std::map< CTxDestination, CAddressBookData > mapAddressBook
Definition: wallet.h:906
CCriticalSection cs_wallet
Definition: wallet.h:836
AddressTableEntry * index(int idx)
Address already in address book.
QFont getFontNormal()
Get the default normal QFont.
Definition: guiutil.cpp:1735
CTxDestination DecodeDestination(const std::string &str)
Definition: base58.cpp:336
Type
Type type
bool GetKeyFromPool(CPubKey &key, bool fInternal)
Definition: wallet.cpp:4521
QVariant headerData(int section, Qt::Orientation orientation, int role) const
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:149
static const QString Send
Specifies send address.
int rowCount(const QModelIndex &parent) const
int columnCount(const QModelIndex &parent) const
bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
bool SetAddressBook(const CTxDestination &address, const std::string &strName, const std::string &purpose)
Definition: wallet.cpp:4277
bool DelAddressBook(const CTxDestination &address)
Definition: wallet.cpp:4295
Wallet could not be unlocked to create new receiving address.
QList< AddressTableEntry > cachedAddressTable
#define LOCK(cs)
Definition: sync.h:178
static secp256k1_context * ctx
Definition: tests.c:46
An encapsulated public key.
Definition: pubkey.h:30
QString labelForAddress(const QString &address) const
AddressTableModel * parent
Qt model of the address book in the core.
AddressTableEntry()
AddressTableEntry(Type _type, const QString &_label, const QString &_address)
bool validateAddress(const QString &address)
QString addRow(const QString &type, const QString &label, const QString &address)
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex())
bool operator()(const AddressTableEntry &a, const QString &b) const
std::string EncodeDestination(const CTxDestination &dest)
Definition: base58.cpp:329
UnlockContext requestUnlock(bool fForMixingOnly=false)
AddressTablePriv * priv
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:100
void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
static const QString Receive
Specifies receive address.
void emitDataChanged(int index)
Notify listeners that data changed.
QVariant data(const QModelIndex &index, int role) const
A CWallet is an extension of a keystore, which also maintains a set of transactions and balances...
Definition: wallet.h:715
static int count
Definition: tests.c:45
QString labelForDestination(const CTxDestination &dest) const
bool setData(const QModelIndex &index, const QVariant &value, int role)
QString label
No changes were made during edit operation.
User specified label.
static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
friend class AddressTablePriv
isminetype IsMine(const CKeyStore &keystore, const CTxDestination &dest)
Definition: ismine.cpp:28
void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
Type of address (Send or Receive)
Released under the MIT license