Fabcoin Core  0.16.2
P2P Digital Currency
tokenitemmodel.cpp
Go to the documentation of this file.
1 #include <tokenitemmodel.h>
2 #include <token.h>
3 #include <walletmodel.h>
4 #include <wallet/wallet.h>
5 #include <validation.h>
6 #include <fabcoinunits.h>
7 #include <algorithm>
8 
9 #include <QDateTime>
10 #include <QFont>
11 #include <QDebug>
12 #include <QThread>
13 
15 {
16 public:
18  {}
19 
20  TokenItemEntry(uint256 tokenHash, CTokenInfo tokenInfo)
21  {
22  hash = tokenHash;
23  createTime.setTime_t(tokenInfo.nCreateTime);
24  contractAddress = QString::fromStdString(tokenInfo.strContractAddress);
25  tokenName = QString::fromStdString(tokenInfo.strTokenName);
26  tokenSymbol = QString::fromStdString(tokenInfo.strTokenSymbol);
27  decimals = tokenInfo.nDecimals;
28  senderAddress = QString::fromStdString(tokenInfo.strSenderAddress);
29  }
30 
32  {
33  hash = obj.hash;
34  createTime = obj.createTime;
36  tokenName = obj.tokenName;
38  decimals = obj.decimals;
40  balance = obj.balance;
41  }
42 
43  bool update(Token* tokenAbi)
44  {
45  bool modified;
46  return update(tokenAbi, modified);
47  }
48 
49  bool update(Token* tokenAbi, bool& modified)
50  {
51  modified = false;
52 
53  if(!tokenAbi)
54  return false;
55 
56  bool ret = true;
57  tokenAbi->setAddress(contractAddress.toStdString());
58  tokenAbi->setSender(senderAddress.toStdString());
59  std::string strBalance;
60  ret &= tokenAbi->balanceOf(strBalance);
61  if(ret)
62  {
63  int256_t val(strBalance);
64  if(val != balance)
65  {
66  modified = true;
67  }
68  balance = val;
69  }
70 
71  return ret;
72  }
73 
75  {}
76 
78  QDateTime createTime;
79  QString contractAddress;
80  QString tokenName;
81  QString tokenSymbol;
82  quint8 decimals;
83  QString senderAddress;
84  int256_t balance;
85 };
86 
87 class TokenTxWorker : public QObject
88 {
89  Q_OBJECT
90 public:
92  bool first;
95  wallet(_wallet), first(true) {}
96 
97 private Q_SLOTS:
98  void updateTokenTx(const QString &hash)
99  {
100  // Initialize variables
101  uint256 tokenHash = uint256S(hash.toStdString());
102  int64_t fromBlock = 0;
103  int64_t toBlock = -1;
104  CTokenInfo tokenInfo;
105  uint256 blockHash;
106  bool found = false;
107 
108  LOCK2(cs_main, wallet->cs_wallet);
109 
110  int64_t backInPast = first ? COINBASE_MATURITY : 10;
111  first = false;
112 
113  CBlockIndex* tip = chainActive.Tip();
114  if(tip)
115  {
116  // Get current block hash and height
117  blockHash = tip->GetBlockHash();
118  toBlock = chainActive.Height();
119 
120  // Find the token tx in the wallet
121  std::map<uint256, CTokenInfo>::iterator mi = wallet->mapToken.find(tokenHash);
122  found = mi != wallet->mapToken.end();
123  if(found)
124  {
125  // Get the start location for search the event log
126  tokenInfo = mi->second;
127  CBlockIndex* index = chainActive[tokenInfo.blockNumber];
128  if(tokenInfo.blockNumber < toBlock)
129  {
130  if(index && index->GetBlockHash() == tokenInfo.blockHash)
131  {
132  fromBlock = tokenInfo.blockNumber;
133  }
134  else
135  {
136  fromBlock = tokenInfo.blockNumber - backInPast;
137  }
138  }
139  else
140  {
141  fromBlock = toBlock - backInPast;
142  }
143  if(fromBlock < 0)
144  fromBlock = 0;
145 
146  tokenInfo.blockHash = blockHash;
147  tokenInfo.blockNumber = toBlock;
148  }
149  }
150 
151  if(found)
152  {
153  // List the events and update the token tx
154  std::vector<TokenEvent> tokenEvents;
155  tokenTxAbi.setAddress(tokenInfo.strContractAddress);
156  tokenTxAbi.setSender(tokenInfo.strSenderAddress);
157  tokenTxAbi.transferEvents(tokenEvents, fromBlock, toBlock);
158  for(size_t i = 0; i < tokenEvents.size(); i++)
159  {
160  TokenEvent event = tokenEvents[i];
161  CTokenTx tokenTx;
162  tokenTx.strContractAddress = event.address;
163  tokenTx.strSenderAddress = event.sender;
164  tokenTx.strReceiverAddress = event.receiver;
165  tokenTx.nValue = event.value;
166  tokenTx.transactionHash = event.transactionHash;
167  tokenTx.blockHash = event.blockHash;
168  tokenTx.blockNumber = event.blockNumber;
169  wallet->AddTokenTxEntry(tokenTx, false);
170  }
171 
172  wallet->AddTokenEntry(tokenInfo);
173  }
174  }
175 };
176 
177 #include <tokenitemmodel.moc>
178 
180 {
181  bool operator()(const TokenItemEntry &a, const TokenItemEntry &b) const
182  {
183  return a.hash < b.hash;
184  }
185  bool operator()(const TokenItemEntry &a, const uint256 &b) const
186  {
187  return a.hash < b;
188  }
189  bool operator()(const uint256 &a, const TokenItemEntry &b) const
190  {
191  return a < b.hash;
192  }
193 };
194 
196 {
197 public:
199  QList<TokenItemEntry> cachedTokenItem;
201 
202  TokenItemPriv(CWallet *_wallet, TokenItemModel *_parent):
203  wallet(_wallet), parent(_parent) {}
204 
206  {
207  cachedTokenItem.clear();
208  {
209  LOCK2(cs_main, wallet->cs_wallet);
210  for(std::map<uint256, CTokenInfo>::iterator it = wallet->mapToken.begin(); it != wallet->mapToken.end(); ++it)
211  {
212  TokenItemEntry tokenItem(it->first, it->second);
213  if(parent)
214  {
215  tokenItem.update(parent->getTokenAbi());
216  }
217  cachedTokenItem.append(tokenItem);
218  }
219  }
220  std::sort(cachedTokenItem.begin(), cachedTokenItem.end(), TokenItemEntryLessThan());
221  }
222 
223  void updateEntry(const TokenItemEntry &item, int status)
224  {
225  // Find address / label in model
226  QList<TokenItemEntry>::iterator lower = qLowerBound(
227  cachedTokenItem.begin(), cachedTokenItem.end(), item, TokenItemEntryLessThan());
228  QList<TokenItemEntry>::iterator upper = qUpperBound(
229  cachedTokenItem.begin(), cachedTokenItem.end(), item, TokenItemEntryLessThan());
230  int lowerIndex = (lower - cachedTokenItem.begin());
231  int upperIndex = (upper - cachedTokenItem.begin());
232  bool inModel = (lower != upper);
233 
234  switch(status)
235  {
236  case CT_NEW:
237  if(inModel)
238  {
239  qWarning() << "TokenItemPriv::updateEntry: Warning: Got CT_NEW, but entry is already in model";
240  break;
241  }
242  parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
243  cachedTokenItem.insert(lowerIndex, item);
244  parent->endInsertRows();
245  break;
246  case CT_UPDATED:
247  if(!inModel)
248  {
249  qWarning() << "TokenItemPriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model";
250  break;
251  }
252  cachedTokenItem[lowerIndex] = item;
253  parent->emitDataChanged(lowerIndex);
254  break;
255  case CT_DELETED:
256  if(!inModel)
257  {
258  qWarning() << "TokenItemPriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model";
259  break;
260  }
261  parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
262  cachedTokenItem.erase(lower, upper);
263  parent->endRemoveRows();
264  break;
265  }
266  }
267 
268  int size()
269  {
270  return cachedTokenItem.size();
271  }
272 
274  {
275  if(idx >= 0 && idx < cachedTokenItem.size())
276  {
277  return &cachedTokenItem[idx];
278  }
279  else
280  {
281  return 0;
282  }
283  }
284 };
285 
287  QAbstractItemModel(parent),
288  tokenAbi(0),
289  walletModel(parent),
290  wallet(_wallet),
291  priv(0),
292  worker(0)
293 {
294  columns << tr("Token Name") << tr("Token Symbol") << tr("Balance");
295  tokenAbi = new Token();
296 
297  priv = new TokenItemPriv(wallet, this);
299 
300  worker = new TokenTxWorker(wallet);
301  worker->moveToThread(&(t));
302 
303  t.start();
304 
306 }
307 
309 {
311 
312  t.quit();
313  t.wait();
314 
315  if(tokenAbi)
316  {
317  delete tokenAbi;
318  tokenAbi = 0;
319  }
320 
321  if(priv)
322  {
323  delete priv;
324  priv = 0;
325  }
326 }
327 
328 QModelIndex TokenItemModel::index(int row, int column, const QModelIndex &parent) const
329 {
330  Q_UNUSED(parent);
331  TokenItemEntry *data = priv->index(row);
332  if(data)
333  {
334  return createIndex(row, column, priv->index(row));
335  }
336  return QModelIndex();
337 }
338 
339 QModelIndex TokenItemModel::parent(const QModelIndex &child) const
340 {
341  Q_UNUSED(child);
342  return QModelIndex();
343 }
344 
345 int TokenItemModel::rowCount(const QModelIndex &parent) const
346 {
347  Q_UNUSED(parent);
348  return priv->size();
349 }
350 
351 int TokenItemModel::columnCount(const QModelIndex &parent) const
352 {
353  Q_UNUSED(parent);
354  return columns.length();
355 }
356 
357 QVariant TokenItemModel::data(const QModelIndex &index, int role) const
358 {
359  if(!index.isValid())
360  return QVariant();
361 
362  TokenItemEntry *rec = static_cast<TokenItemEntry*>(index.internalPointer());
363 
364  switch (role) {
365  case Qt::DisplayRole:
366  switch(index.column())
367  {
368  case Name:
369  return rec->tokenName;
370  case Symbol:
371  return rec->tokenSymbol;
372  case Balance:
374  default:
375  break;
376  }
377  break;
379  return QString::fromStdString(rec->hash.ToString());
380  break;
382  return rec->contractAddress;
383  break;
385  return rec->tokenName;
386  break;
388  return rec->tokenSymbol;
389  break;
391  return rec->decimals;
392  break;
394  return rec->senderAddress;
395  break;
398  break;
400  return QString::fromStdString(rec->balance.str());
401  break;
402  default:
403  break;
404  }
405 
406  return QVariant();
407 }
408 
410 {
411  return tokenAbi;
412 }
413 
414 void TokenItemModel::updateToken(const QString &hash, int status, bool showToken)
415 {
416  // Find token in wallet
418 
419  uint256 updated;
420  updated.SetHex(hash.toStdString());
421  std::map<uint256, CTokenInfo>::iterator mi = wallet->mapToken.find(updated);
422  showToken &= mi != wallet->mapToken.end();
423 
424  TokenItemEntry tokenEntry;
425  if(showToken)
426  {
427  tokenEntry = TokenItemEntry(mi->first, mi->second);
428  tokenEntry.update(getTokenAbi());
429  }
430  else
431  {
432  tokenEntry.hash = updated;
433  }
434  priv->updateEntry(tokenEntry, status);
435 }
436 
438 {
439  if(!priv && !tokenAbi)
440  return;
441 
442  for(int i = 0; i < priv->cachedTokenItem.size(); i++)
443  {
444  TokenItemEntry tokenEntry = priv->cachedTokenItem[i];
445  bool modified = false;
446  tokenEntry.update(tokenAbi, modified);
447  if(modified)
448  {
449  priv->cachedTokenItem[i] = tokenEntry;
450  emitDataChanged(i);
451  }
452 
453  // Search for token transactions
454  if(fLogEvents)
455  {
456  QString hash = QString::fromStdString(tokenEntry.hash.ToString());
457  QMetaObject::invokeMethod(worker, "updateTokenTx", Qt::QueuedConnection,
458  Q_ARG(QString, hash));
459  }
460  }
461 }
462 
464 {
465  Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
466 }
467 
469 {
470 public:
472  TokenNotification(uint256 _hash, ChangeType _status, bool _showToken):
473  hash(_hash), status(_status), showToken(_showToken) {}
474 
475  void invoke(QObject *tim)
476  {
477  QString strHash = QString::fromStdString(hash.GetHex());
478  qDebug() << "NotifyTokenChanged: " + strHash + " status= " + QString::number(status);
479 
480  QMetaObject::invokeMethod(tim, "updateToken", Qt::QueuedConnection,
481  Q_ARG(QString, strHash),
482  Q_ARG(int, status),
483  Q_ARG(bool, showToken));
484  }
485 private:
488  bool showToken;
489 };
490 
491 static void NotifyTokenChanged(TokenItemModel *tim, CWallet *wallet, const uint256 &hash, ChangeType status)
492 {
493  // Find token in wallet
494  LOCK2(cs_main, wallet->cs_wallet);
495 
496  std::map<uint256, CTokenInfo>::iterator mi = wallet->mapToken.find(hash);
497  bool showToken = mi != wallet->mapToken.end();
498 
499  TokenNotification notification(hash, status, showToken);
500  notification.invoke(tim);
501 }
502 
504 {
505  // Connect signals to wallet
506  wallet->NotifyTokenChanged.connect(boost::bind(NotifyTokenChanged, this, _1, _2, _3));
507 }
508 
510 {
511  // Disconnect signals from wallet
512  wallet->NotifyTokenChanged.disconnect(boost::bind(NotifyTokenChanged, this, _1, _2, _3));
513 }
bool operator()(const uint256 &a, const TokenItemEntry &b) const
bool fLogEvents
Definition: validation.cpp:88
std::string strTokenSymbol
Definition: wallet.h:1302
QString tokenName
TokenItemEntry()
std::string strReceiverAddress
Definition: wallet.h:1357
QStringList columns
int64_t nCreateTime
Definition: wallet.h:1307
quint8 decimals
CWallet * wallet
CCriticalSection cs_wallet
Definition: wallet.h:748
TokenItemModel * parent
uint256 hash
int rowCount(const QModelIndex &parent=QModelIndex()) const
std::map< uint256, CTokenInfo > mapToken
Definition: wallet.h:834
QString tokenSymbol
CCriticalSection cs_main
Definition: validation.cpp:77
void invoke(QObject *tim)
std::string strSenderAddress
Definition: wallet.h:1304
bool update(Token *tokenAbi)
bool AddTokenEntry(const CTokenInfo &token, bool fFlushOnClose=true)
Definition: wallet.cpp:4372
TokenItemPriv * priv
TokenItemEntry(uint256 tokenHash, CTokenInfo tokenInfo)
~TokenItemEntry()
bool operator()(const TokenItemEntry &a, const TokenItemEntry &b) const
int64_t blockNumber
Definition: wallet.h:1364
Definition: token.h:32
QDateTime createTime
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
TokenItemPriv(CWallet *_wallet, TokenItemModel *_parent)
bool operator()(const TokenItemEntry &a, const uint256 &b) const
CWallet * wallet
std::string ToString() const
Definition: uint256.cpp:95
std::string strContractAddress
Definition: wallet.h:1355
#define a(i)
void emitDataChanged(int index)
Notify listeners that data changed.
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:512
#define LOCK2(cs1, cs2)
Definition: sync.h:176
int Height() const
Return the maximal height in the chain.
Definition: chain.h:543
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
void updateToken(const QString &hash, int status, bool showToken)
uint256 blockHash
Definition: wallet.h:1308
ChangeType
General change type (added, updated, removed).
Definition: ui_interface.h:19
std::string strContractAddress
Definition: wallet.h:1300
TokenItemEntry(const TokenItemEntry &obj)
uint256 uint256S(const char *str)
Definition: uint256.h:153
void setAddress(const std::string &address)
Definition: token.cpp:217
void unsubscribeFromCoreSignals()
Token * getTokenAbi()
QModelIndex parent(const QModelIndex &child) const
QString contractAddress
int64_t blockNumber
Definition: wallet.h:1309
CChain chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:80
bool transferEvents(std::vector< TokenEvent > &tokenEvents, int64_t fromBlock=0, int64_t toBlock=-1)
Definition: token.cpp:510
QString senderAddress
TokenItemModel(CWallet *wallet, WalletModel *parent=0)
void subscribeToCoreSignals()
TokenTxWorker * worker
#define b(i, j)
static QString formatToken(int decimal_units, const int256_t &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format token as string.
void updateEntry(const TokenItemEntry &item, int status)
friend class TokenItemPriv
256-bit opaque blob.
Definition: uint256.h:132
TokenItemEntry * index(int idx)
uint8_t nDecimals
Definition: wallet.h:1303
void updateTokenTx(const QString &hash)
int columnCount(const QModelIndex &parent=QModelIndex()) const
The block chain is a tree shaped structure starting with the genesis block at the root...
Definition: chain.h:177
Interface to Fabcoin wallet from Qt view code.
Definition: walletmodel.h:103
bool update(Token *tokenAbi, bool &modified)
A CWallet is an extension of a keystore, which also maintains a set of transactions and balances...
Definition: wallet.h:672
bool balanceOf(std::string &result, bool sendTo=false)
Definition: token.cpp:381
void checkTokenBalanceChanged()
uint256 nValue
Definition: wallet.h:1358
std::string strSenderAddress
Definition: wallet.h:1356
int256_t balance
TokenNotification(uint256 _hash, ChangeType _status, bool _showToken)
QList< TokenItemEntry > cachedTokenItem
uint256 transactionHash
Definition: wallet.h:1359
void SetHex(const char *psz)
Definition: uint256.cpp:39
void setSender(const std::string &sender)
Definition: token.cpp:242
TokenTxWorker(CWallet *_wallet)
uint256 blockHash
Definition: wallet.h:1363
bool AddTokenTxEntry(const CTokenTx &tokenTx, bool fFlushOnClose=true)
Definition: wallet.cpp:4421
uint256 GetBlockHash() const
Definition: chain.h:324
boost::signals2::signal< void(CWallet *wallet, const uint256 &hashToken, ChangeType status)> NotifyTokenChanged
Wallet transaction added, removed or updated.
Definition: wallet.h:1137
std::string strTokenName
Definition: wallet.h:1301