Merge pull request #177 from UdjinM6/v0.11.1.x_fix_gui_performance_issues

V0.11.2.x Performance and UI Fixes Pack
This commit is contained in:
evan82 2015-02-12 16:50:17 -07:00
commit a9ff5617e9
14 changed files with 332 additions and 166 deletions

View File

@ -54,6 +54,7 @@ void CTxIn::print() const
CTxOut::CTxOut(int64_t nValueIn, CScript scriptPubKeyIn) CTxOut::CTxOut(int64_t nValueIn, CScript scriptPubKeyIn)
{ {
nValue = nValueIn; nValue = nValueIn;
nRounds = -10; // an initial value, should be no way to get this by calculations
scriptPubKey = scriptPubKeyIn; scriptPubKey = scriptPubKeyIn;
} }
@ -285,4 +286,4 @@ void CBlock::print() const
for (unsigned int i = 0; i < vMerkleTree.size(); i++) for (unsigned int i = 0; i < vMerkleTree.size(); i++)
LogPrintf("%s ", vMerkleTree[i].ToString()); LogPrintf("%s ", vMerkleTree[i].ToString());
LogPrintf("\n"); LogPrintf("\n");
} }

View File

@ -153,6 +153,7 @@ class CTxOut
{ {
public: public:
int64_t nValue; int64_t nValue;
int nRounds;
CScript scriptPubKey; CScript scriptPubKey;
CTxOut() CTxOut()
@ -171,6 +172,7 @@ public:
void SetNull() void SetNull()
{ {
nValue = -1; nValue = -1;
nRounds = -10; // an initial value, should be no way to get this by calculations
scriptPubKey.clear(); scriptPubKey.clear();
} }
@ -197,6 +199,7 @@ public:
friend bool operator==(const CTxOut& a, const CTxOut& b) friend bool operator==(const CTxOut& a, const CTxOut& b)
{ {
return (a.nValue == b.nValue && return (a.nValue == b.nValue &&
a.nRounds == b.nRounds &&
a.scriptPubKey == b.scriptPubKey); a.scriptPubKey == b.scriptPubKey);
} }

View File

@ -395,40 +395,90 @@ int randomizeList (int i) { return std::rand()%i;}
// Recursively determine the rounds of a given input (How deep is the darksend chain for a given input) // Recursively determine the rounds of a given input (How deep is the darksend chain for a given input)
int GetInputDarksendRounds(CTxIn in, int rounds) int GetInputDarksendRounds(CTxIn in, int rounds)
{ {
static std::map<uint256, CWalletTx> mDenomWtxes;
if(rounds >= 17) return rounds; if(rounds >= 17) return rounds;
std::string padding = ""; uint256 hash = in.prevout.hash;
padding.insert(0, ((rounds+1)*5)+3, ' '); uint nout = in.prevout.n;
CWalletTx tx; CWalletTx wtx;
if(pwalletMain->GetTransaction(in.prevout.hash,tx)) if(pwalletMain->GetTransaction(hash, wtx))
{ {
// bounds check std::map<uint256, CWalletTx>::const_iterator mdwi = mDenomWtxes.find(hash);
if(in.prevout.n >= tx.vout.size()) return -4; // not known yet, let's add it
if(mdwi == mDenomWtxes.end())
{
if(fDebug) LogPrintf("GetInputDarksendRounds INSERTING %s\n", hash.ToString());
mDenomWtxes[hash] = wtx;
}
// found and it's not an initial value, just return it
else if(mDenomWtxes[hash].vout[nout].nRounds != -10)
{
if(fDebug) LogPrintf("GetInputDarksendRounds INFO %s %3d %d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds);
return mDenomWtxes[hash].vout[nout].nRounds;
}
if(tx.vout[in.prevout.n].nValue == DARKSEND_FEE) return -3;
// bounds check
if(nout >= wtx.vout.size())
{
mDenomWtxes[hash].vout[nout].nRounds = -4;
if(fDebug) LogPrintf("GetInputDarksendRounds UPDATED %s %3d %d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds);
return mDenomWtxes[hash].vout[nout].nRounds;
}
mDenomWtxes[hash].vout[nout].nRounds = -3;
if(pwalletMain->IsCollateralAmount(wtx.vout[nout].nValue))
{
mDenomWtxes[hash].vout[nout].nRounds = -3;
if(fDebug) LogPrintf("GetInputDarksendRounds UPDATED %s %3d %d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds);
return mDenomWtxes[hash].vout[nout].nRounds;
}
//make sure the final output is non-denominate //make sure the final output is non-denominate
if(rounds == 0 && !pwalletMain->IsDenominatedAmount(tx.vout[in.prevout.n].nValue)) return -2; //NOT DENOM mDenomWtxes[hash].vout[nout].nRounds = -2;
if(/*rounds == 0 && */!pwalletMain->IsDenominatedAmount(wtx.vout[nout].nValue)) //NOT DENOM
bool found = false;
BOOST_FOREACH(CTxOut out, tx.vout)
{ {
found = pwalletMain->IsDenominatedAmount(out.nValue); mDenomWtxes[hash].vout[nout].nRounds = -2;
if(found) break; // no need to loop more if(fDebug) LogPrintf("GetInputDarksendRounds UPDATED %s %3d %d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds);
return mDenomWtxes[hash].vout[nout].nRounds;
} }
if(!found) return rounds - 1; //NOT FOUND, "-1" because of the pre-mixing creation of denominated amounts
// find my vin and look that up bool fAllDenoms = true;
BOOST_FOREACH(CTxIn in2, tx.vin) BOOST_FOREACH(CTxOut out, wtx.vout)
{
fAllDenoms = fAllDenoms && pwalletMain->IsDenominatedAmount(out.nValue);
}
// this one is denominated but there is another non-denominated output found in the same tx
if(!fAllDenoms)
{
mDenomWtxes[hash].vout[nout].nRounds = 0;
if(fDebug) LogPrintf("GetInputDarksendRounds UPDATED %s %3d %d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds);
return mDenomWtxes[hash].vout[nout].nRounds;
}
int nShortest = -10; // an initial value, should be no way to get this by calculations
bool fDenomFound = false;
// only denoms here so let's look up
BOOST_FOREACH(CTxIn in2, wtx.vin)
{ {
if(pwalletMain->IsMine(in2)) if(pwalletMain->IsMine(in2))
{ {
//LogPrintf("rounds :: %s %s %d NEXT\n", padding.c_str(), in.ToString().c_str(), rounds);
int n = GetInputDarksendRounds(in2, rounds+1); int n = GetInputDarksendRounds(in2, rounds+1);
if(n != -3) return n; // denom found, find the shortest chain or initially assign nShortest with the first found value
if(n >= 0 && (n < nShortest || nShortest == -10))
{
nShortest = n;
fDenomFound = true;
}
} }
} }
mDenomWtxes[hash].vout[nout].nRounds = fDenomFound
? nShortest + 1 // good, we a +1 to the shortest one
: 0; // too bad, we are the fist one in that chain
if(fDebug) LogPrintf("GetInputDarksendRounds UPDATED %s %3d %d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds);
return mDenomWtxes[hash].vout[nout].nRounds;
} }
return rounds-1; return rounds-1;

View File

@ -1776,7 +1776,7 @@ bool IsInitialBlockDownload()
nLastUpdate = GetTime(); nLastUpdate = GetTime();
} }
return (GetTime() - nLastUpdate < 10 && return (GetTime() - nLastUpdate < 10 &&
chainActive.Tip()->GetBlockTime() < GetTime() - 24 * 60 * 60); chainActive.Tip()->GetBlockTime() < GetTime() - 6 * 60 * 60); // ~144 blocks behind -> 2 x fork detection time
} }
CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL;
@ -1789,7 +1789,7 @@ void CheckForkWarningConditions()
if (IsInitialBlockDownload()) if (IsInitialBlockDownload())
return; return;
// If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it) // If our best fork is no longer within 72 blocks (+/- 3 hours if no one mines it)
// of our head, drop it // of our head, drop it
if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72)
pindexBestForkTip = NULL; pindexBestForkTip = NULL;
@ -1842,7 +1842,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
} }
// We define a condition which we should warn the user about as a fork of at least 7 blocks // We define a condition which we should warn the user about as a fork of at least 7 blocks
// who's tip is within 72 blocks (+/- 12 hours if no one mines it) of ours // who's tip is within 72 blocks (+/- 3 hours if no one mines it) of ours
// We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network
// hash rate operating on the fork. // hash rate operating on the fork.
// or a chain that is entirely longer than ours and invalid (note that this should be detected by both) // or a chain that is entirely longer than ours and invalid (note that this should be detected by both)

View File

@ -170,7 +170,7 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) :
// Progress bar and label for blocks download // Progress bar and label for blocks download
progressBarLabel = new QLabel(); progressBarLabel = new QLabel();
progressBarLabel->setVisible(false); progressBarLabel->setVisible(false);
progressBar = new QProgressBar(); progressBar = new GUIUtil::ProgressBar();
progressBar->setAlignment(Qt::AlignCenter); progressBar->setAlignment(Qt::AlignCenter);
progressBar->setVisible(false); progressBar->setVisible(false);
@ -670,7 +670,7 @@ void BitcoinGUI::setNumBlocks(int count)
tooltip = tr("Processed %1 blocks of transaction history.").arg(count); tooltip = tr("Processed %1 blocks of transaction history.").arg(count);
// Set icon state: spinning if catching up, tick otherwise // Set icon state: spinning if catching up, tick otherwise
if(secs < 90*60) if(secs < 25*60) // 90*60 for bitcoin but we are 4x times faster
{ {
tooltip = tr("Up to date") + QString(".<br>") + tooltip; tooltip = tr("Up to date") + QString(".<br>") + tooltip;
labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));

View File

@ -383,25 +383,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>66</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Darksend</string>
</property>
</widget>
<widget class="QPushButton" name="runAutoDenom"> <widget class="QPushButton" name="runAutoDenom">
<property name="geometry"> <property name="geometry">
<rect> <rect>
@ -943,6 +924,60 @@
<string>Reset</string> <string>Reset</string>
</property> </property>
</widget> </widget>
<widget class="QWidget" name="">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>431</width>
<height>22</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Darksend</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelDarksendSyncStatus">
<property name="toolTip">
<string>The displayed information may be out of date. Your wallet automatically synchronizes with the Darkcoin network after a connection is established, but this process has not completed yet.</string>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color: red; }</string>
</property>
<property name="text">
<string notr="true">(out of sync)</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
<item> <item>

