Changeset View
Changeset View
Standalone View
Standalone View
src/qt/walletmodel.cpp
// Copyright (c) 2011-2016 The Bitcoin Core developers | // Copyright (c) 2011-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "walletmodel.h" | #include <qt/walletmodel.h> | ||||
#include "addresstablemodel.h" | #include <chain.h> | ||||
#include "consensus/validation.h" | #include <config.h> | ||||
#include "guiconstants.h" | #include <consensus/validation.h> | ||||
#include "guiutil.h" | #include <dstencode.h> | ||||
#include "paymentserver.h" | #include <interfaces/handler.h> | ||||
#include "recentrequeststablemodel.h" | #include <interfaces/node.h> | ||||
#include "transactiontablemodel.h" | #include <keystore.h> | ||||
#include <net.h> // for g_connman | |||||
#include "chain.h" | #include <qt/addresstablemodel.h> | ||||
#include "config.h" | #include <qt/guiconstants.h> | ||||
#include "dstencode.h" | #include <qt/guiutil.h> | ||||
#include "keystore.h" | #include <qt/paymentserver.h> | ||||
#include "net.h" // for g_connman | #include <qt/recentrequeststablemodel.h> | ||||
#include "sync.h" | #include <qt/transactiontablemodel.h> | ||||
#include "ui_interface.h" | #include <sync.h> | ||||
#include "util.h" // for GetBoolArg | #include <ui_interface.h> | ||||
#include "validation.h" | #include <util.h> // for GetBoolArg | ||||
#include "wallet/coincontrol.h" | #include <validation.h> | ||||
#include "wallet/wallet.h" | #include <wallet/coincontrol.h> | ||||
#include "wallet/walletdb.h" // for BackupWallet | #include <wallet/wallet.h> | ||||
#include <wallet/walletdb.h> // for BackupWallet | |||||
#include <cstdint> | |||||
#include <QDebug> | #include <QDebug> | ||||
#include <QSet> | #include <QSet> | ||||
#include <QTimer> | #include <QTimer> | ||||
WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, | #include <cstdint> | ||||
WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, | |||||
interfaces::Node &node, | |||||
const PlatformStyle *platformStyle, CWallet *_wallet, | |||||
OptionsModel *_optionsModel, QObject *parent) | OptionsModel *_optionsModel, QObject *parent) | ||||
: QObject(parent), wallet(_wallet), optionsModel(_optionsModel), | : QObject(parent), m_wallet(std::move(wallet)), m_node(node), | ||||
addressTableModel(0), transactionTableModel(0), | cwallet(_wallet), optionsModel(_optionsModel), addressTableModel(0), | ||||
recentRequestsTableModel(0), cachedBalance(), cachedUnconfirmedBalance(), | transactionTableModel(0), recentRequestsTableModel(0), | ||||
cachedImmatureBalance(), cachedEncryptionStatus(Unencrypted), | cachedEncryptionStatus(Unencrypted), cachedNumBlocks(0) { | ||||
cachedNumBlocks(0) { | fHaveWatchOnly = m_wallet->haveWatchOnly(); | ||||
fHaveWatchOnly = wallet->HaveWatchOnly(); | |||||
fForceCheckBalanceChanged = false; | fForceCheckBalanceChanged = false; | ||||
addressTableModel = new AddressTableModel(wallet, this); | addressTableModel = new AddressTableModel(cwallet, this); | ||||
transactionTableModel = | transactionTableModel = | ||||
new TransactionTableModel(platformStyle, wallet, this); | new TransactionTableModel(platformStyle, cwallet, this); | ||||
recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); | recentRequestsTableModel = new RecentRequestsTableModel(cwallet, this); | ||||
// This timer will be fired repeatedly to update the balance | // This timer will be fired repeatedly to update the balance | ||||
pollTimer = new QTimer(this); | pollTimer = new QTimer(this); | ||||
connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollBalanceChanged())); | connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollBalanceChanged())); | ||||
pollTimer->start(MODEL_UPDATE_DELAY); | pollTimer->start(MODEL_UPDATE_DELAY); | ||||
subscribeToCoreSignals(); | subscribeToCoreSignals(); | ||||
} | } | ||||
WalletModel::~WalletModel() { | WalletModel::~WalletModel() { | ||||
unsubscribeFromCoreSignals(); | unsubscribeFromCoreSignals(); | ||||
} | } | ||||
Amount WalletModel::getBalance(const CCoinControl *coinControl) const { | |||||
if (coinControl) { | |||||
return wallet->GetAvailableBalance(coinControl); | |||||
} | |||||
return wallet->GetBalance(); | |||||
} | |||||
Amount WalletModel::getUnconfirmedBalance() const { | |||||
return wallet->GetUnconfirmedBalance(); | |||||
} | |||||
Amount WalletModel::getImmatureBalance() const { | |||||
return wallet->GetImmatureBalance(); | |||||
} | |||||
bool WalletModel::haveWatchOnly() const { | |||||
return fHaveWatchOnly; | |||||
} | |||||
Amount WalletModel::getWatchBalance() const { | |||||
return wallet->GetWatchOnlyBalance(); | |||||
} | |||||
Amount WalletModel::getWatchUnconfirmedBalance() const { | |||||
return wallet->GetUnconfirmedWatchOnlyBalance(); | |||||
} | |||||
Amount WalletModel::getWatchImmatureBalance() const { | |||||
return wallet->GetImmatureWatchOnlyBalance(); | |||||
} | |||||
void WalletModel::updateStatus() { | void WalletModel::updateStatus() { | ||||
EncryptionStatus newEncryptionStatus = getEncryptionStatus(); | EncryptionStatus newEncryptionStatus = getEncryptionStatus(); | ||||
if (cachedEncryptionStatus != newEncryptionStatus) { | if (cachedEncryptionStatus != newEncryptionStatus) { | ||||
Q_EMIT encryptionStatusChanged(); | Q_EMIT encryptionStatusChanged(); | ||||
} | } | ||||
} | } | ||||
void WalletModel::pollBalanceChanged() { | void WalletModel::pollBalanceChanged() { | ||||
// Get required locks upfront. This avoids the GUI from getting stuck on | // Try to get balances and return early if locks can't be acquired. This | ||||
// periodical polls if the core is holding the locks for a longer time - for | // avoids the GUI from getting stuck on periodical polls if the core is | ||||
// example, during a wallet rescan. | // holding the locks for a longer time - for example, during a wallet | ||||
TRY_LOCK(cs_main, lockMain); | // rescan. | ||||
if (!lockMain) { | interfaces::WalletBalances new_balances; | ||||
return; | int numBlocks = -1; | ||||
} | if (!m_wallet->tryGetBalances(new_balances, numBlocks)) { | ||||
TRY_LOCK(wallet->cs_wallet, lockWallet); | |||||
if (!lockWallet) { | |||||
return; | return; | ||||
} | } | ||||
if (fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks) { | if (fForceCheckBalanceChanged || m_node.getNumBlocks() != cachedNumBlocks) { | ||||
fForceCheckBalanceChanged = false; | fForceCheckBalanceChanged = false; | ||||
// Balance and number of transactions might have changed | // Balance and number of transactions might have changed | ||||
cachedNumBlocks = chainActive.Height(); | cachedNumBlocks = m_node.getNumBlocks(); | ||||
checkBalanceChanged(); | checkBalanceChanged(new_balances); | ||||
if (transactionTableModel) { | if (transactionTableModel) { | ||||
transactionTableModel->updateConfirmations(); | transactionTableModel->updateConfirmations(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void WalletModel::checkBalanceChanged() { | void WalletModel::checkBalanceChanged( | ||||
Amount newBalance(getBalance()); | const interfaces::WalletBalances &new_balances) { | ||||
Amount newUnconfirmedBalance(getUnconfirmedBalance()); | if (new_balances.balanceChanged(m_cached_balances)) { | ||||
Amount newImmatureBalance(getImmatureBalance()); | m_cached_balances = new_balances; | ||||
Amount newWatchOnlyBalance = Amount::zero(); | Q_EMIT balanceChanged( | ||||
Amount newWatchUnconfBalance = Amount::zero(); | new_balances.balance, new_balances.unconfirmed_balance, | ||||
Amount newWatchImmatureBalance = Amount::zero(); | new_balances.immature_balance, new_balances.watch_only_balance, | ||||
if (haveWatchOnly()) { | new_balances.unconfirmed_watch_only_balance, | ||||
newWatchOnlyBalance = getWatchBalance(); | new_balances.immature_watch_only_balance); | ||||
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() { | void WalletModel::updateTransaction() { | ||||
// Balance and number of transactions might have changed | // Balance and number of transactions might have changed | ||||
fForceCheckBalanceChanged = true; | fForceCheckBalanceChanged = true; | ||||
} | } | ||||
void WalletModel::updateAddressBook(const QString &address, | void WalletModel::updateAddressBook(const QString &address, | ||||
const QString &label, bool isMine, | const QString &label, bool isMine, | ||||
const QString &purpose, int status) { | const QString &purpose, int status) { | ||||
if (addressTableModel) { | if (addressTableModel) { | ||||
addressTableModel->updateEntry(address, label, isMine, purpose, status); | addressTableModel->updateEntry(address, label, isMine, purpose, status); | ||||
} | } | ||||
} | } | ||||
void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) { | void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) { | ||||
fHaveWatchOnly = fHaveWatchonly; | fHaveWatchOnly = fHaveWatchonly; | ||||
Q_EMIT notifyWatchonlyChanged(fHaveWatchonly); | Q_EMIT notifyWatchonlyChanged(fHaveWatchonly); | ||||
} | } | ||||
bool WalletModel::validateAddress(const QString &address) { | bool WalletModel::validateAddress(const QString &address) { | ||||
return IsValidDestinationString(address.toStdString(), | return IsValidDestinationString(address.toStdString(), getChainParams()); | ||||
GetConfig().GetChainParams()); | |||||
} | } | ||||
WalletModel::SendCoinsReturn | WalletModel::SendCoinsReturn | ||||
WalletModel::prepareTransaction(WalletModelTransaction &transaction, | WalletModel::prepareTransaction(WalletModelTransaction &transaction, | ||||
const CCoinControl &coinControl) { | const CCoinControl &coinControl) { | ||||
Amount total = Amount::zero(); | Amount total = Amount::zero(); | ||||
bool fSubtractFeeFromAmount = false; | bool fSubtractFeeFromAmount = false; | ||||
QList<SendCoinsRecipient> recipients = transaction.getRecipients(); | QList<SendCoinsRecipient> recipients = transaction.getRecipients(); | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | for (const SendCoinsRecipient &rcp : recipients) { | ||||
return InvalidAddress; | return InvalidAddress; | ||||
} | } | ||||
if (rcp.amount <= Amount::zero()) { | if (rcp.amount <= Amount::zero()) { | ||||
return InvalidAmount; | return InvalidAmount; | ||||
} | } | ||||
setAddress.insert(rcp.address); | setAddress.insert(rcp.address); | ||||
++nAddresses; | ++nAddresses; | ||||
CScript scriptPubKey = GetScriptForDestination(DecodeDestination( | CScript scriptPubKey = GetScriptForDestination( | ||||
rcp.address.toStdString(), wallet->chainParams)); | DecodeDestination(rcp.address.toStdString(), getChainParams())); | ||||
CRecipient recipient = {scriptPubKey, Amount(rcp.amount), | CRecipient recipient = {scriptPubKey, Amount(rcp.amount), | ||||
rcp.fSubtractFeeFromAmount}; | rcp.fSubtractFeeFromAmount}; | ||||
vecSend.push_back(recipient); | vecSend.push_back(recipient); | ||||
total += rcp.amount; | total += rcp.amount; | ||||
} | } | ||||
} | } | ||||
if (setAddress.size() != nAddresses) { | if (setAddress.size() != nAddresses) { | ||||
return DuplicateAddress; | return DuplicateAddress; | ||||
} | } | ||||
Amount nBalance = getBalance(&coinControl); | Amount nBalance = m_wallet->getAvailableBalance(coinControl); | ||||
if (total > nBalance) { | if (total > nBalance) { | ||||
return AmountExceedsBalance; | return AmountExceedsBalance; | ||||
} | } | ||||
{ | |||||
LOCK2(cs_main, wallet->cs_wallet); | |||||
transaction.newPossibleKeyChange(wallet); | |||||
Amount nFeeRequired = Amount::zero(); | Amount nFeeRequired = Amount::zero(); | ||||
int nChangePosRet = -1; | int nChangePosRet = -1; | ||||
std::string strFailReason; | std::string strFailReason; | ||||
CTransactionRef &newTx = transaction.getTransaction(); | auto &newTx = transaction.getWtx(); | ||||
CReserveKey *keyChange = transaction.getPossibleKeyChange(); | newTx = | ||||
bool fCreated = wallet->CreateTransaction(vecSend, newTx, *keyChange, | m_wallet->createTransaction(vecSend, coinControl, true /* sign */, | ||||
nFeeRequired, nChangePosRet, | nChangePosRet, nFeeRequired, strFailReason); | ||||
strFailReason, coinControl); | |||||
transaction.setTransactionFee(nFeeRequired); | transaction.setTransactionFee(nFeeRequired); | ||||
if (fSubtractFeeFromAmount && fCreated) { | if (fSubtractFeeFromAmount && newTx) { | ||||
transaction.reassignAmounts(nChangePosRet); | transaction.reassignAmounts(nChangePosRet); | ||||
} | } | ||||
if (!fCreated) { | if (!newTx) { | ||||
if (!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { | if (!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { | ||||
return SendCoinsReturn(AmountWithFeeExceedsBalance); | return SendCoinsReturn(AmountWithFeeExceedsBalance); | ||||
} | } | ||||
Q_EMIT message(tr("Send Coins"), | Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason), | ||||
QString::fromStdString(strFailReason), | |||||
CClientUIInterface::MSG_ERROR); | CClientUIInterface::MSG_ERROR); | ||||
return TransactionCreationFailed; | return TransactionCreationFailed; | ||||
} | } | ||||
// reject absurdly high fee. (This can never happen because the wallet | // reject absurdly high fee. (This can never happen because the | ||||
// caps the fee at maxTxFee. This merely serves as a belt-and-suspenders | // wallet caps the fee at maxTxFee. This merely serves as a | ||||
// check) | // belt-and-suspenders check) | ||||
if (nFeeRequired > Amount(maxTxFee)) { | if (nFeeRequired > m_node.getMaxTxFee()) { | ||||
return AbsurdFee; | return AbsurdFee; | ||||
} | } | ||||
} | |||||
return SendCoinsReturn(OK); | return SendCoinsReturn(OK); | ||||
} | } | ||||
WalletModel::SendCoinsReturn | WalletModel::SendCoinsReturn | ||||
WalletModel::sendCoins(WalletModelTransaction &transaction) { | WalletModel::sendCoins(WalletModelTransaction &transaction) { | ||||
/* store serialized transaction */ | /* store serialized transaction */ | ||||
QByteArray transaction_array; | QByteArray transaction_array; | ||||
{ | |||||
LOCK2(cs_main, wallet->cs_wallet); | |||||
std::vector<std::pair<std::string, std::string>> vOrderForm; | std::vector<std::pair<std::string, std::string>> vOrderForm; | ||||
for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { | for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { | ||||
if (rcp.paymentRequest.IsInitialized()) { | if (rcp.paymentRequest.IsInitialized()) { | ||||
// Make sure any payment requests involved are still valid. | // Make sure any payment requests involved are still valid. | ||||
if (PaymentServer::verifyExpired( | if (PaymentServer::verifyExpired(rcp.paymentRequest.getDetails())) { | ||||
rcp.paymentRequest.getDetails())) { | |||||
return PaymentRequestExpired; | return PaymentRequestExpired; | ||||
} | } | ||||
// Store PaymentRequests in wtx.vOrderForm in wallet. | // Store PaymentRequests in wtx.vOrderForm in wallet. | ||||
std::string value; | std::string value; | ||||
rcp.paymentRequest.SerializeToString(&value); | rcp.paymentRequest.SerializeToString(&value); | ||||
vOrderForm.emplace_back("PaymentRequest", std::move(value)); | vOrderForm.emplace_back("PaymentRequest", std::move(value)); | ||||
} else if (!rcp.message.isEmpty()) { | } else if (!rcp.message.isEmpty()) { | ||||
// Message from normal bitcoincash:URI | // Message from normal bitcoincash:URI | ||||
// (bitcoincash:123...?message=example) | // (bitcoincash:123...?message=example) | ||||
vOrderForm.emplace_back("Message", rcp.message.toStdString()); | vOrderForm.emplace_back("Message", rcp.message.toStdString()); | ||||
} | } | ||||
} | } | ||||
CTransactionRef &newTx = transaction.getTransaction(); | auto &newTx = transaction.getWtx(); | ||||
CReserveKey *keyChange = transaction.getPossibleKeyChange(); | std::string rejectReason; | ||||
CValidationState state; | if (!newTx->commit({} /* mapValue */, std::move(vOrderForm), | ||||
if (!wallet->CommitTransaction( | {} /* fromAccount */, rejectReason)) { | ||||
newTx, {} /* mapValue */, std::move(vOrderForm), | return SendCoinsReturn(TransactionCommitFailed, | ||||
{} /* fromAccount */, *keyChange, g_connman.get(), state)) { | QString::fromStdString(rejectReason)); | ||||
return SendCoinsReturn( | |||||
TransactionCommitFailed, | |||||
QString::fromStdString(state.GetRejectReason())); | |||||
} | } | ||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); | CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); | ||||
ssTx << newTx; | ssTx << newTx->get(); | ||||
transaction_array.append(&(ssTx[0]), ssTx.size()); | transaction_array.append(&(ssTx[0]), ssTx.size()); | ||||
} | |||||
// Add addresses / update labels that we've sent to to the address book, and | // Add addresses / update labels that we've sent to to the address book, and | ||||
// emit coinsSent signal for each recipient | // emit coinsSent signal for each recipient | ||||
for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { | for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { | ||||
// Don't touch the address book when we have a payment request | // Don't touch the address book when we have a payment request | ||||
if (!rcp.paymentRequest.IsInitialized()) { | if (!rcp.paymentRequest.IsInitialized()) { | ||||
std::string strAddress = rcp.address.toStdString(); | std::string strAddress = rcp.address.toStdString(); | ||||
CTxDestination dest = | CTxDestination dest = | ||||
DecodeDestination(strAddress, wallet->chainParams); | DecodeDestination(strAddress, getChainParams()); | ||||
std::string strLabel = rcp.label.toStdString(); | 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 | // Check if we have a new address or an updated label | ||||
if (mi == wallet->mapAddressBook.end()) { | std::string name; | ||||
wallet->SetAddressBook(dest, strLabel, "send"); | if (!m_wallet->getAddress(dest, &name)) { | ||||
} else if (mi->second.name != strLabel) { | m_wallet->setAddressBook(dest, strLabel, "send"); | ||||
} else if (name != strLabel) { | |||||
// "" means don't change purpose | // "" means don't change purpose | ||||
wallet->SetAddressBook(dest, strLabel, ""); | m_wallet->setAddressBook(dest, strLabel, ""); | ||||
} | } | ||||
} | } | ||||
} | Q_EMIT coinsSent(cwallet, rcp, transaction_array); | ||||
Q_EMIT coinsSent(wallet, rcp, transaction_array); | |||||
} | } | ||||
// update balance immediately, otherwise there could be a short noticeable | // update balance immediately, otherwise there could be a short noticeable | ||||
// delay until pollBalanceChanged hits | // delay until pollBalanceChanged hits | ||||
checkBalanceChanged(); | checkBalanceChanged(m_wallet->getBalances()); | ||||
return SendCoinsReturn(OK); | return SendCoinsReturn(OK); | ||||
} | } | ||||
OptionsModel *WalletModel::getOptionsModel() { | OptionsModel *WalletModel::getOptionsModel() { | ||||
return optionsModel; | return optionsModel; | ||||
} | } | ||||
AddressTableModel *WalletModel::getAddressTableModel() { | AddressTableModel *WalletModel::getAddressTableModel() { | ||||
return addressTableModel; | return addressTableModel; | ||||
} | } | ||||
TransactionTableModel *WalletModel::getTransactionTableModel() { | TransactionTableModel *WalletModel::getTransactionTableModel() { | ||||
return transactionTableModel; | return transactionTableModel; | ||||
} | } | ||||
RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel() { | RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel() { | ||||
return recentRequestsTableModel; | return recentRequestsTableModel; | ||||
} | } | ||||
WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { | WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { | ||||
if (!wallet->IsCrypted()) { | if (!m_wallet->isCrypted()) { | ||||
return Unencrypted; | return Unencrypted; | ||||
} else if (wallet->IsLocked()) { | } else if (m_wallet->isLocked()) { | ||||
return Locked; | return Locked; | ||||
} else { | } else { | ||||
return Unlocked; | return Unlocked; | ||||
} | } | ||||
} | } | ||||
bool WalletModel::setWalletEncrypted(bool encrypted, | bool WalletModel::setWalletEncrypted(bool encrypted, | ||||
const SecureString &passphrase) { | const SecureString &passphrase) { | ||||
if (encrypted) { | if (encrypted) { | ||||
// Encrypt | // Encrypt | ||||
return wallet->EncryptWallet(passphrase); | return m_wallet->encryptWallet(passphrase); | ||||
} else { | } else { | ||||
// Decrypt -- TODO; not supported yet | // Decrypt -- TODO; not supported yet | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase) { | bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase) { | ||||
if (locked) { | if (locked) { | ||||
// Lock | // Lock | ||||
return wallet->Lock(); | return m_wallet->lock(); | ||||
} else { | } else { | ||||
// Unlock | // Unlock | ||||
return wallet->Unlock(passPhrase); | return m_wallet->unlock(passPhrase); | ||||
} | } | ||||
} | } | ||||
bool WalletModel::changePassphrase(const SecureString &oldPass, | bool WalletModel::changePassphrase(const SecureString &oldPass, | ||||
const SecureString &newPass) { | const SecureString &newPass) { | ||||
LOCK(wallet->cs_wallet); | |||||
// Make sure wallet is locked before attempting pass change | // Make sure wallet is locked before attempting pass change | ||||
wallet->Lock(); | m_wallet->lock(); | ||||
return wallet->ChangeWalletPassphrase(oldPass, newPass); | return m_wallet->changeWalletPassphrase(oldPass, newPass); | ||||
} | |||||
bool WalletModel::backupWallet(const QString &filename) { | |||||
return wallet->BackupWallet(filename.toLocal8Bit().data()); | |||||
} | } | ||||
// Handlers for core signals | // Handlers for core signals | ||||
static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, | static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel) { | ||||
CCryptoKeyStore *wallet) { | |||||
qDebug() << "NotifyKeyStoreStatusChanged"; | qDebug() << "NotifyKeyStoreStatusChanged"; | ||||
QMetaObject::invokeMethod(walletmodel, "updateStatus", | QMetaObject::invokeMethod(walletmodel, "updateStatus", | ||||
Qt::QueuedConnection); | Qt::QueuedConnection); | ||||
} | } | ||||
static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, | static void NotifyAddressBookChanged(WalletModel *walletmodel, | ||||
const CTxDestination &address, | const CTxDestination &address, | ||||
const std::string &label, bool isMine, | const std::string &label, bool isMine, | ||||
const std::string &purpose, | const std::string &purpose, | ||||
ChangeType status) { | ChangeType status) { | ||||
QString strAddress = QString::fromStdString(EncodeDestination(address)); | QString strAddress = QString::fromStdString(EncodeDestination(address)); | ||||
QString strLabel = QString::fromStdString(label); | QString strLabel = QString::fromStdString(label); | ||||
QString strPurpose = QString::fromStdString(purpose); | QString strPurpose = QString::fromStdString(purpose); | ||||
qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + | qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + | ||||
" isMine=" + QString::number(isMine) + | " isMine=" + QString::number(isMine) + | ||||
" purpose=" + strPurpose + | " purpose=" + strPurpose + | ||||
" status=" + QString::number(status); | " status=" + QString::number(status); | ||||
QMetaObject::invokeMethod(walletmodel, "updateAddressBook", | QMetaObject::invokeMethod(walletmodel, "updateAddressBook", | ||||
Qt::QueuedConnection, Q_ARG(QString, strAddress), | Qt::QueuedConnection, Q_ARG(QString, strAddress), | ||||
Q_ARG(QString, strLabel), Q_ARG(bool, isMine), | Q_ARG(QString, strLabel), Q_ARG(bool, isMine), | ||||
Q_ARG(QString, strPurpose), Q_ARG(int, status)); | Q_ARG(QString, strPurpose), Q_ARG(int, status)); | ||||
} | } | ||||
static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, | static void NotifyTransactionChanged(WalletModel *walletmodel, const TxId &hash, | ||||
const uint256 &hash, ChangeType status) { | ChangeType status) { | ||||
Q_UNUSED(wallet); | |||||
Q_UNUSED(hash); | Q_UNUSED(hash); | ||||
Q_UNUSED(status); | Q_UNUSED(status); | ||||
QMetaObject::invokeMethod(walletmodel, "updateTransaction", | QMetaObject::invokeMethod(walletmodel, "updateTransaction", | ||||
Qt::QueuedConnection); | Qt::QueuedConnection); | ||||
} | } | ||||
static void ShowProgress(WalletModel *walletmodel, const std::string &title, | static void ShowProgress(WalletModel *walletmodel, const std::string &title, | ||||
int nProgress) { | int nProgress) { | ||||
// emits signal "showProgress" | // emits signal "showProgress" | ||||
QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection, | QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection, | ||||
Q_ARG(QString, QString::fromStdString(title)), | Q_ARG(QString, QString::fromStdString(title)), | ||||
Q_ARG(int, nProgress)); | Q_ARG(int, nProgress)); | ||||
} | } | ||||
static void NotifyWatchonlyChanged(WalletModel *walletmodel, | static void NotifyWatchonlyChanged(WalletModel *walletmodel, | ||||
bool fHaveWatchonly) { | bool fHaveWatchonly) { | ||||
QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", | QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", | ||||
Qt::QueuedConnection, | Qt::QueuedConnection, | ||||
Q_ARG(bool, fHaveWatchonly)); | Q_ARG(bool, fHaveWatchonly)); | ||||
} | } | ||||
void WalletModel::subscribeToCoreSignals() { | void WalletModel::subscribeToCoreSignals() { | ||||
// Connect signals to wallet | // Connect signals to wallet | ||||
wallet->NotifyStatusChanged.connect( | m_handler_status_changed = m_wallet->handleStatusChanged( | ||||
boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); | boost::bind(&NotifyKeyStoreStatusChanged, this)); | ||||
wallet->NotifyAddressBookChanged.connect( | m_handler_address_book_changed = m_wallet->handleAddressBookChanged( | ||||
boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); | boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); | ||||
wallet->NotifyTransactionChanged.connect( | m_handler_transaction_changed = m_wallet->handleTransactionChanged( | ||||
boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); | boost::bind(NotifyTransactionChanged, this, _1, _2)); | ||||
wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); | m_handler_show_progress = | ||||
wallet->NotifyWatchonlyChanged.connect( | m_wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2)); | ||||
m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged( | |||||
boost::bind(NotifyWatchonlyChanged, this, _1)); | boost::bind(NotifyWatchonlyChanged, this, _1)); | ||||
} | } | ||||
void WalletModel::unsubscribeFromCoreSignals() { | void WalletModel::unsubscribeFromCoreSignals() { | ||||
// Disconnect signals from wallet | // Disconnect signals from wallet | ||||
wallet->NotifyStatusChanged.disconnect( | m_handler_status_changed->disconnect(); | ||||
boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); | m_handler_address_book_changed->disconnect(); | ||||
wallet->NotifyAddressBookChanged.disconnect( | m_handler_transaction_changed->disconnect(); | ||||
boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); | m_handler_show_progress->disconnect(); | ||||
wallet->NotifyTransactionChanged.disconnect( | m_handler_watch_only_changed->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 implementation | ||||
WalletModel::UnlockContext WalletModel::requestUnlock() { | WalletModel::UnlockContext WalletModel::requestUnlock() { | ||||
bool was_locked = getEncryptionStatus() == Locked; | bool was_locked = getEncryptionStatus() == Locked; | ||||
if (was_locked) { | if (was_locked) { | ||||
// Request UI to unlock wallet | // Request UI to unlock wallet | ||||
Q_EMIT requireUnlock(); | Q_EMIT requireUnlock(); | ||||
Show All 16 Lines | |||||
} | } | ||||
void WalletModel::UnlockContext::CopyFrom(const UnlockContext &rhs) { | void WalletModel::UnlockContext::CopyFrom(const UnlockContext &rhs) { | ||||
// Transfer context; old object no longer relocks wallet | // Transfer context; old object no longer relocks wallet | ||||
*this = rhs; | *this = rhs; | ||||
rhs.relock = false; | rhs.relock = false; | ||||
} | } | ||||
bool WalletModel::getPubKey(const CKeyID &address, | |||||
CPubKey &vchPubKeyOut) const { | |||||
return wallet->GetPubKey(address, vchPubKeyOut); | |||||
} | |||||
bool WalletModel::IsSpendable(const CTxDestination &dest) const { | |||||
return IsMine(*wallet, dest) & ISMINE_SPENDABLE; | |||||
} | |||||
bool WalletModel::getPrivKey(const CKeyID &address, CKey &vchPrivKeyOut) const { | |||||
return wallet->GetKey(address, vchPrivKeyOut); | |||||
} | |||||
// returns a list of COutputs from COutPoints | // returns a list of COutputs from COutPoints | ||||
void WalletModel::getOutputs(const std::vector<COutPoint> &vOutpoints, | void WalletModel::getOutputs(const std::vector<COutPoint> &vOutpoints, | ||||
std::vector<COutput> &vOutputs) { | std::vector<COutput> &vOutputs) { | ||||
LOCK2(cs_main, wallet->cs_wallet); | LOCK2(cs_main, cwallet->cs_wallet); | ||||
for (const COutPoint &outpoint : vOutpoints) { | for (const COutPoint &outpoint : vOutpoints) { | ||||
auto it = wallet->mapWallet.find(outpoint.GetTxId()); | auto it = cwallet->mapWallet.find(outpoint.GetTxId()); | ||||
if (it == wallet->mapWallet.end()) { | if (it == cwallet->mapWallet.end()) { | ||||
continue; | continue; | ||||
} | } | ||||
int nDepth = it->second.GetDepthInMainChain(); | int nDepth = it->second.GetDepthInMainChain(); | ||||
if (nDepth < 0) { | if (nDepth < 0) { | ||||
continue; | continue; | ||||
} | } | ||||
COutput out(&it->second, outpoint.GetN(), nDepth, true /* spendable */, | COutput out(&it->second, outpoint.GetN(), nDepth, true /* spendable */, | ||||
true /* solvable */, true /* safe */); | true /* solvable */, true /* safe */); | ||||
vOutputs.push_back(out); | vOutputs.push_back(out); | ||||
} | } | ||||
} | } | ||||
bool WalletModel::isSpent(const COutPoint &outpoint) const { | bool WalletModel::isSpent(const COutPoint &outpoint) const { | ||||
LOCK2(cs_main, wallet->cs_wallet); | LOCK2(cs_main, cwallet->cs_wallet); | ||||
return wallet->IsSpent(outpoint.GetTxId(), outpoint.GetN()); | return cwallet->IsSpent(outpoint.GetTxId(), outpoint.GetN()); | ||||
} | } | ||||
// AvailableCoins + LockedCoins grouped by wallet address (put change in one | // AvailableCoins + LockedCoins grouped by wallet address (put change in one | ||||
// group with wallet address) | // group with wallet address) | ||||
void WalletModel::listCoins( | void WalletModel::listCoins( | ||||
std::map<QString, std::vector<COutput>> &mapCoins) const { | std::map<QString, std::vector<COutput>> &mapCoins) const { | ||||
for (auto &group : wallet->ListCoins()) { | for (auto &group : cwallet->ListCoins()) { | ||||
auto &resultGroup = | auto &resultGroup = | ||||
mapCoins[QString::fromStdString(EncodeDestination(group.first))]; | mapCoins[QString::fromStdString(EncodeDestination(group.first))]; | ||||
for (auto &coin : group.second) { | for (auto &coin : group.second) { | ||||
resultGroup.emplace_back(std::move(coin)); | resultGroup.emplace_back(std::move(coin)); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool WalletModel::isLockedCoin(const TxId &txid, uint32_t n) const { | |||||
LOCK2(cs_main, wallet->cs_wallet); | |||||
return wallet->IsLockedCoin(txid, 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( | void WalletModel::loadReceiveRequests( | ||||
std::vector<std::string> &vReceiveRequests) { | std::vector<std::string> &vReceiveRequests) { | ||||
// receive request | // receive request | ||||
vReceiveRequests = wallet->GetDestValues("rr"); | vReceiveRequests = m_wallet->getDestValues("rr"); | ||||
} | } | ||||
bool WalletModel::saveReceiveRequest(const std::string &sAddress, | bool WalletModel::saveReceiveRequest(const std::string &sAddress, | ||||
const int64_t nId, | const int64_t nId, | ||||
const std::string &sRequest) { | const std::string &sRequest) { | ||||
CTxDestination dest = DecodeDestination(sAddress, wallet->chainParams); | CTxDestination dest = DecodeDestination(sAddress, getChainParams()); | ||||
std::stringstream ss; | std::stringstream ss; | ||||
ss << nId; | ss << nId; | ||||
// "rr" prefix = "receive request" in destdata | // "rr" prefix = "receive request" in destdata | ||||
std::string key = "rr" + ss.str(); | std::string key = "rr" + ss.str(); | ||||
LOCK(wallet->cs_wallet); | return sRequest.empty() ? m_wallet->eraseDestData(dest, key) | ||||
return sRequest.empty() ? wallet->EraseDestData(dest, key) | : m_wallet->addDestData(dest, key, sRequest); | ||||
: wallet->AddDestData(dest, key, sRequest); | |||||
} | |||||
bool WalletModel::transactionCanBeAbandoned(const TxId &txid) const { | |||||
return wallet->TransactionCanBeAbandoned(txid); | |||||
} | |||||
bool WalletModel::abandonTransaction(const TxId &txid) const { | |||||
LOCK2(cs_main, wallet->cs_wallet); | |||||
return wallet->AbandonTransaction(txid); | |||||
} | } | ||||
bool WalletModel::isWalletEnabled() { | bool WalletModel::isWalletEnabled() { | ||||
return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET); | return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET); | ||||
} | } | ||||
bool WalletModel::hdEnabled() const { | |||||
return wallet->IsHDEnabled(); | |||||
} | |||||
QString WalletModel::getWalletName() const { | QString WalletModel::getWalletName() const { | ||||
LOCK(wallet->cs_wallet); | return QString::fromStdString(m_wallet->getWalletName()); | ||||
return QString::fromStdString(wallet->GetName()); | |||||
} | } | ||||
bool WalletModel::isMultiwallet() { | bool WalletModel::isMultiwallet() { | ||||
return gArgs.GetArgs("-wallet").size() > 1; | return m_node.getWallets().size() > 1; | ||||
} | } | ||||
const CChainParams &WalletModel::getChainParams() const { | const CChainParams &WalletModel::getChainParams() const { | ||||
return wallet->chainParams; | return GetConfig().GetChainParams(); | ||||
} | } |