Remove most direct bitcoin calls from qt/walletmodel.cpp

This commit is contained in:
Russell Yanofsky
2017-04-17 18:56:44 -04:00
committed by John Newbery
parent 90d4640b7e
commit a0704a8996
18 changed files with 588 additions and 330 deletions

View File

@@ -15,6 +15,8 @@
#include <qt/transactiontablemodel.h>
#include <chain.h>
#include <interface/handler.h>
#include <interface/node.h>
#include <key_io.h>
#include <keystore.h>
#include <validation.h>
@@ -37,21 +39,19 @@
#include <QTimer>
WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) :
QObject(parent), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0),
WalletModel::WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) :
QObject(parent), m_wallet(std::move(wallet)), m_node(node), cwallet(_wallet), optionsModel(_optionsModel), addressTableModel(0),
transactionTableModel(0),
recentRequestsTableModel(0),
cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
cachedWatchOnlyBalance{0}, cachedWatchUnconfBalance{0}, cachedWatchImmatureBalance{0},
cachedEncryptionStatus(Unencrypted),
cachedNumBlocks(0)
{
fHaveWatchOnly = wallet->HaveWatchOnly();
fHaveWatchOnly = m_wallet->haveWatchOnly();
fForceCheckBalanceChanged = false;
addressTableModel = new AddressTableModel(wallet, this);
transactionTableModel = new TransactionTableModel(platformStyle, wallet, this);
recentRequestsTableModel = new RecentRequestsTableModel(wallet, this);
addressTableModel = new AddressTableModel(cwallet, this);
transactionTableModel = new TransactionTableModel(platformStyle, cwallet, this);
recentRequestsTableModel = new RecentRequestsTableModel(cwallet, this);
// This timer will be fired repeatedly to update the balance
pollTimer = new QTimer(this);
@@ -66,46 +66,6 @@ WalletModel::~WalletModel()
unsubscribeFromCoreSignals();
}
CAmount WalletModel::getBalance(const CCoinControl *coinControl) const
{
if (coinControl)
{
return wallet->GetAvailableBalance(coinControl);
}
return wallet->GetBalance();
}
CAmount WalletModel::getUnconfirmedBalance() const
{
return wallet->GetUnconfirmedBalance();
}
CAmount WalletModel::getImmatureBalance() const
{
return wallet->GetImmatureBalance();
}
bool WalletModel::haveWatchOnly() const
{
return fHaveWatchOnly;
}
CAmount WalletModel::getWatchBalance() const
{
return wallet->GetWatchOnlyBalance();
}
CAmount WalletModel::getWatchUnconfirmedBalance() const
{
return wallet->GetUnconfirmedWatchOnlyBalance();
}
CAmount WalletModel::getWatchImmatureBalance() const
{
return wallet->GetImmatureWatchOnlyBalance();
}
void WalletModel::updateStatus()
{
EncryptionStatus newEncryptionStatus = getEncryptionStatus();
@@ -117,55 +77,34 @@ void WalletModel::updateStatus()
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)
// Try to get balances and return early if locks can't be acquired. 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.
interface::WalletBalances new_balances;
int numBlocks = -1;
if (!m_wallet->tryGetBalances(new_balances, numBlocks)) {
return;
}
if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks)
if(fForceCheckBalanceChanged || m_node.getNumBlocks() != cachedNumBlocks)
{
fForceCheckBalanceChanged = false;
// Balance and number of transactions might have changed
cachedNumBlocks = chainActive.Height();
cachedNumBlocks = m_node.getNumBlocks();
checkBalanceChanged();
checkBalanceChanged(new_balances);
if(transactionTableModel)
transactionTableModel->updateConfirmations();
}
}
void WalletModel::checkBalanceChanged()
void WalletModel::checkBalanceChanged(const interface::WalletBalances& new_balances)
{
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);
if(new_balances.balanceChanged(m_cached_balances)) {
m_cached_balances = new_balances;
Q_EMIT balanceChanged(new_balances.balance, new_balances.unconfirmed_balance, new_balances.immature_balance, new_balances.watch_only_balance, new_balances.unconfirmed_watch_only_balance, new_balances.immature_watch_only_balance);
}
}
@@ -260,7 +199,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
return DuplicateAddress;
}
CAmount nBalance = getBalance(&coinControl);
CAmount nBalance = m_wallet->getAvailableBalance(coinControl);
if(total > nBalance)
{
@@ -268,22 +207,17 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
}
{
LOCK2(cs_main, wallet->cs_wallet);
transaction.newPossibleKeyChange(wallet);
CAmount nFeeRequired = 0;
int nChangePosRet = -1;
std::string strFailReason;
CTransactionRef& newTx = transaction.getTransaction();
CReserveKey *keyChange = transaction.getPossibleKeyChange();
bool fCreated = wallet->CreateTransaction(vecSend, newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl);
auto& newTx = transaction.getWtx();
newTx = m_wallet->createTransaction(vecSend, coinControl, true /* sign */, nChangePosRet, nFeeRequired, strFailReason);
transaction.setTransactionFee(nFeeRequired);
if (fSubtractFeeFromAmount && fCreated)
if (fSubtractFeeFromAmount && newTx)
transaction.reassignAmounts(nChangePosRet);
if(!fCreated)
if(!newTx)
{
if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance)
{
@@ -297,7 +231,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
// 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)
if (nFeeRequired > m_node.getMaxTxFee())
return AbsurdFee;
}
@@ -309,8 +243,6 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
QByteArray transaction_array; /* store serialized transaction */
{
LOCK2(cs_main, wallet->cs_wallet);
std::vector<std::pair<std::string, std::string>> vOrderForm;
for (const SendCoinsRecipient &rcp : transaction.getRecipients())
{
@@ -330,14 +262,13 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
vOrderForm.emplace_back("Message", rcp.message.toStdString());
}
CTransactionRef& newTx = transaction.getTransaction();
CReserveKey *keyChange = transaction.getPossibleKeyChange();
CValidationState state;
if (!wallet->CommitTransaction(newTx, {} /* mapValue */, std::move(vOrderForm), {} /* fromAccount */, *keyChange, g_connman.get(), state))
return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(state.GetRejectReason()));
auto& newTx = transaction.getWtx();
std::string rejectReason;
if (!newTx->commit({} /* mapValue */, std::move(vOrderForm), {} /* fromAccount */, rejectReason))
return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(rejectReason));
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << newTx;
ssTx << newTx->get();
transaction_array.append(&(ssTx[0]), ssTx.size());
}
@@ -352,24 +283,22 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
CTxDestination dest = DecodeDestination(strAddress);
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())
std::string name;
if (!m_wallet->getAddress(dest, &name))
{
wallet->SetAddressBook(dest, strLabel, "send");
m_wallet->setAddressBook(dest, strLabel, "send");
}
else if (mi->second.name != strLabel)
else if (name != strLabel)
{
wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose
m_wallet->setAddressBook(dest, strLabel, ""); // "" means don't change purpose
}
}
}
Q_EMIT coinsSent(wallet, rcp, transaction_array);
Q_EMIT coinsSent(cwallet, rcp, transaction_array);
}
checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
checkBalanceChanged(m_wallet->getBalances()); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
return SendCoinsReturn(OK);
}
@@ -396,11 +325,11 @@ RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel()
WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
{
if(!wallet->IsCrypted())
if(!m_wallet->isCrypted())
{
return Unencrypted;
}
else if(wallet->IsLocked())
else if(m_wallet->isLocked())
{
return Locked;
}
@@ -415,7 +344,7 @@ bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphr
if(encrypted)
{
// Encrypt
return wallet->EncryptWallet(passphrase);
return m_wallet->encryptWallet(passphrase);
}
else
{
@@ -429,39 +358,29 @@ bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
if(locked)
{
// Lock
return wallet->Lock();
return m_wallet->lock();
}
else
{
// Unlock
return wallet->Unlock(passPhrase);
return m_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 wallet->BackupWallet(filename.toLocal8Bit().data());
m_wallet->lock(); // Make sure wallet is locked before attempting pass change
return m_wallet->changeWalletPassphrase(oldPass, newPass);
}
// Handlers for core signals
static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet)
static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel)
{
qDebug() << "NotifyKeyStoreStatusChanged";
QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
}
static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
static void NotifyAddressBookChanged(WalletModel *walletmodel,
const CTxDestination &address, const std::string &label, bool isMine,
const std::string &purpose, ChangeType status)
{
@@ -478,9 +397,8 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
Q_ARG(int, status));
}
static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status)
static void NotifyTransactionChanged(WalletModel *walletmodel, const uint256 &hash, ChangeType status)
{
Q_UNUSED(wallet);
Q_UNUSED(hash);
Q_UNUSED(status);
QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection);
@@ -503,21 +421,21 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, 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));
m_handler_status_changed = m_wallet->handleStatusChanged(boost::bind(&NotifyKeyStoreStatusChanged, this));
m_handler_address_book_changed = m_wallet->handleAddressBookChanged(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5));
m_handler_transaction_changed = m_wallet->handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2));
m_handler_show_progress = m_wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2));
m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(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));
m_handler_status_changed->disconnect();
m_handler_address_book_changed->disconnect();
m_handler_transaction_changed->disconnect();
m_handler_show_progress->disconnect();
m_handler_watch_only_changed->disconnect();
}
// WalletModel::UnlockContext implementation
@@ -557,29 +475,14 @@ void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
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
void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs)
{
LOCK2(cs_main, wallet->cs_wallet);
LOCK2(cs_main, cwallet->cs_wallet);
for (const COutPoint& outpoint : vOutpoints)
{
auto it = wallet->mapWallet.find(outpoint.hash);
if (it == wallet->mapWallet.end()) continue;
auto it = cwallet->mapWallet.find(outpoint.hash);
if (it == cwallet->mapWallet.end()) continue;
int nDepth = it->second.GetDepthInMainChain();
if (nDepth < 0) continue;
COutput out(&it->second, outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */);
@@ -589,14 +492,14 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect
bool WalletModel::isSpent(const COutPoint& outpoint) const
{
LOCK2(cs_main, wallet->cs_wallet);
return wallet->IsSpent(outpoint.hash, outpoint.n);
LOCK2(cs_main, cwallet->cs_wallet);
return cwallet->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
{
for (auto& group : wallet->ListCoins()) {
for (auto& group : cwallet->ListCoins()) {
auto& resultGroup = mapCoins[QString::fromStdString(EncodeDestination(group.first))];
for (auto& coin : group.second) {
resultGroup.emplace_back(std::move(coin));
@@ -604,33 +507,9 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
}
}
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)
{
vReceiveRequests = wallet->GetDestValues("rr"); // receive request
vReceiveRequests = m_wallet->getDestValues("rr"); // receive request
}
bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
@@ -641,27 +520,10 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t
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);
return m_wallet->eraseDestData(dest, key);
else
return wallet->AddDestData(dest, key, sRequest);
}
bool WalletModel::transactionCanBeAbandoned(uint256 hash) const
{
return wallet->TransactionCanBeAbandoned(hash);
}
bool WalletModel::abandonTransaction(uint256 hash) const
{
LOCK2(cs_main, wallet->cs_wallet);
return wallet->AbandonTransaction(hash);
}
bool WalletModel::transactionCanBeBumped(uint256 hash) const
{
return feebumper::TransactionCanBeBumped(wallet, hash);
return m_wallet->addDestData(dest, key, sRequest);
}
bool WalletModel::bumpFee(uint256 hash)
@@ -672,7 +534,7 @@ bool WalletModel::bumpFee(uint256 hash)
CAmount old_fee;
CAmount new_fee;
CMutableTransaction mtx;
if (feebumper::CreateTransaction(wallet, hash, coin_control, 0 /* totalFee */, errors, old_fee, new_fee, mtx) != feebumper::Result::OK) {
if (!m_wallet->createBumpTransaction(hash, coin_control, 0 /* totalFee */, errors, old_fee, new_fee, mtx)) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Increasing transaction fee failed") + "<br />(" +
(errors.size() ? QString::fromStdString(errors[0]) : "") +")");
return false;
@@ -711,13 +573,13 @@ bool WalletModel::bumpFee(uint256 hash)
}
// sign bumped transaction
if (!feebumper::SignTransaction(wallet, mtx)) {
if (!m_wallet->signBumpTransaction(mtx)) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Can't sign transaction."));
return false;
}
// commit the bumped transaction
uint256 txid;
if (feebumper::CommitTransaction(wallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) {
if(!m_wallet->commitBumpTransaction(hash, std::move(mtx), errors, txid)) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Could not commit transaction") + "<br />(" +
QString::fromStdString(errors[0])+")");
return false;
@@ -730,28 +592,12 @@ bool WalletModel::isWalletEnabled()
return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
}
bool WalletModel::hdEnabled() const
{
return wallet->IsHDEnabled();
}
OutputType WalletModel::getDefaultAddressType() const
{
return wallet->m_default_address_type;
}
int WalletModel::getDefaultConfirmTarget() const
{
return nTxConfirmTarget;
}
QString WalletModel::getWalletName() const
{
LOCK(wallet->cs_wallet);
return QString::fromStdString(wallet->GetName());
return QString::fromStdString(m_wallet->getWalletName());
}
bool WalletModel::isMultiwallet()
{
return gArgs.GetArgs("-wallet").size() > 1;
return m_node.getWallets().size() > 1;
}