View File

@ -5,9 +5,11 @@
#ifndef GUIUTIL_H #ifndef GUIUTIL_H
#define GUIUTIL_H #define GUIUTIL_H
#include <QEvent>
#include <QHeaderView> #include <QHeaderView>
#include <QMessageBox> #include <QMessageBox>
#include <QObject> #include <QObject>
#include <QProgressBar>
#include <QString> #include <QString>
#include <QTableView> #include <QTableView>
@ -177,6 +179,20 @@ namespace GUIUtil
/* Convert OS specific boost path to QString through UTF-8 */ /* Convert OS specific boost path to QString through UTF-8 */
QString boostPathToQString(const boost::filesystem::path &path); QString boostPathToQString(const boost::filesystem::path &path);
#if defined(Q_OS_MAC) && QT_VERSION >= 0x050000
// workaround for Qt OSX Bug:
// https://bugreports.qt-project.org/browse/QTBUG-15631
// QProgressBar uses around 10% CPU even when app is in background
class ProgressBar : public QProgressBar
{
bool event(QEvent *e) {
return (e->type() != QEvent::StyleAnimationUpdate) ? QProgressBar::event(e) : false;
}
};
#else
typedef QProgressBar ProgressBar;
#endif
} // namespace GUIUtil } // namespace GUIUtil
#endif // GUIUTIL_H #endif // GUIUTIL_H

