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