mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-06-17 12:21:17 +02:00
This number was still tracked even though it's shown nowhere in the UI anymore. It was originally removed because it didn't match the actual number of records in the view (which contains outputs, not transactions) thus was confusing people.
646 lines
21 KiB
C++
646 lines
21 KiB
C++
// Copyright (c) 2011-2014 The Bitcoin developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "walletmodel.h"
|
|
|
|
#include "addresstablemodel.h"
|
|
#include "guiconstants.h"
|
|
#include "recentrequeststablemodel.h"
|
|
#include "transactiontablemodel.h"
|
|
|
|
#include "base58.h"
|
|
#include "db.h"
|
|
#include "keystore.h"
|
|
#include "main.h"
|
|
#include "sync.h"
|
|
#include "ui_interface.h"
|
|
#include "wallet.h"
|
|
#include "walletdb.h" // for BackupWallet
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <QDebug>
|
|
#include <QSet>
|
|
#include <QTimer>
|
|
|
|
using namespace std;
|
|
|
|
WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) :
|
|
QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0),
|
|
transactionTableModel(0),
|
|
recentRequestsTableModel(0),
|
|
cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
|
|
cachedEncryptionStatus(Unencrypted),
|
|
cachedNumBlocks(0)
|
|
{
|
|
fProcessingQueuedTransactions = false;
|
|
|
|
addressTableModel = new AddressTableModel(wallet, this);
|
|
transactionTableModel = new TransactionTableModel(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();
|
|
}
|
|
|
|
WalletModel::~WalletModel()
|
|
{
|
|
unsubscribeFromCoreSignals();
|
|
}
|
|
|
|
qint64 WalletModel::getBalance(const CCoinControl *coinControl) const
|
|
{
|
|
if (coinControl)
|
|
{
|
|
qint64 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
|
|
{
|
|
return wallet->GetUnconfirmedBalance();
|
|
}
|
|
|
|
qint64 WalletModel::getImmatureBalance() const
|
|
{
|
|
return wallet->GetImmatureBalance();
|
|
}
|
|
|
|
qint64 WalletModel::getWatchBalance() const
|
|
{
|
|
return wallet->GetWatchOnlyBalance();
|
|
}
|
|
|
|
qint64 WalletModel::getWatchUnconfirmedBalance() const
|
|
{
|
|
return wallet->GetUnconfirmedWatchOnlyBalance();
|
|
}
|
|
|
|
qint64 WalletModel::getWatchImmatureBalance() const
|
|
{
|
|
return wallet->GetImmatureWatchOnlyBalance();
|
|
}
|
|
|
|
void WalletModel::updateStatus()
|
|
{
|
|
EncryptionStatus newEncryptionStatus = getEncryptionStatus();
|
|
|
|
if(cachedEncryptionStatus != newEncryptionStatus)
|
|
emit encryptionStatusChanged(newEncryptionStatus);
|
|
}
|
|
|
|
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(chainActive.Height() != cachedNumBlocks)
|
|
{
|
|
// Balance and number of transactions might have changed
|
|
cachedNumBlocks = chainActive.Height();
|
|
|
|
checkBalanceChanged();
|
|
if(transactionTableModel)
|
|
transactionTableModel->updateConfirmations();
|
|
}
|
|
}
|
|
|
|
void WalletModel::checkBalanceChanged()
|
|
{
|
|
qint64 newBalance = getBalance();
|
|
qint64 newUnconfirmedBalance = getUnconfirmedBalance();
|
|
qint64 newImmatureBalance = getImmatureBalance();
|
|
qint64 newWatchOnlyBalance = getWatchBalance();
|
|
qint64 newWatchUnconfBalance = getWatchUnconfirmedBalance();
|
|
qint64 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;
|
|
emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance,
|
|
newWatchOnlyBalance, newWatchUnconfBalance, newWatchImmatureBalance);
|
|
}
|
|
}
|
|
|
|
void WalletModel::updateTransaction(const QString &hash, int status)
|
|
{
|
|
if(transactionTableModel)
|
|
transactionTableModel->updateTransaction(hash, status);
|
|
|
|
// Balance and number of transactions might have changed
|
|
checkBalanceChanged();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
bool WalletModel::validateAddress(const QString &address)
|
|
{
|
|
CBitcoinAddress addressParsed(address.toStdString());
|
|
return addressParsed.IsValid();
|
|
}
|
|
|
|
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl)
|
|
{
|
|
qint64 total = 0;
|
|
QList<SendCoinsRecipient> recipients = transaction.getRecipients();
|
|
std::vector<std::pair<CScript, int64_t> > 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)
|
|
{
|
|
if (rcp.paymentRequest.IsInitialized())
|
|
{ // PaymentRequest...
|
|
int64_t 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());
|
|
vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, out.amount()));
|
|
}
|
|
if (subtotal <= 0)
|
|
{
|
|
return InvalidAmount;
|
|
}
|
|
total += subtotal;
|
|
}
|
|
else
|
|
{ // User-entered bitcoin address / amount:
|
|
if(!validateAddress(rcp.address))
|
|
{
|
|
return InvalidAddress;
|
|
}
|
|
if(rcp.amount <= 0)
|
|
{
|
|
return InvalidAmount;
|
|
}
|
|
setAddress.insert(rcp.address);
|
|
++nAddresses;
|
|
|
|
CScript scriptPubKey;
|
|
scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get());
|
|
vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, rcp.amount));
|
|
|
|
total += rcp.amount;
|
|
}
|
|
}
|
|
if(setAddress.size() != nAddresses)
|
|
{
|
|
return DuplicateAddress;
|
|
}
|
|
|
|
qint64 nBalance = getBalance(coinControl);
|
|
|
|
if(total > nBalance)
|
|
{
|
|
return AmountExceedsBalance;
|
|
}
|
|
|
|
{
|
|
LOCK2(cs_main, wallet->cs_wallet);
|
|
|
|
transaction.newPossibleKeyChange(wallet);
|
|
int64_t nFeeRequired = 0;
|
|
std::string strFailReason;
|
|
|
|
CWalletTx *newTx = transaction.getTransaction();
|
|
CReserveKey *keyChange = transaction.getPossibleKeyChange();
|
|
bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason, coinControl);
|
|
transaction.setTransactionFee(nFeeRequired);
|
|
|
|
if(!fCreated)
|
|
{
|
|
if((total + nFeeRequired) > nBalance)
|
|
{
|
|
return SendCoinsReturn(AmountWithFeeExceedsBalance);
|
|
}
|
|
emit message(tr("Send Coins"), QString::fromStdString(strFailReason),
|
|
CClientUIInterface::MSG_ERROR);
|
|
return TransactionCreationFailed;
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
// Store PaymentRequests in wtx.vOrderForm in wallet.
|
|
foreach(const SendCoinsRecipient &rcp, transaction.getRecipients())
|
|
{
|
|
if (rcp.paymentRequest.IsInitialized())
|
|
{
|
|
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()));
|
|
}
|
|
|
|
CReserveKey *keyChange = transaction.getPossibleKeyChange();
|
|
if(!wallet->CommitTransaction(*newTx, *keyChange))
|
|
return TransactionCommitFailed;
|
|
|
|
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,
|
|
// and emit coinsSent signal for each recipient
|
|
foreach(const SendCoinsRecipient &rcp, transaction.getRecipients())
|
|
{
|
|
// Don't touch the address book when we have a payment request
|
|
if (!rcp.paymentRequest.IsInitialized())
|
|
{
|
|
std::string strAddress = rcp.address.toStdString();
|
|
CTxDestination dest = CBitcoinAddress(strAddress).Get();
|
|
std::string strLabel = rcp.label.toStdString();
|
|
{
|
|
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
|
|
}
|
|
}
|
|
}
|
|
emit coinsSent(wallet, rcp, transaction_array);
|
|
}
|
|
|
|
return SendCoinsReturn(OK);
|
|
}
|
|
|
|
OptionsModel *WalletModel::getOptionsModel()
|
|
{
|
|
return optionsModel;
|
|
}
|
|
|
|
AddressTableModel *WalletModel::getAddressTableModel()
|
|
{
|
|
return addressTableModel;
|
|
}
|
|
|
|
TransactionTableModel *WalletModel::getTransactionTableModel()
|
|
{
|
|
return transactionTableModel;
|
|
}
|
|
|
|
RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel()
|
|
{
|
|
return recentRequestsTableModel;
|
|
}
|
|
|
|
WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
|
|
{
|
|
if(!wallet->IsCrypted())
|
|
{
|
|
return Unencrypted;
|
|
}
|
|
else if(wallet->IsLocked())
|
|
{
|
|
return Locked;
|
|
}
|
|
else
|
|
{
|
|
return Unlocked;
|
|
}
|
|
}
|
|
|
|
bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase)
|
|
{
|
|
if(encrypted)
|
|
{
|
|
// Encrypt
|
|
return wallet->EncryptWallet(passphrase);
|
|
}
|
|
else
|
|
{
|
|
// Decrypt -- TODO; not supported yet
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
|
|
{
|
|
if(locked)
|
|
{
|
|
// Lock
|
|
return wallet->Lock();
|
|
}
|
|
else
|
|
{
|
|
// Unlock
|
|
return wallet->Unlock(passPhrase);
|
|
}
|
|
}
|
|
|
|
bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass)
|
|
{
|
|
bool retval;
|
|
{
|
|
LOCK(wallet->cs_wallet);
|
|
wallet->Lock(); // Make sure wallet is locked before attempting pass change
|
|
retval = wallet->ChangeWalletPassphrase(oldPass, newPass);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
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));
|
|
}
|
|
|
|
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));
|
|
|
|
if (nProgress == 0)
|
|
fQueueNotifications = true;
|
|
|
|
if (nProgress == 100)
|
|
{
|
|
fQueueNotifications = false;
|
|
if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons
|
|
QMetaObject::invokeMethod(walletmodel, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
|
|
for (unsigned int i = 0; i < vQueueNotifications.size(); ++i)
|
|
{
|
|
if (vQueueNotifications.size() - i <= 10)
|
|
QMetaObject::invokeMethod(walletmodel, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
|
|
|
|
NotifyTransactionChanged(walletmodel, NULL, vQueueNotifications[i].first, vQueueNotifications[i].second);
|
|
}
|
|
std::vector<std::pair<uint256, ChangeType> >().swap(vQueueNotifications); // clear
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
// WalletModel::UnlockContext implementation
|
|
WalletModel::UnlockContext WalletModel::requestUnlock()
|
|
{
|
|
bool was_locked = getEncryptionStatus() == Locked;
|
|
if(was_locked)
|
|
{
|
|
// Request UI to unlock wallet
|
|
emit requireUnlock();
|
|
}
|
|
// If wallet is still locked, unlock was failed or cancelled, mark context as invalid
|
|
bool valid = getEncryptionStatus() != Locked;
|
|
|
|
return UnlockContext(this, valid, was_locked);
|
|
}
|
|
|
|
WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock):
|
|
wallet(wallet),
|
|
valid(valid),
|
|
relock(relock)
|
|
{
|
|
}
|
|
|
|
WalletModel::UnlockContext::~UnlockContext()
|
|
{
|
|
if(valid && relock)
|
|
{
|
|
wallet->setWalletLocked(true);
|
|
}
|
|
}
|
|
|
|
void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
|
|
{
|
|
// Transfer context; old object no longer relocks wallet
|
|
*this = rhs;
|
|
rhs.relock = false;
|
|
}
|
|
|
|
bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
|
|
{
|
|
return wallet->GetPubKey(address, vchPubKeyOut);
|
|
}
|
|
|
|
// 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);
|
|
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[CBitcoinAddress(address).ToString().c_str()].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);
|
|
}
|