View File

@ -124,6 +124,7 @@ OverviewPage::OverviewPage(QWidget *parent) :
// init "out of sync" warning labels // init "out of sync" warning labels
ui->labelWalletStatus->setText("(" + tr("out of sync") + ")"); ui->labelWalletStatus->setText("(" + tr("out of sync") + ")");
ui->labelDarksendSyncStatus->setText("(" + tr("out of sync") + ")");
ui->labelTransactionsStatus->setText("(" + tr("out of sync") + ")"); ui->labelTransactionsStatus->setText("(" + tr("out of sync") + ")");
showingDarkSendMessage = 0; showingDarkSendMessage = 0;
@ -253,6 +254,7 @@ void OverviewPage::updateAlerts(const QString &warnings)
void OverviewPage::showOutOfSyncWarning(bool fShow) void OverviewPage::showOutOfSyncWarning(bool fShow)
{ {
ui->labelWalletStatus->setVisible(fShow); ui->labelWalletStatus->setVisible(fShow);
ui->labelDarksendSyncStatus->setVisible(fShow);
ui->labelTransactionsStatus->setVisible(fShow); ui->labelTransactionsStatus->setVisible(fShow);
} }
@ -291,7 +293,7 @@ void OverviewPage::updateDarksendProgress()
float denomPart = 0; float denomPart = 0;
if(denominatedBalance > 0) if(denominatedBalance > 0)
{ {
denomPart = (float)pwalletMain->GetNormalizedAnonymizedBalance() / pwalletMain->GetDenominatedBalance(); denomPart = (float)pwalletMain->GetNormalizedAnonymizedBalance() / denominatedBalance;
denomPart = denomPart > 1 ? 1 : denomPart; denomPart = denomPart > 1 ? 1 : denomPart;
} }

