diff options
Diffstat (limited to 'src/qt/walletmodel.cpp')
| -rw-r--r-- | src/qt/walletmodel.cpp | 558 |
1 files changed, 472 insertions, 86 deletions
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index b9ccb06c0..690ea0811 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -1,69 +1,186 @@ +// Copyright (c) 2011-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "walletmodel.h" -#include "guiconstants.h" -#include "optionsmodel.h" + #include "addresstablemodel.h" +#include "guiconstants.h" +#include "guiutil.h" +#include "paymentserver.h" +#include "recentrequeststablemodel.h" #include "transactiontablemodel.h" +#include "base58.h" +#include "keystore.h" +#include "main.h" +#include "sync.h" #include "ui_interface.h" -#include "wallet.h" -#include "walletdb.h" // for BackupWallet +#include "wallet/wallet.h" +#include "wallet/walletdb.h" // for BackupWallet + +#include <stdint.h> +#include <QDebug> #include <QSet> +#include <QTimer> + +#include <boost/foreach.hpp> -WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : +WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), transactionTableModel(0), - cachedBalance(0), cachedUnconfirmedBalance(0), cachedNumTransactions(0), - cachedEncryptionStatus(Unencrypted) + recentRequestsTableModel(0), + cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), + cachedEncryptionStatus(Unencrypted), + cachedNumBlocks(0) { + fHaveWatchOnly = wallet->HaveWatchOnly(); + fForceCheckBalanceChanged = false; + addressTableModel = new AddressTableModel(wallet, this); - transactionTableModel = new TransactionTableModel(wallet, this); + transactionTableModel = new TransactionTableModel(platformStyle, wallet, this); + recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); + + // This timer will be fired repeatedly to update the balance + pollTimer = new QTimer(this); + connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollBalanceChanged())); + pollTimer->start(MODEL_UPDATE_DELAY); + + subscribeToCoreSignals(); } -qint64 WalletModel::getBalance() const +WalletModel::~WalletModel() { + unsubscribeFromCoreSignals(); +} + +CAmount WalletModel::getBalance(const CCoinControl *coinControl) const +{ + if (coinControl) + { + CAmount nBalance = 0; + std::vector<COutput> vCoins; + wallet->AvailableCoins(vCoins, true, coinControl); + BOOST_FOREACH(const COutput& out, vCoins) + if(out.fSpendable) + nBalance += out.tx->vout[out.i].nValue; + + return nBalance; + } + return wallet->GetBalance(); } -qint64 WalletModel::getUnconfirmedBalance() const +CAmount WalletModel::getUnconfirmedBalance() const { return wallet->GetUnconfirmedBalance(); } -int WalletModel::getNumTransactions() const +CAmount WalletModel::getImmatureBalance() const { - int numTransactions = 0; - { - LOCK(wallet->cs_wallet); - numTransactions = wallet->mapWallet.size(); - } - return numTransactions; + return wallet->GetImmatureBalance(); } -void WalletModel::update() +bool WalletModel::haveWatchOnly() const { - qint64 newBalance = getBalance(); - qint64 newUnconfirmedBalance = getUnconfirmedBalance(); - int newNumTransactions = getNumTransactions(); - EncryptionStatus newEncryptionStatus = getEncryptionStatus(); + return fHaveWatchOnly; +} - if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance) - emit balanceChanged(newBalance, newUnconfirmedBalance); +CAmount WalletModel::getWatchBalance() const +{ + return wallet->GetWatchOnlyBalance(); +} + +CAmount WalletModel::getWatchUnconfirmedBalance() const +{ + return wallet->GetUnconfirmedWatchOnlyBalance(); +} + +CAmount WalletModel::getWatchImmatureBalance() const +{ + return wallet->GetImmatureWatchOnlyBalance(); +} - if(cachedNumTransactions != newNumTransactions) - emit numTransactionsChanged(newNumTransactions); +void WalletModel::updateStatus() +{ + EncryptionStatus newEncryptionStatus = getEncryptionStatus(); if(cachedEncryptionStatus != newEncryptionStatus) - emit encryptionStatusChanged(newEncryptionStatus); + Q_EMIT encryptionStatusChanged(newEncryptionStatus); +} - cachedBalance = newBalance; - cachedUnconfirmedBalance = newUnconfirmedBalance; - cachedNumTransactions = newNumTransactions; +void WalletModel::pollBalanceChanged() +{ + // Get required locks upfront. This avoids the GUI from getting stuck on + // periodical polls if the core is holding the locks for a longer time - + // for example, during a wallet rescan. + TRY_LOCK(cs_main, lockMain); + if(!lockMain) + return; + TRY_LOCK(wallet->cs_wallet, lockWallet); + if(!lockWallet) + return; + + if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks) + { + fForceCheckBalanceChanged = false; + + // Balance and number of transactions might have changed + cachedNumBlocks = chainActive.Height(); + + checkBalanceChanged(); + if(transactionTableModel) + transactionTableModel->updateConfirmations(); + } } -void WalletModel::updateAddressList() +void WalletModel::checkBalanceChanged() { - addressTableModel->update(); + CAmount newBalance = getBalance(); + CAmount newUnconfirmedBalance = getUnconfirmedBalance(); + CAmount newImmatureBalance = getImmatureBalance(); + CAmount newWatchOnlyBalance = 0; + CAmount newWatchUnconfBalance = 0; + CAmount newWatchImmatureBalance = 0; + if (haveWatchOnly()) + { + newWatchOnlyBalance = getWatchBalance(); + newWatchUnconfBalance = getWatchUnconfirmedBalance(); + newWatchImmatureBalance = getWatchImmatureBalance(); + } + + if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance || + cachedWatchOnlyBalance != newWatchOnlyBalance || cachedWatchUnconfBalance != newWatchUnconfBalance || cachedWatchImmatureBalance != newWatchImmatureBalance) + { + cachedBalance = newBalance; + cachedUnconfirmedBalance = newUnconfirmedBalance; + cachedImmatureBalance = newImmatureBalance; + cachedWatchOnlyBalance = newWatchOnlyBalance; + cachedWatchUnconfBalance = newWatchUnconfBalance; + cachedWatchImmatureBalance = newWatchImmatureBalance; + Q_EMIT balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance, + newWatchOnlyBalance, newWatchUnconfBalance, newWatchImmatureBalance); + } +} + +void WalletModel::updateTransaction() +{ + // Balance and number of transactions might have changed + fForceCheckBalanceChanged = true; +} + +void WalletModel::updateAddressBook(const QString &address, const QString &label, + bool isMine, const QString &purpose, int status) +{ + if(addressTableModel) + addressTableModel->updateEntry(address, label, isMine, purpose, status); +} + +void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) +{ + fHaveWatchOnly = fHaveWatchonly; + Q_EMIT notifyWatchonlyChanged(fHaveWatchonly); } bool WalletModel::validateAddress(const QString &address) @@ -72,103 +189,185 @@ bool WalletModel::validateAddress(const QString &address) return addressParsed.IsValid(); } -WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients) +WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl) { - qint64 total = 0; - QSet<QString> setAddress; - QString hex; + CAmount total = 0; + bool fSubtractFeeFromAmount = false; + QList<SendCoinsRecipient> recipients = transaction.getRecipients(); + std::vector<CRecipient> vecSend; if(recipients.empty()) { return OK; } + QSet<QString> setAddress; // Used to detect duplicates + int nAddresses = 0; + // Pre-check input data for validity - foreach(const SendCoinsRecipient &rcp, recipients) + Q_FOREACH(const SendCoinsRecipient &rcp, recipients) { - if(!validateAddress(rcp.address)) - { - return InvalidAddress; + if (rcp.fSubtractFeeFromAmount) + fSubtractFeeFromAmount = true; + + if (rcp.paymentRequest.IsInitialized()) + { // PaymentRequest... + CAmount subtotal = 0; + const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); + for (int i = 0; i < details.outputs_size(); i++) + { + const payments::Output& out = details.outputs(i); + if (out.amount() <= 0) continue; + subtotal += out.amount(); + const unsigned char* scriptStr = (const unsigned char*)out.script().data(); + CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); + CAmount nAmount = out.amount(); + CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount}; + vecSend.push_back(recipient); + } + if (subtotal <= 0) + { + return InvalidAmount; + } + total += subtotal; } - setAddress.insert(rcp.address); + else + { // User-entered bitcoin address / amount: + if(!validateAddress(rcp.address)) + { + return InvalidAddress; + } + if(rcp.amount <= 0) + { + return InvalidAmount; + } + setAddress.insert(rcp.address); + ++nAddresses; - if(rcp.amount <= 0) - { - return InvalidAmount; + CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; + vecSend.push_back(recipient); + + total += rcp.amount; } - total += rcp.amount; } - - if(recipients.size() > setAddress.size()) + if(setAddress.size() != nAddresses) { return DuplicateAddress; } - if(total > getBalance()) - { - return AmountExceedsBalance; - } + CAmount nBalance = getBalance(coinControl); - if((total + nTransactionFee) > getBalance()) + if(total > nBalance) { - return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); + return AmountExceedsBalance; } { LOCK2(cs_main, wallet->cs_wallet); - // Sendmany - std::vector<std::pair<CScript, int64> > vecSend; - foreach(const SendCoinsRecipient &rcp, recipients) - { - CScript scriptPubKey; - scriptPubKey.SetBitcoinAddress(rcp.address.toStdString()); - vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); - } + transaction.newPossibleKeyChange(wallet); - CWalletTx wtx; - CReserveKey keyChange(wallet); - int64 nFeeRequired = 0; - bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); + CAmount nFeeRequired = 0; + int nChangePosRet = -1; + std::string strFailReason; + + CWalletTx *newTx = transaction.getTransaction(); + CReserveKey *keyChange = transaction.getPossibleKeyChange(); + bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl); + transaction.setTransactionFee(nFeeRequired); + if (fSubtractFeeFromAmount && fCreated) + transaction.reassignAmounts(nChangePosRet); if(!fCreated) { - if((total + nFeeRequired) > wallet->GetBalance()) + if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { - return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); + return SendCoinsReturn(AmountWithFeeExceedsBalance); } + Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason), + CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } - if(!ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString())) + + // reject absurdly high fee. (This can never happen because the + // wallet caps the fee at maxTxFee. This merely serves as a + // belt-and-suspenders check) + if (nFeeRequired > maxTxFee) + return AbsurdFee; + } + + return SendCoinsReturn(OK); +} + +WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction) +{ + QByteArray transaction_array; /* store serialized transaction */ + + { + LOCK2(cs_main, wallet->cs_wallet); + CWalletTx *newTx = transaction.getTransaction(); + + Q_FOREACH(const SendCoinsRecipient &rcp, transaction.getRecipients()) { - return Aborted; + if (rcp.paymentRequest.IsInitialized()) + { + // Make sure any payment requests involved are still valid. + if (PaymentServer::verifyExpired(rcp.paymentRequest.getDetails())) { + return PaymentRequestExpired; + } + + // Store PaymentRequests in wtx.vOrderForm in wallet. + std::string key("PaymentRequest"); + std::string value; + rcp.paymentRequest.SerializeToString(&value); + newTx->vOrderForm.push_back(make_pair(key, value)); + } + else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example) + newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString())); } - if(!wallet->CommitTransaction(wtx, keyChange)) - { + + CReserveKey *keyChange = transaction.getPossibleKeyChange(); + if(!wallet->CommitTransaction(*newTx, *keyChange)) return TransactionCommitFailed; - } - hex = QString::fromStdString(wtx.GetHash().GetHex()); + + CTransaction* t = (CTransaction*)newTx; + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << *t; + transaction_array.append(&(ssTx[0]), ssTx.size()); } - // Add addresses / update labels that we've sent to to the address book - foreach(const SendCoinsRecipient &rcp, recipients) + // Add addresses / update labels that we've sent to to the address book, + // and emit coinsSent signal for each recipient + Q_FOREACH(const SendCoinsRecipient &rcp, transaction.getRecipients()) { - std::string strAddress = rcp.address.toStdString(); - std::string strLabel = rcp.label.toStdString(); + // Don't touch the address book when we have a payment request + if (!rcp.paymentRequest.IsInitialized()) { - LOCK(wallet->cs_wallet); - - std::map<CBitcoinAddress, std::string>::iterator mi = wallet->mapAddressBook.find(strAddress); - - // Check if we have a new address or an updated label - if (mi == wallet->mapAddressBook.end() || mi->second != strLabel) + std::string strAddress = rcp.address.toStdString(); + CTxDestination dest = CBitcoinAddress(strAddress).Get(); + std::string strLabel = rcp.label.toStdString(); { - wallet->SetAddressBookName(strAddress, strLabel); + LOCK(wallet->cs_wallet); + + std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); + + // Check if we have a new address or an updated label + if (mi == wallet->mapAddressBook.end()) + { + wallet->SetAddressBook(dest, strLabel, "send"); + } + else if (mi->second.name != strLabel) + { + wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose + } } } + Q_EMIT coinsSent(wallet, rcp, transaction_array); } + checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits - return SendCoinsReturn(OK, 0, hex); + return SendCoinsReturn(OK); } OptionsModel *WalletModel::getOptionsModel() @@ -186,6 +385,11 @@ TransactionTableModel *WalletModel::getTransactionTableModel() return transactionTableModel; } +RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel() +{ + return recentRequestsTableModel; +} + WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { if(!wallet->IsCrypted()) @@ -246,6 +450,72 @@ bool WalletModel::backupWallet(const QString &filename) return BackupWallet(*wallet, filename.toLocal8Bit().data()); } +// Handlers for core signals +static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet) +{ + qDebug() << "NotifyKeyStoreStatusChanged"; + QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection); +} + +static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, + const CTxDestination &address, const std::string &label, bool isMine, + const std::string &purpose, ChangeType status) +{ + QString strAddress = QString::fromStdString(CBitcoinAddress(address).ToString()); + QString strLabel = QString::fromStdString(label); + QString strPurpose = QString::fromStdString(purpose); + + qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status); + QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection, + Q_ARG(QString, strAddress), + Q_ARG(QString, strLabel), + Q_ARG(bool, isMine), + Q_ARG(QString, strPurpose), + Q_ARG(int, status)); +} + +static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType 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) +{ + // emits signal "showProgress" + QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(title)), + 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 + wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); + wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); + wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); + wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); + wallet->NotifyWatchonlyChanged.connect(boost::bind(NotifyWatchonlyChanged, this, _1)); +} + +void WalletModel::unsubscribeFromCoreSignals() +{ + // Disconnect signals from wallet + wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); + wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); + wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); + wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); + wallet->NotifyWatchonlyChanged.disconnect(boost::bind(NotifyWatchonlyChanged, this, _1)); +} + // WalletModel::UnlockContext implementation WalletModel::UnlockContext WalletModel::requestUnlock() { @@ -253,7 +523,7 @@ WalletModel::UnlockContext WalletModel::requestUnlock() if(was_locked) { // Request UI to unlock wallet - emit requireUnlock(); + Q_EMIT requireUnlock(); } // If wallet is still locked, unlock was failed or cancelled, mark context as invalid bool valid = getEncryptionStatus() != Locked; @@ -282,3 +552,119 @@ void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs) *this = rhs; rhs.relock = false; } + +bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const +{ + return wallet->GetPubKey(address, vchPubKeyOut); +} + +bool WalletModel::havePrivKey(const CKeyID &address) const +{ + return wallet->HaveKey(address); +} + +// returns a list of COutputs from COutPoints +void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs) +{ + LOCK2(cs_main, wallet->cs_wallet); + BOOST_FOREACH(const COutPoint& outpoint, vOutpoints) + { + if (!wallet->mapWallet.count(outpoint.hash)) continue; + int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); + if (nDepth < 0) continue; + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); + vOutputs.push_back(out); + } +} + +bool WalletModel::isSpent(const COutPoint& outpoint) const +{ + LOCK2(cs_main, wallet->cs_wallet); + return wallet->IsSpent(outpoint.hash, outpoint.n); +} + +// AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address) +void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const +{ + std::vector<COutput> vCoins; + wallet->AvailableCoins(vCoins); + + LOCK2(cs_main, wallet->cs_wallet); // ListLockedCoins, mapWallet + std::vector<COutPoint> vLockedCoins; + wallet->ListLockedCoins(vLockedCoins); + + // add locked coins + BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins) + { + if (!wallet->mapWallet.count(outpoint.hash)) continue; + int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); + if (nDepth < 0) continue; + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); + if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == ISMINE_SPENDABLE) + vCoins.push_back(out); + } + + BOOST_FOREACH(const COutput& out, vCoins) + { + COutput cout = out; + + while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) + { + if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; + cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true); + } + + CTxDestination address; + if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) + continue; + mapCoins[QString::fromStdString(CBitcoinAddress(address).ToString())].push_back(out); + } +} + +bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const +{ + LOCK2(cs_main, wallet->cs_wallet); + return wallet->IsLockedCoin(hash, n); +} + +void WalletModel::lockCoin(COutPoint& output) +{ + LOCK2(cs_main, wallet->cs_wallet); + wallet->LockCoin(output); +} + +void WalletModel::unlockCoin(COutPoint& output) +{ + LOCK2(cs_main, wallet->cs_wallet); + wallet->UnlockCoin(output); +} + +void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts) +{ + LOCK2(cs_main, wallet->cs_wallet); + wallet->ListLockedCoins(vOutpts); +} + +void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests) +{ + LOCK(wallet->cs_wallet); + BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook) + BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item2, item.second.destdata) + if (item2.first.size() > 2 && item2.first.substr(0,2) == "rr") // receive request + vReceiveRequests.push_back(item2.second); +} + +bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest) +{ + CTxDestination dest = CBitcoinAddress(sAddress).Get(); + + std::stringstream ss; + ss << nId; + std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata + + LOCK(wallet->cs_wallet); + if (sRequest.empty()) + return wallet->EraseDestData(dest, key); + else + return wallet->AddDestData(dest, key, sRequest); +} |