From 9d9a4e874db82e63a2b876c9f490be7247856282 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 3 Jun 2011 20:48:03 +0200 Subject: [PATCH] support incremental wallet updates --- gui/include/clientmodel.h | 3 + gui/include/transactiontablemodel.h | 2 + gui/src/clientmodel.cpp | 11 +++ gui/src/transactiontablemodel.cpp | 114 ++++++++++++++++++++++------ 4 files changed, 108 insertions(+), 22 deletions(-) diff --git a/gui/include/clientmodel.h b/gui/include/clientmodel.h index 49b346097..01c0d70fc 100644 --- a/gui/include/clientmodel.h +++ b/gui/include/clientmodel.h @@ -29,6 +29,9 @@ public: int getNumBlocks(); int getNumTransactions(); + /* Set default address */ + void setAddress(const QString &defaultAddress); + /* Send coins */ StatusCode sendCoins(const QString &payTo, qint64 payAmount); private: OptionsModel *options_model; diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index b484a5973..70377ea0d 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -49,6 +49,8 @@ private: private slots: void update(); + + friend class TransactionTablePriv; }; #endif diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index 97b5f44fe..7dcbc576a 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -63,6 +63,17 @@ void ClientModel::update() emit numTransactionsChanged(getNumTransactions()); } +void ClientModel::setAddress(const QString &defaultAddress) +{ + uint160 hash160; + std::string strAddress = defaultAddress.toStdString(); + if (!AddressToHash160(strAddress, hash160)) + return; + if (!mapPubKeys.count(hash160)) + return; + CWalletDB().WriteDefaultKey(mapPubKeys[hash160]); +} + ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount) { uint160 hash160 = 0; diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index e23f45dd0..5bea3b598 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -9,14 +9,39 @@ #include #include #include +#include const QString TransactionTableModel::Sent = "s"; const QString TransactionTableModel::Received = "r"; const QString TransactionTableModel::Other = "o"; +/* Comparison operator for sort/binary search of model tx list */ +struct TxLessThan +{ + bool operator()(const TransactionRecord &a, const TransactionRecord &b) const + { + return a.hash < b.hash; + } + bool operator()(const TransactionRecord &a, const uint256 &b) const + { + return a.hash < b; + } + bool operator()(const uint256 &a, const TransactionRecord &b) const + { + return a < b.hash; + } +}; + /* Private implementation */ struct TransactionTablePriv { + TransactionTablePriv(TransactionTableModel *parent): + parent(parent) + { + } + + TransactionTableModel *parent; + /* Local cache of wallet. * As it is in the same order as the CWallet, by definition * this is sorted by sha256. @@ -39,28 +64,79 @@ struct TransactionTablePriv } } - /* Update our model of the wallet. + /* Update our model of the wallet incrementally. Call with list of hashes of transactions that were added, removed or changed. */ void updateWallet(const QList &updated) { - /* TODO: update only transactions in updated, and only if - the transactions are really part of the visible wallet. - - Update status of the other transactions in the cache just in case, - because this call means that a new block came in. + /* Walk through updated transactions, update model as needed. */ qDebug() << "updateWallet"; - foreach(uint256 hash, updated) - { - qDebug() << " " << QString::fromStdString(hash.ToString()); - } - /* beginInsertRows(QModelIndex(), first, last) */ - /* endInsertRows */ - /* beginRemoveRows(QModelIndex(), first, last) */ - /* beginEndRows */ - refreshWallet(); + /* Sort update list, and iterate through it in reverse, so that model updates + can be emitted from end to beginning (so that earlier updates will not influence + the indices of latter ones). + */ + QList updated_sorted = updated; + qSort(updated_sorted); + + CRITICAL_BLOCK(cs_mapWallet) + { + for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx) + { + const uint256 &hash = updated_sorted.at(update_idx); + /* Find transaction in wallet */ + std::map::iterator mi = mapWallet.find(hash); + bool inWallet = mi != mapWallet.end(); + /* 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 = false; + if(lower != upper) + { + inModel = true; + } + + qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel + << lowerIndex << "-" << upperIndex; + + if(inWallet && !inModel) + { + /* Added */ + QList toInsert = + TransactionRecord::decomposeTransaction(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) + { + cachedWallet.insert(insert_idx, rec); + insert_idx += 1; + } + parent->endInsertRows(); + } + } else if(!inWallet && inModel) + { + /* Removed */ + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedWallet.erase(lower, upper); + parent->endRemoveRows(); + } else if(inWallet && inModel) + { + /* Updated */ + + } + } + } + /* TODO: invalidate status for all transactions + Use counter. Emit dataChanged for column. + */ } int size() @@ -92,7 +168,7 @@ static int column_alignments[] = { TransactionTableModel::TransactionTableModel(QObject *parent): QAbstractTableModel(parent), - priv(new TransactionTablePriv()) + priv(new TransactionTablePriv(this)) { columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); @@ -127,13 +203,7 @@ void TransactionTableModel::update() if(!updated.empty()) { - /* TODO: improve this, way too brute-force at the moment, - only update transactions that actually changed, and add/remove - transactions that were added/removed. - */ - beginResetModel(); priv->updateWallet(updated); - endResetModel(); } }