View File

@ -91,88 +91,81 @@ public:
Call with transaction that was added, removed or changed. Call with transaction that was added, removed or changed.
*/ */
void updateWallet(const uint256 &hash, int status) void updateWallet(const uint256 &hash, int status, bool showTransaction)
{ {
qDebug() << "TransactionTablePriv::updateWallet : " + QString::fromStdString(hash.ToString()) + " " + QString::number(status); qDebug() << "TransactionTablePriv::updateWallet : " + QString::fromStdString(hash.ToString()) + " " + QString::number(status);
// Find bounds of this transaction in model
QList<TransactionRecord>::iterator lower = qLowerBound(
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
QList<TransactionRecord>::iterator upper = qUpperBound(
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
int lowerIndex = (lower - cachedWallet.begin());
int upperIndex = (upper - cachedWallet.begin());
bool inModel = (lower != upper);
if(status == CT_UPDATED)
{ {
LOCK2(cs_main, wallet->cs_wallet); if(showTransaction && !inModel)
status = CT_NEW; /* Not in model, but want to show, treat as new */
if(!showTransaction && inModel)
status = CT_DELETED; /* In model, but want to hide, treat as deleted */
}
// Find transaction in wallet qDebug() << " inModel=" + QString::number(inModel) +
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash); " Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) +
bool inWallet = mi != wallet->mapWallet.end(); " showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status);
// Find bounds of this transaction in model switch(status)
QList<TransactionRecord>::iterator lower = qLowerBound( {
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); case CT_NEW:
QList<TransactionRecord>::iterator upper = qUpperBound( if(inModel)
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
int lowerIndex = (lower - cachedWallet.begin());
int upperIndex = (upper - cachedWallet.begin());
bool inModel = (lower != upper);
// Determine whether to show transaction or not
bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second));
if(status == CT_UPDATED)
{ {
if(showTransaction && !inModel) qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model";
status = CT_NEW; /* Not in model, but want to show, treat as new */ break;
if(!showTransaction && inModel)
status = CT_DELETED; /* In model, but want to hide, treat as deleted */
} }
if(showTransaction)
qDebug() << " inWallet=" + QString::number(inWallet) + " inModel=" + QString::number(inModel) +
" Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) +
" showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status);
switch(status)
{ {
case CT_NEW: LOCK2(cs_main, wallet->cs_wallet);
if(inModel) // Find transaction in wallet
{ std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
qDebug() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model"; if(mi == wallet->mapWallet.end())
break;
}
if(!inWallet)
{ {
qDebug() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is not in wallet"; qDebug() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is not in wallet";
break; break;
} }
if(showTransaction) // Added -- insert at the right position
QList<TransactionRecord> toInsert =
TransactionRecord::decomposeTransaction(wallet, mi->second);
if(!toInsert.isEmpty()) /* only if something to insert */
{ {
// Added -- insert at the right position parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
QList<TransactionRecord> toInsert = int insert_idx = lowerIndex;
TransactionRecord::decomposeTransaction(wallet, mi->second); foreach(const TransactionRecord &rec, toInsert)
if(!toInsert.isEmpty()) /* only if something to insert */
{ {
parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); cachedWallet.insert(insert_idx, rec);
int insert_idx = lowerIndex; insert_idx += 1;
foreach(const TransactionRecord &rec, toInsert)
{
cachedWallet.insert(insert_idx, rec);
insert_idx += 1;
}
parent->endInsertRows();
} }
parent->endInsertRows();
} }
break; }
case CT_DELETED: break;
if(!inModel) case CT_DELETED:
{ if(!inModel)
qDebug() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model"; {
break; qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model";
}
// Removed -- remove entire transaction from table
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
cachedWallet.erase(lower, upper);
parent->endRemoveRows();
break;
case CT_UPDATED:
// Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for
// visible transactions.
break; break;
} }
// Removed -- remove entire transaction from table
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
cachedWallet.erase(lower, upper);
parent->endRemoveRows();
break;
case CT_UPDATED:
// Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for
// visible transactions.
break;
} }
} }
@ -234,26 +227,30 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren
QAbstractTableModel(parent), QAbstractTableModel(parent),
wallet(wallet), wallet(wallet),
walletModel(parent), walletModel(parent),
priv(new TransactionTablePriv(wallet, this)) priv(new TransactionTablePriv(wallet, this)),
fProcessingQueuedTransactions(false)
{ {
columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount"); columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount");
priv->refreshWallet(); priv->refreshWallet();
connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
subscribeToCoreSignals();
} }
TransactionTableModel::~TransactionTableModel() TransactionTableModel::~TransactionTableModel()
{ {
unsubscribeFromCoreSignals();
delete priv; delete priv;
} }
void TransactionTableModel::updateTransaction(const QString &hash, int status) void TransactionTableModel::updateTransaction(const QString &hash, int status, bool showTransaction)
{ {
uint256 updated; uint256 updated;
updated.SetHex(hash.toStdString()); updated.SetHex(hash.toStdString());
priv->updateWallet(updated, status); priv->updateWallet(updated, status, showTransaction);
} }
void TransactionTableModel::updateConfirmations() void TransactionTableModel::updateConfirmations()
@ -644,3 +641,82 @@ void TransactionTableModel::updateDisplayUnit()
// emit dataChanged to update Amount column with the current unit // emit dataChanged to update Amount column with the current unit
emit dataChanged(index(0, Amount), index(priv->size()-1, Amount)); emit dataChanged(index(0, Amount), index(priv->size()-1, Amount));
} }
// queue notifications to show a non freezing progress dialog e.g. for rescan
struct TransactionNotification
{
public:
TransactionNotification() {}
TransactionNotification(uint256 hash, ChangeType status, bool showTransaction):
hash(hash), status(status), showTransaction(showTransaction) {}
void invoke(QObject *ttm)
{
QString strHash = QString::fromStdString(hash.GetHex());
qDebug() << "NotifyTransactionChanged : " + strHash + " status= " + QString::number(status);
QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection,
Q_ARG(QString, strHash),
Q_ARG(int, status),
Q_ARG(bool, showTransaction));
}
private:
uint256 hash;
ChangeType status;
bool showTransaction;
};
static bool fQueueNotifications = false;
static std::vector< TransactionNotification > vQueueNotifications;
static void NotifyTransactionChanged(TransactionTableModel *ttm, CWallet *wallet, const uint256 &hash, ChangeType status)
{
// Find transaction in wallet
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
// Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread)
bool inWallet = mi != wallet->mapWallet.end();
bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second));
TransactionNotification notification(hash, status, showTransaction);
if (fQueueNotifications)
{
vQueueNotifications.push_back(notification);
return;
}
notification.invoke(ttm);
}
static void ShowProgress(TransactionTableModel *ttm, const std::string &title, int nProgress)
{
if (nProgress == 0)
fQueueNotifications = true;
if (nProgress == 100)
{
fQueueNotifications = false;
if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons
QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
for (unsigned int i = 0; i < vQueueNotifications.size(); ++i)
{
if (vQueueNotifications.size() - i <= 10)
QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
vQueueNotifications[i].invoke(ttm);
}
std::vector<TransactionNotification >().swap(vQueueNotifications); // clear
}
}
void TransactionTableModel::subscribeToCoreSignals()
{
// Connect signals to wallet
wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
}
void TransactionTableModel::unsubscribeFromCoreSignals()
{
// Disconnect signals from wallet
wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
}

