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::AlignLeft|Qt::AlignVCenter,
30 Qt::AlignLeft|Qt::AlignVCenter,
31 Qt::AlignLeft|Qt::AlignVCenter,
32 Qt::AlignLeft|Qt::AlignVCenter,
33 Qt::AlignLeft|Qt::AlignVCenter
38 bool operator()(const KernelRecord &a, const KernelRecord &b) const
40 return a.hash < b.hash;
42 bool operator()(const KernelRecord &a, const uint256 &b) const
46 bool operator()(const uint256 &a, const KernelRecord &b) const
52 class MintingTablePriv
55 MintingTablePriv(CWallet *wallet, MintingTableModel *parent):
61 MintingTableModel *parent;
63 QList<KernelRecord> cachedWallet;
67 #ifdef WALLET_UPDATE_DEBUG
68 qDebug() << "refreshWallet";
72 LOCK(wallet->cs_wallet);
73 for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
75 std::vector<KernelRecord> txList = KernelRecord::decomposeOutput(wallet, it->second);
76 BOOST_FOREACH(KernelRecord& kr, txList) {
78 cachedWallet.append(kr);
86 /* Update our model of the wallet incrementally, to synchronize our model of the wallet
87 with that of the core.
89 Call with list of hashes of transactions that were added, removed or changed.
91 void updateWallet(const QList<uint256> &updated)
93 // Walk through updated transactions, update model as needed.
94 #ifdef WALLET_UPDATE_DEBUG
95 qDebug() << "updateWallet";
97 // Sort update list, and iterate through it in reverse, so that model updates
98 // can be emitted from end to beginning (so that earlier updates will not influence
99 // the indices of latter ones).
100 QList<uint256> updated_sorted = updated;
101 qSort(updated_sorted);
104 LOCK(wallet->cs_wallet);
105 for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx)
107 const uint256 &hash = updated_sorted.at(update_idx);
108 // Find transaction in wallet
109 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
110 bool inWallet = mi != wallet->mapWallet.end();
111 // Find bounds of this transaction in model
112 QList<KernelRecord>::iterator lower = qLowerBound(
113 cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
114 QList<KernelRecord>::iterator upper = qUpperBound(
115 cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
116 int lowerIndex = (lower - cachedWallet.begin());
117 int upperIndex = (upper - cachedWallet.begin());
119 // Determine if transaction is in model already
120 bool inModel = false;
126 #ifdef WALLET_UPDATE_DEBUG
127 qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel
128 << lowerIndex << "-" << upperIndex;
131 if(inWallet && !inModel)
133 // Added -- insert at the right position
134 std::vector<KernelRecord> toInsert =
135 KernelRecord::decomposeOutput(wallet, mi->second);
136 if(!toInsert.empty()) /* only if something to insert */
138 int insert_idx = lowerIndex;
139 BOOST_FOREACH(const KernelRecord &rec, toInsert)
143 parent->beginInsertRows(QModelIndex(), insert_idx, insert_idx);
144 cachedWallet.insert(insert_idx, rec);
145 parent->endInsertRows();
151 else if(!inWallet && inModel)
153 // Removed -- remove entire transaction from table
154 parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
155 cachedWallet.erase(lower, upper);
156 parent->endRemoveRows();
158 else if(inWallet && inModel)
160 // Updated -- nothing to do, status update will take care of this
168 return cachedWallet.size();
171 KernelRecord *index(int idx)
173 if(idx >= 0 && idx < cachedWallet.size())
175 KernelRecord *rec = &cachedWallet[idx];
184 QString describe(KernelRecord *rec)
187 LOCK(wallet->cs_wallet);
188 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
189 if(mi != wallet->mapWallet.end())
191 return TransactionDesc::toHTML(wallet, mi->second);
200 MintingTableModel::MintingTableModel(CWallet *wallet, WalletModel *parent):
201 QAbstractTableModel(parent),
205 priv(new MintingTablePriv(wallet, this))
207 columns << tr("Transaction") << tr("Address") << tr("Balance") << tr("Age") << tr("CoinDay") << tr("MintProbability") << tr("MintReward");
208 priv->refreshWallet();
210 QTimer *timer = new QTimer(this);
211 connect(timer, SIGNAL(timeout()), this, SLOT(update()));
212 timer->start(MODEL_UPDATE_DELAY);
215 MintingTableModel::~MintingTableModel()
220 void MintingTableModel::update()
222 QList<uint256> updated;
224 // Check if there are changes to wallet map
226 TRY_LOCK(wallet->cs_wallet, lockWallet);
227 if (lockWallet && !wallet->vMintingWalletUpdated.empty())
229 BOOST_FOREACH(uint256 hash, wallet->vMintingWalletUpdated)
231 updated.append(hash);
233 wallet->vMintingWalletUpdated.clear();
239 priv->updateWallet(updated);
243 int MintingTableModel::rowCount(const QModelIndex &parent) const
249 int MintingTableModel::columnCount(const QModelIndex &parent) const
252 return columns.length();
255 QVariant MintingTableModel::data(const QModelIndex &index, int role) const
259 KernelRecord *rec = static_cast<KernelRecord*>(index.internalPointer());
263 case Qt::DisplayRole:
264 switch(index.column())
267 return formatTxAddress(rec, false);
269 return formatTxHash(rec);
271 return formatTxAge(rec);
273 return formatTxBalance(rec);
275 return formatTxCoinDay(rec);
276 case MintProbability:
277 return formatDayToMint(rec);
279 return formatTxPoSReward(rec);
282 case Qt::TextAlignmentRole:
283 return column_alignments[index.column()];
285 case Qt::ToolTipRole:
286 switch(index.column())
288 case MintProbability:
289 int interval = this->mintingInterval;
290 QString unit = tr("minutes");
292 int hours = interval / 60;
293 int days = hours / 24;
304 QString str = QString(tr("You have %1 chance to find a POS block if you mint %2 %3 at current difficulty."));
305 return str.arg(index.data().toString().toUtf8().constData()).arg(interval).arg(unit);
309 switch(index.column())
312 return formatTxAddress(rec, false);
314 return formatTxHash(rec);
316 return rec->getAge();
321 case MintProbability:
322 return getDayToMint(rec);
324 return formatTxPoSReward(rec);
327 case Qt::BackgroundColorRole:
328 int minAge = nStakeMinAge / 60 / 60 / 24;
329 int maxAge = nStakeMaxAge / 60 / 60 / 24;
330 if(rec->getAge() < minAge)
332 return COLOR_MINT_YOUNG;
334 else if (rec->getAge() >= minAge && rec->getAge() < (maxAge + minAge))
336 return COLOR_MINT_MATURE;
340 return COLOR_MINT_OLD;
348 void MintingTableModel::setMintingInterval(int interval)
350 mintingInterval = interval;
353 QString MintingTableModel::lookupAddress(const std::string &address, bool tooltip) const
355 QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
359 description += label + QString(" ");
361 if(label.isEmpty() || walletModel->getOptionsModel()->getDisplayAddresses() || tooltip)
363 description += QString("(") + QString::fromStdString(address) + QString(")");
368 QString MintingTableModel::formatTxPoSReward(KernelRecord *wtx) const
371 int nBits = GetLastBlockIndex(pindexBest, true)->nBits;
372 posReward += QString(QObject::tr("from %1 to %2")).arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->getPoSReward(nBits, 0)),
373 BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->getPoSReward(nBits, mintingInterval)));
377 double MintingTableModel::getDayToMint(KernelRecord *wtx) const
379 const CBlockIndex *p = GetLastBlockIndex(pindexBest, true);
380 double difficulty = GetDifficulty(p);
382 double prob = wtx->getProbToMintWithinNMinutes(difficulty, mintingInterval);
387 QString MintingTableModel::formatDayToMint(KernelRecord *wtx) const
389 double prob = getDayToMint(wtx);
390 return QString::number(prob, 'f', 3) + "%";
393 QString MintingTableModel::formatTxAddress(const KernelRecord *wtx, bool tooltip) const
395 return QString::fromStdString(wtx->address);
398 QString MintingTableModel::formatTxHash(const KernelRecord *wtx) const
400 return QString::fromStdString(wtx->hash.ToString());
403 QString MintingTableModel::formatTxCoinDay(const KernelRecord *wtx) const
405 return QString::number(wtx->getCoinDay());
408 QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const
410 int64 nAge = wtx->getAge();
411 return QString::number(nAge);
414 QString MintingTableModel::formatTxBalance(const KernelRecord *wtx) const
416 return BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue);
419 QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation, int role) const
421 if(orientation == Qt::Horizontal)
423 if(role == Qt::DisplayRole)
425 return columns[section];
427 else if (role == Qt::TextAlignmentRole)
429 return column_alignments[section];
430 } else if (role == Qt::ToolTipRole)
435 return tr("Destination address of the output.");
437 return tr("Original transaction id.");
439 return tr("Age of the transaction in days.");
441 return tr("Balance of the output.");
443 return tr("Coin age in the output.");
444 case MintProbability:
445 return tr("Chance to mint a block within given time interval.");
447 return tr("The size of the potential rewards if the block is found at the beginning and the end given time interval.");
454 QModelIndex MintingTableModel::index(int row, int column, const QModelIndex &parent) const
457 KernelRecord *data = priv->index(row);
460 return createIndex(row, column, priv->index(row));
464 return QModelIndex();