1 #include "mintingtablemodel.h"
2 #include "transactiontablemodel.h"
4 #include "kernelrecord.h"
5 #include "guiconstants.h"
6 #include "transactiondesc.h"
7 #include "walletmodel.h"
8 #include "optionsmodel.h"
9 #include "addresstablemodel.h"
10 #include "bitcoinunits.h"
22 #include <QtAlgorithms>
24 extern double GetDifficulty(const CBlockIndex* blockindex);
26 static int column_alignments[] = {
27 Qt::AlignLeft|Qt::AlignVCenter,
28 Qt::AlignLeft|Qt::AlignVCenter,
29 Qt::AlignRight|Qt::AlignVCenter,
30 Qt::AlignRight|Qt::AlignVCenter,
31 Qt::AlignRight|Qt::AlignVCenter,
32 Qt::AlignRight|Qt::AlignVCenter
37 bool operator()(const KernelRecord &a, const KernelRecord &b) const
39 return a.hash < b.hash;
41 bool operator()(const KernelRecord &a, const uint256 &b) const
45 bool operator()(const uint256 &a, const KernelRecord &b) const
51 class MintingTablePriv
54 MintingTablePriv(CWallet *wallet, MintingTableModel *parent):
60 MintingTableModel *parent;
62 QList<KernelRecord> cachedWallet;
66 #ifdef WALLET_UPDATE_DEBUG
67 qDebug() << "refreshWallet";
71 LOCK(wallet->cs_wallet);
72 for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
74 std::vector<KernelRecord> txList = KernelRecord::decomposeOutput(wallet, it->second);
75 BOOST_FOREACH(KernelRecord& kr, txList) {
77 cachedWallet.append(kr);
85 /* Update our model of the wallet incrementally, to synchronize our model of the wallet
86 with that of the core.
88 Call with list of hashes of transactions that were added, removed or changed.
90 void updateWallet(const QList<uint256> &updated)
92 // Walk through updated transactions, update model as needed.
93 #ifdef WALLET_UPDATE_DEBUG
94 qDebug() << "updateWallet";
96 // Sort update list, and iterate through it in reverse, so that model updates
97 // can be emitted from end to beginning (so that earlier updates will not influence
98 // the indices of latter ones).
99 QList<uint256> updated_sorted = updated;
100 qSort(updated_sorted);
103 LOCK(wallet->cs_wallet);
104 for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx)
106 const uint256 &hash = updated_sorted.at(update_idx);
107 // Find transaction in wallet
108 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
109 bool inWallet = mi != wallet->mapWallet.end();
110 // Find bounds of this transaction in model
111 QList<KernelRecord>::iterator lower = qLowerBound(
112 cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
113 QList<KernelRecord>::iterator upper = qUpperBound(
114 cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
115 int lowerIndex = (lower - cachedWallet.begin());
116 int upperIndex = (upper - cachedWallet.begin());
118 // Determine if transaction is in model already
119 bool inModel = false;
125 #ifdef WALLET_UPDATE_DEBUG
126 qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel
127 << lowerIndex << "-" << upperIndex;
130 if(inWallet && !inModel)
132 // Added -- insert at the right position
133 std::vector<KernelRecord> toInsert =
134 KernelRecord::decomposeOutput(wallet, mi->second);
135 if(!toInsert.empty()) /* only if something to insert */
137 parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
138 int insert_idx = lowerIndex;
139 BOOST_FOREACH(const KernelRecord &rec, toInsert)
142 cachedWallet.insert(insert_idx, rec);
146 parent->endInsertRows();
149 else if(!inWallet && inModel)
151 // Removed -- remove entire transaction from table
152 parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
153 cachedWallet.erase(lower, upper);
154 parent->endRemoveRows();
156 else if(inWallet && inModel)
158 // Updated -- nothing to do, status update will take care of this
166 return cachedWallet.size();
169 KernelRecord *index(int idx)
171 if(idx >= 0 && idx < cachedWallet.size())
173 KernelRecord *rec = &cachedWallet[idx];
182 QString describe(KernelRecord *rec)
185 LOCK(wallet->cs_wallet);
186 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
187 if(mi != wallet->mapWallet.end())
189 return TransactionDesc::toHTML(wallet, mi->second);
198 MintingTableModel::MintingTableModel(CWallet *wallet, WalletModel *parent):
199 QAbstractTableModel(parent),
203 priv(new MintingTablePriv(wallet, this))
205 columns << tr("Transaction") << tr("Address") << tr("Age") << tr("Balance") << tr("CoinDay") << tr("MintProbability");
206 priv->refreshWallet();
208 QTimer *timer = new QTimer(this);
209 connect(timer, SIGNAL(timeout()), this, SLOT(update()));
210 timer->start(MODEL_UPDATE_DELAY);
213 MintingTableModel::~MintingTableModel()
218 void MintingTableModel::update()
220 QList<uint256> updated;
222 // Check if there are changes to wallet map
224 TRY_LOCK(wallet->cs_wallet, lockWallet);
225 if (lockWallet && !wallet->vMintingWalletUpdated.empty())
227 BOOST_FOREACH(uint256 hash, wallet->vMintingWalletUpdated)
229 updated.append(hash);
231 wallet->vMintingWalletUpdated.clear();
237 priv->updateWallet(updated);
238 priv->refreshWallet();
242 int MintingTableModel::rowCount(const QModelIndex &parent) const
248 int MintingTableModel::columnCount(const QModelIndex &parent) const
251 return columns.length();
254 QVariant MintingTableModel::data(const QModelIndex &index, int role) const
258 KernelRecord *rec = static_cast<KernelRecord*>(index.internalPointer());
262 case Qt::DisplayRole:
263 switch(index.column())
266 return formatTxAddress(rec, false);
268 return formatTxHash(rec);
270 return formatTxAge(rec);
272 return formatTxBalance(rec);
274 return formatTxCoinDay(rec);
275 case MintProbability:
276 return formatDayToMint(rec);
279 case Qt::TextAlignmentRole:
280 return column_alignments[index.column()];
282 case Qt::ToolTipRole:
283 switch(index.column())
285 case MintProbability:
286 int interval = this->mintingInterval;
287 QString unit = tr("minutes");
289 int hours = interval / 60;
290 int days = hours / 24;
301 QString str = QString(tr("You have %1 chance to find a POS block if you mint %2 %3 at current difficulty."));
302 return str.arg(index.data().toString().toUtf8().constData()).arg(interval).arg(unit);
306 switch(index.column())
309 return formatTxAddress(rec, false);
311 return formatTxHash(rec);
313 return rec->getAge();
318 case MintProbability:
319 return getDayToMint(rec);
322 case Qt::BackgroundColorRole:
323 int minAge = nStakeMinAge / 60 / 60 / 24;
324 int maxAge = nStakeMaxAge / 60 / 60 / 24;
325 if(rec->getAge() < minAge)
327 return COLOR_MINT_YOUNG;
329 else if (rec->getAge() >= minAge && rec->getAge() < (maxAge + minAge))
331 return COLOR_MINT_MATURE;
335 return COLOR_MINT_OLD;
343 void MintingTableModel::setMintingInterval(int interval)
345 mintingInterval = interval;
348 QString MintingTableModel::lookupAddress(const std::string &address, bool tooltip) const
350 QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
354 description += label + QString(" ");
356 if(label.isEmpty() || walletModel->getOptionsModel()->getDisplayAddresses() || tooltip)
358 description += QString("(") + QString::fromStdString(address) + QString(")");
363 double MintingTableModel::getDayToMint(KernelRecord *wtx) const
365 const CBlockIndex *p = GetLastBlockIndex(pindexBest, true);
366 double difficulty = GetDifficulty(p);
368 double prob = wtx->getProbToMintWithinNMinutes(difficulty, mintingInterval);
373 QString MintingTableModel::formatDayToMint(KernelRecord *wtx) const
375 double prob = getDayToMint(wtx);
376 return QString::number(prob, 'f', 3) + "%";
379 QString MintingTableModel::formatTxAddress(const KernelRecord *wtx, bool tooltip) const
381 return QString::fromStdString(wtx->address);
384 QString MintingTableModel::formatTxHash(const KernelRecord *wtx) const
386 return QString::fromStdString(wtx->hash.ToString());
389 QString MintingTableModel::formatTxCoinDay(const KernelRecord *wtx) const
391 return QString::number(wtx->coinAge);
394 QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const
396 int64 nAge = wtx->getAge();
397 return QString::number(nAge);
400 QString MintingTableModel::formatTxBalance(const KernelRecord *wtx) const
402 return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue);
405 QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation, int role) const
407 if(orientation == Qt::Horizontal)
409 if(role == Qt::DisplayRole)
411 return columns[section];
413 else if (role == Qt::TextAlignmentRole)
415 return column_alignments[section];
416 } else if (role == Qt::ToolTipRole)
421 return tr("Destination address of the output.");
423 return tr("Original transaction id.");
425 return tr("Age of the transaction in days.");
427 return tr("Balance of the output.");
429 return tr("Coin age in the output.");
430 case MintProbability:
431 return tr("Chance to mint a block within given time interval.");
438 QModelIndex MintingTableModel::index(int row, int column, const QModelIndex &parent) const
441 KernelRecord *data = priv->index(row);
444 return createIndex(row, column, priv->index(row));
448 return QModelIndex();