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