View File

@ -65,12 +65,17 @@ public:
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
bool processingQueuedTransactions() { return fProcessingQueuedTransactions; }
private: private:
CWallet* wallet; CWallet* wallet;
WalletModel *walletModel; WalletModel *walletModel;
QStringList columns; QStringList columns;
TransactionTablePriv *priv; TransactionTablePriv *priv;
bool fProcessingQueuedTransactions;
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
QString lookupAddress(const std::string &address, bool tooltip) const; QString lookupAddress(const std::string &address, bool tooltip) const;
QVariant addressColor(const TransactionRecord *wtx) const; QVariant addressColor(const TransactionRecord *wtx) const;
@ -84,9 +89,12 @@ private:
QVariant txAddressDecoration(const TransactionRecord *wtx) const; QVariant txAddressDecoration(const TransactionRecord *wtx) const;
public slots: public slots:
void updateTransaction(const QString &hash, int status); /* New transaction, or transaction changed status */
void updateTransaction(const QString &hash, int status, bool showTransaction);
void updateConfirmations(); void updateConfirmations();
void updateDisplayUnit(); void updateDisplayUnit();
/* Needed to update fProcessingQueuedTransactions through a QueuedConnection */
void setProcessingQueuedTransactions(bool value) { fProcessingQueuedTransactions = value; }
friend class TransactionTablePriv; friend class TransactionTablePriv;
}; };

