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 parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
139 int insert_idx = lowerIndex;
140 BOOST_FOREACH(const KernelRecord &rec, toInsert)
143 cachedWallet.insert(insert_idx, rec);
147 parent->endInsertRows();
150 else if(!inWallet && inModel)
152 // Removed -- remove entire transaction from table
153 parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
154 cachedWallet.erase(lower, upper);
155 parent->endRemoveRows();
157 else if(inWallet && inModel)
159 // Updated -- nothing to do, status update will take care of this
167 return cachedWallet.size();
170 KernelRecord *index(int idx)
172 if(idx >= 0 && idx < cachedWallet.size())
174 KernelRecord *rec = &cachedWallet[idx];
183 QString describe(KernelRecord *rec)
186 LOCK(wallet->cs_wallet);
187 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
188 if(mi != wallet->mapWallet.end())
190 return TransactionDesc::toHTML(wallet, mi->second);
199 MintingTableModel::MintingTableModel(CWallet *wallet, WalletModel *parent):
200 QAbstractTableModel(parent),
204 priv(new MintingTablePriv(wallet, this))
206 columns << tr("Transaction") << tr("Address") << tr("Age") << tr("Balance") << tr("CoinDay") << tr("MintProbability") << tr("MintReward");
207 priv->refreshWallet();
209 QTimer *timer = new QTimer(this);
210 connect(timer, SIGNAL(timeout()), this, SLOT(update()));
211 timer->start(MODEL_UPDATE_DELAY);
214 MintingTableModel::~MintingTableModel()
219 void MintingTableModel::update()
221 QList<uint256> updated;
223 // Check if there are changes to wallet map
225 TRY_LOCK(wallet->cs_wallet, lockWallet);
226 if (lockWallet && !wallet->vMintingWalletUpdated.empty())
228 BOOST_FOREACH(uint256 hash, wallet->vMintingWalletUpdated)
230 updated.append(hash);
232 wallet->vMintingWalletUpdated.clear();
238 priv->updateWallet(updated);
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);
278 return formatTxPoSReward(rec);
281 case Qt::TextAlignmentRole:
282 return column_alignments[index.column()];
284 case Qt::ToolTipRole:
285 switch(index.column())
287 case MintProbability:
288 int interval = this->mintingInterval;
289 QString unit = tr("minutes");
291 int hours = interval / 60;
292 int days = hours / 24;
303 QString str = QString(tr("You have %1 chance to find a POS block if you mint %2 %3 at current difficulty."));
304 return str.arg(index.data().toString().toUtf8().constData()).arg(interval).arg(unit);
308 switch(index.column())
311 return formatTxAddress(rec, false);
313 return formatTxHash(rec);
315 return rec->getAge();
320 case MintProbability:
321 return getDayToMint(rec);
323 return formatTxPoSReward(rec);
326 case Qt::BackgroundColorRole:
327 int minAge = nStakeMinAge / 60 / 60 / 24;
328 int maxAge = nStakeMaxAge / 60 / 60 / 24;
329 if(rec->getAge() < minAge)
331 return COLOR_MINT_YOUNG;
333 else if (rec->getAge() >= minAge && rec->getAge() < (maxAge + minAge))
335 return COLOR_MINT_MATURE;
339 return COLOR_MINT_OLD;
347 void MintingTableModel::setMintingInterval(int interval)
349 mintingInterval = interval;
352 QString MintingTableModel::lookupAddress(const std::string &address, bool tooltip) const
354 QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
358 description += label + QString(" ");
360 if(label.isEmpty() || walletModel->getOptionsModel()->getDisplayAddresses() || tooltip)
362 description += QString("(") + QString::fromStdString(address) + QString(")");
367 QString MintingTableModel::formatTxPoSReward(KernelRecord *wtx) const
370 int nBits = GetLastBlockIndex(pindexBest, true)->nBits;
371 posReward += QString(QObject::tr("from %1 to %2")).arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->getPoSReward(nBits, 0)),
372 BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->getPoSReward(nBits, mintingInterval)));
376 double MintingTableModel::getDayToMint(KernelRecord *wtx) const
378 const CBlockIndex *p = GetLastBlockIndex(pindexBest, true);
379 double difficulty = GetDifficulty(p);
381 double prob = wtx->getProbToMintWithinNMinutes(difficulty, mintingInterval);
386 QString MintingTableModel::formatDayToMint(KernelRecord *wtx) const
388 double prob = getDayToMint(wtx);
389 return QString::number(prob, 'f', 3) + "%";
392 QString MintingTableModel::formatTxAddress(const KernelRecord *wtx, bool tooltip) const
394 return QString::fromStdString(wtx->address);
397 QString MintingTableModel::formatTxHash(const KernelRecord *wtx) const
399 return QString::fromStdString(wtx->hash.ToString());
402 QString MintingTableModel::formatTxCoinDay(const KernelRecord *wtx) const
404 return QString::number(wtx->coinAge);
407 QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const
409 int64 nAge = wtx->getAge();
410 return QString::number(nAge);
413 QString MintingTableModel::formatTxBalance(const KernelRecord *wtx) const
415 return BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue);
418 QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation, int role) const
420 if(orientation == Qt::Horizontal)
422 if(role == Qt::DisplayRole)
424 return columns[section];
426 else if (role == Qt::TextAlignmentRole)
428 return column_alignments[section];
429 } else if (role == Qt::ToolTipRole)
434 return tr("Destination address of the output.");
436 return tr("Original transaction id.");
438 return tr("Age of the transaction in days.");
440 return tr("Balance of the output.");
442 return tr("Coin age in the output.");
443 case MintProbability:
444 return tr("Chance to mint a block within given time interval.");
446 return tr("The size of the potential rewards if the block is found at the beginning and the end given time interval.");
453 QModelIndex MintingTableModel::index(int row, int column, const QModelIndex &parent) const
456 KernelRecord *data = priv->index(row);
459 return createIndex(row, column, priv->index(row));
463 return QModelIndex();