1 #include "mintingtablemodel.h"
2 #include "mintingfilterproxy.h"
3 #include "transactiontablemodel.h"
5 #include "kernelrecord.h"
6 #include "guiconstants.h"
7 #include "transactiondesc.h"
8 #include "walletmodel.h"
9 #include "optionsmodel.h"
10 #include "addresstablemodel.h"
11 #include "bitcoinunits.h"
23 #include <QtAlgorithms>
25 extern double GetDifficulty(const CBlockIndex* blockindex);
27 static int column_alignments[] = {
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,
34 Qt::AlignLeft|Qt::AlignVCenter
39 bool operator()(const KernelRecord &a, const KernelRecord &b) const
41 return a.hash < b.hash;
43 bool operator()(const KernelRecord &a, const uint256 &b) const
47 bool operator()(const uint256 &a, const KernelRecord &b) const
53 class MintingTablePriv
56 MintingTablePriv(CWallet *wallet, MintingTableModel *parent):
62 MintingTableModel *parent;
64 QList<KernelRecord> cachedWallet;
68 #ifdef WALLET_UPDATE_DEBUG
69 qDebug() << "refreshWallet";
73 LOCK(wallet->cs_wallet);
74 for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
76 std::vector<KernelRecord> txList = KernelRecord::decomposeOutput(wallet, it->second);
77 BOOST_FOREACH(KernelRecord& kr, txList) {
79 cachedWallet.append(kr);
87 /* Update our model of the wallet incrementally, to synchronize our model of the wallet
88 with that of the core.
90 Call with list of hashes of transactions that were added, removed or changed.
92 void updateWallet(const QList<uint256> &updated)
94 // Walk through updated transactions, update model as needed.
95 #ifdef WALLET_UPDATE_DEBUG
96 qDebug() << "updateWallet";
98 // Sort update list, and iterate through it in reverse, so that model updates
99 // can be emitted from end to beginning (so that earlier updates will not influence
100 // the indices of latter ones).
101 QList<uint256> updated_sorted = updated;
102 qSort(updated_sorted);
105 LOCK(wallet->cs_wallet);
106 for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx)
108 const uint256 &hash = updated_sorted.at(update_idx);
109 // Find transaction in wallet
110 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
111 bool inWallet = mi != wallet->mapWallet.end();
112 // Find bounds of this transaction in model
113 QList<KernelRecord>::iterator lower = qLowerBound(
114 cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
115 QList<KernelRecord>::iterator upper = qUpperBound(
116 cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
117 int lowerIndex = (lower - cachedWallet.begin());
118 int upperIndex = (upper - cachedWallet.begin());
120 // Determine if transaction is in model already
121 bool inModel = false;
127 #ifdef WALLET_UPDATE_DEBUG
128 qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel
129 << lowerIndex << "-" << upperIndex;
132 if(inWallet && !inModel)
134 // Added -- insert at the right position
135 std::vector<KernelRecord> toInsert =
136 KernelRecord::decomposeOutput(wallet, mi->second);
137 if(!toInsert.empty()) /* only if something to insert */
139 int insert_idx = lowerIndex;
140 BOOST_FOREACH(const KernelRecord &rec, toInsert)
144 parent->beginInsertRows(QModelIndex(), insert_idx, insert_idx);
145 cachedWallet.insert(insert_idx, rec);
146 parent->endInsertRows();
152 else if(!inWallet && inModel)
154 // Removed -- remove entire transaction from table
155 parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
156 cachedWallet.erase(lower, upper);
157 parent->endRemoveRows();
159 else if(inWallet && inModel)
161 // Updated -- remove spent coins from table
162 std::vector<KernelRecord> toCheck = KernelRecord::decomposeOutput(wallet, mi->second);
163 BOOST_FOREACH(const KernelRecord &rec, toCheck)
167 for(int i = 0; i < cachedWallet.size(); i++)
169 KernelRecord cachedRec = cachedWallet.at(i);
170 if((rec.hash == cachedRec.hash)
171 && (rec.nTime == cachedRec.nTime)
172 && (rec.nValue == cachedRec.nValue))
174 parent->beginRemoveRows(QModelIndex(), i, i);
175 cachedWallet.removeAt(i);
176 parent->endRemoveRows();
189 return cachedWallet.size();
192 KernelRecord *index(int idx)
194 if(idx >= 0 && idx < cachedWallet.size())
196 KernelRecord *rec = &cachedWallet[idx];
205 QString describe(KernelRecord *rec)
208 LOCK(wallet->cs_wallet);
209 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
210 if(mi != wallet->mapWallet.end())
212 return TransactionDesc::toHTML(wallet, mi->second);
221 MintingTableModel::MintingTableModel(CWallet *wallet, WalletModel *parent):
222 QAbstractTableModel(parent),
226 priv(new MintingTablePriv(wallet, this))
228 columns << tr("Transaction") << tr("Address") << tr("Balance") << tr("Age") << tr("CoinDay") << tr("MintProbability") << tr("MintReward");
229 priv->refreshWallet();
231 QTimer *timer = new QTimer(this);
232 connect(timer, SIGNAL(timeout()), this, SLOT(update()));
233 timer->start(MODEL_UPDATE_DELAY);
236 MintingTableModel::~MintingTableModel()
241 void MintingTableModel::update()
243 QList<uint256> updated;
245 // Check if there are changes to wallet map
247 TRY_LOCK(wallet->cs_wallet, lockWallet);
248 if (lockWallet && !wallet->vMintingWalletUpdated.empty())
250 BOOST_FOREACH(uint256 hash, wallet->vMintingWalletUpdated)
252 updated.append(hash);
254 // Also check the inputs to remove spent outputs from the table if necessary
256 if(wallet->GetTransaction(hash, wtx))
258 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
260 updated.append(txin.prevout.hash);
264 wallet->vMintingWalletUpdated.clear();
270 priv->updateWallet(updated);
271 mintingProxyModel->invalidate(); // Force deletion of empty rows
275 void MintingTableModel::setMintingProxyModel(MintingFilterProxy *mintingProxy)
277 mintingProxyModel = mintingProxy;
280 int MintingTableModel::rowCount(const QModelIndex &parent) const
286 int MintingTableModel::columnCount(const QModelIndex &parent) const
289 return columns.length();
292 QVariant MintingTableModel::data(const QModelIndex &index, int role) const
296 KernelRecord *rec = static_cast<KernelRecord*>(index.internalPointer());
300 case Qt::DisplayRole:
301 switch(index.column())
304 return formatTxAddress(rec, false);
306 return formatTxHash(rec);
308 return formatTxAge(rec);
310 return formatTxBalance(rec);
312 return formatTxCoinDay(rec);
313 case MintProbability:
314 return formatDayToMint(rec);
316 return formatTxPoSReward(rec);
319 case Qt::TextAlignmentRole:
320 return column_alignments[index.column()];
322 case Qt::ToolTipRole:
323 switch(index.column())
325 case MintProbability:
326 int interval = this->mintingInterval;
327 QString unit = tr("minutes");
329 int hours = interval / 60;
330 int days = hours / 24;
341 QString str = QString(tr("You have %1 chance to find a POS block if you mint %2 %3 at current difficulty."));
342 return str.arg(index.data().toString().toUtf8().constData()).arg(interval).arg(unit);
346 switch(index.column())
349 return formatTxAddress(rec, false);
351 return formatTxHash(rec);
353 return static_cast<qlonglong>(rec->getAge());
355 return static_cast<qlonglong>(rec->getCoinDay());
357 return static_cast<qlonglong>(rec->nValue);
358 case MintProbability:
359 return getDayToMint(rec);
361 return formatTxPoSReward(rec);
364 case Qt::BackgroundColorRole:
365 int minAge = nStakeMinAge / 60 / 60 / 24;
366 int maxAge = nStakeMaxAge / 60 / 60 / 24;
367 if(rec->getAge() < minAge)
369 return COLOR_MINT_YOUNG;
371 else if (rec->getAge() >= minAge && rec->getAge() < (maxAge + minAge))
373 return COLOR_MINT_MATURE;
377 return COLOR_MINT_OLD;
385 void MintingTableModel::setMintingInterval(int interval)
387 mintingInterval = interval;
390 QString MintingTableModel::lookupAddress(const std::string &address, bool tooltip) const
392 QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
396 description += label + QString(" ");
398 if(label.isEmpty() || walletModel->getOptionsModel()->getDisplayAddresses() || tooltip)
400 description += QString("(") + QString::fromStdString(address) + QString(")");
405 QString MintingTableModel::formatTxPoSReward(KernelRecord *wtx) const
408 int nBits = GetLastBlockIndex(pindexBest, true)->nBits;
409 posReward += QString(QObject::tr("from %1 to %2")).arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->getPoSReward(nBits, 0)),
410 BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->getPoSReward(nBits, mintingInterval)));
414 double MintingTableModel::getDayToMint(KernelRecord *wtx) const
416 const CBlockIndex *p = GetLastBlockIndex(pindexBest, true);
417 double difficulty = GetDifficulty(p);
419 double prob = wtx->getProbToMintWithinNMinutes(difficulty, mintingInterval);
424 QString MintingTableModel::formatDayToMint(KernelRecord *wtx) const
426 double prob = getDayToMint(wtx);
427 return QString::number(prob, 'f', 3) + "%";
430 QString MintingTableModel::formatTxAddress(const KernelRecord *wtx, bool tooltip) const
432 return QString::fromStdString(wtx->address);
435 QString MintingTableModel::formatTxHash(const KernelRecord *wtx) const
437 return QString::fromStdString(wtx->hash.ToString());
440 QString MintingTableModel::formatTxCoinDay(const KernelRecord *wtx) const
442 return QString::number(wtx->getCoinDay());
445 QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const
447 int64_t nAge = wtx->getAge();
448 return QString::number(nAge);
451 QString MintingTableModel::formatTxBalance(const KernelRecord *wtx) const
453 return BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue);
456 QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation, int role) const
458 if(orientation == Qt::Horizontal)
460 if(role == Qt::DisplayRole)
462 return columns[section];
464 else if (role == Qt::TextAlignmentRole)
466 return column_alignments[section];
467 } else if (role == Qt::ToolTipRole)
472 return tr("Destination address of the output.");
474 return tr("Original transaction id.");
476 return tr("Age of the transaction in days.");
478 return tr("Balance of the output.");
480 return tr("Coin age in the output.");
481 case MintProbability:
482 return tr("Chance to mint a block within given time interval.");
484 return tr("The size of the potential rewards if the block is found at the beginning and the end given time interval.");
491 QModelIndex MintingTableModel::index(int row, int column, const QModelIndex &parent) const
494 KernelRecord *data = priv->index(row);
497 return createIndex(row, column, priv->index(row));
501 return QModelIndex();