View File

@ -35,6 +35,8 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p
cachedEncryptionStatus(Unencrypted), cachedEncryptionStatus(Unencrypted),
cachedNumBlocks(0) cachedNumBlocks(0)
{ {
fForceCheckBalanceChanged = false;
addressTableModel = new AddressTableModel(wallet, this); addressTableModel = new AddressTableModel(wallet, this);
transactionTableModel = new TransactionTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this);
recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); recentRequestsTableModel = new RecentRequestsTableModel(wallet, this);
@ -71,8 +73,8 @@ qint64 WalletModel::getBalance(const CCoinControl *coinControl) const
qint64 WalletModel::getAnonymizedBalance() const qint64 WalletModel::getAnonymizedBalance() const
{ {
qint64 ret = wallet->GetAnonymizedBalance(); if(fLiteMode) return 0;
return ret; return wallet->GetAnonymizedBalance();
} }
qint64 WalletModel::getUnconfirmedBalance() const qint64 WalletModel::getUnconfirmedBalance() const
@ -149,11 +151,8 @@ void WalletModel::checkBalanceChanged()
} }
} }
void WalletModel::updateTransaction(const QString &hash, int status) void WalletModel::updateTransaction()
{ {
if(transactionTableModel)
transactionTableModel->updateTransaction(hash, status);
// Balance and number of transactions might have changed // Balance and number of transactions might have changed
checkBalanceChanged(); checkBalanceChanged();
@ -485,23 +484,12 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
Q_ARG(int, status)); Q_ARG(int, status));
} }
// queue notifications to show a non freezing progress dialog e.g. for rescan
static bool fQueueNotifications = false;
static std::vector<std::pair<uint256, ChangeType> > vQueueNotifications;
static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status) static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status)
{ {
if (fQueueNotifications) Q_UNUSED(wallet);
{ Q_UNUSED(hash);
vQueueNotifications.push_back(make_pair(hash, status)); Q_UNUSED(status);
return; QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection);
}
QString strHash = QString::fromStdString(hash.GetHex());
qDebug() << "NotifyTransactionChanged : " + strHash + " status= " + QString::number(status);
QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection,
Q_ARG(QString, strHash),
Q_ARG(int, status));
} }
static void ShowProgress(WalletModel *walletmodel, const std::string &title, int nProgress) static void ShowProgress(WalletModel *walletmodel, const std::string &title, int nProgress)
@ -510,17 +498,6 @@ static void ShowProgress(WalletModel *walletmodel, const std::string &title, int
QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection, QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(title)), Q_ARG(QString, QString::fromStdString(title)),
Q_ARG(int, nProgress)); Q_ARG(int, nProgress));
if (nProgress == 0)
fQueueNotifications = true;
if (nProgress == 100)
{
fQueueNotifications = false;
BOOST_FOREACH(const PAIRTYPE(uint256, ChangeType)& notification, vQueueNotifications)
NotifyTransactionChanged(walletmodel, NULL, notification.first, notification.second);
std::vector<std::pair<uint256, ChangeType> >().swap(vQueueNotifications); // clear
}
} }
void WalletModel::subscribeToCoreSignals() void WalletModel::subscribeToCoreSignals()

