X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fqt%2Faddresstablemodel.cpp;h=a5dc784e09fd3902fd65b3f066563d36f0fde511;hb=db1eaec482383385156b3e7cb6b2baa9454975f0;hp=4578ca740fed240122a314e5de9f7a164c7a9188;hpb=d4211176208b5e4ae4a699c6ce3239447752cdb2;p=novacoin.git diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 4578ca7..a5dc784 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -1,7 +1,9 @@ #include "addresstablemodel.h" #include "guiutil.h" +#include "walletmodel.h" -#include "headers.h" +#include "wallet.h" +#include "base58.h" #include #include @@ -25,32 +27,96 @@ struct AddressTableEntry type(type), label(label), address(address) {} }; +struct AddressTableEntryLessThan +{ + bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const + { + return a.address < b.address; + } + bool operator()(const AddressTableEntry &a, const QString &b) const + { + return a.address < b; + } + bool operator()(const QString &a, const AddressTableEntry &b) const + { + return a < b.address; + } +}; + // Private implementation -struct AddressTablePriv +class AddressTablePriv { +public: CWallet *wallet; QList cachedAddressTable; + AddressTableModel *parent; - AddressTablePriv(CWallet *wallet): - wallet(wallet) {} + AddressTablePriv(CWallet *wallet, AddressTableModel *parent): + wallet(wallet), parent(parent) {} void refreshAddressTable() { cachedAddressTable.clear(); - - CRITICAL_BLOCK(cs_mapPubKeys) - CRITICAL_BLOCK(wallet->cs_mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item, wallet->mapAddressBook) + LOCK(wallet->cs_wallet); + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, std::string)& item, wallet->mapAddressBook) { - std::string strAddress = item.first; - std::string strName = item.second; - uint160 hash160; - bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160)); + const CBitcoinAddress& address = item.first; + const std::string& strName = item.second; + bool fMine = IsMine(*wallet, address); cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending, QString::fromStdString(strName), - QString::fromStdString(strAddress))); + QString::fromStdString(address.ToString()))); + } + } + // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order + qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan()); + } + + void updateEntry(const QString &address, const QString &label, bool isMine, int status) + { + // Find address / label in model + QList::iterator lower = qLowerBound( + cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan()); + QList::iterator upper = qUpperBound( + cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan()); + int lowerIndex = (lower - cachedAddressTable.begin()); + int upperIndex = (upper - cachedAddressTable.begin()); + bool inModel = (lower != upper); + AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending; + + switch(status) + { + case CT_NEW: + if(inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_NOW, but entry is already in model\n"); + break; + } + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); + cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address)); + parent->endInsertRows(); + break; + case CT_UPDATED: + if(!inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model\n"); + break; + } + lower->type = newEntryType; + lower->label = label; + parent->emitDataChanged(lowerIndex); + break; + case CT_DELETED: + if(!inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry is not in model\n"); + break; } + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedAddressTable.erase(lower, upper); + parent->endRemoveRows(); + break; } } @@ -72,11 +138,11 @@ struct AddressTablePriv } }; -AddressTableModel::AddressTableModel(CWallet *wallet, QObject *parent) : - QAbstractTableModel(parent),wallet(wallet),priv(0) +AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : + QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0) { columns << tr("Label") << tr("Address"); - priv = new AddressTablePriv(wallet); + priv = new AddressTablePriv(wallet, this); priv->refreshAddressTable(); } @@ -144,41 +210,60 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const return QVariant(); } -bool AddressTableModel::setData(const QModelIndex & index, const QVariant & value, int role) +bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!index.isValid()) return false; AddressTableEntry *rec = static_cast(index.internalPointer()); + editStatus = OK; + if(role == Qt::EditRole) { switch(index.column()) { case Label: - wallet->SetAddressBookName(rec->address.toStdString(), value.toString().toStdString()); - rec->label = value.toString(); + // Do nothing, if old label == new label + if(rec->label == value.toString()) + { + editStatus = NO_CHANGES; + return false; + } + wallet->SetAddressBookName(CBitcoinAddress(rec->address.toStdString()), value.toString().toStdString()); break; case Address: - // Refuse to set invalid address - if(!validateAddress(value.toString())) + // Do nothing, if old address == new address + if(CBitcoinAddress(rec->address.toStdString()) == CBitcoinAddress(value.toString().toStdString())) + { + editStatus = NO_CHANGES; return false; - // Double-check that we're not overwriting receiving address - if(rec->type == AddressTableEntry::Sending) + } + // Refuse to set invalid address, set error status and return false + else if(!walletModel->validateAddress(value.toString())) + { + editStatus = INVALID_ADDRESS; + return false; + } + // Check for duplicate addresses to prevent accidental deletion of addresses, if you try + // to paste an existing address over another address (with a different label) + else if(wallet->mapAddressBook.count(CBitcoinAddress(value.toString().toStdString()))) + { + editStatus = DUPLICATE_ADDRESS; + return false; + } + // Double-check that we're not overwriting a receiving address + else if(rec->type == AddressTableEntry::Sending) { - CRITICAL_BLOCK(wallet->cs_mapAddressBook) { + LOCK(wallet->cs_wallet); // Remove old entry - wallet->DelAddressBookName(rec->address.toStdString()); + wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString())); // Add new entry with new address - wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); + wallet->SetAddressBookName(CBitcoinAddress(value.toString().toStdString()), rec->label.toStdString()); } - - rec->address = value.toString(); } break; } - emit dataChanged(index, index); - return true; } return false; @@ -196,7 +281,7 @@ QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } -Qt::ItemFlags AddressTableModel::flags(const QModelIndex & index) const +Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const { if(!index.isValid()) return 0; @@ -213,7 +298,7 @@ Qt::ItemFlags AddressTableModel::flags(const QModelIndex & index) const return retval; } -QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const +QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); AddressTableEntry *data = priv->index(row); @@ -227,12 +312,10 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & pa } } -void AddressTableModel::updateList() +void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status) { - // Update internal model from Bitcoin core - beginResetModel(); - priv->refreshAddressTable(); - endResetModel(); + // Update address book model from Bitcoin core + priv->updateEntry(address, label, isMine, status); } QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) @@ -240,35 +323,57 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); + editStatus = OK; + if(type == Send) { - // Check for duplicate - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + if(!walletModel->validateAddress(address)) + { + editStatus = INVALID_ADDRESS; + return QString(); + } + // Check for duplicate addresses { - if(wallet->mapAddressBook.count(strAddress)) + LOCK(wallet->cs_wallet); + if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress))) { + editStatus = DUPLICATE_ADDRESS; return QString(); } } } else if(type == Receive) { - // Generate a new address to associate with given label, optionally - // set as default receiving address. - strAddress = PubKeyToAddress(wallet->GetOrReuseKeyFromPool()); + // Generate a new address to associate with given label + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet failed or was cancelled + editStatus = WALLET_UNLOCK_FAILURE; + return QString(); + } + CPubKey newKey; + if(!wallet->GetKeyFromPool(newKey, true)) + { + editStatus = KEY_GENERATION_FAILURE; + return QString(); + } + strAddress = CBitcoinAddress(newKey.GetID()).ToString(); } else { return QString(); } - // Add entry and update list - CRITICAL_BLOCK(wallet->cs_mapAddressBook) - wallet->SetAddressBookName(strAddress, strLabel); - updateList(); + + // Add entry + { + LOCK(wallet->cs_wallet); + wallet->SetAddressBookName(CBitcoinAddress(strAddress), strLabel); + } return QString::fromStdString(strAddress); } -bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent) +bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); AddressTableEntry *rec = priv->index(row); @@ -278,33 +383,21 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex & paren // Also refuse to remove receiving addresses. return false; } - CRITICAL_BLOCK(wallet->cs_mapAddressBook) { - wallet->DelAddressBookName(rec->address.toStdString()); + LOCK(wallet->cs_wallet); + wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString())); } - updateList(); return true; } -void AddressTableModel::update() -{ - -} - -bool AddressTableModel::validateAddress(const QString &address) -{ - uint160 hash160 = 0; - - return AddressToHash160(address.toStdString(), hash160); -} - /* Look up label for address in address book, if not found return empty string. */ QString AddressTableModel::labelForAddress(const QString &address) const { - CRITICAL_BLOCK(wallet->cs_mapAddressBook) { - std::map::iterator mi = wallet->mapAddressBook.find(address.toStdString()); + LOCK(wallet->cs_wallet); + CBitcoinAddress address_parsed(address.toStdString()); + std::map::iterator mi = wallet->mapAddressBook.find(address_parsed); if (mi != wallet->mapAddressBook.end()) { return QString::fromStdString(mi->second); @@ -327,3 +420,7 @@ int AddressTableModel::lookupAddress(const QString &address) const } } +void AddressTableModel::emitDataChanged(int idx) +{ + emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex())); +}