diff --git a/gui/include/transactionrecord.h b/gui/include/transactionrecord.h index d8aca761..c082fffe 100644 --- a/gui/include/transactionrecord.h +++ b/gui/include/transactionrecord.h @@ -10,7 +10,7 @@ class TransactionStatus public: TransactionStatus(): confirmed(false), sortKey(""), maturity(Mature), - matures_in(0), status(Offline), depth(0), open_for(0) + matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) { } enum Maturity @@ -40,6 +40,9 @@ public: Status status; int64 depth; int64 open_for; /* Timestamp if status==OpenUntilDate, otherwise number of blocks */ + + /* Current number of blocks (to know whether cached status is still valid. */ + int cur_num_blocks; }; class TransactionRecord @@ -57,21 +60,21 @@ public: }; TransactionRecord(): - hash(), time(0), type(Other), address(""), debit(0), credit(0) + hash(), time(0), type(Other), address(""), debit(0), credit(0), idx(0) { } - TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status): + TransactionRecord(uint256 hash, int64 time): hash(hash), time(time), type(Other), address(""), debit(0), - credit(0), status(status) + credit(0), idx(0) { } - TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status, + TransactionRecord(uint256 hash, int64 time, Type type, const std::string &address, int64 debit, int64 credit): hash(hash), time(time), type(type), address(address), debit(debit), credit(credit), - status(status) + idx(0) { } @@ -88,8 +91,19 @@ public: int64 debit; int64 credit; + /* Subtransaction index, for sort key */ + int idx; + /* Status: can change with block chain update */ TransactionStatus status; + + /* Update status from wallet tx. + */ + void updateStatus(const CWalletTx &wtx); + + /* Is a status update needed? + */ + bool statusUpdateNeeded(); }; #endif // TRANSACTIONRECORD_H diff --git a/gui/src/transactionrecord.cpp b/gui/src/transactionrecord.cpp index 9feca443..83c7d635 100644 --- a/gui/src/transactionrecord.cpp +++ b/gui/src/transactionrecord.cpp @@ -38,47 +38,6 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx uint256 hash = wtx.GetHash(); std::map mapValue = wtx.mapValue; - // Find the block the tx is in - CBlockIndex* pindex = NULL; - std::map::iterator mi = mapBlockIndex.find(wtx.hashBlock); - if (mi != mapBlockIndex.end()) - pindex = (*mi).second; - - // Determine transaction status - TransactionStatus status; - // Sort order, unrecorded transactions sort to the top - status.sortKey = strprintf("%010d-%01d-%010u", - (pindex ? pindex->nHeight : INT_MAX), - (wtx.IsCoinBase() ? 1 : 0), - wtx.nTimeReceived); - status.confirmed = wtx.IsConfirmed(); - status.depth = wtx.GetDepthInMainChain(); - - if (!wtx.IsFinal()) - { - if (wtx.nLockTime < 500000000) - { - status.status = TransactionStatus::OpenUntilBlock; - status.open_for = nBestHeight - wtx.nLockTime; - } else { - status.status = TransactionStatus::OpenUntilDate; - status.open_for = wtx.nLockTime; - } - } - else - { - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - { - status.status = TransactionStatus::Offline; - } else if (status.depth < 6) - { - status.status = TransactionStatus::Unconfirmed; - } else - { - status.status = TransactionStatus::HaveConfirmations; - } - } - if (showTransaction(wtx)) { if (nNet > 0 || wtx.IsCoinBase()) @@ -86,7 +45,7 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx // // Credit // - TransactionRecord sub(hash, nTime, status); + TransactionRecord sub(hash, nTime); sub.credit = nNet; @@ -97,25 +56,10 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx if (nCredit == 0) { - sub.status.maturity = TransactionStatus::Immature; - int64 nUnmatured = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) nUnmatured += txout.GetCredit(); sub.credit = nUnmatured; - - if (wtx.IsInMainChain()) - { - sub.status.matures_in = wtx.GetBlocksToMaturity(); - - // Check if the block was requested by anyone - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - sub.status.maturity = TransactionStatus::MaturesWarning; - } - else - { - sub.status.maturity = TransactionStatus::NotAccepted; - } } } else if (!mapValue["from"].empty() || !mapValue["message"].empty()) @@ -159,7 +103,7 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx // Payment to self int64 nChange = wtx.GetChange(); - parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::SendToSelf, "", + parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", -(nDebit - nChange), nCredit - nChange)); } else if (fAllFromMe) @@ -172,7 +116,8 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx for (int nOut = 0; nOut < wtx.vout.size(); nOut++) { const CTxOut& txout = wtx.vout[nOut]; - TransactionRecord sub(hash, nTime, status); + TransactionRecord sub(hash, nTime); + sub.idx = parts.size(); if (txout.IsMine()) { @@ -200,7 +145,6 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx nTxFee = 0; } sub.debit = -nValue; - sub.status.sortKey += strprintf("-%d", nOut); parts.append(sub); } @@ -214,10 +158,84 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx BOOST_FOREACH(const CTxIn& txin, wtx.vin) fAllMine = fAllMine && txin.IsMine(); - parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::Other, "", nNet, 0)); + parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0)); } } } return parts; } + +void TransactionRecord::updateStatus(const CWalletTx &wtx) +{ + // Determine transaction status + + // Find the block the tx is in + CBlockIndex* pindex = NULL; + std::map::iterator mi = mapBlockIndex.find(wtx.hashBlock); + if (mi != mapBlockIndex.end()) + pindex = (*mi).second; + + // Sort order, unrecorded transactions sort to the top + status.sortKey = strprintf("%010d-%01d-%010u-%03d", + (pindex ? pindex->nHeight : INT_MAX), + (wtx.IsCoinBase() ? 1 : 0), + wtx.nTimeReceived, + idx); + status.confirmed = wtx.IsConfirmed(); + status.depth = wtx.GetDepthInMainChain(); + status.cur_num_blocks = nBestHeight; + + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < 500000000) + { + status.status = TransactionStatus::OpenUntilBlock; + status.open_for = nBestHeight - wtx.nLockTime; + } else { + status.status = TransactionStatus::OpenUntilDate; + status.open_for = wtx.nLockTime; + } + } + else + { + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + { + status.status = TransactionStatus::Offline; + } else if (status.depth < 6) + { + status.status = TransactionStatus::Unconfirmed; + } else + { + status.status = TransactionStatus::HaveConfirmations; + } + } + + // For generated transactions, determine maturity + if(type == TransactionRecord::Generated) + { + int64 nCredit = wtx.GetCredit(true); + if (nCredit == 0) + { + status.maturity = TransactionStatus::Immature; + + if (wtx.IsInMainChain()) + { + status.matures_in = wtx.GetBlocksToMaturity(); + + // Check if the block was requested by anyone + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + status.maturity = TransactionStatus::MaturesWarning; + } else { + status.maturity = TransactionStatus::NotAccepted; + } + } else { + status.maturity = TransactionStatus::Mature; + } + } +} + +bool TransactionRecord::statusUpdateNeeded() +{ + return status.cur_num_blocks != nBestHeight; +} diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index 5bea3b59..0d50e6e2 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -148,7 +148,25 @@ struct TransactionTablePriv { if(idx >= 0 && idx < cachedWallet.size()) { - return &cachedWallet[idx]; + TransactionRecord *rec = &cachedWallet[idx]; + + /* If a status update is needed (blocks came in since last check), + update the status of this transaction from the wallet. Otherwise, + simply re-use the cached status. + */ + if(rec->statusUpdateNeeded()) + { + CRITICAL_BLOCK(cs_mapWallet) + { + std::map::iterator mi = mapWallet.find(rec->hash); + + if(mi != mapWallet.end()) + { + rec->updateStatus(mi->second); + } + } + } + return rec; } else { return 0; } @@ -204,6 +222,12 @@ void TransactionTableModel::update() if(!updated.empty()) { priv->updateWallet(updated); + + /* Status (number of confirmations) and (possibly) description + columns changed for all rows. + */ + emit dataChanged(index(0, Status), index(priv->size()-1, Status)); + emit dataChanged(index(0, Description), index(priv->size()-1, Description)); } }