View File

@ -202,6 +202,7 @@ public:
private: private:
CWallet *wallet; CWallet *wallet;
bool fForceCheckBalanceChanged;
// Wallet has an options model for wallet-specific options // Wallet has an options model for wallet-specific options
// (transaction fee, for example) // (transaction fee, for example)
@ -256,7 +257,7 @@ public slots:
/* Wallet status might have changed */ /* Wallet status might have changed */
void updateStatus(); void updateStatus();
/* New transaction, or transaction changed status */ /* New transaction, or transaction changed status */
void updateTransaction(const QString &hash, int status); void updateTransaction();
/* New, updated or removed address book entry */ /* New, updated or removed address book entry */
void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status); void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status);
/* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */

View File

@ -141,6 +141,8 @@ void WalletView::processNewTransaction(const QModelIndex& parent, int start, int
return; return;
TransactionTableModel *ttm = walletModel->getTransactionTableModel(); TransactionTableModel *ttm = walletModel->getTransactionTableModel();
if (!ttm || ttm->processingQueuedTransactions())
return;
QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString(); QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString();
qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();

View File

@ -1092,21 +1092,19 @@ int64_t CWallet::GetAnonymizedBalance() const
{ {
int64_t nTotal = 0; int64_t nTotal = 0;
{ {
LOCK(cs_wallet); LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{ {
const CWalletTx* pcoin = &(*it).second; const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsTrusted()) if (pcoin->IsTrusted())
{ {
int nDepth = pcoin->GetDepthInMainChain(false); uint256 hash = (*it).first;
for (unsigned int i = 0; i < pcoin->vout.size(); i++)
{
CTxIn vin = CTxIn(hash, i);
for (unsigned int i = 0; i < pcoin->vout.size(); i++) { if(IsSpent(hash, i) || !IsMine(pcoin->vout[i]) || !IsDenominated(vin)) continue;
COutput out = COutput(pcoin, i, nDepth);
CTxIn vin = CTxIn(out.tx->GetHash(), out.i);
if(IsSpent(out.tx->GetHash(), i) || !IsMine(pcoin->vout[i]) || !IsDenominated(vin)) continue;
int rounds = GetInputDarksendRounds(vin); int rounds = GetInputDarksendRounds(vin);
if(rounds >= nDarksendRounds){ if(rounds >= nDarksendRounds){
@ -1126,21 +1124,20 @@ double CWallet::GetAverageAnonymizedRounds() const
double fCount = 0; double fCount = 0;
{ {
LOCK(cs_wallet); LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{ {
const CWalletTx* pcoin = &(*it).second; const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsTrusted()) if (pcoin->IsTrusted())
{ {
int nDepth = pcoin->GetDepthInMainChain(false); uint256 hash = (*it).first;
for (unsigned int i = 0; i < pcoin->vout.size(); i++) { for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
COutput out = COutput(pcoin, i, nDepth); CTxIn vin = CTxIn(hash, i);
CTxIn vin = CTxIn(out.tx->GetHash(), out.i);
if(IsSpent(out.tx->GetHash(), i) || !IsMine(pcoin->vout[i]) || !IsDenominated(vin)) continue; if(IsSpent(hash, i) || !IsMine(pcoin->vout[i]) || !IsDenominated(vin)) continue;
int rounds = GetInputDarksendRounds(vin); int rounds = GetInputDarksendRounds(vin);
fTotal += (float)rounds; fTotal += (float)rounds;
@ -1160,21 +1157,20 @@ int64_t CWallet::GetNormalizedAnonymizedBalance() const
int64_t nTotal = 0; int64_t nTotal = 0;
{ {
LOCK(cs_wallet); LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{ {
const CWalletTx* pcoin = &(*it).second; const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsTrusted()) if (pcoin->IsTrusted())
{ {
int nDepth = pcoin->GetDepthInMainChain(false); uint256 hash = (*it).first;
for (unsigned int i = 0; i < pcoin->vout.size(); i++) { for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
COutput out = COutput(pcoin, i, nDepth); CTxIn vin = CTxIn(hash, i);
CTxIn vin = CTxIn(out.tx->GetHash(), out.i);
if(IsSpent(out.tx->GetHash(), i) || !IsMine(pcoin->vout[i]) || !IsDenominated(vin)) continue; if(IsSpent(hash, i) || !IsMine(pcoin->vout[i]) || !IsDenominated(vin)) continue;
int rounds = GetInputDarksendRounds(vin); int rounds = GetInputDarksendRounds(vin);
nTotal += pcoin->vout[i].nValue * rounds / nDarksendRounds; nTotal += pcoin->vout[i].nValue * rounds / nDarksendRounds;
@ -1190,7 +1186,7 @@ int64_t CWallet::GetDenominatedBalance(bool onlyDenom, bool onlyUnconfirmed) con
{ {
int64_t nTotal = 0; int64_t nTotal = 0;
{ {
LOCK(cs_wallet); LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{ {
const CWalletTx* pcoin = &(*it).second; const CWalletTx* pcoin = &(*it).second;
@ -1202,12 +1198,11 @@ int64_t CWallet::GetDenominatedBalance(bool onlyDenom, bool onlyUnconfirmed) con
bool unconfirmed = (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && nDepth == 0)); bool unconfirmed = (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && nDepth == 0));
if(onlyUnconfirmed != unconfirmed) continue; if(onlyUnconfirmed != unconfirmed) continue;
uint256 hash = (*it).first;
for (unsigned int i = 0; i < pcoin->vout.size(); i++) for (unsigned int i = 0; i < pcoin->vout.size(); i++)
{ {
COutput out = COutput(pcoin, i, nDepth); if(IsSpent(hash, i)) continue;
if(IsSpent(out.tx->GetHash(), i)) continue;
if(!IsMine(pcoin->vout[i])) continue; if(!IsMine(pcoin->vout[i])) continue;
if(onlyDenom != IsDenominatedAmount(pcoin->vout[i].nValue)) continue; if(onlyDenom != IsDenominatedAmount(pcoin->vout[i].nValue)) continue;