From 24e5470c0dfe364ecee53a5f11e938c6a9cf1165 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Tue, 10 Feb 2015 16:24:05 +0300 Subject: [PATCH 01/11] cherry-pick https://github.com/laanwj/bitcoin/commit/023e63df78b847812040bf6958c97476606dfbfd --- src/qt/transactiontablemodel.cpp | 217 ++++++++++++++++++++++--------- src/qt/transactiontablemodel.h | 12 +- src/qt/walletmodel.cpp | 42 ++---- src/qt/walletmodel.h | 4 +- src/qt/walletview.cpp | 2 + 5 files changed, 181 insertions(+), 96 deletions(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 9f2e57f857..30faaeeaf0 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -91,88 +91,81 @@ public: 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); + + // Find bounds of this transaction in model + QList::iterator lower = qLowerBound( + cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); + QList::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 - std::map::iterator mi = wallet->mapWallet.find(hash); - bool inWallet = mi != wallet->mapWallet.end(); + qDebug() << " inModel=" + QString::number(inModel) + + " Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) + + " showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status); - // Find bounds of this transaction in model - QList::iterator lower = qLowerBound( - cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); - QList::iterator upper = qUpperBound( - 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) + switch(status) + { + case CT_NEW: + if(inModel) { - 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 */ + qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model"; + break; } - - 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) + if(showTransaction) { - case CT_NEW: - if(inModel) - { - qDebug() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model"; - break; - } - if(!inWallet) + LOCK2(cs_main, wallet->cs_wallet); + // Find transaction in wallet + std::map::iterator mi = wallet->mapWallet.find(hash); + if(mi == wallet->mapWallet.end()) { qDebug() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is not in wallet"; break; } - if(showTransaction) + // Added -- insert at the right position + QList toInsert = + TransactionRecord::decomposeTransaction(wallet, mi->second); + if(!toInsert.isEmpty()) /* only if something to insert */ { - // Added -- insert at the right position - QList toInsert = - TransactionRecord::decomposeTransaction(wallet, mi->second); - if(!toInsert.isEmpty()) /* only if something to insert */ + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); + int insert_idx = lowerIndex; + foreach(const TransactionRecord &rec, toInsert) { - parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); - int insert_idx = lowerIndex; - foreach(const TransactionRecord &rec, toInsert) - { - cachedWallet.insert(insert_idx, rec); - insert_idx += 1; - } - parent->endInsertRows(); + cachedWallet.insert(insert_idx, rec); + insert_idx += 1; } + parent->endInsertRows(); } - break; - case CT_DELETED: - if(!inModel) - { - qDebug() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model"; - 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; + case CT_DELETED: + if(!inModel) + { + qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model"; 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,41 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren QAbstractTableModel(parent), wallet(wallet), walletModel(parent), - priv(new TransactionTablePriv(wallet, this)) + priv(new TransactionTablePriv(wallet, this)), + fProcessingQueuedTransactions(false) { columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount"); priv->refreshWallet(); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + subscribeToCoreSignals(); } TransactionTableModel::~TransactionTableModel() { + unsubscribeFromCoreSignals(); delete priv; } +<<<<<<< HEAD void TransactionTableModel::updateTransaction(const QString &hash, int status) +======= +/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ +void TransactionTableModel::updateAmountColumnTitle() +{ + columns[Amount] = BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); + emit headerDataChanged(Qt::Horizontal,Amount,Amount); +} + +void TransactionTableModel::updateTransaction(const QString &hash, int status, bool showTransaction) +>>>>>>> 023e63d... qt: Move transaction notification to transaction table model { uint256 updated; updated.SetHex(hash.toStdString()); - priv->updateWallet(updated, status); + priv->updateWallet(updated, status, showTransaction); } void TransactionTableModel::updateConfirmations() @@ -644,3 +652,82 @@ void TransactionTableModel::updateDisplayUnit() // emit dataChanged to update Amount column with the current unit 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::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().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)); +} diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 333e6bc6ed..5cd011523e 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -65,12 +65,17 @@ public: QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + bool processingQueuedTransactions() { return fProcessingQueuedTransactions; } private: CWallet* wallet; WalletModel *walletModel; QStringList columns; TransactionTablePriv *priv; + bool fProcessingQueuedTransactions; + + void subscribeToCoreSignals(); + void unsubscribeFromCoreSignals(); QString lookupAddress(const std::string &address, bool tooltip) const; QVariant addressColor(const TransactionRecord *wtx) const; @@ -84,9 +89,14 @@ private: QVariant txAddressDecoration(const TransactionRecord *wtx) const; 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 updateDisplayUnit(); + /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ + void updateAmountColumnTitle(); + /* Needed to update fProcessingQueuedTransactions through a QueuedConnection */ + void setProcessingQueuedTransactions(bool value) { fProcessingQueuedTransactions = value; } friend class TransactionTablePriv; }; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index e260303b7c..df4d05919b 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -34,6 +34,9 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p cachedEncryptionStatus(Unencrypted), cachedNumBlocks(0) { + fHaveWatchOnly = wallet->HaveWatchOnly(); + fForceCheckBalanceChanged = false; + addressTableModel = new AddressTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); @@ -148,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 checkBalanceChanged(); @@ -476,23 +476,12 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, Q_ARG(int, status)); } -// queue notifications to show a non freezing progress dialog e.g. for rescan -static bool fQueueNotifications = false; -static std::vector > vQueueNotifications; static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status) { - if (fQueueNotifications) - { - vQueueNotifications.push_back(make_pair(hash, status)); - return; - } - - 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)); + Q_UNUSED(wallet); + Q_UNUSED(hash); + Q_UNUSED(status); + QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection); } static void ShowProgress(WalletModel *walletmodel, const std::string &title, int nProgress) @@ -501,17 +490,12 @@ static void ShowProgress(WalletModel *walletmodel, const std::string &title, int QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(title)), 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 >().swap(vQueueNotifications); // clear - } +static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly) +{ + QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", Qt::QueuedConnection, + Q_ARG(bool, fHaveWatchonly)); } void WalletModel::subscribeToCoreSignals() diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 90daee3483..6863bc6fff 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -202,6 +202,8 @@ public: private: CWallet *wallet; + bool fHaveWatchOnly; + bool fForceCheckBalanceChanged; // Wallet has an options model for wallet-specific options // (transaction fee, for example) @@ -256,7 +258,7 @@ public slots: /* Wallet status might have changed */ void updateStatus(); /* New transaction, or transaction changed status */ - void updateTransaction(const QString &hash, int status); + void updateTransaction(); /* New, updated or removed address book entry */ 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 */ diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 04c9000c60..2c1d73fa77 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -141,6 +141,8 @@ void WalletView::processNewTransaction(const QModelIndex& parent, int start, int return; TransactionTableModel *ttm = walletModel->getTransactionTableModel(); + if (!ttm || ttm->processingQueuedTransactions()) + return; QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong(); From f9e80690b9815dd2ef00c40e548a7691becbfb9b Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Tue, 10 Feb 2015 16:25:21 +0300 Subject: [PATCH 02/11] resolve conflicts / clean yet unused functionality --- src/qt/transactiontablemodel.cpp | 11 ----------- src/qt/transactiontablemodel.h | 2 -- src/qt/walletmodel.cpp | 7 ------- src/qt/walletmodel.h | 1 - 4 files changed, 21 deletions(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 30faaeeaf0..a91e1407d2 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -245,18 +245,7 @@ TransactionTableModel::~TransactionTableModel() delete priv; } -<<<<<<< HEAD -void TransactionTableModel::updateTransaction(const QString &hash, int status) -======= -/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ -void TransactionTableModel::updateAmountColumnTitle() -{ - columns[Amount] = BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); - emit headerDataChanged(Qt::Horizontal,Amount,Amount); -} - void TransactionTableModel::updateTransaction(const QString &hash, int status, bool showTransaction) ->>>>>>> 023e63d... qt: Move transaction notification to transaction table model { uint256 updated; updated.SetHex(hash.toStdString()); diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 5cd011523e..a322156a3b 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -93,8 +93,6 @@ public slots: void updateTransaction(const QString &hash, int status, bool showTransaction); void updateConfirmations(); void updateDisplayUnit(); - /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ - void updateAmountColumnTitle(); /* Needed to update fProcessingQueuedTransactions through a QueuedConnection */ void setProcessingQueuedTransactions(bool value) { fProcessingQueuedTransactions = value; } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index df4d05919b..55c59230ec 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -34,7 +34,6 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p cachedEncryptionStatus(Unencrypted), cachedNumBlocks(0) { - fHaveWatchOnly = wallet->HaveWatchOnly(); fForceCheckBalanceChanged = false; addressTableModel = new AddressTableModel(wallet, this); @@ -492,12 +491,6 @@ static void ShowProgress(WalletModel *walletmodel, const std::string &title, int Q_ARG(int, nProgress)); } -static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly) -{ - QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", Qt::QueuedConnection, - Q_ARG(bool, fHaveWatchonly)); -} - void WalletModel::subscribeToCoreSignals() { // Connect signals to wallet diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 6863bc6fff..32f6d51a88 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -202,7 +202,6 @@ public: private: CWallet *wallet; - bool fHaveWatchOnly; bool fForceCheckBalanceChanged; // Wallet has an options model for wallet-specific options From 4e94fe1d52881ce71d4b33528f665227e75c0840 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Tue, 10 Feb 2015 16:45:37 +0300 Subject: [PATCH 03/11] cherry-pick: [Qt, OSX] QProgressBar CPU-Issue workaround https://github.com/jonasschnelli/bitcoin/commit/6093aa1bb03b03331cc2f48d8f6b749f8817c016 https://github.com/jonasschnelli/bitcoin/commit/0ceab00d16a1d23da06a748889db25662b16bfb3 [Qt, OSX] fix Qt4.8 compatibility with QProgressBar issue https://github.com/bitcoin/bitcoin/commit/c5a22828559bdac8fd347e30a3027dbaf54934ae --- src/qt/bitcoingui.cpp | 2 +- src/qt/guiutil.h | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 924d425a9e..c36fd1caf8 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -170,7 +170,7 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : // Progress bar and label for blocks download progressBarLabel = new QLabel(); progressBarLabel->setVisible(false); - progressBar = new QProgressBar(); + progressBar = new GUIUtil::ProgressBar(); progressBar->setAlignment(Qt::AlignCenter); progressBar->setVisible(false); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 847633a752..c79c477ff8 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -5,9 +5,11 @@ #ifndef GUIUTIL_H #define GUIUTIL_H +#include #include #include #include +#include #include #include @@ -177,6 +179,20 @@ namespace GUIUtil /* Convert OS specific boost path to QString through UTF-8 */ 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 #endif // GUIUTIL_H From a948de80094f7ac1d7b67d591ba0ac8b7076911f Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Wed, 11 Feb 2015 02:50:35 +0300 Subject: [PATCH 04/11] do not calculate anonymized balance during initial download or while in lite mode --- src/qt/walletmodel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 55c59230ec..6496d4b00a 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -72,8 +72,8 @@ qint64 WalletModel::getBalance(const CCoinControl *coinControl) const qint64 WalletModel::getAnonymizedBalance() const { - qint64 ret = wallet->GetAnonymizedBalance(); - return ret; + if(IsInitialBlockDownload() || fLiteMode) return 0; + return wallet->GetAnonymizedBalance(); } qint64 WalletModel::getUnconfirmedBalance() const From 6dc44bdfa50157af28a9e26a8fcae0f31b9fb2e2 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Wed, 11 Feb 2015 03:07:52 +0300 Subject: [PATCH 05/11] IsInitialBlockDownload time range down to 6 hours = ~144 blocks -> 2 x forks detection time (fixing comments there too) --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 07e184cc7e..356b1b942a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1776,7 +1776,7 @@ bool IsInitialBlockDownload() nLastUpdate = GetTime(); } 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; @@ -1789,7 +1789,7 @@ void CheckForkWarningConditions() if (IsInitialBlockDownload()) 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 if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) 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 - // 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 // 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) From 90b2a440454a3810abb60a8ba728c866e645efe7 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 12 Feb 2015 16:26:32 +0300 Subject: [PATCH 06/11] wallet - fix locks / cleanup a bit --- src/wallet.cpp | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/wallet.cpp b/src/wallet.cpp index a3d04fe90b..7501f57347 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1092,21 +1092,19 @@ int64_t CWallet::GetAnonymizedBalance() const { int64_t nTotal = 0; { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; 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++) { - - 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; + if(IsSpent(hash, i) || !IsMine(pcoin->vout[i]) || !IsDenominated(vin)) continue; int rounds = GetInputDarksendRounds(vin); if(rounds >= nDarksendRounds){ @@ -1126,21 +1124,20 @@ double CWallet::GetAverageAnonymizedRounds() const double fCount = 0; { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsTrusted()) { - int nDepth = pcoin->GetDepthInMainChain(false); + uint256 hash = (*it).first; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { - COutput out = COutput(pcoin, i, nDepth); - CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + CTxIn vin = CTxIn(hash, 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); fTotal += (float)rounds; @@ -1160,21 +1157,20 @@ int64_t CWallet::GetNormalizedAnonymizedBalance() const int64_t nTotal = 0; { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsTrusted()) { - int nDepth = pcoin->GetDepthInMainChain(false); + uint256 hash = (*it).first; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { - COutput out = COutput(pcoin, i, nDepth); - CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + CTxIn vin = CTxIn(hash, 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); nTotal += pcoin->vout[i].nValue * rounds / nDarksendRounds; @@ -1190,7 +1186,7 @@ int64_t CWallet::GetDenominatedBalance(bool onlyDenom, bool onlyUnconfirmed) con { int64_t nTotal = 0; { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { 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)); if(onlyUnconfirmed != unconfirmed) continue; + uint256 hash = (*it).first; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { - COutput out = COutput(pcoin, i, nDepth); - - if(IsSpent(out.tx->GetHash(), i)) continue; + if(IsSpent(hash, i)) continue; if(!IsMine(pcoin->vout[i])) continue; if(onlyDenom != IsDenominatedAmount(pcoin->vout[i].nValue)) continue; From 843dc81178ac3b0cc85537fdd8d4543725fd1e40 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 12 Feb 2015 16:32:34 +0300 Subject: [PATCH 07/11] add "out of sync" message to DS section too --- src/qt/forms/overviewpage.ui | 73 ++++++++++++++++++++++++++---------- src/qt/overviewpage.cpp | 2 + 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 2fe095a938..01b9c55c06 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -383,25 +383,6 @@ - - - - 10 - 10 - 66 - 20 - - - - - 75 - true - - - - Darksend - - @@ -943,6 +924,60 @@ Reset + + + + 10 + 10 + 431 + 22 + + + + + + + + 75 + true + + + + Darksend + + + + + + + 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. + + + QLabel { color: red; } + + + (out of sync) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 590008168d..7c00ff664d 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -124,6 +124,7 @@ OverviewPage::OverviewPage(QWidget *parent) : // init "out of sync" warning labels ui->labelWalletStatus->setText("(" + tr("out of sync") + ")"); + ui->labelDarksendSyncStatus->setText("(" + tr("out of sync") + ")"); ui->labelTransactionsStatus->setText("(" + tr("out of sync") + ")"); showingDarkSendMessage = 0; @@ -253,6 +254,7 @@ void OverviewPage::updateAlerts(const QString &warnings) void OverviewPage::showOutOfSyncWarning(bool fShow) { ui->labelWalletStatus->setVisible(fShow); + ui->labelDarksendSyncStatus->setVisible(fShow); ui->labelTransactionsStatus->setVisible(fShow); } From 5011bc2e2aa7cfb06df96fd81e012cb140487cec Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 12 Feb 2015 16:54:24 +0300 Subject: [PATCH 08/11] fix twice denominatedBalance calculations --- src/qt/overviewpage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 7c00ff664d..8884401e45 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -293,7 +293,7 @@ void OverviewPage::updateDarksendProgress() float denomPart = 0; if(denominatedBalance > 0) { - denomPart = (float)pwalletMain->GetNormalizedAnonymizedBalance() / pwalletMain->GetDenominatedBalance(); + denomPart = (float)pwalletMain->GetNormalizedAnonymizedBalance() / denominatedBalance; denomPart = denomPart > 1 ? 1 : denomPart; } From 9823b9ebad29ff133f3281dd38bc89bfb58bab2e Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 12 Feb 2015 16:58:12 +0300 Subject: [PATCH 09/11] Solve performance problems by caching and fix probable security issue - add number of rounds to outputs - cache rounds calculation results of GetInputDarksendRounds - select shortest rounds chain instead of the first found --- src/core.cpp | 3 +- src/core.h | 3 ++ src/darksend.cpp | 86 ++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index 32e6e4c5ea..92353da665 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -54,6 +54,7 @@ void CTxIn::print() const CTxOut::CTxOut(int64_t nValueIn, CScript scriptPubKeyIn) { nValue = nValueIn; + nRounds = -10; // an initial value, should be no way to get this by calculations scriptPubKey = scriptPubKeyIn; } @@ -285,4 +286,4 @@ void CBlock::print() const for (unsigned int i = 0; i < vMerkleTree.size(); i++) LogPrintf("%s ", vMerkleTree[i].ToString()); LogPrintf("\n"); -} \ No newline at end of file +} diff --git a/src/core.h b/src/core.h index 41b1d2d8d2..5b51265189 100644 --- a/src/core.h +++ b/src/core.h @@ -153,6 +153,7 @@ class CTxOut { public: int64_t nValue; + int nRounds; CScript scriptPubKey; CTxOut() @@ -171,6 +172,7 @@ public: void SetNull() { nValue = -1; + nRounds = -10; // an initial value, should be no way to get this by calculations scriptPubKey.clear(); } @@ -197,6 +199,7 @@ public: friend bool operator==(const CTxOut& a, const CTxOut& b) { return (a.nValue == b.nValue && + a.nRounds == b.nRounds && a.scriptPubKey == b.scriptPubKey); } diff --git a/src/darksend.cpp b/src/darksend.cpp index 0ad9b1747e..a41f3d65bd 100644 --- a/src/darksend.cpp +++ b/src/darksend.cpp @@ -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) int GetInputDarksendRounds(CTxIn in, int rounds) { + static std::map mDenomWtxes; + if(rounds >= 17) return rounds; - std::string padding = ""; - padding.insert(0, ((rounds+1)*5)+3, ' '); + uint256 hash = in.prevout.hash; + uint nout = in.prevout.n; - CWalletTx tx; - if(pwalletMain->GetTransaction(in.prevout.hash,tx)) + CWalletTx wtx; + if(pwalletMain->GetTransaction(hash, wtx)) { - // bounds check - if(in.prevout.n >= tx.vout.size()) return -4; + std::map::const_iterator mdwi = mDenomWtxes.find(hash); + // 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 - if(rounds == 0 && !pwalletMain->IsDenominatedAmount(tx.vout[in.prevout.n].nValue)) return -2; //NOT DENOM - - bool found = false; - BOOST_FOREACH(CTxOut out, tx.vout) + mDenomWtxes[hash].vout[nout].nRounds = -2; + if(/*rounds == 0 && */!pwalletMain->IsDenominatedAmount(wtx.vout[nout].nValue)) //NOT DENOM { - found = pwalletMain->IsDenominatedAmount(out.nValue); - if(found) break; // no need to loop more + mDenomWtxes[hash].vout[nout].nRounds = -2; + 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 - BOOST_FOREACH(CTxIn in2, tx.vin) + bool fAllDenoms = true; + 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)) { - //LogPrintf("rounds :: %s %s %d NEXT\n", padding.c_str(), in.ToString().c_str(), rounds); 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; From 68cbba162169df764e3963ff45e378fcf186b5cb Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 12 Feb 2015 17:04:20 +0300 Subject: [PATCH 10/11] allow getAnonymizedBalance calculation on sync again --- src/qt/walletmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 6496d4b00a..c088dafdfa 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -72,7 +72,7 @@ qint64 WalletModel::getBalance(const CCoinControl *coinControl) const qint64 WalletModel::getAnonymizedBalance() const { - if(IsInitialBlockDownload() || fLiteMode) return 0; + if(fLiteMode) return 0; return wallet->GetAnonymizedBalance(); } From cc9eef01a24cde4498afb0412558fd88850b34ed Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 12 Feb 2015 16:32:34 +0300 Subject: [PATCH 11/11] fix time to switch "out of sync" message on/of to make UI more consistent (as we are 4x times faser than bitcoin) --- src/qt/bitcoingui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c36fd1caf8..63a4d8fa27 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -670,7 +670,7 @@ void BitcoinGUI::setNumBlocks(int count) tooltip = tr("Processed %1 blocks of transaction history.").arg(count); // 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(".
") + tooltip; labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));