Page MenuHomePhabricator

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 59df99d883..0d18fbeb1f 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -1,287 +1,301 @@
// Copyright (c) 2018 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 <interfaces/node.h>
#include <addrdb.h>
+#include <amount.h>
#include <chain.h>
#include <chainparams.h>
#include <config.h>
#include <init.h>
#include <interfaces/handler.h>
#include <interfaces/wallet.h>
#include <net.h>
#include <net_processing.h>
#include <netaddress.h>
#include <netbase.h>
#include <primitives/block.h>
#include <rpc/server.h>
#include <scheduler.h>
#include <sync.h>
#include <txmempool.h>
#include <ui_interface.h>
#include <util.h>
#include <validation.h>
#include <warnings.h>
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#ifdef ENABLE_WALLET
+#include <wallet/wallet.h>
#define CHECK_WALLET(x) x
#else
#define CHECK_WALLET(x) \
throw std::logic_error("Wallet function called in non-wallet build.")
#endif
#include <boost/thread/thread.hpp>
#include <univalue.h>
#include <atomic>
-class CWallet;
class HTTPRPCRequestProcessor;
namespace interfaces {
namespace {
class NodeImpl : public Node {
void parseParameters(int argc, const char *const argv[]) override {
gArgs.ParseParameters(argc, argv);
}
void readConfigFile(const std::string &conf_path) override {
gArgs.ReadConfigFile(conf_path);
}
bool softSetArg(const std::string &arg,
const std::string &value) override {
return gArgs.SoftSetArg(arg, value);
}
bool softSetBoolArg(const std::string &arg, bool value) override {
return gArgs.SoftSetBoolArg(arg, value);
}
void selectParams(const std::string &network) override {
SelectParams(network);
}
void initLogging() override { InitLogging(); }
void initParameterInteraction() override { InitParameterInteraction(); }
std::string getWarnings(const std::string &type) override {
return GetWarnings(type);
}
bool baseInitialize(Config &config, RPCServer &rpcServer) override {
return AppInitBasicSetup() &&
AppInitParameterInteraction(config, rpcServer) &&
AppInitSanityChecks() && AppInitLockDataDirectory();
}
bool
appInitMain(Config &config,
HTTPRPCRequestProcessor &httpRPCRequestProcessor) override {
return AppInitMain(config, httpRPCRequestProcessor);
}
void appShutdown() override {
Interrupt();
Shutdown();
}
void startShutdown() override { StartShutdown(); }
bool shutdownRequested() override { return ShutdownRequested(); }
void mapPort(bool use_upnp) override {
if (use_upnp) {
StartMapPort();
} else {
InterruptMapPort();
StopMapPort();
}
}
std::string helpMessage(HelpMessageMode mode) override {
return HelpMessage(mode);
}
bool getProxy(Network net, proxyType &proxy_info) override {
return GetProxy(net, proxy_info);
}
size_t getNodeCount(CConnman::NumConnections flags) override {
return g_connman ? g_connman->GetNodeCount(flags) : 0;
}
bool getNodesStats(NodesStats &stats) override {
stats.clear();
if (g_connman) {
std::vector<CNodeStats> stats_temp;
g_connman->GetNodeStats(stats_temp);
stats.reserve(stats_temp.size());
for (auto &node_stats_temp : stats_temp) {
stats.emplace_back(std::move(node_stats_temp), false,
CNodeStateStats());
}
// Try to retrieve the CNodeStateStats for each node.
TRY_LOCK(::cs_main, lockMain);
if (lockMain) {
for (auto &node_stats : stats) {
std::get<1>(node_stats) =
GetNodeStateStats(std::get<0>(node_stats).nodeid,
std::get<2>(node_stats));
}
}
return true;
}
return false;
}
bool getBanned(banmap_t &banmap) override {
if (g_connman) {
g_connman->GetBanned(banmap);
return true;
}
return false;
}
bool ban(const CNetAddr &net_addr, BanReason reason,
int64_t ban_time_offset) override {
if (g_connman) {
g_connman->Ban(net_addr, reason, ban_time_offset);
return true;
}
return false;
}
bool unban(const CSubNet &ip) override {
if (g_connman) {
g_connman->Unban(ip);
return true;
}
return false;
}
bool disconnect(NodeId id) override {
if (g_connman) {
return g_connman->DisconnectNode(id);
}
return false;
}
int64_t getTotalBytesRecv() override {
return g_connman ? g_connman->GetTotalBytesRecv() : 0;
}
int64_t getTotalBytesSent() override {
return g_connman ? g_connman->GetTotalBytesSent() : 0;
}
size_t getMempoolSize() override { return g_mempool.size(); }
size_t getMempoolDynamicUsage() override {
return g_mempool.DynamicMemoryUsage();
}
bool getHeaderTip(int &height, int64_t &block_time) override {
LOCK(::cs_main);
if (::pindexBestHeader) {
height = ::pindexBestHeader->nHeight;
block_time = ::pindexBestHeader->GetBlockTime();
return true;
}
return false;
}
int getNumBlocks() override {
LOCK(::cs_main);
return ::chainActive.Height();
}
int64_t getLastBlockTime() override {
LOCK(::cs_main);
if (::chainActive.Tip()) {
return ::chainActive.Tip()->GetBlockTime();
}
// Genesis block's time of current network
return Params().GenesisBlock().GetBlockTime();
}
double getVerificationProgress() override {
const CBlockIndex *tip;
{
LOCK(::cs_main);
tip = ::chainActive.Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
}
bool isInitialBlockDownload() override {
return IsInitialBlockDownload();
}
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
void setNetworkActive(bool active) override {
if (g_connman) {
g_connman->SetNetworkActive(active);
}
}
bool getNetworkActive() override {
return g_connman && g_connman->GetNetworkActive();
}
+ Amount getMaxTxFee() override { return ::maxTxFee; }
UniValue executeRpc(Config &config, const std::string &command,
const UniValue &params,
const std::string &uri) override {
JSONRPCRequest req;
req.params = params;
req.strMethod = command;
req.URI = uri;
return ::tableRPC.execute(config, req);
}
std::vector<std::string> listRpcCommands() override {
return ::tableRPC.listCommands();
}
void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) override {
RPCSetTimerInterfaceIfUnset(iface);
}
void rpcUnsetTimerInterface(RPCTimerInterface *iface) override {
RPCUnsetTimerInterface(iface);
}
+ std::vector<std::unique_ptr<Wallet>> getWallets() override {
+#ifdef ENABLE_WALLET
+ std::vector<std::unique_ptr<Wallet>> wallets;
+ for (CWalletRef wallet : ::vpwallets) {
+ wallets.emplace_back(MakeWallet(*wallet));
+ }
+ return wallets;
+#else
+ throw std::logic_error(
+ "Node::getWallets() called in non-wallet build.");
+#endif
+ }
std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override {
return MakeHandler(::uiInterface.InitMessage.connect(fn));
}
std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) override {
return MakeHandler(::uiInterface.ThreadSafeMessageBox.connect(fn));
}
std::unique_ptr<Handler> handleQuestion(QuestionFn fn) override {
return MakeHandler(::uiInterface.ThreadSafeQuestion.connect(fn));
}
std::unique_ptr<Handler>
handleShowProgress(ShowProgressFn fn) override {
return MakeHandler(::uiInterface.ShowProgress.connect(fn));
}
std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override {
CHECK_WALLET(return MakeHandler(::uiInterface.LoadWallet.connect(
[fn](CWallet *wallet) { fn(MakeWallet(*wallet)); })));
}
std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(
NotifyNumConnectionsChangedFn fn) override {
return MakeHandler(
::uiInterface.NotifyNumConnectionsChanged.connect(fn));
}
std::unique_ptr<Handler> handleNotifyNetworkActiveChanged(
NotifyNetworkActiveChangedFn fn) override {
return MakeHandler(
::uiInterface.NotifyNetworkActiveChanged.connect(fn));
}
std::unique_ptr<Handler>
handleNotifyAlertChanged(NotifyAlertChangedFn fn) override {
return MakeHandler(::uiInterface.NotifyAlertChanged.connect(fn));
}
std::unique_ptr<Handler>
handleBannedListChanged(BannedListChangedFn fn) override {
return MakeHandler(::uiInterface.BannedListChanged.connect(fn));
}
std::unique_ptr<Handler>
handleNotifyBlockTip(NotifyBlockTipFn fn) override {
return MakeHandler(::uiInterface.NotifyBlockTip.connect(
[fn](bool initial_download, const CBlockIndex *block) {
fn(initial_download, block->nHeight, block->GetBlockTime(),
GuessVerificationProgress(Params().TxData(), block));
}));
}
std::unique_ptr<Handler>
handleNotifyHeaderTip(NotifyHeaderTipFn fn) override {
return MakeHandler(::uiInterface.NotifyHeaderTip.connect(
[fn](bool initial_download, const CBlockIndex *block) {
fn(initial_download, block->nHeight, block->GetBlockTime(),
GuessVerificationProgress(Params().TxData(), block));
}));
}
};
} // namespace
std::unique_ptr<Node> MakeNode() {
return std::make_unique<NodeImpl>();
}
} // namespace interfaces
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index a2ef8493da..3fab620409 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -1,232 +1,239 @@
// Copyright (c) 2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_INTERFACES_NODE_H
#define BITCOIN_INTERFACES_NODE_H
#include <addrdb.h> // For banmap_t
+#include <amount.h> // For Amount
#include <init.h> // For HelpMessageMode
#include <net.h> // For CConnman::NumConnections
#include <netaddress.h> // For Network
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <tuple>
#include <vector>
struct CNodeStateStats;
struct CNodeStats;
class Config;
class HTTPRPCRequestProcessor;
class proxyType;
class RPCServer;
class RPCTimerInterface;
class UniValue;
namespace interfaces {
class Handler;
class Wallet;
//! Top-level interface for a bitcoin node (bitcoind process).
class Node {
public:
virtual ~Node() {}
//! Set command line arguments.
virtual void parseParameters(int argc, const char *const argv[]) = 0;
//! Set a command line argument if it doesn't already have a value
virtual bool softSetArg(const std::string &arg,
const std::string &value) = 0;
//! Set a command line boolean argument if it doesn't already have a value
virtual bool softSetBoolArg(const std::string &arg, bool value) = 0;
//! Load settings from configuration file.
virtual void readConfigFile(const std::string &conf_path) = 0;
//! Choose network parameters.
virtual void selectParams(const std::string &network) = 0;
//! Init logging.
virtual void initLogging() = 0;
//! Init parameter interaction.
virtual void initParameterInteraction() = 0;
//! Get warnings.
virtual std::string getWarnings(const std::string &type) = 0;
//! Initialize app dependencies.
virtual bool baseInitialize(Config &config, RPCServer &rpcServer) = 0;
//! Start node.
virtual bool
appInitMain(Config &config,
HTTPRPCRequestProcessor &httpRPCRequestProcessor) = 0;
//! Stop node.
virtual void appShutdown() = 0;
//! Start shutdown.
virtual void startShutdown() = 0;
//! Return whether shutdown was requested.
virtual bool shutdownRequested() = 0;
//! Get help message string.
virtual std::string helpMessage(HelpMessageMode mode) = 0;
//! Map port.
virtual void mapPort(bool use_upnp) = 0;
//! Get proxy.
virtual bool getProxy(Network net, proxyType &proxy_info) = 0;
//! Get number of connections.
virtual size_t getNodeCount(CConnman::NumConnections flags) = 0;
//! Get stats for connected nodes.
using NodesStats =
std::vector<std::tuple<CNodeStats, bool, CNodeStateStats>>;
virtual bool getNodesStats(NodesStats &stats) = 0;
//! Get ban map entries.
virtual bool getBanned(banmap_t &banmap) = 0;
//! Ban node.
virtual bool ban(const CNetAddr &net_addr, BanReason reason,
int64_t ban_time_offset) = 0;
//! Unban node.
virtual bool unban(const CSubNet &ip) = 0;
//! Disconnect node.
virtual bool disconnect(NodeId id) = 0;
//! Get total bytes recv.
virtual int64_t getTotalBytesRecv() = 0;
//! Get total bytes sent.
virtual int64_t getTotalBytesSent() = 0;
//! Get mempool size.
virtual size_t getMempoolSize() = 0;
//! Get mempool dynamic usage.
virtual size_t getMempoolDynamicUsage() = 0;
//! Get header tip height and time.
virtual bool getHeaderTip(int &height, int64_t &block_time) = 0;
//! Get num blocks.
virtual int getNumBlocks() = 0;
//! Get last block time.
virtual int64_t getLastBlockTime() = 0;
//! Get verification progress.
virtual double getVerificationProgress() = 0;
//! Is initial block download.
virtual bool isInitialBlockDownload() = 0;
//! Get reindex.
virtual bool getReindex() = 0;
//! Get importing.
virtual bool getImporting() = 0;
//! Set network active.
virtual void setNetworkActive(bool active) = 0;
//! Get network active.
virtual bool getNetworkActive() = 0;
+ //! Get max tx fee.
+ virtual Amount getMaxTxFee() = 0;
+
//! Execute rpc command.
virtual UniValue executeRpc(Config &config, const std::string &command,
const UniValue &params,
const std::string &uri) = 0;
//! List rpc commands.
virtual std::vector<std::string> listRpcCommands() = 0;
//! Set RPC timer interface if unset.
virtual void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) = 0;
//! Unset RPC timer interface.
virtual void rpcUnsetTimerInterface(RPCTimerInterface *iface) = 0;
+ //! Return interfaces for accessing wallets (if any).
+ virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
+
//! Register handler for init messages.
using InitMessageFn = std::function<void(const std::string &message)>;
virtual std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) = 0;
//! Register handler for message box messages.
using MessageBoxFn =
std::function<bool(const std::string &message,
const std::string &caption, unsigned int style)>;
virtual std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) = 0;
//! Register handler for question messages.
using QuestionFn = std::function<bool(
const std::string &message, const std::string &non_interactive_message,
const std::string &caption, unsigned int style)>;
virtual std::unique_ptr<Handler> handleQuestion(QuestionFn fn) = 0;
//! Register handler for progress messages.
using ShowProgressFn = std::function<void(
const std::string &title, int progress, bool resume_possible)>;
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
//! Register handler for load wallet messages.
using LoadWalletFn = std::function<void(std::unique_ptr<Wallet> wallet)>;
virtual std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) = 0;
//! Register handler for number of connections changed messages.
using NotifyNumConnectionsChangedFn =
std::function<void(int new_num_connections)>;
virtual std::unique_ptr<Handler>
handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0;
//! Register handler for network active messages.
using NotifyNetworkActiveChangedFn =
std::function<void(bool network_active)>;
virtual std::unique_ptr<Handler>
handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) = 0;
//! Register handler for notify alert messages.
using NotifyAlertChangedFn = std::function<void()>;
virtual std::unique_ptr<Handler>
handleNotifyAlertChanged(NotifyAlertChangedFn fn) = 0;
//! Register handler for ban list messages.
using BannedListChangedFn = std::function<void()>;
virtual std::unique_ptr<Handler>
handleBannedListChanged(BannedListChangedFn fn) = 0;
//! Register handler for block tip messages.
using NotifyBlockTipFn =
std::function<void(bool initial_download, int height,
int64_t block_time, double verification_progress)>;
virtual std::unique_ptr<Handler>
handleNotifyBlockTip(NotifyBlockTipFn fn) = 0;
//! Register handler for header tip messages.
using NotifyHeaderTipFn =
std::function<void(bool initial_download, int height,
int64_t block_time, double verification_progress)>;
virtual std::unique_ptr<Handler>
handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0;
};
//! Return implementation of Node interface.
std::unique_ptr<Node> MakeNode();
} // namespace interfaces
#endif // BITCOIN_INTERFACES_NODE_H
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 3667e2d7c3..b6525415b1 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -1,33 +1,232 @@
// Copyright (c) 2018 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 <interfaces/wallet.h>
+#include <amount.h>
+#include <chain.h>
+#include <consensus/validation.h>
#include <interfaces/handler.h>
+#include <net.h>
+#include <policy/policy.h>
+#include <primitives/transaction.h>
+#include <script/ismine.h>
+#include <script/standard.h>
+#include <support/allocators/secure.h>
+#include <sync.h>
+#include <ui_interface.h>
+#include <validation.h>
#include <wallet/wallet.h>
#include <memory>
namespace interfaces {
namespace {
+ class PendingWalletTxImpl : public PendingWalletTx {
+ public:
+ PendingWalletTxImpl(CWallet &wallet)
+ : m_wallet(wallet), m_key(&wallet) {}
+
+ const CTransaction &get() override { return *m_tx; }
+
+ bool commit(WalletValueMap value_map, WalletOrderForm order_form,
+ std::string from_account,
+ std::string &reject_reason) override {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ CValidationState state;
+ if (!m_wallet.CommitTransaction(
+ m_tx, std::move(value_map), std::move(order_form),
+ std::move(from_account), m_key, g_connman.get(), state)) {
+ reject_reason = state.GetRejectReason();
+ return false;
+ }
+ return true;
+ }
+
+ CTransactionRef m_tx;
+ CWallet &m_wallet;
+ CReserveKey m_key;
+ };
+
class WalletImpl : public Wallet {
public:
WalletImpl(CWallet &wallet) : m_wallet(wallet) {}
+ bool encryptWallet(const SecureString &wallet_passphrase) override {
+ return m_wallet.EncryptWallet(wallet_passphrase);
+ }
+ bool isCrypted() override { return m_wallet.IsCrypted(); }
+ bool lock() override { return m_wallet.Lock(); }
+ bool unlock(const SecureString &wallet_passphrase) override {
+ return m_wallet.Unlock(wallet_passphrase);
+ }
+ bool isLocked() override { return m_wallet.IsLocked(); }
+ bool changeWalletPassphrase(
+ const SecureString &old_wallet_passphrase,
+ const SecureString &new_wallet_passphrase) override {
+ return m_wallet.ChangeWalletPassphrase(old_wallet_passphrase,
+ new_wallet_passphrase);
+ }
+ bool backupWallet(const std::string &filename) override {
+ return m_wallet.BackupWallet(filename);
+ }
+ std::string getWalletName() override { return m_wallet.GetName(); }
+ const CChainParams &getChainParams() override {
+ return m_wallet.chainParams;
+ }
+ bool getPubKey(const CKeyID &address, CPubKey &pub_key) override {
+ return m_wallet.GetPubKey(address, pub_key);
+ }
+ bool getPrivKey(const CKeyID &address, CKey &key) override {
+ return m_wallet.GetKey(address, key);
+ }
+ bool isSpendable(const CTxDestination &dest) override {
+ return IsMine(m_wallet, dest) & ISMINE_SPENDABLE;
+ }
+ bool haveWatchOnly() override { return m_wallet.HaveWatchOnly(); };
+ bool setAddressBook(const CTxDestination &dest, const std::string &name,
+ const std::string &purpose) override {
+ return m_wallet.SetAddressBook(dest, name, purpose);
+ }
+ bool getAddress(const CTxDestination &dest, std::string *name,
+ isminetype *is_mine) override {
+ LOCK(m_wallet.cs_wallet);
+ auto it = m_wallet.mapAddressBook.find(dest);
+ if (it == m_wallet.mapAddressBook.end()) {
+ return false;
+ }
+ if (name) {
+ *name = it->second.name;
+ }
+ if (is_mine) {
+ *is_mine = IsMine(m_wallet, dest);
+ }
+ return true;
+ }
+ bool addDestData(const CTxDestination &dest, const std::string &key,
+ const std::string &value) override {
+ LOCK(m_wallet.cs_wallet);
+ return m_wallet.AddDestData(dest, key, value);
+ }
+ bool eraseDestData(const CTxDestination &dest,
+ const std::string &key) override {
+ LOCK(m_wallet.cs_wallet);
+ return m_wallet.EraseDestData(dest, key);
+ }
+ std::vector<std::string>
+ getDestValues(const std::string &prefix) override {
+ return m_wallet.GetDestValues(prefix);
+ }
+ void lockCoin(const COutPoint &output) override {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ return m_wallet.LockCoin(output);
+ }
+ void unlockCoin(const COutPoint &output) override {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ return m_wallet.UnlockCoin(output);
+ }
+ bool isLockedCoin(const COutPoint &output) override {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ return m_wallet.IsLockedCoin(output.GetTxId(), output.GetN());
+ }
+ void listLockedCoins(std::vector<COutPoint> &outputs) override {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ return m_wallet.ListLockedCoins(outputs);
+ }
+ std::unique_ptr<PendingWalletTx>
+ createTransaction(const std::vector<CRecipient> &recipients,
+ const CCoinControl &coin_control, bool sign,
+ int &change_pos, Amount &fee,
+ std::string &fail_reason) override {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ auto pending = std::make_unique<PendingWalletTxImpl>(m_wallet);
+ if (!m_wallet.CreateTransaction(recipients, pending->m_tx,
+ pending->m_key, fee, change_pos,
+ fail_reason, coin_control, sign)) {
+ return {};
+ }
+ return std::move(pending);
+ }
+ bool transactionCanBeAbandoned(const TxId &txid) override {
+ return m_wallet.TransactionCanBeAbandoned(txid);
+ }
+ bool abandonTransaction(const TxId &txid) override {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ return m_wallet.AbandonTransaction(txid);
+ }
+ WalletBalances getBalances() override {
+ WalletBalances result;
+ result.balance = m_wallet.GetBalance();
+ result.unconfirmed_balance = m_wallet.GetUnconfirmedBalance();
+ result.immature_balance = m_wallet.GetImmatureBalance();
+ result.have_watch_only = m_wallet.HaveWatchOnly();
+ if (result.have_watch_only) {
+ result.watch_only_balance = m_wallet.GetWatchOnlyBalance();
+ result.unconfirmed_watch_only_balance =
+ m_wallet.GetUnconfirmedWatchOnlyBalance();
+ result.immature_watch_only_balance =
+ m_wallet.GetImmatureWatchOnlyBalance();
+ }
+ return result;
+ }
+ bool tryGetBalances(WalletBalances &balances,
+ int &num_blocks) override {
+ TRY_LOCK(cs_main, locked_chain);
+ if (!locked_chain) {
+ return false;
+ }
+ TRY_LOCK(m_wallet.cs_wallet, locked_wallet);
+ if (!locked_wallet) {
+ return false;
+ }
+ balances = getBalances();
+ num_blocks = ::chainActive.Height();
+ return true;
+ }
+ Amount getBalance() override { return m_wallet.GetBalance(); }
+ Amount getAvailableBalance(const CCoinControl &coin_control) override {
+ return m_wallet.GetAvailableBalance(&coin_control);
+ }
+ bool hdEnabled() override { return m_wallet.IsHDEnabled(); }
std::unique_ptr<Handler>
handleShowProgress(ShowProgressFn fn) override {
return MakeHandler(m_wallet.ShowProgress.connect(fn));
}
+ std::unique_ptr<Handler>
+ handleStatusChanged(StatusChangedFn fn) override {
+ return MakeHandler(m_wallet.NotifyStatusChanged.connect(
+ [fn](CCryptoKeyStore *) { fn(); }));
+ }
+ std::unique_ptr<Handler>
+ handleAddressBookChanged(AddressBookChangedFn fn) override {
+ return MakeHandler(m_wallet.NotifyAddressBookChanged.connect(
+ [fn](CWallet *, const CTxDestination &address,
+ const std::string &label, bool is_mine,
+ const std::string &purpose, ChangeType status) {
+ fn(address, label, is_mine, purpose, status);
+ }));
+ }
+ std::unique_ptr<Handler>
+ handleTransactionChanged(TransactionChangedFn fn) override {
+ return MakeHandler(m_wallet.NotifyTransactionChanged.connect(
+ [fn, this](CWallet *, const TxId &txid, ChangeType status) {
+ fn(txid, status);
+ }));
+ }
+ std::unique_ptr<Handler>
+ handleWatchOnlyChanged(WatchOnlyChangedFn fn) override {
+ return MakeHandler(m_wallet.NotifyWatchonlyChanged.connect(fn));
+ }
CWallet &m_wallet;
};
} // namespace
std::unique_ptr<Wallet> MakeWallet(CWallet &wallet) {
return std::make_unique<WalletImpl>(wallet);
}
} // namespace interfaces
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 7e0b856a69..e7cd542d59 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -1,35 +1,221 @@
// Copyright (c) 2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_INTERFACES_WALLET_H
#define BITCOIN_INTERFACES_WALLET_H
+#include <amount.h> // For Amount
+#include <script/ismine.h> // For isminefilter, isminetype
+#include <script/standard.h> // For CTxDestination
+#include <support/allocators/secure.h> // For SecureString
+#include <ui_interface.h> // For ChangeType
+
+#include <cstdint>
#include <functional>
+#include <map>
#include <memory>
#include <string>
+#include <utility>
+#include <vector>
+class CChainParams;
+class CCoinControl;
+class CKey;
+class CMutableTransaction;
+class COutPoint;
+class CTransaction;
class CWallet;
+enum class OutputType;
+struct CRecipient;
+struct TxId;
namespace interfaces {
class Handler;
+class PendingWalletTx;
+struct WalletBalances;
+
+using WalletOrderForm = std::vector<std::pair<std::string, std::string>>;
+using WalletValueMap = std::map<std::string, std::string>;
//! Interface for accessing a wallet.
class Wallet {
public:
virtual ~Wallet() {}
+ //! Encrypt wallet.
+ virtual bool encryptWallet(const SecureString &wallet_passphrase) = 0;
+
+ //! Return whether wallet is encrypted.
+ virtual bool isCrypted() = 0;
+
+ //! Lock wallet.
+ virtual bool lock() = 0;
+
+ //! Unlock wallet.
+ virtual bool unlock(const SecureString &wallet_passphrase) = 0;
+
+ //! Return whether wallet is locked.
+ virtual bool isLocked() = 0;
+
+ //! Change wallet passphrase.
+ virtual bool
+ changeWalletPassphrase(const SecureString &old_wallet_passphrase,
+ const SecureString &new_wallet_passphrase) = 0;
+
+ //! Back up wallet.
+ virtual bool backupWallet(const std::string &filename) = 0;
+
+ //! Get wallet name.
+ virtual std::string getWalletName() = 0;
+
+ //! Get chainparams.
+ virtual const CChainParams &getChainParams() = 0;
+
+ //! Get public key.
+ virtual bool getPubKey(const CKeyID &address, CPubKey &pub_key) = 0;
+
+ //! Get private key.
+ virtual bool getPrivKey(const CKeyID &address, CKey &key) = 0;
+
+ //! Return whether wallet has private key.
+ virtual bool isSpendable(const CTxDestination &dest) = 0;
+
+ //! Return whether wallet has watch only keys.
+ virtual bool haveWatchOnly() = 0;
+
+ //! Add or update address.
+ virtual bool setAddressBook(const CTxDestination &dest,
+ const std::string &name,
+ const std::string &purpose) = 0;
+
+ //! Look up address in wallet, return whether exists.
+ virtual bool getAddress(const CTxDestination &dest,
+ std::string *name = nullptr,
+ isminetype *is_mine = nullptr) = 0;
+
+ //! Add dest data.
+ virtual bool addDestData(const CTxDestination &dest, const std::string &key,
+ const std::string &value) = 0;
+
+ //! Erase dest data.
+ virtual bool eraseDestData(const CTxDestination &dest,
+ const std::string &key) = 0;
+
+ //! Get dest values with prefix.
+ virtual std::vector<std::string>
+ getDestValues(const std::string &prefix) = 0;
+
+ //! Lock coin.
+ virtual void lockCoin(const COutPoint &output) = 0;
+
+ //! Unlock coin.
+ virtual void unlockCoin(const COutPoint &output) = 0;
+
+ //! Return whether coin is locked.
+ virtual bool isLockedCoin(const COutPoint &output) = 0;
+
+ //! List locked coins.
+ virtual void listLockedCoins(std::vector<COutPoint> &outputs) = 0;
+
+ //! Create transaction.
+ virtual std::unique_ptr<PendingWalletTx>
+ createTransaction(const std::vector<CRecipient> &recipients,
+ const CCoinControl &coin_control, bool sign,
+ int &change_pos, Amount &fee,
+ std::string &fail_reason) = 0;
+
+ //! Return whether transaction can be abandoned.
+ virtual bool transactionCanBeAbandoned(const TxId &txid) = 0;
+
+ //! Abandon transaction.
+ virtual bool abandonTransaction(const TxId &txid) = 0;
+
+ //! Get balances.
+ virtual WalletBalances getBalances() = 0;
+
+ //! Get balances if possible without blocking.
+ virtual bool tryGetBalances(WalletBalances &balances, int &num_blocks) = 0;
+
+ //! Get balance.
+ virtual Amount getBalance() = 0;
+
+ //! Get available balance.
+ virtual Amount getAvailableBalance(const CCoinControl &coin_control) = 0;
+
+ // Return whether HD enabled.
+ virtual bool hdEnabled() = 0;
+
//! Register handler for show progress messages.
using ShowProgressFn =
std::function<void(const std::string &title, int progress)>;
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
+
+ //! Register handler for status changed messages.
+ using StatusChangedFn = std::function<void()>;
+ virtual std::unique_ptr<Handler>
+ handleStatusChanged(StatusChangedFn fn) = 0;
+
+ //! Register handler for address book changed messages.
+ using AddressBookChangedFn = std::function<void(
+ const CTxDestination &address, const std::string &label, bool is_mine,
+ const std::string &purpose, ChangeType status)>;
+ virtual std::unique_ptr<Handler>
+ handleAddressBookChanged(AddressBookChangedFn fn) = 0;
+
+ //! Register handler for transaction changed messages.
+ using TransactionChangedFn =
+ std::function<void(const TxId &txid, ChangeType status)>;
+ virtual std::unique_ptr<Handler>
+ handleTransactionChanged(TransactionChangedFn fn) = 0;
+
+ //! Register handler for watchonly changed messages.
+ using WatchOnlyChangedFn = std::function<void(bool have_watch_only)>;
+ virtual std::unique_ptr<Handler>
+ handleWatchOnlyChanged(WatchOnlyChangedFn fn) = 0;
+};
+
+//! Tracking object returned by CreateTransaction and passed to
+//! CommitTransaction.
+class PendingWalletTx {
+public:
+ virtual ~PendingWalletTx() {}
+
+ //! Get transaction data.
+ virtual const CTransaction &get() = 0;
+
+ //! Send pending transaction and commit to wallet.
+ virtual bool commit(WalletValueMap value_map, WalletOrderForm order_form,
+ std::string from_account,
+ std::string &reject_reason) = 0;
+};
+
+//! Collection of wallet balances.
+struct WalletBalances {
+ Amount balance = Amount::zero();
+ Amount unconfirmed_balance = Amount::zero();
+ Amount immature_balance = Amount::zero();
+ bool have_watch_only = false;
+ Amount watch_only_balance = Amount::zero();
+ Amount unconfirmed_watch_only_balance = Amount::zero();
+ Amount immature_watch_only_balance = Amount::zero();
+
+ bool balanceChanged(const WalletBalances &prev) const {
+ return balance != prev.balance ||
+ unconfirmed_balance != prev.unconfirmed_balance ||
+ immature_balance != prev.immature_balance ||
+ watch_only_balance != prev.watch_only_balance ||
+ unconfirmed_watch_only_balance !=
+ prev.unconfirmed_watch_only_balance ||
+ immature_watch_only_balance != prev.immature_watch_only_balance;
+ }
};
//! Return implementation of Wallet interface. This function will be undefined
//! in builds where ENABLE_WALLET is false.
std::unique_ptr<Wallet> MakeWallet(CWallet &wallet);
} // namespace interfaces
#endif // BITCOIN_INTERFACES_WALLET_H
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 046d8f7b19..32289ed927 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -1,799 +1,801 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include "bitcoingui.h"
#include "chainparams.h"
#include "clientmodel.h"
#include "config.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "httprpc.h"
#include "intro.h"
#include "networkstyle.h"
#include "optionsmodel.h"
#include "platformstyle.h"
#include "splashscreen.h"
#include "utilitydialog.h"
#include "winshutdownmonitor.h"
#ifdef ENABLE_WALLET
#include "paymentserver.h"
#include "walletmodel.h"
#endif
#include "init.h"
#include "interfaces/handler.h"
#include "interfaces/node.h"
#include "rpc/server.h"
#include "ui_interface.h"
#include "uint256.h"
#include "util.h"
#include "warnings.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif
#include "walletinitinterface.h"
#include <cstdint>
#include <boost/filesystem/operations.hpp>
#include <boost/thread.hpp>
#include <QApplication>
#include <QDebug>
#include <QLibraryInfo>
#include <QLocale>
#include <QMessageBox>
#include <QSettings>
#include <QStringList>
#include <QThread>
#include <QTimer>
#include <QTranslator>
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
#if QT_VERSION < 0x050400
Q_IMPORT_PLUGIN(AccessibleFactory)
#endif
#if defined(QT_QPA_PLATFORM_XCB)
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
#elif defined(QT_QPA_PLATFORM_WINDOWS)
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
#elif defined(QT_QPA_PLATFORM_COCOA)
Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
#endif
#endif
// Declare meta types used for QMetaObject::invokeMethod
Q_DECLARE_METATYPE(bool *)
Q_DECLARE_METATYPE(Amount)
Q_DECLARE_METATYPE(uint256)
// Config is non-copyable so we can only register pointers to it
Q_DECLARE_METATYPE(Config *)
static void InitMessage(const std::string &message) {
LogPrintf("init message: %s\n", message);
}
/**
* Translate string to current locale using Qt.
*/
static std::string Translate(const char *psz) {
return QCoreApplication::translate("bitcoin-abc", psz).toStdString();
}
static QString GetLangTerritory() {
QSettings settings;
// Get desired locale (e.g. "de_DE")
// 1) System default language
QString lang_territory = QLocale::system().name();
// 2) Language from QSettings
QString lang_territory_qsettings =
settings.value("language", "").toString();
if (!lang_territory_qsettings.isEmpty()) {
lang_territory = lang_territory_qsettings;
}
// 3) -lang command line argument
lang_territory = QString::fromStdString(
gArgs.GetArg("-lang", lang_territory.toStdString()));
return lang_territory;
}
/** Set up translations */
static void initTranslations(QTranslator &qtTranslatorBase,
QTranslator &qtTranslator,
QTranslator &translatorBase,
QTranslator &translator) {
// Remove old translators
QApplication::removeTranslator(&qtTranslatorBase);
QApplication::removeTranslator(&qtTranslator);
QApplication::removeTranslator(&translatorBase);
QApplication::removeTranslator(&translator);
// Get desired locale (e.g. "de_DE")
// 1) System default language
QString lang_territory = GetLangTerritory();
// Convert to "de" only by truncating "_DE"
QString lang = lang_territory;
lang.truncate(lang_territory.lastIndexOf('_'));
// Load language files for configured locale:
// - First load the translator for the base language, without territory
// - Then load the more specific locale translator
// Load e.g. qt_de.qm
if (qtTranslatorBase.load(
"qt_" + lang,
QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
QApplication::installTranslator(&qtTranslatorBase);
}
// Load e.g. qt_de_DE.qm
if (qtTranslator.load(
"qt_" + lang_territory,
QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
QApplication::installTranslator(&qtTranslator);
}
// Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in
// bitcoin.qrc)
if (translatorBase.load(lang, ":/translations/")) {
QApplication::installTranslator(&translatorBase);
}
// Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in
// bitcoin.qrc)
if (translator.load(lang_territory, ":/translations/")) {
QApplication::installTranslator(&translator);
}
}
/* qDebug() message handler --> debug.log */
void DebugMessageHandler(QtMsgType type, const QMessageLogContext &context,
const QString &msg) {
Q_UNUSED(context);
if (type == QtDebugMsg) {
LogPrint(BCLog::QT, "GUI: %s\n", msg.toStdString());
} else {
LogPrintf("GUI: %s\n", msg.toStdString());
}
}
/**
* Class encapsulating Bitcoin ABC startup and shutdown.
* Allows running startup and shutdown in a different thread from the UI thread.
*/
class BitcoinABC : public QObject {
Q_OBJECT
public:
explicit BitcoinABC(interfaces::Node &node);
public Q_SLOTS:
void initialize(Config *config,
HTTPRPCRequestProcessor *httpRPCRequestProcessor);
void shutdown();
Q_SIGNALS:
void initializeResult(bool success);
void shutdownResult();
void runawayException(const QString &message);
private:
/// Pass fatal exception message to UI thread
void handleRunawayException(const std::exception *e);
interfaces::Node &m_node;
};
/** Main Bitcoin application object */
class BitcoinApplication : public QApplication {
Q_OBJECT
public:
explicit BitcoinApplication(interfaces::Node &node, int &argc, char **argv);
~BitcoinApplication();
#ifdef ENABLE_WALLET
/// Create payment server
void createPaymentServer();
#endif
/// parameter interaction/setup based on rules
void parameterSetup();
/// Create options model
void createOptionsModel(bool resetSettings);
/// Create main window
void createWindow(const Config *, const NetworkStyle *networkStyle);
/// Create splash screen
void createSplashScreen(const NetworkStyle *networkStyle);
/// Request core initialization
void requestInitialize(Config &config,
HTTPRPCRequestProcessor &httpRPCRequestProcessor);
/// Request core shutdown
void requestShutdown(Config &config);
/// Get process return value
int getReturnValue() const { return returnValue; }
/// Get window identifier of QMainWindow (BitcoinGUI)
WId getMainWinId() const;
public Q_SLOTS:
void initializeResult(bool success);
void shutdownResult();
/// Handle runaway exceptions. Shows a message box with the problem and
/// quits the program.
void handleRunawayException(const QString &message);
Q_SIGNALS:
void requestedInitialize(Config *config,
HTTPRPCRequestProcessor *httpRPCRequestProcessor);
void requestedShutdown();
void stopThread();
void splashFinished(QWidget *window);
private:
QThread *coreThread;
interfaces::Node &m_node;
OptionsModel *optionsModel;
ClientModel *clientModel;
BitcoinGUI *window;
QTimer *pollShutdownTimer;
#ifdef ENABLE_WALLET
PaymentServer *paymentServer;
std::vector<WalletModel *> m_wallet_models;
#endif
int returnValue;
const PlatformStyle *platformStyle;
std::unique_ptr<QWidget> shutdownWindow;
void startThread();
};
#include "bitcoin.moc"
BitcoinABC::BitcoinABC(interfaces::Node &node) : QObject(), m_node(node) {}
void BitcoinABC::handleRunawayException(const std::exception *e) {
PrintExceptionContinue(e, "Runaway exception");
Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings("gui")));
}
void BitcoinABC::initialize(Config *cfg,
HTTPRPCRequestProcessor *httpRPCRequestProcessor) {
Config &config(*cfg);
try {
qDebug() << __func__ << ": Running initialization in thread";
bool rv = m_node.appInitMain(config, *httpRPCRequestProcessor);
Q_EMIT initializeResult(rv);
} catch (const std::exception &e) {
handleRunawayException(&e);
} catch (...) {
handleRunawayException(nullptr);
}
}
void BitcoinABC::shutdown() {
try {
qDebug() << __func__ << ": Running Shutdown in thread";
m_node.appShutdown();
qDebug() << __func__ << ": Shutdown finished";
Q_EMIT shutdownResult();
} catch (const std::exception &e) {
handleRunawayException(&e);
} catch (...) {
handleRunawayException(nullptr);
}
}
BitcoinApplication::BitcoinApplication(interfaces::Node &node, int &argc,
char **argv)
: QApplication(argc, argv), coreThread(0), m_node(node), optionsModel(0),
clientModel(0), window(0), pollShutdownTimer(0),
#ifdef ENABLE_WALLET
paymentServer(0), m_wallet_models(),
#endif
returnValue(0) {
setQuitOnLastWindowClosed(false);
// UI per-platform customization.
// This must be done inside the BitcoinApplication constructor, or after it,
// because PlatformStyle::instantiate requires a QApplication.
std::string platformName;
platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM);
platformStyle =
PlatformStyle::instantiate(QString::fromStdString(platformName));
// Fall back to "other" if specified name not found.
if (!platformStyle) {
platformStyle = PlatformStyle::instantiate("other");
}
assert(platformStyle);
}
BitcoinApplication::~BitcoinApplication() {
if (coreThread) {
qDebug() << __func__ << ": Stopping thread";
Q_EMIT stopThread();
coreThread->wait();
qDebug() << __func__ << ": Stopped thread";
}
delete window;
window = 0;
#ifdef ENABLE_WALLET
delete paymentServer;
paymentServer = 0;
#endif
delete optionsModel;
optionsModel = 0;
delete platformStyle;
platformStyle = 0;
}
#ifdef ENABLE_WALLET
void BitcoinApplication::createPaymentServer() {
paymentServer = new PaymentServer(this);
}
#endif
void BitcoinApplication::createOptionsModel(bool resetSettings) {
optionsModel = new OptionsModel(m_node, nullptr, resetSettings);
}
void BitcoinApplication::createWindow(const Config *config,
const NetworkStyle *networkStyle) {
window = new BitcoinGUI(m_node, config, platformStyle, networkStyle, 0);
pollShutdownTimer = new QTimer(window);
connect(pollShutdownTimer, SIGNAL(timeout()), window,
SLOT(detectShutdown()));
}
void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) {
SplashScreen *splash = new SplashScreen(m_node, 0, networkStyle);
// We don't hold a direct pointer to the splash screen after creation, but
// the splash screen will take care of deleting itself when slotFinish
// happens.
splash->show();
connect(this, SIGNAL(splashFinished(QWidget *)), splash,
SLOT(slotFinish(QWidget *)));
connect(this, SIGNAL(requestedShutdown()), splash, SLOT(close()));
}
void BitcoinApplication::startThread() {
if (coreThread) {
return;
}
coreThread = new QThread(this);
BitcoinABC *executor = new BitcoinABC(m_node);
executor->moveToThread(coreThread);
/* communication to and from thread */
connect(executor, SIGNAL(initializeResult(bool)), this,
SLOT(initializeResult(bool)));
connect(executor, SIGNAL(shutdownResult()), this, SLOT(shutdownResult()));
connect(executor, SIGNAL(runawayException(QString)), this,
SLOT(handleRunawayException(QString)));
// Note on how Qt works: it tries to directly invoke methods if the signal
// is emitted on the same thread that the target object 'lives' on.
// But if the target object 'lives' on another thread (executor here does)
// the SLOT will be invoked asynchronously at a later time in the thread
// of the target object. So.. we pass a pointer around. If you pass
// a reference around (even if it's non-const) you'll get Qt generating
// code to copy-construct the parameter in question (Q_DECLARE_METATYPE
// and qRegisterMetaType generate this code). For the Config class,
// which is noncopyable, we can't do this. So.. we have to pass
// pointers to Config around. Make sure Config &/Config * isn't a
// temporary (eg it lives somewhere aside from the stack) or this will
// crash because initialize() gets executed in another thread at some
// unspecified time (after) requestedInitialize() is emitted!
connect(this,
SIGNAL(requestedInitialize(Config *, HTTPRPCRequestProcessor *)),
executor, SLOT(initialize(Config *, HTTPRPCRequestProcessor *)));
connect(this, SIGNAL(requestedShutdown()), executor, SLOT(shutdown()));
/* make sure executor object is deleted in its own thread */
connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
connect(this, SIGNAL(stopThread()), coreThread, SLOT(quit()));
coreThread->start();
}
void BitcoinApplication::parameterSetup() {
m_node.initLogging();
m_node.initParameterInteraction();
}
void BitcoinApplication::requestInitialize(
Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor) {
qDebug() << __func__ << ": Requesting initialize";
startThread();
// IMPORTANT: config must NOT be a reference to a temporary because below
// signal may be connected to a slot that will be executed as a queued
// connection in another thread!
Q_EMIT requestedInitialize(&config, &httpRPCRequestProcessor);
}
void BitcoinApplication::requestShutdown(Config &config) {
// Show a simple window indicating shutdown status. Do this first as some of
// the steps may take some time below, for example the RPC console may still
// be executing a command.
shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
qDebug() << __func__ << ": Requesting shutdown";
startThread();
window->hide();
window->setClientModel(0);
pollShutdownTimer->stop();
#ifdef ENABLE_WALLET
window->removeAllWallets();
for (WalletModel *walletModel : m_wallet_models) {
delete walletModel;
}
m_wallet_models.clear();
#endif
delete clientModel;
clientModel = 0;
m_node.startShutdown();
// Request shutdown from core thread
Q_EMIT requestedShutdown();
}
void BitcoinApplication::initializeResult(bool success) {
qDebug() << __func__ << ": Initialization result: " << success;
returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE;
if (!success) {
// Make sure splash screen doesn't stick around during shutdown.
Q_EMIT splashFinished(window);
// Exit first main loop invocation.
quit();
return;
}
// Log this only after AppInit2 finishes, as then logging setup is
// guaranteed complete.
qWarning() << "Platform customization:" << platformStyle->getName();
#ifdef ENABLE_WALLET
PaymentServer::LoadRootCAs();
paymentServer->setOptionsModel(optionsModel);
#endif
clientModel = new ClientModel(m_node, optionsModel);
window->setClientModel(clientModel);
#ifdef ENABLE_WALLET
bool fFirstWallet = true;
- for (CWalletRef pwallet : vpwallets) {
- WalletModel *const walletModel =
- new WalletModel(platformStyle, pwallet, optionsModel);
+ auto wallets = m_node.getWallets();
+ auto cwallet = ::vpwallets.begin();
+ for (auto &wallet : wallets) {
+ WalletModel *const walletModel = new WalletModel(
+ std::move(wallet), m_node, platformStyle, *cwallet++, optionsModel);
window->addWallet(walletModel);
if (fFirstWallet) {
window->setCurrentWallet(walletModel->getWalletName());
fFirstWallet = false;
}
connect(walletModel,
SIGNAL(coinsSent(CWallet *, SendCoinsRecipient, QByteArray)),
paymentServer,
SLOT(fetchPaymentACK(CWallet *, const SendCoinsRecipient &,
QByteArray)));
m_wallet_models.push_back(walletModel);
}
#endif
// If -min option passed, start window minimized.
if (gArgs.GetBoolArg("-min", false)) {
window->showMinimized();
} else {
window->show();
}
Q_EMIT splashFinished(window);
#ifdef ENABLE_WALLET
// Now that initialization/startup is done, process any command-line
// bitcoincash: URIs or payment requests:
connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)),
window, SLOT(handlePaymentRequest(SendCoinsRecipient)));
connect(window, SIGNAL(receivedURI(QString)), paymentServer,
SLOT(handleURIOrFile(QString)));
connect(paymentServer, SIGNAL(message(QString, QString, unsigned int)),
window, SLOT(message(QString, QString, unsigned int)));
QTimer::singleShot(100, paymentServer, SLOT(uiReady()));
#endif
pollShutdownTimer->start(200);
}
void BitcoinApplication::shutdownResult() {
// Exit second main loop invocation after shutdown finished.
quit();
}
void BitcoinApplication::handleRunawayException(const QString &message) {
QMessageBox::critical(
0, "Runaway exception",
BitcoinGUI::tr("A fatal error occurred. Bitcoin can no longer continue "
"safely and will quit.") +
QString("\n\n") + message);
::exit(EXIT_FAILURE);
}
WId BitcoinApplication::getMainWinId() const {
if (!window) {
return 0;
}
return window->winId();
}
#ifndef BITCOIN_QT_TEST
static void MigrateSettings() {
assert(!QApplication::applicationName().isEmpty());
static const QString legacyAppName("Bitcoin-Qt"),
#ifdef Q_OS_DARWIN
// Macs and/or iOS et al use a domain-style name for Settings
// files. All other platforms use a simple orgname. This
// difference is documented in the QSettings class documentation.
legacyOrg("bitcoin.org");
#else
legacyOrg("Bitcoin");
#endif
QSettings
// below picks up settings file location based on orgname,appname
legacy(legacyOrg, legacyAppName),
// default c'tor below picks up settings file location based on
// QApplication::applicationName(), et al -- which was already set
// in main()
abc;
#ifdef Q_OS_DARWIN
// Disable bogus OSX keys from MacOS system-wide prefs that may cloud our
// judgement ;) (this behavior is also documented in QSettings docs)
legacy.setFallbacksEnabled(false);
abc.setFallbacksEnabled(false);
#endif
const QStringList legacyKeys(legacy.allKeys());
// We only migrate settings if we have Core settings but no Bitcoin-ABC
// settings
if (!legacyKeys.isEmpty() && abc.allKeys().isEmpty()) {
for (const QString &key : legacyKeys) {
// now, copy settings over
abc.setValue(key, legacy.value(key));
}
}
}
int main(int argc, char *argv[]) {
SetupEnvironment();
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
/// 1. Parse command-line options. These take precedence over anything else.
// Command-line options take precedence:
node->parseParameters(argc, argv);
// Do not refer to data directory yet, this can be overridden by
// Intro::pickDataDirectory
/// 2. Basic Qt initialization (not dependent on parameters or
/// configuration)
Q_INIT_RESOURCE(bitcoin);
Q_INIT_RESOURCE(bitcoin_locale);
BitcoinApplication app(*node, argc, argv);
#if QT_VERSION > 0x050100
// Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
#if QT_VERSION >= 0x050600
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
#ifdef Q_OS_MAC
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType<bool *>();
// Need to pass name here as Amount is a typedef (see
// http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
// IMPORTANT if it is no longer a typedef use the normal variant above
qRegisterMetaType<Amount>("Amount");
qRegisterMetaType<std::function<void(void)>>("std::function<void(void)>");
// Need to register any types Qt doesn't know about if you intend
// to use them with the signal/slot mechanism Qt provides. Even pointers.
// Note that class Config is noncopyable and so we can't register a
// non-pointer version of it with Qt, because Qt expects to be able to
// copy-construct non-pointers to objects for invoking slots
// behind-the-scenes in the 'Queued' connection case.
qRegisterMetaType<Config *>();
/// 3. Application identification
// must be set before OptionsModel is initialized or translations are
// loaded, as it is used to locate QSettings.
// Note: If you move these calls somewhere else, be sure to bring
// MigrateSettings() below along for the ride.
QApplication::setOrganizationName(QAPP_ORG_NAME);
QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN);
QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT);
// Migrate settings from core's/our old GUI settings to Bitcoin ABC
// only if core's exist but Bitcoin ABC's doesn't.
// NOTE -- this function needs to be called *after* the above 3 lines
// that set the app orgname and app name! If you move the above 3 lines
// to elsewhere, take this call with you!
MigrateSettings();
GUIUtil::SubstituteFonts(GetLangTerritory());
/// 4. Initialization of translations, so that intro dialog is in user's
/// language. Now that QSettings are accessible, initialize translations.
QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
initTranslations(qtTranslatorBase, qtTranslator, translatorBase,
translator);
translationInterface.Translate.connect(Translate);
// Show help message immediately after parsing command-line options (for
// "-lang") and setting locale, but before showing splash screen.
if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
HelpMessageDialog help(*node, nullptr, gArgs.IsArgSet("-version"));
help.showOrPrint();
return EXIT_SUCCESS;
}
/// 5. Now that settings and translations are available, ask user for data
/// directory. User language is set up: pick a data directory.
if (!Intro::pickDataDirectory(*node)) {
return EXIT_SUCCESS;
}
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes.
if (!fs::is_directory(GetDataDir(false))) {
QMessageBox::critical(
0, QObject::tr(PACKAGE_NAME),
QObject::tr(
"Error: Specified data directory \"%1\" does not exist.")
.arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
return EXIT_FAILURE;
}
try {
node->readConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
} catch (const std::exception &e) {
QMessageBox::critical(
0, QObject::tr(PACKAGE_NAME),
QObject::tr("Error: Cannot parse configuration file: %1. Only use "
"key=value syntax.")
.arg(e.what()));
return EXIT_FAILURE;
}
/// 7. Determine network (and switch to network specific options)
// - Do not call Params() before this step.
// - Do this after parsing the configuration file, as the network can be
// switched there.
// - QSettings() will use the new application name after this, resulting in
// network-specific settings.
// - Needs to be done before createOptionsModel.
// Check for -testnet or -regtest parameter (Params() calls are only valid
// after this clause)
try {
node->selectParams(gArgs.GetChainName());
} catch (std::exception &e) {
QMessageBox::critical(0, QObject::tr(PACKAGE_NAME),
QObject::tr("Error: %1").arg(e.what()));
return EXIT_FAILURE;
}
#ifdef ENABLE_WALLET
// Parse URIs on command line -- this can affect Params()
PaymentServer::ipcParseCommandLine(argc, argv);
#endif
QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(
QString::fromStdString(Params().NetworkIDString())));
assert(!networkStyle.isNull());
// Allow for separate UI settings for testnets
QApplication::setApplicationName(networkStyle->getAppName());
// Re-initialize translations after changing application name (language in
// network-specific settings can be different)
initTranslations(qtTranslatorBase, qtTranslator, translatorBase,
translator);
#ifdef ENABLE_WALLET
/// 8. URI IPC sending
// - Do this early as we don't want to bother initializing if we are just
// calling IPC
// - Do this *after* setting up the data directory, as the data directory
// hash is used in the name
// of the server.
// - Do this after creating app and setting up translations, so errors are
// translated properly.
if (PaymentServer::ipcSendCommandLine()) {
exit(EXIT_SUCCESS);
}
// Start up the payment server early, too, so impatient users that click on
// bitcoincash: links repeatedly have their payment requests routed to this
// process:
app.createPaymentServer();
#endif
/// 9. Main GUI initialization
// Install global event filter that makes sure that long tooltips can be
// word-wrapped.
app.installEventFilter(
new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
#if defined(Q_OS_WIN)
// Install global event filter for processing Windows session related
// Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
qApp->installNativeEventFilter(new WinShutdownMonitor());
#endif
// Install qDebug() message handler to route to debug.log
qInstallMessageHandler(DebugMessageHandler);
// Allow parameter interaction before we create the options model
app.parameterSetup();
// Load GUI settings from QSettings
app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
// Subscribe to global signals from core
std::unique_ptr<interfaces::Handler> handler =
node->handleInitMessage(InitMessage);
// Get global config
Config &config = const_cast<Config &>(GetConfig());
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) &&
!gArgs.GetBoolArg("-min", false)) {
app.createSplashScreen(networkStyle.data());
}
RPCServer rpcServer;
HTTPRPCRequestProcessor httpRPCRequestProcessor(config, rpcServer);
try {
app.createWindow(&config, networkStyle.data());
// Perform base initialization before spinning up
// initialization/shutdown thread. This is acceptable because this
// function only contains steps that are quick to execute, so the GUI
// thread won't be held up.
if (!node->baseInitialize(config, rpcServer)) {
// A dialog with detailed error will have been shown by InitError()
return EXIT_FAILURE;
}
app.requestInitialize(config, httpRPCRequestProcessor);
#if defined(Q_OS_WIN)
WinShutdownMonitor::registerShutdownBlockReason(
QObject::tr("%1 didn't yet exit safely...")
.arg(QObject::tr(PACKAGE_NAME)),
(HWND)app.getMainWinId());
#endif
app.exec();
app.requestShutdown(config);
app.exec();
return app.getReturnValue();
} catch (const std::exception &e) {
PrintExceptionContinue(&e, "Runaway exception");
app.handleRunawayException(
QString::fromStdString(node->getWarnings("gui")));
} catch (...) {
PrintExceptionContinue(nullptr, "Runaway exception");
app.handleRunawayException(
QString::fromStdString(node->getWarnings("gui")));
}
return EXIT_FAILURE;
}
#endif // BITCOIN_QT_TEST
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 9acace2f71..e20defb49d 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -1,1333 +1,1333 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include "bitcoingui.h"
#include "bitcoinunits.h"
#include "clientmodel.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "modaloverlay.h"
#include "networkstyle.h"
#include "notificator.h"
#include "openuridialog.h"
#include "optionsdialog.h"
#include "optionsmodel.h"
#include "platformstyle.h"
#include "rpcconsole.h"
#include "utilitydialog.h"
#ifdef ENABLE_WALLET
#include "walletframe.h"
#include "walletmodel.h"
#include "walletview.h"
#endif // ENABLE_WALLET
#ifdef Q_OS_MAC
#include "macdockiconhandler.h"
#endif
#include "chainparams.h"
#include "init.h"
#include "interfaces/handler.h"
#include "interfaces/node.h"
#include "ui_interface.h"
#include "util.h"
#include <iostream>
#include <QAction>
#include <QApplication>
#include <QComboBox>
#include <QDateTime>
#include <QDesktopWidget>
#include <QDragEnterEvent>
#include <QListWidget>
#include <QMenuBar>
#include <QMessageBox>
#include <QMimeData>
#include <QProgressDialog>
#include <QSettings>
#include <QShortcut>
#include <QStackedWidget>
#include <QStatusBar>
#include <QStyle>
#include <QTimer>
#include <QToolBar>
#include <QUrlQuery>
#include <QVBoxLayout>
const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
#if defined(Q_OS_MAC)
"macosx"
#elif defined(Q_OS_WIN)
"windows"
#else
"other"
#endif
;
BitcoinGUI::BitcoinGUI(interfaces::Node &node, const Config *configIn,
const PlatformStyle *_platformStyle,
const NetworkStyle *networkStyle, QWidget *parent)
: QMainWindow(parent), enableWallet(false), m_node(node),
platformStyle(_platformStyle), config(configIn) {
QSettings settings;
if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) {
// Restore failed (perhaps missing setting), center the window
move(QApplication::desktop()->availableGeometry().center() -
frameGeometry().center());
}
QString windowTitle = tr(PACKAGE_NAME) + " - ";
#ifdef ENABLE_WALLET
enableWallet = WalletModel::isWalletEnabled();
#endif // ENABLE_WALLET
if (enableWallet) {
windowTitle += tr("Wallet");
} else {
windowTitle += tr("Node");
}
windowTitle += " " + networkStyle->getTitleAddText();
#ifndef Q_OS_MAC
QApplication::setWindowIcon(networkStyle->getTrayAndWindowIcon());
setWindowIcon(networkStyle->getTrayAndWindowIcon());
#else
MacDockIconHandler::instance()->setIcon(networkStyle->getAppIcon());
#endif
setWindowTitle(windowTitle);
rpcConsole = new RPCConsole(node, _platformStyle, 0);
helpMessageDialog = new HelpMessageDialog(node, this, false);
#ifdef ENABLE_WALLET
if (enableWallet) {
/** Create wallet frame and make it the central widget */
walletFrame = new WalletFrame(_platformStyle, config, this);
setCentralWidget(walletFrame);
} else
#endif // ENABLE_WALLET
{
/**
* When compiled without wallet or -disablewallet is provided, the
* central widget is the rpc console.
*/
setCentralWidget(rpcConsole);
}
// Accept D&D of URIs
setAcceptDrops(true);
// Create actions for the toolbar, menu bar and tray/dock icon
// Needs walletFrame to be initialized
createActions();
// Create application menu bar
createMenuBar();
// Create the toolbars
createToolBars();
// Create system tray icon and notification
createTrayIcon(networkStyle);
// Create status bar
statusBar();
// Disable size grip because it looks ugly and nobody needs it
statusBar()->setSizeGripEnabled(false);
// Status bar notification icons
QFrame *frameBlocks = new QFrame();
frameBlocks->setContentsMargins(0, 0, 0, 0);
frameBlocks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
frameBlocksLayout->setContentsMargins(3, 0, 3, 0);
frameBlocksLayout->setSpacing(3);
unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle);
labelWalletEncryptionIcon = new QLabel();
labelWalletHDStatusIcon = new QLabel();
connectionsControl = new GUIUtil::ClickableLabel();
labelBlocksIcon = new GUIUtil::ClickableLabel();
if (enableWallet) {
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(unitDisplayControl);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelWalletEncryptionIcon);
frameBlocksLayout->addWidget(labelWalletHDStatusIcon);
}
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(connectionsControl);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelBlocksIcon);
frameBlocksLayout->addStretch();
// Progress bar and label for blocks download
progressBarLabel = new QLabel();
progressBarLabel->setVisible(false);
progressBar = new GUIUtil::ProgressBar();
progressBar->setAlignment(Qt::AlignCenter);
progressBar->setVisible(false);
// Override style sheet for progress bar for styles that have a segmented
// progress bar, as they make the text unreadable (workaround for issue
// #1071)
// See https://doc.qt.io/qt-5/gallery.html
QString curStyle = QApplication::style()->metaObject()->className();
if (curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle") {
progressBar->setStyleSheet(
"QProgressBar { background-color: #e8e8e8; border: 1px solid grey; "
"border-radius: 7px; padding: 1px; text-align: center; } "
"QProgressBar::chunk { background: QLinearGradient(x1: 0, y1: 0, "
"x2: 1, y2: 0, stop: 0 #FF8000, stop: 1 orange); border-radius: "
"7px; margin: 0px; }");
}
statusBar()->addWidget(progressBarLabel);
statusBar()->addWidget(progressBar);
statusBar()->addPermanentWidget(frameBlocks);
// Install event filter to be able to catch status tip events
// (QEvent::StatusTip)
this->installEventFilter(this);
// Initially wallet actions should be disabled
setWalletActionsEnabled(false);
// Subscribe to notifications from core
subscribeToCoreSignals();
connect(connectionsControl, SIGNAL(clicked(QPoint)), this,
SLOT(toggleNetworkActive()));
modalOverlay = new ModalOverlay(this->centralWidget());
#ifdef ENABLE_WALLET
if (enableWallet) {
connect(walletFrame, SIGNAL(requestedSyncWarningInfo()), this,
SLOT(showModalOverlay()));
connect(labelBlocksIcon, SIGNAL(clicked(QPoint)), this,
SLOT(showModalOverlay()));
connect(progressBar, SIGNAL(clicked(QPoint)), this,
SLOT(showModalOverlay()));
}
#endif
}
BitcoinGUI::~BitcoinGUI() {
// Unsubscribe from notifications from core
unsubscribeFromCoreSignals();
QSettings settings;
settings.setValue("MainWindowGeometry", saveGeometry());
// Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
if (trayIcon) {
trayIcon->hide();
}
#ifdef Q_OS_MAC
delete appMenuBar;
MacDockIconHandler::cleanup();
#endif
delete rpcConsole;
}
void BitcoinGUI::createActions() {
QActionGroup *tabGroup = new QActionGroup(this);
overviewAction =
new QAction(platformStyle->SingleColorIcon(":/icons/overview"),
tr("&Overview"), this);
overviewAction->setStatusTip(tr("Show general overview of wallet"));
overviewAction->setToolTip(overviewAction->statusTip());
overviewAction->setCheckable(true);
overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1));
tabGroup->addAction(overviewAction);
sendCoinsAction = new QAction(
platformStyle->SingleColorIcon(":/icons/send"), tr("&Send"), this);
sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address"));
sendCoinsAction->setToolTip(sendCoinsAction->statusTip());
sendCoinsAction->setCheckable(true);
sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2));
tabGroup->addAction(sendCoinsAction);
sendCoinsMenuAction =
new QAction(platformStyle->TextColorIcon(":/icons/send"),
sendCoinsAction->text(), this);
sendCoinsMenuAction->setStatusTip(sendCoinsAction->statusTip());
sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip());
receiveCoinsAction = new QAction(
platformStyle->SingleColorIcon(":/icons/receiving_addresses"),
tr("&Receive"), this);
receiveCoinsAction->setStatusTip(
tr("Request payments (generates QR codes and %1: URIs)")
.arg(GUIUtil::bitcoinURIScheme(*config)));
receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip());
receiveCoinsAction->setCheckable(true);
receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3));
tabGroup->addAction(receiveCoinsAction);
receiveCoinsMenuAction =
new QAction(platformStyle->TextColorIcon(":/icons/receiving_addresses"),
receiveCoinsAction->text(), this);
receiveCoinsMenuAction->setStatusTip(receiveCoinsAction->statusTip());
receiveCoinsMenuAction->setToolTip(receiveCoinsMenuAction->statusTip());
historyAction =
new QAction(platformStyle->SingleColorIcon(":/icons/history"),
tr("&Transactions"), this);
historyAction->setStatusTip(tr("Browse transaction history"));
historyAction->setToolTip(historyAction->statusTip());
historyAction->setCheckable(true);
historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4));
tabGroup->addAction(historyAction);
#ifdef ENABLE_WALLET
// These showNormalIfMinimized are needed because Send Coins and Receive
// Coins can be triggered from the tray menu, and need to show the GUI to be
// useful.
connect(overviewAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(overviewAction, SIGNAL(triggered()), this,
SLOT(gotoOverviewPage()));
connect(sendCoinsAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(sendCoinsAction, SIGNAL(triggered()), this,
SLOT(gotoSendCoinsPage()));
connect(sendCoinsMenuAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(sendCoinsMenuAction, SIGNAL(triggered()), this,
SLOT(gotoSendCoinsPage()));
connect(receiveCoinsAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(receiveCoinsAction, SIGNAL(triggered()), this,
SLOT(gotoReceiveCoinsPage()));
connect(receiveCoinsMenuAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(receiveCoinsMenuAction, SIGNAL(triggered()), this,
SLOT(gotoReceiveCoinsPage()));
connect(historyAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
#endif // ENABLE_WALLET
quitAction = new QAction(platformStyle->TextColorIcon(":/icons/quit"),
tr("E&xit"), this);
quitAction->setStatusTip(tr("Quit application"));
quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
quitAction->setMenuRole(QAction::QuitRole);
aboutAction = new QAction(platformStyle->TextColorIcon(":/icons/about"),
tr("&About %1").arg(tr(PACKAGE_NAME)), this);
aboutAction->setStatusTip(
tr("Show information about %1").arg(tr(PACKAGE_NAME)));
aboutAction->setMenuRole(QAction::AboutRole);
aboutAction->setEnabled(false);
aboutQtAction =
new QAction(platformStyle->TextColorIcon(":/icons/about_qt"),
tr("About &Qt"), this);
aboutQtAction->setStatusTip(tr("Show information about Qt"));
aboutQtAction->setMenuRole(QAction::AboutQtRole);
optionsAction = new QAction(platformStyle->TextColorIcon(":/icons/options"),
tr("&Options..."), this);
optionsAction->setStatusTip(
tr("Modify configuration options for %1").arg(tr(PACKAGE_NAME)));
optionsAction->setMenuRole(QAction::PreferencesRole);
optionsAction->setEnabled(false);
toggleHideAction =
new QAction(platformStyle->TextColorIcon(":/icons/about"),
tr("&Show / Hide"), this);
toggleHideAction->setStatusTip(tr("Show or hide the main Window"));
encryptWalletAction =
new QAction(platformStyle->TextColorIcon(":/icons/lock_closed"),
tr("&Encrypt Wallet..."), this);
encryptWalletAction->setStatusTip(
tr("Encrypt the private keys that belong to your wallet"));
encryptWalletAction->setCheckable(true);
backupWalletAction =
new QAction(platformStyle->TextColorIcon(":/icons/filesave"),
tr("&Backup Wallet..."), this);
backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
changePassphraseAction =
new QAction(platformStyle->TextColorIcon(":/icons/key"),
tr("&Change Passphrase..."), this);
changePassphraseAction->setStatusTip(
tr("Change the passphrase used for wallet encryption"));
signMessageAction =
new QAction(platformStyle->TextColorIcon(":/icons/edit"),
tr("Sign &message..."), this);
signMessageAction->setStatusTip(
tr("Sign messages with your Bitcoin addresses to prove you own them"));
verifyMessageAction =
new QAction(platformStyle->TextColorIcon(":/icons/verify"),
tr("&Verify message..."), this);
verifyMessageAction->setStatusTip(
tr("Verify messages to ensure they were signed with specified Bitcoin "
"addresses"));
openRPCConsoleAction =
new QAction(platformStyle->TextColorIcon(":/icons/debugwindow"),
tr("&Debug window"), this);
openRPCConsoleAction->setStatusTip(
tr("Open debugging and diagnostic console"));
// initially disable the debug window menu item
openRPCConsoleAction->setEnabled(false);
usedSendingAddressesAction =
new QAction(platformStyle->TextColorIcon(":/icons/address-book"),
tr("&Sending addresses..."), this);
usedSendingAddressesAction->setStatusTip(
tr("Show the list of used sending addresses and labels"));
usedReceivingAddressesAction =
new QAction(platformStyle->TextColorIcon(":/icons/address-book"),
tr("&Receiving addresses..."), this);
usedReceivingAddressesAction->setStatusTip(
tr("Show the list of used receiving addresses and labels"));
openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"),
tr("Open &URI..."), this);
openAction->setStatusTip(tr("Open a %1: URI or payment request")
.arg(GUIUtil::bitcoinURIScheme(*config)));
showHelpMessageAction =
new QAction(platformStyle->TextColorIcon(":/icons/info"),
tr("&Command-line options"), this);
showHelpMessageAction->setMenuRole(QAction::NoRole);
showHelpMessageAction->setStatusTip(
tr("Show the %1 help message to get a list with possible Bitcoin "
"command-line options")
.arg(tr(PACKAGE_NAME)));
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked()));
connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked()));
connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden()));
connect(showHelpMessageAction, SIGNAL(triggered()), this,
SLOT(showHelpMessageClicked()));
connect(openRPCConsoleAction, SIGNAL(triggered()), this,
SLOT(showDebugWindow()));
// prevents an open debug window from becoming stuck/unusable on client
// shutdown
connect(quitAction, SIGNAL(triggered()), rpcConsole, SLOT(hide()));
#ifdef ENABLE_WALLET
if (walletFrame) {
connect(encryptWalletAction, SIGNAL(triggered(bool)), walletFrame,
SLOT(encryptWallet(bool)));
connect(backupWalletAction, SIGNAL(triggered()), walletFrame,
SLOT(backupWallet()));
connect(changePassphraseAction, SIGNAL(triggered()), walletFrame,
SLOT(changePassphrase()));
connect(signMessageAction, SIGNAL(triggered()), this,
SLOT(gotoSignMessageTab()));
connect(verifyMessageAction, SIGNAL(triggered()), this,
SLOT(gotoVerifyMessageTab()));
connect(usedSendingAddressesAction, SIGNAL(triggered()), walletFrame,
SLOT(usedSendingAddresses()));
connect(usedReceivingAddressesAction, SIGNAL(triggered()), walletFrame,
SLOT(usedReceivingAddresses()));
connect(openAction, SIGNAL(triggered()), this, SLOT(openClicked()));
}
#endif // ENABLE_WALLET
new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C), this,
SLOT(showDebugWindowActivateConsole()));
new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_D), this,
SLOT(showDebugWindow()));
}
void BitcoinGUI::createMenuBar() {
#ifdef Q_OS_MAC
// Create a decoupled menu bar on Mac which stays even if the window is
// closed
appMenuBar = new QMenuBar();
#else
// Get the main window's menu bar on other platforms
appMenuBar = menuBar();
#endif
// Configure the menus
QMenu *file = appMenuBar->addMenu(tr("&File"));
if (walletFrame) {
file->addAction(openAction);
file->addAction(backupWalletAction);
file->addAction(signMessageAction);
file->addAction(verifyMessageAction);
file->addSeparator();
file->addAction(usedSendingAddressesAction);
file->addAction(usedReceivingAddressesAction);
file->addSeparator();
}
file->addAction(quitAction);
QMenu *settings = appMenuBar->addMenu(tr("&Settings"));
if (walletFrame) {
settings->addAction(encryptWalletAction);
settings->addAction(changePassphraseAction);
settings->addSeparator();
}
settings->addAction(optionsAction);
QMenu *help = appMenuBar->addMenu(tr("&Help"));
if (walletFrame) {
help->addAction(openRPCConsoleAction);
}
help->addAction(showHelpMessageAction);
help->addSeparator();
help->addAction(aboutAction);
help->addAction(aboutQtAction);
}
void BitcoinGUI::createToolBars() {
if (walletFrame) {
QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
appToolBar = toolbar;
toolbar->setContextMenuPolicy(Qt::PreventContextMenu);
toolbar->setMovable(false);
toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolbar->addAction(overviewAction);
toolbar->addAction(sendCoinsAction);
toolbar->addAction(receiveCoinsAction);
toolbar->addAction(historyAction);
overviewAction->setChecked(true);
#ifdef ENABLE_WALLET
QWidget *spacer = new QWidget();
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
toolbar->addWidget(spacer);
m_wallet_selector = new QComboBox();
connect(m_wallet_selector, SIGNAL(currentIndexChanged(const QString &)),
this, SLOT(setCurrentWallet(const QString &)));
#endif
}
}
void BitcoinGUI::setClientModel(ClientModel *_clientModel) {
this->clientModel = _clientModel;
if (_clientModel) {
// Create system tray menu (or setup the dock menu) that late to prevent
// users from calling actions, while the client has not yet fully loaded
createTrayIconMenu();
// Keep up to date with client
updateNetworkState();
connect(_clientModel, SIGNAL(numConnectionsChanged(int)), this,
SLOT(setNumConnections(int)));
connect(_clientModel, SIGNAL(networkActiveChanged(bool)), this,
SLOT(setNetworkActive(bool)));
modalOverlay->setKnownBestHeight(
_clientModel->getHeaderTipHeight(),
QDateTime::fromTime_t(_clientModel->getHeaderTipTime()));
setNumBlocks(m_node.getNumBlocks(),
QDateTime::fromTime_t(m_node.getLastBlockTime()),
m_node.getVerificationProgress(), false);
connect(_clientModel,
SIGNAL(numBlocksChanged(int, QDateTime, double, bool)), this,
SLOT(setNumBlocks(int, QDateTime, double, bool)));
// Receive and report messages from client model
connect(_clientModel, SIGNAL(message(QString, QString, unsigned int)),
this, SLOT(message(QString, QString, unsigned int)));
// Show progress dialog
connect(_clientModel, SIGNAL(showProgress(QString, int)), this,
SLOT(showProgress(QString, int)));
rpcConsole->setClientModel(_clientModel);
#ifdef ENABLE_WALLET
if (walletFrame) {
walletFrame->setClientModel(_clientModel);
}
#endif // ENABLE_WALLET
unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel());
OptionsModel *optionsModel = _clientModel->getOptionsModel();
if (optionsModel) {
// be aware of the tray icon disable state change reported by the
// OptionsModel object.
connect(optionsModel, SIGNAL(hideTrayIconChanged(bool)), this,
SLOT(setTrayIconVisible(bool)));
// initialize the disable state of the tray icon with the current
// value in the model.
setTrayIconVisible(optionsModel->getHideTrayIcon());
}
} else {
// Disable possibility to show main window via action
toggleHideAction->setEnabled(false);
if (trayIconMenu) {
// Disable context menu on tray icon
trayIconMenu->clear();
}
// Propagate cleared model to child objects
rpcConsole->setClientModel(nullptr);
#ifdef ENABLE_WALLET
if (walletFrame) {
walletFrame->setClientModel(nullptr);
}
#endif // ENABLE_WALLET
unitDisplayControl->setOptionsModel(nullptr);
}
}
#ifdef ENABLE_WALLET
bool BitcoinGUI::addWallet(WalletModel *walletModel) {
if (!walletFrame) return false;
const QString name = walletModel->getWalletName();
setWalletActionsEnabled(true);
m_wallet_selector->addItem(name);
if (m_wallet_selector->count() == 2) {
m_wallet_selector_label = new QLabel();
m_wallet_selector_label->setText(tr("Wallet:") + " ");
m_wallet_selector_label->setBuddy(m_wallet_selector);
appToolBar->addWidget(m_wallet_selector_label);
appToolBar->addWidget(m_wallet_selector);
}
rpcConsole->addWallet(walletModel);
return walletFrame->addWallet(walletModel);
}
bool BitcoinGUI::setCurrentWallet(const QString &name) {
if (!walletFrame) return false;
return walletFrame->setCurrentWallet(name);
}
void BitcoinGUI::removeAllWallets() {
if (!walletFrame) return;
setWalletActionsEnabled(false);
walletFrame->removeAllWallets();
}
#endif // ENABLE_WALLET
void BitcoinGUI::setWalletActionsEnabled(bool enabled) {
overviewAction->setEnabled(enabled);
sendCoinsAction->setEnabled(enabled);
sendCoinsMenuAction->setEnabled(enabled);
receiveCoinsAction->setEnabled(enabled);
receiveCoinsMenuAction->setEnabled(enabled);
historyAction->setEnabled(enabled);
encryptWalletAction->setEnabled(enabled);
backupWalletAction->setEnabled(enabled);
changePassphraseAction->setEnabled(enabled);
signMessageAction->setEnabled(enabled);
verifyMessageAction->setEnabled(enabled);
usedSendingAddressesAction->setEnabled(enabled);
usedReceivingAddressesAction->setEnabled(enabled);
openAction->setEnabled(enabled);
}
void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle) {
#ifndef Q_OS_MAC
trayIcon = new QSystemTrayIcon(this);
QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " +
networkStyle->getTitleAddText();
trayIcon->setToolTip(toolTip);
trayIcon->setIcon(networkStyle->getTrayAndWindowIcon());
trayIcon->hide();
#endif
notificator =
new Notificator(QApplication::applicationName(), trayIcon, this);
}
void BitcoinGUI::createTrayIconMenu() {
#ifndef Q_OS_MAC
// return if trayIcon is unset (only on non-Mac OSes)
if (!trayIcon) return;
trayIconMenu = new QMenu(this);
trayIcon->setContextMenu(trayIconMenu);
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
#else
// Note: On Mac, the dock icon is used to provide the tray's functionality.
MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
dockIconHandler->setMainWindow(static_cast<QMainWindow *>(this));
trayIconMenu = dockIconHandler->dockMenu();
#endif
// Configuration of the tray icon (or dock icon) icon menu
trayIconMenu->addAction(toggleHideAction);
trayIconMenu->addSeparator();
trayIconMenu->addAction(sendCoinsMenuAction);
trayIconMenu->addAction(receiveCoinsMenuAction);
trayIconMenu->addSeparator();
trayIconMenu->addAction(signMessageAction);
trayIconMenu->addAction(verifyMessageAction);
trayIconMenu->addSeparator();
trayIconMenu->addAction(optionsAction);
trayIconMenu->addAction(openRPCConsoleAction);
#ifndef Q_OS_MAC // This is built-in on Mac
trayIconMenu->addSeparator();
trayIconMenu->addAction(quitAction);
#endif
}
#ifndef Q_OS_MAC
void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) {
if (reason == QSystemTrayIcon::Trigger) {
// Click on system tray icon triggers show/hide of the main window
toggleHidden();
}
}
#endif
void BitcoinGUI::optionsClicked() {
if (!clientModel || !clientModel->getOptionsModel()) return;
OptionsDialog dlg(this, enableWallet);
dlg.setModel(clientModel->getOptionsModel());
dlg.exec();
}
void BitcoinGUI::aboutClicked() {
if (!clientModel) return;
HelpMessageDialog dlg(m_node, this, true);
dlg.exec();
}
void BitcoinGUI::showDebugWindow() {
rpcConsole->showNormal();
rpcConsole->show();
rpcConsole->raise();
rpcConsole->activateWindow();
}
void BitcoinGUI::showDebugWindowActivateConsole() {
rpcConsole->setTabFocus(RPCConsole::TAB_CONSOLE);
showDebugWindow();
}
void BitcoinGUI::showHelpMessageClicked() {
helpMessageDialog->show();
}
#ifdef ENABLE_WALLET
void BitcoinGUI::openClicked() {
OpenURIDialog dlg(config, this);
if (dlg.exec()) {
Q_EMIT receivedURI(dlg.getURI());
}
}
void BitcoinGUI::gotoOverviewPage() {
overviewAction->setChecked(true);
if (walletFrame) walletFrame->gotoOverviewPage();
}
void BitcoinGUI::gotoHistoryPage() {
historyAction->setChecked(true);
if (walletFrame) walletFrame->gotoHistoryPage();
}
void BitcoinGUI::gotoReceiveCoinsPage() {
receiveCoinsAction->setChecked(true);
if (walletFrame) walletFrame->gotoReceiveCoinsPage();
}
void BitcoinGUI::gotoSendCoinsPage(QString addr) {
sendCoinsAction->setChecked(true);
if (walletFrame) walletFrame->gotoSendCoinsPage(addr);
}
void BitcoinGUI::gotoSignMessageTab(QString addr) {
if (walletFrame) walletFrame->gotoSignMessageTab(addr);
}
void BitcoinGUI::gotoVerifyMessageTab(QString addr) {
if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
}
#endif // ENABLE_WALLET
void BitcoinGUI::updateNetworkState() {
int count = clientModel->getNumConnections();
QString icon;
switch (count) {
case 0:
icon = ":/icons/connect_0";
break;
case 1:
case 2:
case 3:
icon = ":/icons/connect_1";
break;
case 4:
case 5:
case 6:
icon = ":/icons/connect_2";
break;
case 7:
case 8:
case 9:
icon = ":/icons/connect_3";
break;
default:
icon = ":/icons/connect_4";
break;
}
QString tooltip;
if (m_node.getNetworkActive()) {
tooltip = tr("%n active connection(s) to Bitcoin network", "", count) +
QString(".<br>") + tr("Click to disable network activity.");
} else {
tooltip = tr("Network activity disabled.") + QString("<br>") +
tr("Click to enable network activity again.");
icon = ":/icons/network_disabled";
}
// Don't word-wrap this (fixed-width) tooltip
tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
connectionsControl->setToolTip(tooltip);
connectionsControl->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(
STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
}
void BitcoinGUI::setNumConnections(int count) {
updateNetworkState();
}
void BitcoinGUI::setNetworkActive(bool networkActive) {
updateNetworkState();
}
void BitcoinGUI::updateHeadersSyncProgressLabel() {
int64_t headersTipTime = clientModel->getHeaderTipTime();
int headersTipHeight = clientModel->getHeaderTipHeight();
int estHeadersLeft = (GetTime() - headersTipTime) /
Params().GetConsensus().nPowTargetSpacing;
if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC) {
progressBarLabel->setText(
tr("Syncing Headers (%1%)...")
.arg(QString::number(100.0 /
(headersTipHeight + estHeadersLeft) *
headersTipHeight,
'f', 1)));
}
}
void BitcoinGUI::setNumBlocks(int count, const QDateTime &blockDate,
double nVerificationProgress, bool header) {
if (modalOverlay) {
if (header) {
modalOverlay->setKnownBestHeight(count, blockDate);
} else {
modalOverlay->tipUpdate(count, blockDate, nVerificationProgress);
}
}
if (!clientModel) {
return;
}
// Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait
// until chain-sync starts -> garbled text)
statusBar()->clearMessage();
// Acquire current block source
enum BlockSource blockSource = clientModel->getBlockSource();
switch (blockSource) {
case BlockSource::NETWORK:
if (header) {
updateHeadersSyncProgressLabel();
return;
}
progressBarLabel->setText(tr("Synchronizing with network..."));
updateHeadersSyncProgressLabel();
break;
case BlockSource::DISK:
if (header) {
progressBarLabel->setText(tr("Indexing blocks on disk..."));
} else {
progressBarLabel->setText(tr("Processing blocks on disk..."));
}
break;
case BlockSource::REINDEX:
progressBarLabel->setText(tr("Reindexing blocks on disk..."));
break;
case BlockSource::NONE:
if (header) {
return;
}
progressBarLabel->setText(tr("Connecting to peers..."));
break;
}
QString tooltip;
QDateTime currentDate = QDateTime::currentDateTime();
qint64 secs = blockDate.secsTo(currentDate);
tooltip = tr("Processed %n block(s) of transaction history.", "", count);
// Set icon state: spinning if catching up, tick otherwise
if (secs < 90 * 60) {
tooltip = tr("Up to date") + QString(".<br>") + tooltip;
labelBlocksIcon->setPixmap(
platformStyle->SingleColorIcon(":/icons/synced")
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
#ifdef ENABLE_WALLET
if (walletFrame) {
walletFrame->showOutOfSyncWarning(false);
modalOverlay->showHide(true, true);
}
#endif // ENABLE_WALLET
progressBarLabel->setVisible(false);
progressBar->setVisible(false);
} else {
QString timeBehindText = GUIUtil::formatNiceTimeOffset(secs);
progressBarLabel->setVisible(true);
progressBar->setFormat(tr("%1 behind").arg(timeBehindText));
progressBar->setMaximum(1000000000);
progressBar->setValue(nVerificationProgress * 1000000000.0 + 0.5);
progressBar->setVisible(true);
tooltip = tr("Catching up...") + QString("<br>") + tooltip;
if (count != prevBlocks) {
labelBlocksIcon->setPixmap(
platformStyle
->SingleColorIcon(QString(":/movies/spinner-%1")
.arg(spinnerFrame, 3, 10, QChar('0')))
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES;
}
prevBlocks = count;
#ifdef ENABLE_WALLET
if (walletFrame) {
walletFrame->showOutOfSyncWarning(true);
modalOverlay->showHide();
}
#endif // ENABLE_WALLET
tooltip += QString("<br>");
tooltip +=
tr("Last received block was generated %1 ago.").arg(timeBehindText);
tooltip += QString("<br>");
tooltip += tr("Transactions after this will not yet be visible.");
}
// Don't word-wrap this (fixed-width) tooltip
tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
labelBlocksIcon->setToolTip(tooltip);
progressBarLabel->setToolTip(tooltip);
progressBar->setToolTip(tooltip);
}
void BitcoinGUI::message(const QString &title, const QString &message,
unsigned int style, bool *ret) {
// default title
QString strTitle = tr("Bitcoin");
// Default to information icon
int nMBoxIcon = QMessageBox::Information;
int nNotifyIcon = Notificator::Information;
QString msgType;
// Prefer supplied title over style based title
if (!title.isEmpty()) {
msgType = title;
} else {
switch (style) {
case CClientUIInterface::MSG_ERROR:
msgType = tr("Error");
break;
case CClientUIInterface::MSG_WARNING:
msgType = tr("Warning");
break;
case CClientUIInterface::MSG_INFORMATION:
msgType = tr("Information");
break;
default:
break;
}
}
// Append title to "Bitcoin - "
if (!msgType.isEmpty()) {
strTitle += " - " + msgType;
}
// Check for error/warning icon
if (style & CClientUIInterface::ICON_ERROR) {
nMBoxIcon = QMessageBox::Critical;
nNotifyIcon = Notificator::Critical;
} else if (style & CClientUIInterface::ICON_WARNING) {
nMBoxIcon = QMessageBox::Warning;
nNotifyIcon = Notificator::Warning;
}
// Display message
if (style & CClientUIInterface::MODAL) {
// Check for buttons, use OK as default, if none was supplied
QMessageBox::StandardButton buttons;
if (!(buttons = (QMessageBox::StandardButton)(
style & CClientUIInterface::BTN_MASK)))
buttons = QMessageBox::Ok;
showNormalIfMinimized();
QMessageBox mBox(static_cast<QMessageBox::Icon>(nMBoxIcon), strTitle,
message, buttons, this);
int r = mBox.exec();
if (ret != nullptr) {
*ret = r == QMessageBox::Ok;
}
} else
notificator->notify(static_cast<Notificator::Class>(nNotifyIcon),
strTitle, message);
}
void BitcoinGUI::changeEvent(QEvent *e) {
QMainWindow::changeEvent(e);
#ifndef Q_OS_MAC // Ignored on Mac
if (e->type() == QEvent::WindowStateChange) {
if (clientModel && clientModel->getOptionsModel() &&
clientModel->getOptionsModel()->getMinimizeToTray()) {
QWindowStateChangeEvent *wsevt =
static_cast<QWindowStateChangeEvent *>(e);
if (!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized()) {
QTimer::singleShot(0, this, SLOT(hide()));
e->ignore();
}
}
}
#endif
}
void BitcoinGUI::closeEvent(QCloseEvent *event) {
#ifndef Q_OS_MAC // Ignored on Mac
if (clientModel && clientModel->getOptionsModel()) {
if (!clientModel->getOptionsModel()->getMinimizeOnClose()) {
// close rpcConsole in case it was open to make some space for the
// shutdown window
rpcConsole->close();
QApplication::quit();
} else {
QMainWindow::showMinimized();
event->ignore();
}
}
#else
QMainWindow::closeEvent(event);
#endif
}
void BitcoinGUI::showEvent(QShowEvent *event) {
// enable the debug window when the main window shows up
openRPCConsoleAction->setEnabled(true);
aboutAction->setEnabled(true);
optionsAction->setEnabled(true);
}
#ifdef ENABLE_WALLET
void BitcoinGUI::incomingTransaction(const QString &date, int unit,
const Amount amount, const QString &type,
const QString &address,
const QString &label,
const QString &walletName) {
// On new transaction, make an info balloon
QString msg = tr("Date: %1\n").arg(date) +
tr("Amount: %1\n")
.arg(BitcoinUnits::formatWithUnit(unit, amount, true));
- if (WalletModel::isMultiwallet() && !walletName.isEmpty()) {
+ if (m_node.getWallets().size() > 1 && !walletName.isEmpty()) {
msg += tr("Wallet: %1\n").arg(walletName);
}
msg += tr("Type: %1\n").arg(type);
if (!label.isEmpty()) {
msg += tr("Label: %1\n").arg(label);
} else if (!address.isEmpty()) {
msg += tr("Address: %1\n").arg(address);
}
message(amount < Amount::zero() ? tr("Sent transaction")
: tr("Incoming transaction"),
msg, CClientUIInterface::MSG_INFORMATION);
}
#endif // ENABLE_WALLET
void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event) {
// Accept only URIs
if (event->mimeData()->hasUrls()) {
event->acceptProposedAction();
}
}
void BitcoinGUI::dropEvent(QDropEvent *event) {
if (event->mimeData()->hasUrls()) {
for (const QUrl &uri : event->mimeData()->urls()) {
Q_EMIT receivedURI(uri.toString());
}
}
event->acceptProposedAction();
}
bool BitcoinGUI::eventFilter(QObject *object, QEvent *event) {
// Catch status tip events
if (event->type() == QEvent::StatusTip) {
// Prevent adding text from setStatusTip(), if we currently use the
// status bar for displaying other stuff
if (progressBarLabel->isVisible() || progressBar->isVisible()) {
return true;
}
}
return QMainWindow::eventFilter(object, event);
}
#ifdef ENABLE_WALLET
bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient &recipient) {
// URI has to be valid
if (walletFrame && walletFrame->handlePaymentRequest(recipient)) {
showNormalIfMinimized();
gotoSendCoinsPage();
return true;
}
return false;
}
void BitcoinGUI::setHDStatus(int hdEnabled) {
labelWalletHDStatusIcon->setPixmap(
platformStyle
->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled"
: ":/icons/hd_disabled")
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
labelWalletHDStatusIcon->setToolTip(
hdEnabled ? tr("HD key generation is <b>enabled</b>")
: tr("HD key generation is <b>disabled</b>"));
// eventually disable the QLabel to set its opacity to 50%
labelWalletHDStatusIcon->setEnabled(hdEnabled);
}
void BitcoinGUI::setEncryptionStatus(int status) {
switch (status) {
case WalletModel::Unencrypted:
labelWalletEncryptionIcon->hide();
encryptWalletAction->setChecked(false);
changePassphraseAction->setEnabled(false);
encryptWalletAction->setEnabled(true);
break;
case WalletModel::Unlocked:
labelWalletEncryptionIcon->show();
labelWalletEncryptionIcon->setPixmap(
platformStyle->SingleColorIcon(":/icons/lock_open")
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
labelWalletEncryptionIcon->setToolTip(
tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
encryptWalletAction->setEnabled(
false); // TODO: decrypt currently not supported
break;
case WalletModel::Locked:
labelWalletEncryptionIcon->show();
labelWalletEncryptionIcon->setPixmap(
platformStyle->SingleColorIcon(":/icons/lock_closed")
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
labelWalletEncryptionIcon->setToolTip(
tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
encryptWalletAction->setEnabled(
false); // TODO: decrypt currently not supported
break;
}
}
void BitcoinGUI::updateWalletStatus() {
if (!walletFrame) {
return;
}
WalletView *const walletView = walletFrame->currentWalletView();
if (!walletView) {
return;
}
WalletModel *const walletModel = walletView->getWalletModel();
setEncryptionStatus(walletModel->getEncryptionStatus());
- setHDStatus(walletModel->hdEnabled());
+ setHDStatus(walletModel->wallet().hdEnabled());
}
#endif // ENABLE_WALLET
void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) {
if (!clientModel) {
return;
}
// activateWindow() (sometimes) helps with keyboard focus on Windows
if (isHidden()) {
show();
activateWindow();
} else if (isMinimized()) {
showNormal();
activateWindow();
} else if (GUIUtil::isObscured(this)) {
raise();
activateWindow();
} else if (fToggleHidden) {
hide();
}
}
void BitcoinGUI::toggleHidden() {
showNormalIfMinimized(true);
}
void BitcoinGUI::detectShutdown() {
if (m_node.shutdownRequested()) {
if (rpcConsole) {
rpcConsole->hide();
}
qApp->quit();
}
}
void BitcoinGUI::showProgress(const QString &title, int nProgress) {
if (nProgress == 0) {
progressDialog = new QProgressDialog(title, "", 0, 100);
progressDialog->setWindowModality(Qt::ApplicationModal);
progressDialog->setMinimumDuration(0);
progressDialog->setCancelButton(0);
progressDialog->setAutoClose(false);
progressDialog->setValue(0);
} else if (progressDialog) {
if (nProgress == 100) {
progressDialog->close();
progressDialog->deleteLater();
} else {
progressDialog->setValue(nProgress);
}
}
}
void BitcoinGUI::setTrayIconVisible(bool fHideTrayIcon) {
if (trayIcon) {
trayIcon->setVisible(!fHideTrayIcon);
}
}
void BitcoinGUI::showModalOverlay() {
if (modalOverlay &&
(progressBar->isVisible() || modalOverlay->isLayerVisible())) {
modalOverlay->toggleVisibility();
}
}
static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string &message,
const std::string &caption,
unsigned int style) {
bool modal = (style & CClientUIInterface::MODAL);
// The SECURE flag has no effect in the Qt GUI.
// bool secure = (style & CClientUIInterface::SECURE);
style &= ~CClientUIInterface::SECURE;
bool ret = false;
// In case of modal message, use blocking connection to wait for user to
// click a button
QMetaObject::invokeMethod(gui, "message",
modal ? GUIUtil::blockingGUIThreadConnection()
: Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(caption)),
Q_ARG(QString, QString::fromStdString(message)),
Q_ARG(unsigned int, style), Q_ARG(bool *, &ret));
return ret;
}
void BitcoinGUI::subscribeToCoreSignals() {
// Connect signals to client
m_handler_message_box = m_node.handleMessageBox(
boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
m_handler_question = m_node.handleQuestion(
boost::bind(ThreadSafeMessageBox, this, _1, _3, _4));
}
void BitcoinGUI::unsubscribeFromCoreSignals() {
// Disconnect signals from client
m_handler_message_box->disconnect();
m_handler_question->disconnect();
}
void BitcoinGUI::toggleNetworkActive() {
m_node.setNetworkActive(!m_node.getNetworkActive());
}
UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(
const PlatformStyle *platformStyle)
: optionsModel(0), menu(0) {
createContextMenu();
setToolTip(tr("Unit to show amounts in. Click to select another unit."));
QList<BitcoinUnits::Unit> units = BitcoinUnits::availableUnits();
int max_width = 0;
const QFontMetrics fm(font());
for (const BitcoinUnits::Unit unit : units) {
max_width = qMax(max_width, fm.width(BitcoinUnits::name(unit)));
}
setMinimumSize(max_width, 0);
setAlignment(Qt::AlignRight | Qt::AlignVCenter);
setStyleSheet(QString("QLabel { color : %1 }")
.arg(platformStyle->SingleColor().name()));
}
/** So that it responds to button clicks */
void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) {
onDisplayUnitsClicked(event->pos());
}
/** Creates context menu, its actions, and wires up all the relevant signals for
* mouse events. */
void UnitDisplayStatusBarControl::createContextMenu() {
menu = new QMenu(this);
for (BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) {
QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this);
menuAction->setData(QVariant(u));
menu->addAction(menuAction);
}
connect(menu, SIGNAL(triggered(QAction *)), this,
SLOT(onMenuSelection(QAction *)));
}
/** Lets the control know about the Options Model (and its signals) */
void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel) {
if (_optionsModel) {
this->optionsModel = _optionsModel;
// be aware of a display unit change reported by the OptionsModel
// object.
connect(_optionsModel, SIGNAL(displayUnitChanged(int)), this,
SLOT(updateDisplayUnit(int)));
// initialize the display units label with the current value in the
// model.
updateDisplayUnit(_optionsModel->getDisplayUnit());
}
}
/** When Display Units are changed on OptionsModel it will refresh the display
* text of the control on the status bar */
void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits) {
setText(BitcoinUnits::name(newUnits));
}
/** Shows context menu with Display Unit options by the mouse coordinates */
void UnitDisplayStatusBarControl::onDisplayUnitsClicked(const QPoint &point) {
QPoint globalPos = mapToGlobal(point);
menu->exec(globalPos);
}
/** Tells underlying optionsModel to update its current display unit. */
void UnitDisplayStatusBarControl::onMenuSelection(QAction *action) {
if (action) {
optionsModel->setDisplayUnit(action->data());
}
}
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 5f4e25bab4..4deeb972d7 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -1,836 +1,836 @@
// Copyright (c) 2011-2016 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 "coincontroldialog.h"
#include "ui_coincontroldialog.h"
#include "addresstablemodel.h"
#include "bitcoinunits.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "platformstyle.h"
#include "txmempool.h"
#include "walletmodel.h"
#include "dstencode.h"
#include "init.h"
#include "policy/policy.h"
#include "validation.h" // For mempool
#include "wallet/coincontrol.h"
#include "wallet/fees.h"
#include "wallet/wallet.h"
#include <QApplication>
#include <QCheckBox>
#include <QCursor>
#include <QDialogButtonBox>
#include <QFlags>
#include <QIcon>
#include <QSettings>
#include <QString>
#include <QTreeWidget>
#include <QTreeWidgetItem>
QList<Amount> CoinControlDialog::payAmounts;
bool CoinControlDialog::fSubtractFeeFromAmount = false;
bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const {
int column = treeWidget()->sortColumn();
if (column == CoinControlDialog::COLUMN_AMOUNT ||
column == CoinControlDialog::COLUMN_DATE ||
column == CoinControlDialog::COLUMN_CONFIRMATIONS)
return data(column, Qt::UserRole).toLongLong() <
other.data(column, Qt::UserRole).toLongLong();
return QTreeWidgetItem::operator<(other);
}
CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle,
QWidget *parent)
: QDialog(parent), ui(new Ui::CoinControlDialog), model(0),
platformStyle(_platformStyle) {
ui->setupUi(this);
// context menu actions
QAction *copyAddressAction = new QAction(tr("Copy address"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
// we need to enable/disable this
copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this);
// we need to enable/disable this
lockAction = new QAction(tr("Lock unspent"), this);
// we need to enable/disable this
unlockAction = new QAction(tr("Unlock unspent"), this);
// context menu
contextMenu = new QMenu(this);
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyAmountAction);
contextMenu->addAction(copyTransactionHashAction);
contextMenu->addSeparator();
contextMenu->addAction(lockAction);
contextMenu->addAction(unlockAction);
// context menu signals
connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this,
SLOT(showMenu(QPoint)));
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
connect(copyTransactionHashAction, SIGNAL(triggered()), this,
SLOT(copyTransactionHash()));
connect(lockAction, SIGNAL(triggered()), this, SLOT(lockCoin()));
connect(unlockAction, SIGNAL(triggered()), this, SLOT(unlockCoin()));
// clipboard actions
QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
connect(clipboardQuantityAction, SIGNAL(triggered()), this,
SLOT(clipboardQuantity()));
connect(clipboardAmountAction, SIGNAL(triggered()), this,
SLOT(clipboardAmount()));
connect(clipboardFeeAction, SIGNAL(triggered()), this,
SLOT(clipboardFee()));
connect(clipboardAfterFeeAction, SIGNAL(triggered()), this,
SLOT(clipboardAfterFee()));
connect(clipboardBytesAction, SIGNAL(triggered()), this,
SLOT(clipboardBytes()));
connect(clipboardLowOutputAction, SIGNAL(triggered()), this,
SLOT(clipboardLowOutput()));
connect(clipboardChangeAction, SIGNAL(triggered()), this,
SLOT(clipboardChange()));
ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
ui->labelCoinControlAmount->addAction(clipboardAmountAction);
ui->labelCoinControlFee->addAction(clipboardFeeAction);
ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
ui->labelCoinControlBytes->addAction(clipboardBytesAction);
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
ui->labelCoinControlChange->addAction(clipboardChangeAction);
// toggle tree/list mode
connect(ui->radioTreeMode, SIGNAL(toggled(bool)), this,
SLOT(radioTreeMode(bool)));
connect(ui->radioListMode, SIGNAL(toggled(bool)), this,
SLOT(radioListMode(bool)));
// click on checkbox
connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this,
SLOT(viewItemChanged(QTreeWidgetItem *, int)));
// click on header
ui->treeWidget->header()->setSectionsClickable(true);
connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this,
SLOT(headerSectionClicked(int)));
// ok button
connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton *)), this,
SLOT(buttonBoxClicked(QAbstractButton *)));
// (un)select all
connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this,
SLOT(buttonSelectAllClicked()));
// change coin control first column label due Qt4 bug.
// see https://github.com/bitcoin/bitcoin/issues/5716
ui->treeWidget->headerItem()->setText(COLUMN_CHECKBOX, QString());
ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 110);
ui->treeWidget->setColumnWidth(COLUMN_LABEL, 190);
ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 320);
ui->treeWidget->setColumnWidth(COLUMN_DATE, 130);
ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 110);
// store transaction hash in this column, but don't show it
ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true);
// store vout index in this column, but don't show it
ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true);
// default view is sorted by amount desc
sortView(COLUMN_AMOUNT, Qt::DescendingOrder);
// restore list mode and sortorder as a convenience feature
QSettings settings;
if (settings.contains("nCoinControlMode") &&
!settings.value("nCoinControlMode").toBool())
ui->radioTreeMode->click();
if (settings.contains("nCoinControlSortColumn") &&
settings.contains("nCoinControlSortOrder"))
sortView(settings.value("nCoinControlSortColumn").toInt(),
(static_cast<Qt::SortOrder>(
settings.value("nCoinControlSortOrder").toInt())));
}
CoinControlDialog::~CoinControlDialog() {
QSettings settings;
settings.setValue("nCoinControlMode", ui->radioListMode->isChecked());
settings.setValue("nCoinControlSortColumn", sortColumn);
settings.setValue("nCoinControlSortOrder", (int)sortOrder);
delete ui;
}
void CoinControlDialog::setModel(WalletModel *_model) {
this->model = _model;
if (_model && _model->getOptionsModel() && _model->getAddressTableModel()) {
updateView();
updateLabelLocked();
CoinControlDialog::updateLabels(_model, this);
}
}
// ok button
void CoinControlDialog::buttonBoxClicked(QAbstractButton *button) {
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {
// closes the dialog
done(QDialog::Accepted);
}
}
// (un)select all
void CoinControlDialog::buttonSelectAllClicked() {
Qt::CheckState state = Qt::Checked;
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) {
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) !=
Qt::Unchecked) {
state = Qt::Unchecked;
break;
}
}
ui->treeWidget->setEnabled(false);
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) !=
state) {
ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX,
state);
}
ui->treeWidget->setEnabled(true);
if (state == Qt::Unchecked) {
// just to be sure
coinControl()->UnSelectAll();
}
CoinControlDialog::updateLabels(model, this);
}
// context menu
void CoinControlDialog::showMenu(const QPoint &point) {
QTreeWidgetItem *item = ui->treeWidget->itemAt(point);
if (item) {
contextMenuItem = item;
// disable some items (like Copy Transaction ID, lock, unlock) for tree
// roots in context menu
if (item->text(COLUMN_TXHASH).length() == 64) {
TxId txid;
txid.SetHex(item->text(COLUMN_TXHASH).toStdString());
// transaction hash is 64 characters (this means its a child node,
// so its not a parent node in tree mode)
copyTransactionHashAction->setEnabled(true);
- if (model->isLockedCoin(txid,
- item->text(COLUMN_VOUT_INDEX).toUInt())) {
+ if (model->wallet().isLockedCoin(
+ COutPoint(txid, item->text(COLUMN_VOUT_INDEX).toUInt()))) {
lockAction->setEnabled(false);
unlockAction->setEnabled(true);
} else {
lockAction->setEnabled(true);
unlockAction->setEnabled(false);
}
} else {
// this means click on parent node in tree mode -> disable all
copyTransactionHashAction->setEnabled(false);
lockAction->setEnabled(false);
unlockAction->setEnabled(false);
}
// show context menu
contextMenu->exec(QCursor::pos());
}
}
// context menu action: copy amount
void CoinControlDialog::copyAmount() {
GUIUtil::setClipboard(
BitcoinUnits::removeSpaces(contextMenuItem->text(COLUMN_AMOUNT)));
}
// context menu action: copy label
void CoinControlDialog::copyLabel() {
if (ui->radioTreeMode->isChecked() &&
contextMenuItem->text(COLUMN_LABEL).length() == 0 &&
contextMenuItem->parent()) {
GUIUtil::setClipboard(contextMenuItem->parent()->text(COLUMN_LABEL));
} else {
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_LABEL));
}
}
// context menu action: copy address
void CoinControlDialog::copyAddress() {
if (ui->radioTreeMode->isChecked() &&
contextMenuItem->text(COLUMN_ADDRESS).length() == 0 &&
contextMenuItem->parent()) {
GUIUtil::setClipboard(contextMenuItem->parent()->text(COLUMN_ADDRESS));
} else {
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_ADDRESS));
}
}
// context menu action: copy transaction id
void CoinControlDialog::copyTransactionHash() {
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_TXHASH));
}
// context menu action: lock coin
void CoinControlDialog::lockCoin() {
if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked) {
contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
}
COutPoint outpt(
uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()),
contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
- model->lockCoin(outpt);
+ model->wallet().lockCoin(outpt);
contextMenuItem->setDisabled(true);
contextMenuItem->setIcon(
COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
updateLabelLocked();
}
// context menu action: unlock coin
void CoinControlDialog::unlockCoin() {
COutPoint outpt(
uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()),
contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
- model->unlockCoin(outpt);
+ model->wallet().unlockCoin(outpt);
contextMenuItem->setDisabled(false);
contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
updateLabelLocked();
}
// copy label "Quantity" to clipboard
void CoinControlDialog::clipboardQuantity() {
GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
}
// copy label "Amount" to clipboard
void CoinControlDialog::clipboardAmount() {
GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(
ui->labelCoinControlAmount->text().indexOf(" ")));
}
// copy label "Fee" to clipboard
void CoinControlDialog::clipboardFee() {
GUIUtil::setClipboard(
ui->labelCoinControlFee->text()
.left(ui->labelCoinControlFee->text().indexOf(" "))
.replace(ASYMP_UTF8, ""));
}
// copy label "After fee" to clipboard
void CoinControlDialog::clipboardAfterFee() {
GUIUtil::setClipboard(
ui->labelCoinControlAfterFee->text()
.left(ui->labelCoinControlAfterFee->text().indexOf(" "))
.replace(ASYMP_UTF8, ""));
}
// copy label "Bytes" to clipboard
void CoinControlDialog::clipboardBytes() {
GUIUtil::setClipboard(
ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
}
// copy label "Dust" to clipboard
void CoinControlDialog::clipboardLowOutput() {
GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
}
// copy label "Change" to clipboard
void CoinControlDialog::clipboardChange() {
GUIUtil::setClipboard(
ui->labelCoinControlChange->text()
.left(ui->labelCoinControlChange->text().indexOf(" "))
.replace(ASYMP_UTF8, ""));
}
// treeview: sort
void CoinControlDialog::sortView(int column, Qt::SortOrder order) {
sortColumn = column;
sortOrder = order;
ui->treeWidget->sortItems(column, order);
ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
}
// treeview: clicked on header
void CoinControlDialog::headerSectionClicked(int logicalIndex) {
// click on most left column -> do nothing
if (logicalIndex == COLUMN_CHECKBOX) {
ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
} else {
if (sortColumn == logicalIndex) {
sortOrder =
((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder
: Qt::AscendingOrder);
} else {
sortColumn = logicalIndex;
// if label or address then default => asc, else default => desc
sortOrder =
((sortColumn == COLUMN_LABEL || sortColumn == COLUMN_ADDRESS)
? Qt::AscendingOrder
: Qt::DescendingOrder);
}
sortView(sortColumn, sortOrder);
}
}
// toggle tree mode
void CoinControlDialog::radioTreeMode(bool checked) {
if (checked && model) {
updateView();
}
}
// toggle list mode
void CoinControlDialog::radioListMode(bool checked) {
if (checked && model) {
updateView();
}
}
// checkbox clicked by user
void CoinControlDialog::viewItemChanged(QTreeWidgetItem *item, int column) {
// transaction hash is 64 characters (this means its a child node, so its
// not a parent node in tree mode)
if (column == COLUMN_CHECKBOX && item->text(COLUMN_TXHASH).length() == 64) {
COutPoint outpt(uint256S(item->text(COLUMN_TXHASH).toStdString()),
item->text(COLUMN_VOUT_INDEX).toUInt());
if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked) {
coinControl()->UnSelect(outpt);
} else if (item->isDisabled()) {
// locked (this happens if "check all" through parent node)
item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
} else {
coinControl()->Select(outpt);
}
// selection changed -> update labels
if (ui->treeWidget->isEnabled()) {
// do not update on every click for (un)select all
CoinControlDialog::updateLabels(model, this);
}
}
// TODO: Remove this temporary qt5 fix after Qt5.3 and Qt5.4 are no longer
// used.
// Fixed in Qt5.5 and above: https://bugreports.qt.io/browse/QTBUG-43473
else if (column == COLUMN_CHECKBOX && item->childCount() > 0) {
if (item->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked &&
item->child(0)->checkState(COLUMN_CHECKBOX) ==
Qt::PartiallyChecked) {
item->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
}
}
}
// shows count of locked unspent outputs
void CoinControlDialog::updateLabelLocked() {
std::vector<COutPoint> vOutpts;
- model->listLockedCoins(vOutpts);
+ model->wallet().listLockedCoins(vOutpts);
if (vOutpts.size() > 0) {
ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size()));
ui->labelLocked->setVisible(true);
} else {
ui->labelLocked->setVisible(false);
}
}
void CoinControlDialog::updateLabels(WalletModel *model, QDialog *dialog) {
if (!model) {
return;
}
// nPayAmount
Amount nPayAmount = Amount::zero();
bool fDust = false;
CMutableTransaction txDummy;
for (const Amount amount : CoinControlDialog::payAmounts) {
nPayAmount += amount;
if (amount > Amount::zero()) {
CTxOut txout(amount,
static_cast<CScript>(std::vector<uint8_t>(24, 0)));
txDummy.vout.push_back(txout);
if (txout.IsDust(dustRelayFee)) {
fDust = true;
}
}
}
Amount nAmount = Amount::zero();
Amount nPayFee = Amount::zero();
Amount nAfterFee = Amount::zero();
Amount nChange = Amount::zero();
unsigned int nBytes = 0;
unsigned int nBytesInputs = 0;
unsigned int nQuantity = 0;
int nQuantityUncompressed = 0;
std::vector<COutPoint> vCoinControl;
std::vector<COutput> vOutputs;
coinControl()->ListSelected(vCoinControl);
model->getOutputs(vCoinControl, vOutputs);
for (const COutput &out : vOutputs) {
// unselect already spent, very unlikely scenario, this could happen
// when selected are spent elsewhere, like rpc or another computer
uint256 txhash = out.tx->GetId();
COutPoint outpt(txhash, out.i);
if (model->isSpent(outpt)) {
coinControl()->UnSelect(outpt);
continue;
}
// Quantity
nQuantity++;
// Amount
nAmount += out.tx->tx->vout[out.i].nValue;
// Bytes
CTxDestination address;
if (ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address)) {
CPubKey pubkey;
CKeyID *keyid = boost::get<CKeyID>(&address);
- if (keyid && model->getPubKey(*keyid, pubkey)) {
+ if (keyid && model->wallet().getPubKey(*keyid, pubkey)) {
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
if (!pubkey.IsCompressed()) {
nQuantityUncompressed++;
}
} else {
// in all error cases, simply assume 148 here
nBytesInputs += 148;
}
} else {
nBytesInputs += 148;
}
}
// calculation
if (nQuantity > 0) {
// Bytes
// always assume +1 output for change here
nBytes = nBytesInputs +
((CoinControlDialog::payAmounts.size() > 0
? CoinControlDialog::payAmounts.size() + 1
: 2) *
34) +
10;
// in the subtract fee from amount case, we can tell if zero change
// already and subtract the bytes, so that fee calculation afterwards is
// accurate
if (CoinControlDialog::fSubtractFeeFromAmount) {
if (nAmount - nPayAmount == Amount::zero()) {
nBytes -= 34;
}
}
// Fee
nPayFee = GetMinimumFee(nBytes, g_mempool, *coinControl());
if (nPayAmount > Amount::zero()) {
nChange = nAmount - nPayAmount;
if (!CoinControlDialog::fSubtractFeeFromAmount) {
nChange -= nPayFee;
}
// Never create dust outputs; if we would, just add the dust to the
// fee.
if (nChange > Amount::zero() && nChange < MIN_CHANGE) {
CTxOut txout(nChange,
static_cast<CScript>(std::vector<uint8_t>(24, 0)));
if (txout.IsDust(dustRelayFee)) {
// dust-change will be raised until no dust
if (CoinControlDialog::fSubtractFeeFromAmount) {
nChange = txout.GetDustThreshold(dustRelayFee);
} else {
nPayFee += nChange;
nChange = Amount::zero();
}
}
}
if (nChange == Amount::zero() &&
!CoinControlDialog::fSubtractFeeFromAmount) {
nBytes -= 34;
}
}
// after fee
nAfterFee = std::max(nAmount - nPayFee, Amount::zero());
}
// actually update labels
int nDisplayUnit = BitcoinUnits::BCH;
if (model && model->getOptionsModel()) {
nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
}
QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity");
QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount");
QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee");
QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes");
QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
// enable/disable "dust" and "change"
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")
->setEnabled(nPayAmount > Amount::zero());
dialog->findChild<QLabel *>("labelCoinControlLowOutput")
->setEnabled(nPayAmount > Amount::zero());
dialog->findChild<QLabel *>("labelCoinControlChangeText")
->setEnabled(nPayAmount > Amount::zero());
dialog->findChild<QLabel *>("labelCoinControlChange")
->setEnabled(nPayAmount > Amount::zero());
// stats
// Quantity
l1->setText(QString::number(nQuantity));
// Amount
l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount));
// Fee
l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee));
// After Fee
l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee));
// Bytes
l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes));
// Dust
l7->setText(fDust ? tr("yes") : tr("no"));
// Change
l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange));
if (nPayFee > Amount::zero()) {
l3->setText(ASYMP_UTF8 + l3->text());
l4->setText(ASYMP_UTF8 + l4->text());
if (nChange > Amount::zero() &&
!CoinControlDialog::fSubtractFeeFromAmount) {
l8->setText(ASYMP_UTF8 + l8->text());
}
}
// turn label red when dust
l7->setStyleSheet((fDust) ? "color:red;" : "");
// tool tips
QString toolTipDust =
tr("This label turns red if any recipient receives an amount smaller "
"than the current dust threshold.");
// how many satoshis the estimated fee can vary per byte we guess wrong
double dFeeVary = GetMinimumFee(1000, g_mempool) / (1000 * SATOSHI);
QString toolTip4 =
tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
l3->setToolTip(toolTip4);
l4->setToolTip(toolTip4);
l7->setToolTip(toolTipDust);
l8->setToolTip(toolTip4);
dialog->findChild<QLabel *>("labelCoinControlFeeText")
->setToolTip(l3->toolTip());
dialog->findChild<QLabel *>("labelCoinControlAfterFeeText")
->setToolTip(l4->toolTip());
dialog->findChild<QLabel *>("labelCoinControlBytesText")
->setToolTip(l5->toolTip());
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")
->setToolTip(l7->toolTip());
dialog->findChild<QLabel *>("labelCoinControlChangeText")
->setToolTip(l8->toolTip());
// Insufficient funds
QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds");
if (label) {
label->setVisible(nChange < Amount::zero());
}
}
CCoinControl *CoinControlDialog::coinControl() {
static CCoinControl coin_control;
return &coin_control;
}
void CoinControlDialog::updateView() {
if (!model || !model->getOptionsModel() || !model->getAddressTableModel()) {
return;
}
bool treeMode = ui->radioTreeMode->isChecked();
ui->treeWidget->clear();
// performance, otherwise updateLabels would be called for every checked
// checkbox
ui->treeWidget->setEnabled(false);
ui->treeWidget->setAlternatingRowColors(!treeMode);
QFlags<Qt::ItemFlag> flgCheckbox =
Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
QFlags<Qt::ItemFlag> flgTristate =
Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable |
Qt::ItemIsTristate;
int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
std::map<QString, std::vector<COutput>> mapCoins;
model->listCoins(mapCoins);
for (const std::pair<QString, std::vector<COutput>> &coins : mapCoins) {
CCoinControlWidgetItem *itemWalletAddress =
new CCoinControlWidgetItem();
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
QString sWalletAddress = coins.first;
QString sWalletLabel =
model->getAddressTableModel()->labelForAddress(sWalletAddress);
if (sWalletLabel.isEmpty()) {
sWalletLabel = tr("(no label)");
}
if (treeMode) {
// wallet address
ui->treeWidget->addTopLevelItem(itemWalletAddress);
itemWalletAddress->setFlags(flgTristate);
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
// label
itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel);
// address
itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress);
}
Amount nSum = Amount::zero();
int nChildren = 0;
for (const COutput &out : coins.second) {
nSum += out.tx->tx->vout[out.i].nValue;
nChildren++;
CCoinControlWidgetItem *itemOutput;
if (treeMode) {
itemOutput = new CCoinControlWidgetItem(itemWalletAddress);
} else {
itemOutput = new CCoinControlWidgetItem(ui->treeWidget);
}
itemOutput->setFlags(flgCheckbox);
itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
// address
CTxDestination outputAddress;
QString sAddress = "";
if (ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey,
outputAddress)) {
sAddress =
QString::fromStdString(EncodeDestination(outputAddress));
// if listMode or change => show bitcoin address. In tree mode,
// address is not shown again for direct wallet address outputs
if (!treeMode || (!(sAddress == sWalletAddress))) {
itemOutput->setText(COLUMN_ADDRESS, sAddress);
}
}
// label
if (!(sAddress == sWalletAddress)) {
// change tooltip from where the change comes from
itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)")
.arg(sWalletLabel)
.arg(sWalletAddress));
itemOutput->setText(COLUMN_LABEL, tr("(change)"));
} else if (!treeMode) {
QString sLabel =
model->getAddressTableModel()->labelForAddress(sAddress);
if (sLabel.isEmpty()) {
sLabel = tr("(no label)");
}
itemOutput->setText(COLUMN_LABEL, sLabel);
}
// amount
itemOutput->setText(
COLUMN_AMOUNT,
BitcoinUnits::format(nDisplayUnit,
out.tx->tx->vout[out.i].nValue));
// padding so that sorting works correctly
itemOutput->setData(
COLUMN_AMOUNT, Qt::UserRole,
QVariant(qlonglong(out.tx->tx->vout[out.i].nValue / SATOSHI)));
// date
itemOutput->setText(COLUMN_DATE,
GUIUtil::dateTimeStr(out.tx->GetTxTime()));
itemOutput->setData(COLUMN_DATE, Qt::UserRole,
QVariant((qlonglong)out.tx->GetTxTime()));
// confirmations
itemOutput->setText(COLUMN_CONFIRMATIONS,
QString::number(out.nDepth));
itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole,
QVariant((qlonglong)out.nDepth));
// transaction id
const TxId txid = out.tx->GetId();
itemOutput->setText(COLUMN_TXHASH,
QString::fromStdString(txid.GetHex()));
// vout index
itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i));
// disable locked coins
- if (model->isLockedCoin(txid, out.i)) {
+ if (model->wallet().isLockedCoin(COutPoint(txid, out.i))) {
COutPoint outpt(txid, out.i);
// just to be sure
coinControl()->UnSelect(outpt);
itemOutput->setDisabled(true);
itemOutput->setIcon(
COLUMN_CHECKBOX,
platformStyle->SingleColorIcon(":/icons/lock_closed"));
}
// set checkbox
if (coinControl()->IsSelected(COutPoint(txid, out.i))) {
itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
}
}
// amount
if (treeMode) {
itemWalletAddress->setText(COLUMN_CHECKBOX,
"(" + QString::number(nChildren) + ")");
itemWalletAddress->setText(
COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
itemWalletAddress->setData(COLUMN_AMOUNT, Qt::UserRole,
QVariant(qlonglong(nSum / SATOSHI)));
}
}
// expand all partially selected
if (treeMode) {
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) {
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) ==
Qt::PartiallyChecked)
ui->treeWidget->topLevelItem(i)->setExpanded(true);
}
}
// sort view
sortView(sortColumn, sortOrder);
ui->treeWidget->setEnabled(true);
}
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 7252013937..6f410e58b9 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -1,301 +1,303 @@
// Copyright (c) 2011-2016 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 "overviewpage.h"
#include "ui_overviewpage.h"
#include "bitcoinunits.h"
#include "clientmodel.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "platformstyle.h"
#include "transactionfilterproxy.h"
#include "transactiontablemodel.h"
#include "walletmodel.h"
#include <QAbstractItemDelegate>
#include <QPainter>
#define DECORATION_SIZE 54
#define NUM_ITEMS 5
class TxViewDelegate : public QAbstractItemDelegate {
Q_OBJECT
public:
explicit TxViewDelegate(const PlatformStyle *_platformStyle,
QObject *parent = nullptr)
: QAbstractItemDelegate(parent), unit(BitcoinUnits::BCH),
platformStyle(_platformStyle) {}
inline void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const {
painter->save();
QIcon icon = qvariant_cast<QIcon>(
index.data(TransactionTableModel::RawDecorationRole));
QRect mainRect = option.rect;
QRect decorationRect(mainRect.topLeft(),
QSize(DECORATION_SIZE, DECORATION_SIZE));
int xspace = DECORATION_SIZE + 8;
int ypad = 6;
int halfheight = (mainRect.height() - 2 * ypad) / 2;
QRect amountRect(mainRect.left() + xspace, mainRect.top() + ypad,
mainRect.width() - xspace, halfheight);
QRect addressRect(mainRect.left() + xspace,
mainRect.top() + ypad + halfheight,
mainRect.width() - xspace, halfheight);
icon = platformStyle->SingleColorIcon(icon);
icon.paint(painter, decorationRect);
QDateTime date =
index.data(TransactionTableModel::DateRole).toDateTime();
QString address = index.data(Qt::DisplayRole).toString();
Amount amount(
int64_t(
index.data(TransactionTableModel::AmountRole).toLongLong()) *
SATOSHI);
bool confirmed =
index.data(TransactionTableModel::ConfirmedRole).toBool();
QVariant value = index.data(Qt::ForegroundRole);
QColor foreground = option.palette.color(QPalette::Text);
if (value.canConvert<QBrush>()) {
QBrush brush = qvariant_cast<QBrush>(value);
foreground = brush.color();
}
painter->setPen(foreground);
QRect boundingRect;
painter->drawText(addressRect, Qt::AlignLeft | Qt::AlignVCenter,
address, &boundingRect);
if (index.data(TransactionTableModel::WatchonlyRole).toBool()) {
QIcon iconWatchonly = qvariant_cast<QIcon>(
index.data(TransactionTableModel::WatchonlyDecorationRole));
QRect watchonlyRect(boundingRect.right() + 5,
mainRect.top() + ypad + halfheight, 16,
halfheight);
iconWatchonly.paint(painter, watchonlyRect);
}
if (amount < Amount::zero()) {
foreground = COLOR_NEGATIVE;
} else if (!confirmed) {
foreground = COLOR_UNCONFIRMED;
} else {
foreground = option.palette.color(QPalette::Text);
}
painter->setPen(foreground);
QString amountText = BitcoinUnits::formatWithUnit(
unit, amount, true, BitcoinUnits::separatorAlways);
if (!confirmed) {
amountText = QString("[") + amountText + QString("]");
}
painter->drawText(amountRect, Qt::AlignRight | Qt::AlignVCenter,
amountText);
painter->setPen(option.palette.color(QPalette::Text));
painter->drawText(amountRect, Qt::AlignLeft | Qt::AlignVCenter,
GUIUtil::dateTimeStr(date));
painter->restore();
}
inline QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const {
return QSize(DECORATION_SIZE, DECORATION_SIZE);
}
int unit;
const PlatformStyle *platformStyle;
};
#include "overviewpage.moc"
OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
: QWidget(parent), ui(new Ui::OverviewPage), clientModel(0), walletModel(0),
currentBalance(-SATOSHI), currentUnconfirmedBalance(-SATOSHI),
currentImmatureBalance(-SATOSHI), currentWatchOnlyBalance(-SATOSHI),
currentWatchUnconfBalance(-SATOSHI),
currentWatchImmatureBalance(-SATOSHI),
txdelegate(new TxViewDelegate(platformStyle, this)) {
ui->setupUi(this);
// use a SingleColorIcon for the "out of sync warning" icon
QIcon icon = platformStyle->SingleColorIcon(":/icons/warning");
// also set the disabled icon because we are using a disabled QPushButton to
// work around missing HiDPI support of QLabel
// (https://bugreports.qt.io/browse/QTBUG-42503)
icon.addPixmap(icon.pixmap(QSize(64, 64), QIcon::Normal), QIcon::Disabled);
ui->labelTransactionsStatus->setIcon(icon);
ui->labelWalletStatus->setIcon(icon);
// Recent transactions
ui->listTransactions->setItemDelegate(txdelegate);
ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE));
ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2));
ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false);
connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this,
SLOT(handleTransactionClicked(QModelIndex)));
// start with displaying the "out of sync" warnings
showOutOfSyncWarning(true);
connect(ui->labelWalletStatus, SIGNAL(clicked()), this,
SLOT(handleOutOfSyncWarningClicks()));
connect(ui->labelTransactionsStatus, SIGNAL(clicked()), this,
SLOT(handleOutOfSyncWarningClicks()));
}
void OverviewPage::handleTransactionClicked(const QModelIndex &index) {
if (filter) Q_EMIT transactionClicked(filter->mapToSource(index));
}
void OverviewPage::handleOutOfSyncWarningClicks() {
Q_EMIT outOfSyncWarningClicked();
}
OverviewPage::~OverviewPage() {
delete ui;
}
void OverviewPage::setBalance(const Amount balance,
const Amount unconfirmedBalance,
const Amount immatureBalance,
const Amount watchOnlyBalance,
const Amount watchUnconfBalance,
const Amount watchImmatureBalance) {
int unit = walletModel->getOptionsModel()->getDisplayUnit();
currentBalance = balance;
currentUnconfirmedBalance = unconfirmedBalance;
currentImmatureBalance = immatureBalance;
currentWatchOnlyBalance = watchOnlyBalance;
currentWatchUnconfBalance = watchUnconfBalance;
currentWatchImmatureBalance = watchImmatureBalance;
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(
unit, balance, false, BitcoinUnits::separatorAlways));
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(
unit, unconfirmedBalance, false, BitcoinUnits::separatorAlways));
ui->labelImmature->setText(BitcoinUnits::formatWithUnit(
unit, immatureBalance, false, BitcoinUnits::separatorAlways));
ui->labelTotal->setText(BitcoinUnits::formatWithUnit(
unit, balance + unconfirmedBalance + immatureBalance, false,
BitcoinUnits::separatorAlways));
ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(
unit, watchOnlyBalance, false, BitcoinUnits::separatorAlways));
ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(
unit, watchUnconfBalance, false, BitcoinUnits::separatorAlways));
ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(
unit, watchImmatureBalance, false, BitcoinUnits::separatorAlways));
ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(
unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance,
false, BitcoinUnits::separatorAlways));
// only show immature (newly mined) balance if it's non-zero, so as not to
// complicate things
// for the non-mining users
bool showImmature = immatureBalance != Amount::zero();
bool showWatchOnlyImmature = watchImmatureBalance != Amount::zero();
// for symmetry reasons also show immature label when the watch-only one is
// shown
ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature);
ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature);
ui->labelWatchImmature->setVisible(
showWatchOnlyImmature); // show watch-only immature balance
}
// show/hide watch-only labels
void OverviewPage::updateWatchOnlyLabels(bool showWatchOnly) {
// show spendable label (only when watch-only is active)
ui->labelSpendable->setVisible(showWatchOnly);
// show watch-only label
ui->labelWatchonly->setVisible(showWatchOnly);
// show watch-only balance separator line
ui->lineWatchBalance->setVisible(showWatchOnly);
// show watch-only available balance
ui->labelWatchAvailable->setVisible(showWatchOnly);
// show watch-only pending balance
ui->labelWatchPending->setVisible(showWatchOnly);
// show watch-only total balance
ui->labelWatchTotal->setVisible(showWatchOnly);
if (!showWatchOnly) {
ui->labelWatchImmature->hide();
}
}
void OverviewPage::setClientModel(ClientModel *model) {
this->clientModel = model;
if (model) {
// Show warning if this is a prerelease version
connect(model, SIGNAL(alertsChanged(QString)), this,
SLOT(updateAlerts(QString)));
updateAlerts(model->getStatusBarWarnings());
}
}
void OverviewPage::setWalletModel(WalletModel *model) {
this->walletModel = model;
if (model && model->getOptionsModel()) {
// Set up transaction list
filter.reset(new TransactionFilterProxy());
filter->setSourceModel(model->getTransactionTableModel());
filter->setLimit(NUM_ITEMS);
filter->setDynamicSortFilter(true);
filter->setSortRole(Qt::EditRole);
filter->setShowInactive(false);
filter->sort(TransactionTableModel::Date, Qt::DescendingOrder);
ui->listTransactions->setModel(filter.get());
ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);
// Keep up to date with wallet
- setBalance(model->getBalance(), model->getUnconfirmedBalance(),
- model->getImmatureBalance(), model->getWatchBalance(),
- model->getWatchUnconfirmedBalance(),
- model->getWatchImmatureBalance());
+ interfaces::Wallet &wallet = model->wallet();
+ interfaces::WalletBalances balances = wallet.getBalances();
+ setBalance(balances.balance, balances.unconfirmed_balance,
+ balances.immature_balance, balances.watch_only_balance,
+ balances.unconfirmed_watch_only_balance,
+ balances.immature_watch_only_balance);
connect(
model,
SIGNAL(
balanceChanged(Amount, Amount, Amount, Amount, Amount, Amount)),
this,
SLOT(setBalance(Amount, Amount, Amount, Amount, Amount, Amount)));
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this,
SLOT(updateDisplayUnit()));
- updateWatchOnlyLabels(model->haveWatchOnly());
+ updateWatchOnlyLabels(wallet.haveWatchOnly());
connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this,
SLOT(updateWatchOnlyLabels(bool)));
}
// update the display unit, to not use the default ("BCH")
updateDisplayUnit();
}
void OverviewPage::updateDisplayUnit() {
if (walletModel && walletModel->getOptionsModel()) {
if (currentBalance != -SATOSHI) {
setBalance(currentBalance, currentUnconfirmedBalance,
currentImmatureBalance, currentWatchOnlyBalance,
currentWatchUnconfBalance, currentWatchImmatureBalance);
}
// Update txdelegate->unit with the current unit
txdelegate->unit = walletModel->getOptionsModel()->getDisplayUnit();
ui->listTransactions->update();
}
}
void OverviewPage::updateAlerts(const QString &warnings) {
this->ui->labelAlerts->setVisible(!warnings.isEmpty());
this->ui->labelAlerts->setText(warnings);
}
void OverviewPage::showOutOfSyncWarning(bool fShow) {
ui->labelWalletStatus->setVisible(fShow);
ui->labelTransactionsStatus->setVisible(fShow);
}
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 79e40cbc6b..ec43ab5af5 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -1,954 +1,955 @@
// Copyright (c) 2011-2016 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 "sendcoinsdialog.h"
-#include "ui_sendcoinsdialog.h"
-
-#include "addresstablemodel.h"
-#include "bitcoinunits.h"
-#include "clientmodel.h"
-#include "coincontroldialog.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "platformstyle.h"
-#include "sendcoinsentry.h"
-#include "walletmodel.h"
-
-#include "chainparams.h"
-#include "dstencode.h"
-#include "txmempool.h"
-#include "ui_interface.h"
-#include "validation.h" // mempool and minRelayTxFee
-#include "wallet/coincontrol.h"
-#include "wallet/fees.h"
-#include "wallet/wallet.h"
+#include <qt/forms/ui_sendcoinsdialog.h>
+#include <qt/sendcoinsdialog.h>
+
+#include <chainparams.h>
+#include <dstencode.h>
+#include <interfaces/node.h>
+#include <qt/addresstablemodel.h>
+#include <qt/bitcoinunits.h>
+#include <qt/clientmodel.h>
+#include <qt/coincontroldialog.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <qt/sendcoinsentry.h>
+#include <qt/walletmodel.h>
+#include <txmempool.h>
+#include <ui_interface.h>
+#include <validation.h> // mempool and minRelayTxFee
+#include <wallet/coincontrol.h>
+#include <wallet/fees.h>
+#include <wallet/wallet.h>
#include <QMessageBox>
#include <QScrollBar>
#include <QSettings>
#include <QTextDocument>
#include <QTimer>
SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle,
QWidget *parent)
: QDialog(parent), ui(new Ui::SendCoinsDialog), clientModel(0), model(0),
fNewRecipientAllowed(true), fFeeMinimized(true),
platformStyle(_platformStyle) {
ui->setupUi(this);
if (!_platformStyle->getImagesOnButtons()) {
ui->addButton->setIcon(QIcon());
ui->clearButton->setIcon(QIcon());
ui->sendButton->setIcon(QIcon());
} else {
ui->addButton->setIcon(_platformStyle->SingleColorIcon(":/icons/add"));
ui->clearButton->setIcon(
_platformStyle->SingleColorIcon(":/icons/remove"));
ui->sendButton->setIcon(
_platformStyle->SingleColorIcon(":/icons/send"));
}
GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
addEntry();
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
// Coin Control
connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this,
SLOT(coinControlButtonClicked()));
connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this,
SLOT(coinControlChangeChecked(int)));
connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)),
this, SLOT(coinControlChangeEdited(const QString &)));
// Coin Control: clipboard actions
QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
connect(clipboardQuantityAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardQuantity()));
connect(clipboardAmountAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardAmount()));
connect(clipboardFeeAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardFee()));
connect(clipboardAfterFeeAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardAfterFee()));
connect(clipboardBytesAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardBytes()));
connect(clipboardLowOutputAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardLowOutput()));
connect(clipboardChangeAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardChange()));
ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
ui->labelCoinControlAmount->addAction(clipboardAmountAction);
ui->labelCoinControlFee->addAction(clipboardFeeAction);
ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
ui->labelCoinControlBytes->addAction(clipboardBytesAction);
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
ui->labelCoinControlChange->addAction(clipboardChangeAction);
// init transaction fee section
QSettings settings;
if (!settings.contains("fFeeSectionMinimized")) {
settings.setValue("fFeeSectionMinimized", true);
}
// compatibility
if (!settings.contains("nFeeRadio") &&
settings.contains("nTransactionFee") &&
settings.value("nTransactionFee").toLongLong() > 0) {
// custom
settings.setValue("nFeeRadio", 1);
}
if (!settings.contains("nFeeRadio")) {
// recommended
settings.setValue("nFeeRadio", 0);
}
// compatibility
if (!settings.contains("nCustomFeeRadio") &&
settings.contains("nTransactionFee") &&
settings.value("nTransactionFee").toLongLong() > 0) {
// total at least
settings.setValue("nCustomFeeRadio", 1);
}
if (!settings.contains("nCustomFeeRadio")) {
// per kilobyte
settings.setValue("nCustomFeeRadio", 0);
}
if (!settings.contains("nTransactionFee")) {
settings.setValue("nTransactionFee",
qint64(DEFAULT_TRANSACTION_FEE / SATOSHI));
}
if (!settings.contains("fPayOnlyMinFee")) {
settings.setValue("fPayOnlyMinFee", false);
}
ui->groupFee->setId(ui->radioSmartFee, 0);
ui->groupFee->setId(ui->radioCustomFee, 1);
ui->groupFee
->button(
std::max<int>(0, std::min(1, settings.value("nFeeRadio").toInt())))
->setChecked(true);
ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0);
ui->groupCustomFee->button(0)->setChecked(true);
ui->customFee->setValue(
int64_t(settings.value("nTransactionFee").toLongLong()) * SATOSHI);
ui->checkBoxMinimumFee->setChecked(
settings.value("fPayOnlyMinFee").toBool());
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
}
void SendCoinsDialog::setClientModel(ClientModel *_clientModel) {
this->clientModel = _clientModel;
if (_clientModel) {
connect(_clientModel,
SIGNAL(numBlocksChanged(int, QDateTime, double, bool)), this,
SLOT(updateSmartFeeLabel()));
}
}
void SendCoinsDialog::setModel(WalletModel *_model) {
this->model = _model;
if (_model && _model->getOptionsModel()) {
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(
ui->entries->itemAt(i)->widget());
if (entry) {
entry->setModel(_model);
}
}
- setBalance(_model->getBalance(), _model->getUnconfirmedBalance(),
- _model->getImmatureBalance(), _model->getWatchBalance(),
- _model->getWatchUnconfirmedBalance(),
- _model->getWatchImmatureBalance());
+ interfaces::WalletBalances balances = _model->wallet().getBalances();
+ setBalance(balances.balance, balances.unconfirmed_balance,
+ balances.immature_balance, balances.watch_only_balance,
+ balances.unconfirmed_watch_only_balance,
+ balances.immature_watch_only_balance);
connect(
_model,
SIGNAL(
balanceChanged(Amount, Amount, Amount, Amount, Amount, Amount)),
this,
SLOT(setBalance(Amount, Amount, Amount, Amount, Amount, Amount)));
connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)),
this, SLOT(updateDisplayUnit()));
updateDisplayUnit();
// Coin Control
connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)),
this, SLOT(coinControlUpdateLabels()));
connect(_model->getOptionsModel(),
SIGNAL(coinControlFeaturesChanged(bool)), this,
SLOT(coinControlFeatureChanged(bool)));
ui->frameCoinControl->setVisible(
_model->getOptionsModel()->getCoinControlFeatures());
coinControlUpdateLabels();
// fee section
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this,
SLOT(updateFeeSectionControls()));
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this,
SLOT(coinControlUpdateLabels()));
connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this,
SLOT(coinControlUpdateLabels()));
connect(ui->customFee, SIGNAL(valueChanged()), this,
SLOT(coinControlUpdateLabels()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this,
SLOT(setMinimumFee()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this,
SLOT(updateFeeSectionControls()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this,
SLOT(coinControlUpdateLabels()));
ui->customFee->setSingleStep(GetMinimumFee(1000, g_mempool));
updateFeeSectionControls();
updateMinFeeLabel();
updateSmartFeeLabel();
// Cleanup old confirmation target related settings
// TODO: Remove these in 0.20
QSettings settings;
if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
settings.remove("nSmartFeeSliderPosition");
}
if (settings.value("nConfTarget").toInt() != 0) {
settings.remove("nConfTarget");
}
}
}
SendCoinsDialog::~SendCoinsDialog() {
QSettings settings;
settings.setValue("fFeeSectionMinimized", fFeeMinimized);
settings.setValue("nFeeRadio", ui->groupFee->checkedId());
settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId());
settings.setValue("nTransactionFee",
qint64(ui->customFee->value() / SATOSHI));
settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
delete ui;
}
void SendCoinsDialog::on_sendButton_clicked() {
if (!model || !model->getOptionsModel()) {
return;
}
QList<SendCoinsRecipient> recipients;
bool valid = true;
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry *entry =
qobject_cast<SendCoinsEntry *>(ui->entries->itemAt(i)->widget());
if (entry) {
if (entry->validate()) {
recipients.append(entry->getValue());
} else {
valid = false;
}
}
}
if (!valid || recipients.isEmpty()) {
return;
}
fNewRecipientAllowed = false;
WalletModel::UnlockContext ctx(model->requestUnlock());
if (!ctx.isValid()) {
// Unlock wallet was cancelled
fNewRecipientAllowed = true;
return;
}
// prepare transaction for getting txFee earlier
WalletModelTransaction currentTransaction(recipients);
WalletModel::SendCoinsReturn prepareStatus;
// Always use a CCoinControl instance, use the CoinControlDialog instance if
// CoinControl has been enabled
CCoinControl ctrl;
if (model->getOptionsModel()->getCoinControlFeatures()) {
ctrl = *CoinControlDialog::coinControl();
}
updateCoinControlState(ctrl);
prepareStatus = model->prepareTransaction(currentTransaction, ctrl);
// process prepareStatus and on error generate message shown to user
processSendCoinsReturn(
prepareStatus,
BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
currentTransaction.getTransactionFee()));
if (prepareStatus.status != WalletModel::OK) {
fNewRecipientAllowed = true;
return;
}
Amount txFee = currentTransaction.getTransactionFee();
// Format confirmation message
QStringList formatted;
for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients()) {
// generate bold amount string with wallet name in case of multiwallet
QString amount =
"<b>" + BitcoinUnits::formatHtmlWithUnit(
model->getOptionsModel()->getDisplayUnit(), rcp.amount);
if (model->isMultiwallet()) {
amount.append(
" <u>" +
tr("from wallet %1")
.arg(GUIUtil::HtmlEscape(model->getWalletName())) +
"</u> ");
}
amount.append("</b>");
// generate monospace address string
QString address =
"<span style='font-family: monospace;'>" + rcp.address;
address.append("</span>");
QString recipientElement;
// normal payment
if (!rcp.paymentRequest.IsInitialized()) {
if (rcp.label.length() > 0) {
// label with address
recipientElement =
tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label));
recipientElement.append(QString(" (%1)").arg(address));
} else {
// just address
recipientElement = tr("%1 to %2").arg(amount, address);
}
} else if (!rcp.authenticatedMerchant.isEmpty()) {
// authenticated payment request
recipientElement =
tr("%1 to %2")
.arg(amount,
GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
} else {
// unauthenticated payment request
recipientElement = tr("%1 to %2").arg(amount, address);
}
formatted.append(recipientElement);
}
QString questionString = tr("Are you sure you want to send?");
questionString.append("<br /><br />%1");
if (txFee > Amount::zero()) {
// append fee string if a fee is required
questionString.append("<hr /><span style='color:#aa0000;'>");
questionString.append(BitcoinUnits::formatHtmlWithUnit(
model->getOptionsModel()->getDisplayUnit(), txFee));
questionString.append("</span> ");
questionString.append(tr("added as transaction fee"));
// append transaction size
questionString.append(
" (" +
QString::number((double)currentTransaction.getTransactionSize() /
1000) +
" kB)");
}
// add total amount in all subdivision units
questionString.append("<hr />");
Amount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
QStringList alternativeUnits;
for (BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) {
if (u != model->getOptionsModel()->getDisplayUnit()) {
alternativeUnits.append(
BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
}
}
questionString.append(
tr("Total Amount %1")
.arg(BitcoinUnits::formatHtmlWithUnit(
model->getOptionsModel()->getDisplayUnit(), totalAmount)));
questionString.append(
QString("<span style='font-size:10pt;font-weight:normal;'><br "
"/>(=%2)</span>")
.arg(alternativeUnits.join(" " + tr("or") + "<br />")));
SendConfirmationDialog confirmationDialog(
tr("Confirm send coins"), questionString.arg(formatted.join("<br />")),
SEND_CONFIRM_DELAY, this);
confirmationDialog.exec();
QMessageBox::StandardButton retval =
static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
if (retval != QMessageBox::Yes) {
fNewRecipientAllowed = true;
return;
}
// now send the prepared transaction
WalletModel::SendCoinsReturn sendStatus =
model->sendCoins(currentTransaction);
// process sendStatus and on error generate message shown to user
processSendCoinsReturn(sendStatus);
if (sendStatus.status == WalletModel::OK) {
accept();
CoinControlDialog::coinControl()->UnSelectAll();
coinControlUpdateLabels();
- Q_EMIT coinsSent(currentTransaction.getTransaction()->GetId());
+ Q_EMIT coinsSent(currentTransaction.getWtx()->get().GetId());
}
fNewRecipientAllowed = true;
}
void SendCoinsDialog::clear() {
// Remove entries until only one left
while (ui->entries->count()) {
ui->entries->takeAt(0)->widget()->deleteLater();
}
addEntry();
updateTabsAndLabels();
}
void SendCoinsDialog::reject() {
clear();
}
void SendCoinsDialog::accept() {
clear();
}
SendCoinsEntry *SendCoinsDialog::addEntry() {
SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
entry->setModel(model);
ui->entries->addWidget(entry);
connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this,
SLOT(removeEntry(SendCoinsEntry *)));
connect(entry, SIGNAL(useAvailableBalance(SendCoinsEntry *)), this,
SLOT(useAvailableBalance(SendCoinsEntry *)));
connect(entry, SIGNAL(payAmountChanged()), this,
SLOT(coinControlUpdateLabels()));
connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this,
SLOT(coinControlUpdateLabels()));
// Focus the field, so that entry can start immediately
entry->clear();
entry->setFocus();
ui->scrollAreaWidgetContents->resize(
ui->scrollAreaWidgetContents->sizeHint());
qApp->processEvents();
QScrollBar *bar = ui->scrollArea->verticalScrollBar();
if (bar) {
bar->setSliderPosition(bar->maximum());
}
updateTabsAndLabels();
return entry;
}
void SendCoinsDialog::updateTabsAndLabels() {
setupTabChain(0);
coinControlUpdateLabels();
}
void SendCoinsDialog::removeEntry(SendCoinsEntry *entry) {
entry->hide();
// If the last entry is about to be removed add an empty one
if (ui->entries->count() == 1) {
addEntry();
}
entry->deleteLater();
updateTabsAndLabels();
}
QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) {
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry *entry =
qobject_cast<SendCoinsEntry *>(ui->entries->itemAt(i)->widget());
if (entry) {
prev = entry->setupTabChain(prev);
}
}
QWidget::setTabOrder(prev, ui->sendButton);
QWidget::setTabOrder(ui->sendButton, ui->clearButton);
QWidget::setTabOrder(ui->clearButton, ui->addButton);
return ui->addButton;
}
void SendCoinsDialog::setAddress(const QString &address) {
SendCoinsEntry *entry = 0;
// Replace the first entry if it is still unused
if (ui->entries->count() == 1) {
SendCoinsEntry *first =
qobject_cast<SendCoinsEntry *>(ui->entries->itemAt(0)->widget());
if (first->isClear()) {
entry = first;
}
}
if (!entry) {
entry = addEntry();
}
entry->setAddress(address);
}
void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) {
if (!fNewRecipientAllowed) {
return;
}
SendCoinsEntry *entry = 0;
// Replace the first entry if it is still unused
if (ui->entries->count() == 1) {
SendCoinsEntry *first =
qobject_cast<SendCoinsEntry *>(ui->entries->itemAt(0)->widget());
if (first->isClear()) {
entry = first;
}
}
if (!entry) {
entry = addEntry();
}
entry->setValue(rv);
updateTabsAndLabels();
}
bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) {
// Just paste the entry, all pre-checks are done in paymentserver.cpp.
pasteEntry(rv);
return true;
}
void SendCoinsDialog::setBalance(const Amount balance,
const Amount unconfirmedBalance,
const Amount immatureBalance,
const Amount watchBalance,
const Amount watchUnconfirmedBalance,
const Amount watchImmatureBalance) {
Q_UNUSED(unconfirmedBalance);
Q_UNUSED(immatureBalance);
Q_UNUSED(watchBalance);
Q_UNUSED(watchUnconfirmedBalance);
Q_UNUSED(watchImmatureBalance);
if (model && model->getOptionsModel()) {
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(), balance));
}
}
void SendCoinsDialog::updateDisplayUnit() {
- setBalance(model->getBalance(), Amount::zero(), Amount::zero(),
+ setBalance(model->wallet().getBalance(), Amount::zero(), Amount::zero(),
Amount::zero(), Amount::zero(), Amount::zero());
ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
updateMinFeeLabel();
updateSmartFeeLabel();
}
void SendCoinsDialog::processSendCoinsReturn(
const WalletModel::SendCoinsReturn &sendCoinsReturn,
const QString &msgArg) {
QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
// Default to a warning message, override if error message is needed
msgParams.second = CClientUIInterface::MSG_WARNING;
// This comment is specific to SendCoinsDialog usage of
// WalletModel::SendCoinsReturn.
// WalletModel::TransactionCommitFailed is used only in
// WalletModel::sendCoins() all others are used only in
// WalletModel::prepareTransaction()
switch (sendCoinsReturn.status) {
case WalletModel::InvalidAddress:
msgParams.first =
tr("The recipient address is not valid. Please recheck.");
break;
case WalletModel::InvalidAmount:
msgParams.first = tr("The amount to pay must be larger than 0.");
break;
case WalletModel::AmountExceedsBalance:
msgParams.first = tr("The amount exceeds your balance.");
break;
case WalletModel::AmountWithFeeExceedsBalance:
msgParams.first = tr("The total exceeds your balance when the %1 "
"transaction fee is included.")
.arg(msgArg);
break;
case WalletModel::DuplicateAddress:
msgParams.first = tr("Duplicate address found: addresses should "
"only be used once each.");
break;
case WalletModel::TransactionCreationFailed:
msgParams.first = tr("Transaction creation failed!");
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::TransactionCommitFailed:
msgParams.first =
tr("The transaction was rejected with the following reason: %1")
.arg(sendCoinsReturn.reasonCommitFailed);
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::AbsurdFee:
msgParams.first =
tr("A fee higher than %1 is considered an absurdly high fee.")
.arg(BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(), maxTxFee));
break;
case WalletModel::PaymentRequestExpired:
msgParams.first = tr("Payment request expired.");
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
// included to prevent a compiler warning.
case WalletModel::OK:
default:
return;
}
Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
}
void SendCoinsDialog::minimizeFeeSection(bool fMinimize) {
ui->labelFeeMinimized->setVisible(fMinimize);
ui->buttonChooseFee->setVisible(fMinimize);
ui->buttonMinimizeFee->setVisible(!fMinimize);
ui->frameFeeSelection->setVisible(!fMinimize);
ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0,
0);
fFeeMinimized = fMinimize;
}
void SendCoinsDialog::on_buttonChooseFee_clicked() {
minimizeFeeSection(false);
}
void SendCoinsDialog::on_buttonMinimizeFee_clicked() {
updateFeeMinimizedLabel();
minimizeFeeSection(true);
}
void SendCoinsDialog::useAvailableBalance(SendCoinsEntry *entry) {
// Get CCoinControl instance if CoinControl is enabled or create a new one.
CCoinControl coin_control;
if (model->getOptionsModel()->getCoinControlFeatures()) {
coin_control = *CoinControlDialog::coinControl();
}
// Calculate available amount to send.
- Amount amount = model->getBalance(&coin_control);
+ Amount amount = model->wallet().getAvailableBalance(coin_control);
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry *e =
qobject_cast<SendCoinsEntry *>(ui->entries->itemAt(i)->widget());
if (e && !e->isHidden() && e != entry) {
amount -= e->getValue().amount;
}
}
if (amount > Amount::zero()) {
entry->checkSubtractFeeFromAmount();
entry->setAmount(amount);
} else {
entry->setAmount(Amount::zero());
}
}
void SendCoinsDialog::setMinimumFee() {
ui->radioCustomPerKilobyte->setChecked(true);
ui->customFee->setValue(GetMinimumFee(1000, g_mempool));
}
void SendCoinsDialog::updateFeeSectionControls() {
ui->labelSmartFee->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFee2->setEnabled(ui->radioSmartFee->isChecked());
ui->labelFeeEstimation->setEnabled(ui->radioSmartFee->isChecked());
ui->checkBoxMinimumFee->setEnabled(ui->radioCustomFee->isChecked());
ui->labelMinFeeWarning->setEnabled(ui->radioCustomFee->isChecked());
ui->radioCustomPerKilobyte->setEnabled(
ui->radioCustomFee->isChecked() &&
!ui->checkBoxMinimumFee->isChecked());
ui->customFee->setEnabled(ui->radioCustomFee->isChecked() &&
!ui->checkBoxMinimumFee->isChecked());
}
void SendCoinsDialog::updateFeeMinimizedLabel() {
if (!model || !model->getOptionsModel()) {
return;
}
if (ui->radioSmartFee->isChecked()) {
ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
} else {
ui->labelFeeMinimized->setText(
BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(),
ui->customFee->value()) +
((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : ""));
}
}
void SendCoinsDialog::updateMinFeeLabel() {
if (model && model->getOptionsModel()) {
ui->checkBoxMinimumFee->setText(
tr("Pay only the required fee of %1")
.arg(BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(),
GetMinimumFee(1000, g_mempool)) +
"/kB"));
}
}
void SendCoinsDialog::updateCoinControlState(CCoinControl &ctrl) {
if (ui->radioCustomFee->isChecked()) {
ctrl.m_feerate = CFeeRate(ui->customFee->value());
} else {
ctrl.m_feerate.reset();
}
}
void SendCoinsDialog::updateSmartFeeLabel() {
if (!model || !model->getOptionsModel()) {
return;
}
CFeeRate feeRate = g_mempool.estimateFee();
ui->labelSmartFee->setText(
BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(),
std::max(feeRate.GetFeePerK(), GetMinimumFee(1000, g_mempool))) +
"/kB");
// not enough data => minfee
if (feeRate <= CFeeRate(Amount::zero())) {
// (Smart fee not initialized yet. This usually takes a few blocks...)
ui->labelSmartFee2->show();
ui->labelFeeEstimation->setText("");
} else {
ui->labelSmartFee2->hide();
ui->labelFeeEstimation->setText(
tr("Estimated to begin confirmation by next block."));
}
updateFeeMinimizedLabel();
}
// Coin Control: copy label "Quantity" to clipboard
void SendCoinsDialog::coinControlClipboardQuantity() {
GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
}
// Coin Control: copy label "Amount" to clipboard
void SendCoinsDialog::coinControlClipboardAmount() {
GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(
ui->labelCoinControlAmount->text().indexOf(" ")));
}
// Coin Control: copy label "Fee" to clipboard
void SendCoinsDialog::coinControlClipboardFee() {
GUIUtil::setClipboard(
ui->labelCoinControlFee->text()
.left(ui->labelCoinControlFee->text().indexOf(" "))
.replace(ASYMP_UTF8, ""));
}
// Coin Control: copy label "After fee" to clipboard
void SendCoinsDialog::coinControlClipboardAfterFee() {
GUIUtil::setClipboard(
ui->labelCoinControlAfterFee->text()
.left(ui->labelCoinControlAfterFee->text().indexOf(" "))
.replace(ASYMP_UTF8, ""));
}
// Coin Control: copy label "Bytes" to clipboard
void SendCoinsDialog::coinControlClipboardBytes() {
GUIUtil::setClipboard(
ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
}
// Coin Control: copy label "Dust" to clipboard
void SendCoinsDialog::coinControlClipboardLowOutput() {
GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
}
// Coin Control: copy label "Change" to clipboard
void SendCoinsDialog::coinControlClipboardChange() {
GUIUtil::setClipboard(
ui->labelCoinControlChange->text()
.left(ui->labelCoinControlChange->text().indexOf(" "))
.replace(ASYMP_UTF8, ""));
}
// Coin Control: settings menu - coin control enabled/disabled by user
void SendCoinsDialog::coinControlFeatureChanged(bool checked) {
ui->frameCoinControl->setVisible(checked);
// coin control features disabled
if (!checked && model) {
CoinControlDialog::coinControl()->SetNull();
}
coinControlUpdateLabels();
}
// Coin Control: button inputs -> show actual coin control dialog
void SendCoinsDialog::coinControlButtonClicked() {
CoinControlDialog dlg(platformStyle);
dlg.setModel(model);
dlg.exec();
coinControlUpdateLabels();
}
// Coin Control: checkbox custom change address
void SendCoinsDialog::coinControlChangeChecked(int state) {
if (state == Qt::Unchecked) {
CoinControlDialog::coinControl()->destChange = CNoDestination();
ui->labelCoinControlChangeLabel->clear();
} else {
// use this to re-validate an already entered address
coinControlChangeEdited(ui->lineEditCoinControlChange->text());
}
ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
}
// Coin Control: custom change address changed
void SendCoinsDialog::coinControlChangeEdited(const QString &text) {
if (model && model->getAddressTableModel()) {
// Default to no change address until verified
CoinControlDialog::coinControl()->destChange = CNoDestination();
ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
const CTxDestination dest =
DecodeDestination(text.toStdString(), model->getChainParams());
if (text.isEmpty()) {
// Nothing entered
ui->labelCoinControlChangeLabel->setText("");
} else if (!IsValidDestination(dest)) {
// Invalid address
ui->labelCoinControlChangeLabel->setText(
tr("Warning: Invalid Bitcoin address"));
} else {
// Valid address
- if (!model->IsSpendable(dest)) {
+ if (!model->wallet().isSpendable(dest)) {
ui->labelCoinControlChangeLabel->setText(
tr("Warning: Unknown change address"));
// confirmation dialog
QMessageBox::StandardButton btnRetVal = QMessageBox::question(
this, tr("Confirm custom change address"),
tr("The address you selected for change is not part of "
"this wallet. Any or all funds in your wallet may be "
"sent to this address. Are you sure?"),
QMessageBox::Yes | QMessageBox::Cancel,
QMessageBox::Cancel);
if (btnRetVal == QMessageBox::Yes) {
CoinControlDialog::coinControl()->destChange = dest;
} else {
ui->lineEditCoinControlChange->setText("");
ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:black;}");
ui->labelCoinControlChangeLabel->setText("");
}
} else {
// Known change address
ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:black;}");
// Query label
QString associatedLabel =
model->getAddressTableModel()->labelForAddress(text);
if (!associatedLabel.isEmpty()) {
ui->labelCoinControlChangeLabel->setText(associatedLabel);
} else {
ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
}
CoinControlDialog::coinControl()->destChange = dest;
}
}
}
}
// Coin Control: update labels
void SendCoinsDialog::coinControlUpdateLabels() {
if (!model || !model->getOptionsModel()) {
return;
}
updateCoinControlState(*CoinControlDialog::coinControl());
// set pay amounts
CoinControlDialog::payAmounts.clear();
CoinControlDialog::fSubtractFeeFromAmount = false;
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry *entry =
qobject_cast<SendCoinsEntry *>(ui->entries->itemAt(i)->widget());
if (entry && !entry->isHidden()) {
SendCoinsRecipient rcp = entry->getValue();
CoinControlDialog::payAmounts.append(rcp.amount);
if (rcp.fSubtractFeeFromAmount) {
CoinControlDialog::fSubtractFeeFromAmount = true;
}
}
}
if (CoinControlDialog::coinControl()->HasSelected()) {
// actual coin control calculation
CoinControlDialog::updateLabels(model, this);
// show coin control stats
ui->labelCoinControlAutomaticallySelected->hide();
ui->widgetCoinControl->show();
} else {
// hide coin control stats
ui->labelCoinControlAutomaticallySelected->show();
ui->widgetCoinControl->hide();
ui->labelCoinControlInsuffFunds->hide();
}
}
SendConfirmationDialog::SendConfirmationDialog(const QString &title,
const QString &text,
int _secDelay, QWidget *parent)
: QMessageBox(QMessageBox::Question, title, text,
QMessageBox::Yes | QMessageBox::Cancel, parent),
secDelay(_secDelay) {
setDefaultButton(QMessageBox::Cancel);
yesButton = button(QMessageBox::Yes);
updateYesButton();
connect(&countDownTimer, SIGNAL(timeout()), this, SLOT(countDown()));
}
int SendConfirmationDialog::exec() {
updateYesButton();
countDownTimer.start(1000);
return QMessageBox::exec();
}
void SendConfirmationDialog::countDown() {
secDelay--;
updateYesButton();
if (secDelay <= 0) {
countDownTimer.stop();
}
}
void SendConfirmationDialog::updateYesButton() {
if (secDelay > 0) {
yesButton->setEnabled(false);
yesButton->setText(tr("Yes") + " (" + QString::number(secDelay) + ")");
} else {
yesButton->setEnabled(true);
yesButton->setText(tr("Yes"));
}
}
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index ed27504dd6..3ed0543acf 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -1,278 +1,278 @@
// Copyright (c) 2011-2016 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 "signverifymessagedialog.h"
#include "ui_signverifymessagedialog.h"
#include "addressbookpage.h"
#include "guiutil.h"
#include "platformstyle.h"
#include "walletmodel.h"
#include "dstencode.h"
#include "init.h"
#include "validation.h" // For strMessageMagic
#include "wallet/wallet.h"
#include <string>
#include <vector>
#include <QClipboard>
SignVerifyMessageDialog::SignVerifyMessageDialog(
const PlatformStyle *_platformStyle, QWidget *parent)
: QDialog(parent), ui(new Ui::SignVerifyMessageDialog), model(0),
platformStyle(_platformStyle) {
ui->setupUi(this);
ui->addressBookButton_SM->setIcon(
platformStyle->SingleColorIcon(":/icons/address-book"));
ui->pasteButton_SM->setIcon(
platformStyle->SingleColorIcon(":/icons/editpaste"));
ui->copySignatureButton_SM->setIcon(
platformStyle->SingleColorIcon(":/icons/editcopy"));
ui->signMessageButton_SM->setIcon(
platformStyle->SingleColorIcon(":/icons/edit"));
ui->clearButton_SM->setIcon(
platformStyle->SingleColorIcon(":/icons/remove"));
ui->addressBookButton_VM->setIcon(
platformStyle->SingleColorIcon(":/icons/address-book"));
ui->verifyMessageButton_VM->setIcon(
platformStyle->SingleColorIcon(":/icons/transaction_0"));
ui->clearButton_VM->setIcon(
platformStyle->SingleColorIcon(":/icons/remove"));
ui->signatureOut_SM->setPlaceholderText(
tr("Click \"Sign Message\" to generate signature"));
GUIUtil::setupAddressWidget(ui->addressIn_SM, this);
GUIUtil::setupAddressWidget(ui->addressIn_VM, this);
ui->addressIn_SM->installEventFilter(this);
ui->messageIn_SM->installEventFilter(this);
ui->signatureOut_SM->installEventFilter(this);
ui->addressIn_VM->installEventFilter(this);
ui->messageIn_VM->installEventFilter(this);
ui->signatureIn_VM->installEventFilter(this);
ui->signatureOut_SM->setFont(GUIUtil::fixedPitchFont());
ui->signatureIn_VM->setFont(GUIUtil::fixedPitchFont());
}
SignVerifyMessageDialog::~SignVerifyMessageDialog() {
delete ui;
}
void SignVerifyMessageDialog::setModel(WalletModel *_model) {
this->model = _model;
}
void SignVerifyMessageDialog::setAddress_SM(const QString &address) {
ui->addressIn_SM->setText(address);
ui->messageIn_SM->setFocus();
}
void SignVerifyMessageDialog::setAddress_VM(const QString &address) {
ui->addressIn_VM->setText(address);
ui->messageIn_VM->setFocus();
}
void SignVerifyMessageDialog::showTab_SM(bool fShow) {
ui->tabWidget->setCurrentIndex(0);
if (fShow) this->show();
}
void SignVerifyMessageDialog::showTab_VM(bool fShow) {
ui->tabWidget->setCurrentIndex(1);
if (fShow) this->show();
}
void SignVerifyMessageDialog::on_addressBookButton_SM_clicked() {
if (model && model->getAddressTableModel()) {
AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection,
AddressBookPage::ReceivingTab, this);
dlg.setModel(model->getAddressTableModel());
if (dlg.exec()) {
setAddress_SM(dlg.getReturnValue());
}
}
}
void SignVerifyMessageDialog::on_pasteButton_SM_clicked() {
setAddress_SM(QApplication::clipboard()->text());
}
void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() {
if (!model) return;
/* Clear old signature to ensure users don't get confused on error with an
* old signature displayed */
ui->signatureOut_SM->clear();
CTxDestination destination = DecodeDestination(
ui->addressIn_SM->text().toStdString(), model->getChainParams());
if (!IsValidDestination(destination)) {
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(
tr("The entered address is invalid.") + QString(" ") +
tr("Please check the address and try again."));
return;
}
const CKeyID *keyID = boost::get<CKeyID>(&destination);
if (!keyID) {
ui->addressIn_SM->setValid(false);
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(
tr("The entered address does not refer to a key.") + QString(" ") +
tr("Please check the address and try again."));
return;
}
WalletModel::UnlockContext ctx(model->requestUnlock());
if (!ctx.isValid()) {
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("Wallet unlock was cancelled."));
return;
}
CKey key;
- if (!model->getPrivKey(*keyID, key)) {
+ if (!model->wallet().getPrivKey(*keyID, key)) {
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(
tr("Private key for the entered address is not available."));
return;
}
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << ui->messageIn_SM->document()->toPlainText().toStdString();
std::vector<uint8_t> vchSig;
if (!key.SignCompact(ss.GetHash(), vchSig)) {
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(QString("<nobr>") +
tr("Message signing failed.") +
QString("</nobr>"));
return;
}
ui->statusLabel_SM->setStyleSheet("QLabel { color: green; }");
ui->statusLabel_SM->setText(QString("<nobr>") + tr("Message signed.") +
QString("</nobr>"));
ui->signatureOut_SM->setText(
QString::fromStdString(EncodeBase64(&vchSig[0], vchSig.size())));
}
void SignVerifyMessageDialog::on_copySignatureButton_SM_clicked() {
GUIUtil::setClipboard(ui->signatureOut_SM->text());
}
void SignVerifyMessageDialog::on_clearButton_SM_clicked() {
ui->addressIn_SM->clear();
ui->messageIn_SM->clear();
ui->signatureOut_SM->clear();
ui->statusLabel_SM->clear();
ui->addressIn_SM->setFocus();
}
void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() {
if (model && model->getAddressTableModel()) {
AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection,
AddressBookPage::SendingTab, this);
dlg.setModel(model->getAddressTableModel());
if (dlg.exec()) {
setAddress_VM(dlg.getReturnValue());
}
}
}
void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() {
CTxDestination destination = DecodeDestination(
ui->addressIn_VM->text().toStdString(), model->getChainParams());
if (!IsValidDestination(destination)) {
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(
tr("The entered address is invalid.") + QString(" ") +
tr("Please check the address and try again."));
return;
}
if (!boost::get<CKeyID>(&destination)) {
ui->addressIn_VM->setValid(false);
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(
tr("The entered address does not refer to a key.") + QString(" ") +
tr("Please check the address and try again."));
return;
}
bool fInvalid = false;
std::vector<uint8_t> vchSig = DecodeBase64(
ui->signatureIn_VM->text().toStdString().c_str(), &fInvalid);
if (fInvalid) {
ui->signatureIn_VM->setValid(false);
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(
tr("The signature could not be decoded.") + QString(" ") +
tr("Please check the signature and try again."));
return;
}
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << ui->messageIn_VM->document()->toPlainText().toStdString();
CPubKey pubkey;
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) {
ui->signatureIn_VM->setValid(false);
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(
tr("The signature did not match the message digest.") +
QString(" ") + tr("Please check the signature and try again."));
return;
}
if (!(CTxDestination(pubkey.GetID()) == destination)) {
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(QString("<nobr>") +
tr("Message verification failed.") +
QString("</nobr>"));
return;
}
ui->statusLabel_VM->setStyleSheet("QLabel { color: green; }");
ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verified.") +
QString("</nobr>"));
}
void SignVerifyMessageDialog::on_clearButton_VM_clicked() {
ui->addressIn_VM->clear();
ui->signatureIn_VM->clear();
ui->messageIn_VM->clear();
ui->statusLabel_VM->clear();
ui->addressIn_VM->setFocus();
}
bool SignVerifyMessageDialog::eventFilter(QObject *object, QEvent *event) {
if (event->type() == QEvent::MouseButtonPress ||
event->type() == QEvent::FocusIn) {
if (ui->tabWidget->currentIndex() == 0) {
/* Clear status message on focus change */
ui->statusLabel_SM->clear();
/* Select generated signature */
if (object == ui->signatureOut_SM) {
ui->signatureOut_SM->selectAll();
return true;
}
} else if (ui->tabWidget->currentIndex() == 1) {
/* Clear status message on focus change */
ui->statusLabel_VM->clear();
}
}
return QDialog::eventFilter(object, event);
}
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 15366a0c56..9a5865e1bf 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -1,245 +1,248 @@
#include "wallettests.h"
#include "chainparams.h"
#include "config.h"
#include "dstencode.h"
#include "interfaces/node.h"
#include "qt/bitcoinamountfield.h"
#include "qt/optionsmodel.h"
#include "qt/overviewpage.h"
#include "qt/platformstyle.h"
#include "qt/qvalidatedlineedit.h"
#include "qt/receivecoinsdialog.h"
#include "qt/receiverequestdialog.h"
#include "qt/recentrequeststablemodel.h"
#include "qt/sendcoinsdialog.h"
#include "qt/sendcoinsentry.h"
#include "qt/transactiontablemodel.h"
#include "qt/walletmodel.h"
#include "test/test_bitcoin.h"
#include "validation.h"
#include "wallet/wallet.h"
#include <QAbstractButton>
#include <QApplication>
#include <QDialogButtonBox>
#include <QListView>
#include <QPushButton>
#include <QTextEdit>
#include <QTimer>
#include <QVBoxLayout>
namespace {
//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.
void ConfirmSend(QString *text = nullptr, bool cancel = false) {
QTimer::singleShot(0, Qt::PreciseTimer, [text, cancel]() {
for (QWidget *widget : QApplication::topLevelWidgets()) {
if (widget->inherits("SendConfirmationDialog")) {
SendConfirmationDialog *dialog =
qobject_cast<SendConfirmationDialog *>(widget);
if (text) {
*text = dialog->text();
}
QAbstractButton *button = dialog->button(
cancel ? QMessageBox::Cancel : QMessageBox::Yes);
button->setEnabled(true);
button->click();
}
}
});
}
//! Send coins to address and return txid.
uint256 SendCoins(CWallet &wallet, SendCoinsDialog &sendCoinsDialog,
const CTxDestination &address, Amount amount) {
QVBoxLayout *entries = sendCoinsDialog.findChild<QVBoxLayout *>("entries");
SendCoinsEntry *entry =
qobject_cast<SendCoinsEntry *>(entries->itemAt(0)->widget());
entry->findChild<QValidatedLineEdit *>("payTo")->setText(
QString::fromStdString(EncodeDestination(address)));
entry->findChild<BitcoinAmountField *>("payAmount")->setValue(amount);
uint256 txid;
boost::signals2::scoped_connection c =
wallet.NotifyTransactionChanged.connect(
[&txid](CWallet *, const uint256 &hash, ChangeType status) {
if (status == CT_NEW) {
txid = hash;
}
});
ConfirmSend();
QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked");
return txid;
}
//! Find index of txid in transaction list.
QModelIndex FindTx(const QAbstractItemModel &model, const uint256 &txid) {
QString hash = QString::fromStdString(txid.ToString());
int rows = model.rowCount({});
for (int row = 0; row < rows; ++row) {
QModelIndex index = model.index(row, 0, {});
if (model.data(index, TransactionTableModel::TxHashRole) == hash) {
return index;
}
}
return {};
}
//! Simple qt wallet tests.
//
// Test widgets can be debugged interactively calling show() on them and
// manually running the event loop, e.g.:
//
// sendCoinsDialog.show();
// QEventLoop().exec();
//
// This also requires overriding the default minimal Qt platform:
//
// src/qt/test/test_bitcoin-qt -platform xcb # Linux
// src/qt/test/test_bitcoin-qt -platform windows # Windows
// src/qt/test/test_bitcoin-qt -platform cocoa # macOS
void TestGUI() {
#ifdef Q_OS_MAC
if (QApplication::platformName() == "minimal") {
// Disable for mac on "minimal" platform to avoid crashes inside the Qt
// framework when it tries to look up unimplemented cocoa functions,
// and fails to handle returned nulls
// (https://bugreports.qt.io/browse/QTBUG-49686).
QWARN("Skipping WalletTests on mac build with 'minimal' platform set "
"due to Qt bugs. To run AppTests, invoke "
"with 'test_bitcoin-qt -platform cocoa' on mac, or else use a "
"linux or windows build.");
return;
}
#endif
// Set up wallet and chain with 105 blocks (5 mature blocks for spending).
TestChain100Setup test;
for (int i = 0; i < 5; ++i) {
test.CreateAndProcessBlock(
{}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
bitdb.MakeMock();
std::unique_ptr<CWalletDBWrapper> dbw(
new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
CWallet wallet(Params(), std::move(dbw));
bool firstRun;
wallet.LoadWallet(firstRun);
{
LOCK(wallet.cs_wallet);
wallet.SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "",
"receive");
wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
}
{
LOCK(cs_main);
wallet.ScanForWalletTransactions(chainActive.Genesis(), nullptr, true);
}
wallet.SetBroadcastTransactions(true);
// Create widgets for sending coins and listing transactions.
std::unique_ptr<const PlatformStyle> platformStyle(
PlatformStyle::instantiate("other"));
SendCoinsDialog sendCoinsDialog(platformStyle.get());
auto node = interfaces::MakeNode();
OptionsModel optionsModel(*node);
- WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel);
+ vpwallets.insert(vpwallets.begin(), &wallet);
+ WalletModel walletModel(std::move(node->getWallets()[0]), *node,
+ platformStyle.get(), &wallet, &optionsModel);
+ vpwallets.erase(vpwallets.begin());
sendCoinsDialog.setModel(&walletModel);
// Send two transactions, and verify they are added to transaction list.
TransactionTableModel *transactionTableModel =
walletModel.getTransactionTableModel();
QCOMPARE(transactionTableModel->rowCount({}), 105);
uint256 txid1 =
SendCoins(wallet, sendCoinsDialog, CTxDestination(CKeyID()), 5 * COIN);
uint256 txid2 =
SendCoins(wallet, sendCoinsDialog, CTxDestination(CKeyID()), 10 * COIN);
QCOMPARE(transactionTableModel->rowCount({}), 107);
QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
// Check current balance on OverviewPage
OverviewPage overviewPage(platformStyle.get());
overviewPage.setWalletModel(&walletModel);
QLabel *balanceLabel = overviewPage.findChild<QLabel *>("labelBalance");
QString balanceText = balanceLabel->text();
int unit = walletModel.getOptionsModel()->getDisplayUnit();
- Amount balance = walletModel.getBalance();
+ Amount balance = walletModel.wallet().getBalance();
QString balanceComparison = BitcoinUnits::formatWithUnit(
unit, balance, false, BitcoinUnits::separatorAlways);
QCOMPARE(balanceText, balanceComparison);
// Check Request Payment button
const Config &config = GetConfig();
ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get(), &config);
receiveCoinsDialog.setModel(&walletModel);
RecentRequestsTableModel *requestTableModel =
walletModel.getRecentRequestsTableModel();
// Label input
QLineEdit *labelInput =
receiveCoinsDialog.findChild<QLineEdit *>("reqLabel");
labelInput->setText("TEST_LABEL_1");
// Amount input
BitcoinAmountField *amountInput =
receiveCoinsDialog.findChild<BitcoinAmountField *>("reqAmount");
amountInput->setValue(1 * SATOSHI);
// Message input
QLineEdit *messageInput =
receiveCoinsDialog.findChild<QLineEdit *>("reqMessage");
messageInput->setText("TEST_MESSAGE_1");
int initialRowCount = requestTableModel->rowCount({});
QPushButton *requestPaymentButton =
receiveCoinsDialog.findChild<QPushButton *>("receiveButton");
requestPaymentButton->click();
for (QWidget *widget : QApplication::topLevelWidgets()) {
if (widget->inherits("ReceiveRequestDialog")) {
ReceiveRequestDialog *receiveRequestDialog =
qobject_cast<ReceiveRequestDialog *>(widget);
QTextEdit *rlist =
receiveRequestDialog->QObject::findChild<QTextEdit *>("outUri");
QString paymentText = rlist->toPlainText();
QStringList paymentTextList = paymentText.split('\n');
QCOMPARE(paymentTextList.at(0), QString("Payment information"));
QVERIFY(paymentTextList.at(1).indexOf(
QString("URI: bitcoincash:")) != -1);
QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1);
QCOMPARE(paymentTextList.at(3),
QString("Amount: 0.00000001 ") +
QString::fromStdString(CURRENCY_UNIT));
QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1"));
QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1"));
}
}
// Clear button
QPushButton *clearButton =
receiveCoinsDialog.findChild<QPushButton *>("clearButton");
clearButton->click();
QCOMPARE(labelInput->text(), QString(""));
QCOMPARE(amountInput->value(), Amount::zero());
QCOMPARE(messageInput->text(), QString(""));
// Check addition to history
int currentRowCount = requestTableModel->rowCount({});
QCOMPARE(currentRowCount, initialRowCount + 1);
// Check Remove button
QTableView *table =
receiveCoinsDialog.findChild<QTableView *>("recentRequestsView");
table->selectRow(currentRowCount - 1);
QPushButton *removeRequestButton =
receiveCoinsDialog.findChild<QPushButton *>("removeRequestButton");
removeRequestButton->click();
QCOMPARE(requestTableModel->rowCount({}), currentRowCount - 1);
bitdb.Flush(true);
bitdb.Reset();
}
}
void WalletTests::walletTests() {
TestGUI();
}
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 945c91ff05..9ae5334ce0 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -1,666 +1,666 @@
// Copyright (c) 2011-2016 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 "transactionview.h"
#include "addresstablemodel.h"
#include "bitcoinunits.h"
#include "csvmodelwriter.h"
#include "editaddressdialog.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "platformstyle.h"
#include "transactiondescdialog.h"
#include "transactionfilterproxy.h"
#include "transactionrecord.h"
#include "transactiontablemodel.h"
#include "walletmodel.h"
#include "ui_interface.h"
#include <QComboBox>
#include <QDateTimeEdit>
#include <QDesktopServices>
#include <QDoubleValidator>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QPoint>
#include <QScrollBar>
#include <QSignalMapper>
#include <QTableView>
#include <QUrl>
#include <QVBoxLayout>
TransactionView::TransactionView(const PlatformStyle *platformStyle,
QWidget *parent)
: QWidget(parent), model(0), transactionProxyModel(0), transactionView(0),
abandonAction(0), columnResizingFixer(0) {
// Build filter row
setContentsMargins(0, 0, 0, 0);
QHBoxLayout *hlayout = new QHBoxLayout();
hlayout->setContentsMargins(0, 0, 0, 0);
if (platformStyle->getUseExtraSpacing()) {
hlayout->setSpacing(5);
hlayout->addSpacing(26);
} else {
hlayout->setSpacing(0);
hlayout->addSpacing(23);
}
watchOnlyWidget = new QComboBox(this);
watchOnlyWidget->setFixedWidth(24);
watchOnlyWidget->addItem("", TransactionFilterProxy::WatchOnlyFilter_All);
watchOnlyWidget->addItem(platformStyle->SingleColorIcon(":/icons/eye_plus"),
"", TransactionFilterProxy::WatchOnlyFilter_Yes);
watchOnlyWidget->addItem(
platformStyle->SingleColorIcon(":/icons/eye_minus"), "",
TransactionFilterProxy::WatchOnlyFilter_No);
hlayout->addWidget(watchOnlyWidget);
dateWidget = new QComboBox(this);
if (platformStyle->getUseExtraSpacing()) {
dateWidget->setFixedWidth(121);
} else {
dateWidget->setFixedWidth(120);
}
dateWidget->addItem(tr("All"), All);
dateWidget->addItem(tr("Today"), Today);
dateWidget->addItem(tr("This week"), ThisWeek);
dateWidget->addItem(tr("This month"), ThisMonth);
dateWidget->addItem(tr("Last month"), LastMonth);
dateWidget->addItem(tr("This year"), ThisYear);
dateWidget->addItem(tr("Range..."), Range);
hlayout->addWidget(dateWidget);
typeWidget = new QComboBox(this);
if (platformStyle->getUseExtraSpacing()) {
typeWidget->setFixedWidth(121);
} else {
typeWidget->setFixedWidth(120);
}
typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES);
typeWidget->addItem(
tr("Received with"),
TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) |
TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther));
typeWidget->addItem(
tr("Sent to"),
TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) |
TransactionFilterProxy::TYPE(TransactionRecord::SendToOther));
typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(
TransactionRecord::SendToSelf));
typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(
TransactionRecord::Generated));
typeWidget->addItem(tr("Other"),
TransactionFilterProxy::TYPE(TransactionRecord::Other));
hlayout->addWidget(typeWidget);
addressWidget = new QLineEdit(this);
addressWidget->setPlaceholderText(tr("Enter address or label to search"));
hlayout->addWidget(addressWidget);
amountWidget = new QLineEdit(this);
amountWidget->setPlaceholderText(tr("Min amount"));
if (platformStyle->getUseExtraSpacing()) {
amountWidget->setFixedWidth(97);
} else {
amountWidget->setFixedWidth(100);
}
amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
hlayout->addWidget(amountWidget);
QVBoxLayout *vlayout = new QVBoxLayout(this);
vlayout->setContentsMargins(0, 0, 0, 0);
vlayout->setSpacing(0);
QTableView *view = new QTableView(this);
vlayout->addLayout(hlayout);
vlayout->addWidget(createDateRangeWidget());
vlayout->addWidget(view);
vlayout->setSpacing(0);
int width = view->verticalScrollBar()->sizeHint().width();
// Cover scroll bar width with spacing
if (platformStyle->getUseExtraSpacing()) {
hlayout->addSpacing(width + 2);
} else {
hlayout->addSpacing(width);
}
// Always show scroll bar
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
view->setTabKeyNavigation(false);
view->setContextMenuPolicy(Qt::CustomContextMenu);
view->installEventFilter(this);
transactionView = view;
// Actions
abandonAction = new QAction(tr("Abandon transaction"), this);
QAction *copyAddressAction = new QAction(tr("Copy address"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this);
QAction *copyTxHexAction = new QAction(tr("Copy raw transaction"), this);
QAction *copyTxPlainText =
new QAction(tr("Copy full transaction details"), this);
QAction *editLabelAction = new QAction(tr("Edit label"), this);
QAction *showDetailsAction =
new QAction(tr("Show transaction details"), this);
contextMenu = new QMenu(this);
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyAmountAction);
contextMenu->addAction(copyTxIDAction);
contextMenu->addAction(copyTxHexAction);
contextMenu->addAction(copyTxPlainText);
contextMenu->addAction(showDetailsAction);
contextMenu->addSeparator();
contextMenu->addAction(abandonAction);
contextMenu->addAction(editLabelAction);
mapperThirdPartyTxUrls = new QSignalMapper(this);
// Connect actions
connect(mapperThirdPartyTxUrls, SIGNAL(mapped(QString)), this,
SLOT(openThirdPartyTxUrl(QString)));
connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int)));
connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int)));
connect(watchOnlyWidget, SIGNAL(activated(int)), this,
SLOT(chooseWatchonly(int)));
connect(addressWidget, SIGNAL(textChanged(QString)), this,
SLOT(changedPrefix(QString)));
connect(amountWidget, SIGNAL(textChanged(QString)), this,
SLOT(changedAmount(QString)));
connect(view, SIGNAL(doubleClicked(QModelIndex)), this,
SIGNAL(doubleClicked(QModelIndex)));
connect(view, SIGNAL(customContextMenuRequested(QPoint)), this,
SLOT(contextualMenu(QPoint)));
connect(abandonAction, SIGNAL(triggered()), this, SLOT(abandonTx()));
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
connect(copyTxIDAction, SIGNAL(triggered()), this, SLOT(copyTxID()));
connect(copyTxHexAction, SIGNAL(triggered()), this, SLOT(copyTxHex()));
connect(copyTxPlainText, SIGNAL(triggered()), this,
SLOT(copyTxPlainText()));
connect(editLabelAction, SIGNAL(triggered()), this, SLOT(editLabel()));
connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails()));
}
void TransactionView::setModel(WalletModel *_model) {
this->model = _model;
if (_model) {
transactionProxyModel = new TransactionFilterProxy(this);
transactionProxyModel->setSourceModel(
_model->getTransactionTableModel());
transactionProxyModel->setDynamicSortFilter(true);
transactionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
transactionProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
transactionProxyModel->setSortRole(Qt::EditRole);
transactionView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
transactionView->setModel(transactionProxyModel);
transactionView->setAlternatingRowColors(true);
transactionView->setSelectionBehavior(QAbstractItemView::SelectRows);
transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection);
transactionView->setSortingEnabled(true);
transactionView->sortByColumn(TransactionTableModel::Date,
Qt::DescendingOrder);
transactionView->verticalHeader()->hide();
transactionView->setColumnWidth(TransactionTableModel::Status,
STATUS_COLUMN_WIDTH);
transactionView->setColumnWidth(TransactionTableModel::Watchonly,
WATCHONLY_COLUMN_WIDTH);
transactionView->setColumnWidth(TransactionTableModel::Date,
DATE_COLUMN_WIDTH);
transactionView->setColumnWidth(TransactionTableModel::Type,
TYPE_COLUMN_WIDTH);
transactionView->setColumnWidth(TransactionTableModel::Amount,
AMOUNT_MINIMUM_COLUMN_WIDTH);
columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(
transactionView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH,
this);
if (_model->getOptionsModel()) {
// Add third party transaction URLs to context menu
QStringList listUrls =
_model->getOptionsModel()->getThirdPartyTxUrls().split(
"|", QString::SkipEmptyParts);
for (int i = 0; i < listUrls.size(); ++i) {
QString host =
QUrl(listUrls[i].trimmed(), QUrl::StrictMode).host();
if (!host.isEmpty()) {
// use host as menu item label
QAction *thirdPartyTxUrlAction = new QAction(host, this);
if (i == 0) {
contextMenu->addSeparator();
}
contextMenu->addAction(thirdPartyTxUrlAction);
connect(thirdPartyTxUrlAction, SIGNAL(triggered()),
mapperThirdPartyTxUrls, SLOT(map()));
mapperThirdPartyTxUrls->setMapping(thirdPartyTxUrlAction,
listUrls[i].trimmed());
}
}
}
// show/hide column Watch-only
- updateWatchOnlyColumn(_model->haveWatchOnly());
+ updateWatchOnlyColumn(_model->wallet().haveWatchOnly());
// Watch-only signal
connect(_model, SIGNAL(notifyWatchonlyChanged(bool)), this,
SLOT(updateWatchOnlyColumn(bool)));
}
}
void TransactionView::chooseDate(int idx) {
if (!transactionProxyModel) {
return;
}
QDate current = QDate::currentDate();
dateRangeWidget->setVisible(false);
switch (dateWidget->itemData(idx).toInt()) {
case All:
transactionProxyModel->setDateRange(
TransactionFilterProxy::MIN_DATE,
TransactionFilterProxy::MAX_DATE);
break;
case Today:
transactionProxyModel->setDateRange(
QDateTime(current), TransactionFilterProxy::MAX_DATE);
break;
case ThisWeek: {
// Find last Monday
QDate startOfWeek = current.addDays(-(current.dayOfWeek() - 1));
transactionProxyModel->setDateRange(
QDateTime(startOfWeek), TransactionFilterProxy::MAX_DATE);
} break;
case ThisMonth:
transactionProxyModel->setDateRange(
QDateTime(QDate(current.year(), current.month(), 1)),
TransactionFilterProxy::MAX_DATE);
break;
case LastMonth:
transactionProxyModel->setDateRange(
QDateTime(
QDate(current.year(), current.month(), 1).addMonths(-1)),
QDateTime(QDate(current.year(), current.month(), 1)));
break;
case ThisYear:
transactionProxyModel->setDateRange(
QDateTime(QDate(current.year(), 1, 1)),
TransactionFilterProxy::MAX_DATE);
break;
case Range:
dateRangeWidget->setVisible(true);
dateRangeChanged();
break;
}
}
void TransactionView::chooseType(int idx) {
if (!transactionProxyModel) {
return;
}
transactionProxyModel->setTypeFilter(typeWidget->itemData(idx).toInt());
}
void TransactionView::chooseWatchonly(int idx) {
if (!transactionProxyModel) {
return;
}
transactionProxyModel->setWatchOnlyFilter(
static_cast<TransactionFilterProxy::WatchOnlyFilter>(
watchOnlyWidget->itemData(idx).toInt()));
}
void TransactionView::changedPrefix(const QString &prefix) {
if (!transactionProxyModel) {
return;
}
transactionProxyModel->setAddressPrefix(prefix);
}
void TransactionView::changedAmount(const QString &amount) {
if (!transactionProxyModel) {
return;
}
Amount amount_parsed = Amount::zero();
if (BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount,
&amount_parsed)) {
transactionProxyModel->setMinAmount(amount_parsed);
} else {
transactionProxyModel->setMinAmount(Amount::zero());
}
}
void TransactionView::exportClicked() {
if (!model || !model->getOptionsModel()) {
return;
}
// CSV is currently the only supported format
QString filename = GUIUtil::getSaveFileName(
this, tr("Export Transaction History"), QString(),
tr("Comma separated file (*.csv)"), nullptr);
if (filename.isNull()) {
return;
}
CSVModelWriter writer(filename);
// name, column, role
writer.setModel(transactionProxyModel);
writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole);
- if (model->haveWatchOnly()) {
+ if (model->wallet().haveWatchOnly()) {
writer.addColumn(tr("Watch-only"), TransactionTableModel::Watchonly);
}
writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole);
writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole);
writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole);
writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole);
writer.addColumn(BitcoinUnits::getAmountColumnTitle(
model->getOptionsModel()->getDisplayUnit()),
0, TransactionTableModel::FormattedAmountRole);
writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole);
if (!writer.write()) {
Q_EMIT message(tr("Exporting Failed"),
tr("There was an error trying to save the transaction "
"history to %1.")
.arg(filename),
CClientUIInterface::MSG_ERROR);
} else {
Q_EMIT message(
tr("Exporting Successful"),
tr("The transaction history was successfully saved to %1.")
.arg(filename),
CClientUIInterface::MSG_INFORMATION);
}
}
void TransactionView::contextualMenu(const QPoint &point) {
QModelIndex index = transactionView->indexAt(point);
QModelIndexList selection =
transactionView->selectionModel()->selectedRows(0);
if (selection.empty()) {
return;
}
// check if transaction can be abandoned, disable context menu action in
// case it doesn't
TxId txid;
txid.SetHex(selection.at(0)
.data(TransactionTableModel::TxHashRole)
.toString()
.toStdString());
- abandonAction->setEnabled(model->transactionCanBeAbandoned(txid));
+ abandonAction->setEnabled(model->wallet().transactionCanBeAbandoned(txid));
if (index.isValid()) {
contextMenu->popup(transactionView->viewport()->mapToGlobal(point));
}
}
void TransactionView::abandonTx() {
if (!transactionView || !transactionView->selectionModel()) {
return;
}
QModelIndexList selection =
transactionView->selectionModel()->selectedRows(0);
// get the hash from the TxHashRole (QVariant / QString)
QString hashQStr =
selection.at(0).data(TransactionTableModel::TxHashRole).toString();
TxId txid;
txid.SetHex(hashQStr.toStdString());
// Abandon the wallet transaction over the walletModel
- model->abandonTransaction(txid);
+ model->wallet().abandonTransaction(txid);
// Update the table
model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED,
false);
}
void TransactionView::copyAddress() {
GUIUtil::copyEntryData(transactionView, 0,
TransactionTableModel::AddressRole);
}
void TransactionView::copyLabel() {
GUIUtil::copyEntryData(transactionView, 0,
TransactionTableModel::LabelRole);
}
void TransactionView::copyAmount() {
GUIUtil::copyEntryData(transactionView, 0,
TransactionTableModel::FormattedAmountRole);
}
void TransactionView::copyTxID() {
GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxIDRole);
}
void TransactionView::copyTxHex() {
GUIUtil::copyEntryData(transactionView, 0,
TransactionTableModel::TxHexRole);
}
void TransactionView::copyTxPlainText() {
GUIUtil::copyEntryData(transactionView, 0,
TransactionTableModel::TxPlainTextRole);
}
void TransactionView::editLabel() {
if (!transactionView->selectionModel() || !model) {
return;
}
QModelIndexList selection =
transactionView->selectionModel()->selectedRows();
if (!selection.isEmpty()) {
AddressTableModel *addressBook = model->getAddressTableModel();
if (!addressBook) {
return;
}
QString address =
selection.at(0).data(TransactionTableModel::AddressRole).toString();
if (address.isEmpty()) {
// If this transaction has no associated address, exit
return;
}
// Is address in address book? Address book can miss address when a
// transaction is sent from outside the UI.
int idx = addressBook->lookupAddress(address);
if (idx != -1) {
// Edit sending / receiving address
QModelIndex modelIdx = addressBook->index(idx, 0, QModelIndex());
// Determine type of address, launch appropriate editor dialog type
QString type =
modelIdx.data(AddressTableModel::TypeRole).toString();
EditAddressDialog dlg(type == AddressTableModel::Receive
? EditAddressDialog::EditReceivingAddress
: EditAddressDialog::EditSendingAddress,
this);
dlg.setModel(addressBook);
dlg.loadRow(idx);
dlg.exec();
} else {
// Add sending address
EditAddressDialog dlg(EditAddressDialog::NewSendingAddress, this);
dlg.setModel(addressBook);
dlg.setAddress(address);
dlg.exec();
}
}
}
void TransactionView::showDetails() {
if (!transactionView->selectionModel()) {
return;
}
QModelIndexList selection =
transactionView->selectionModel()->selectedRows();
if (!selection.isEmpty()) {
TransactionDescDialog *dlg = new TransactionDescDialog(selection.at(0));
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->show();
}
}
void TransactionView::openThirdPartyTxUrl(QString url) {
if (!transactionView || !transactionView->selectionModel()) {
return;
}
QModelIndexList selection =
transactionView->selectionModel()->selectedRows(0);
if (!selection.isEmpty()) {
QDesktopServices::openUrl(QUrl::fromUserInput(
url.replace("%s", selection.at(0)
.data(TransactionTableModel::TxHashRole)
.toString())));
}
}
QWidget *TransactionView::createDateRangeWidget() {
dateRangeWidget = new QFrame();
dateRangeWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
dateRangeWidget->setContentsMargins(1, 1, 1, 1);
QHBoxLayout *layout = new QHBoxLayout(dateRangeWidget);
layout->setContentsMargins(0, 0, 0, 0);
layout->addSpacing(23);
layout->addWidget(new QLabel(tr("Range:")));
dateFrom = new QDateTimeEdit(this);
dateFrom->setDisplayFormat("dd/MM/yy");
dateFrom->setCalendarPopup(true);
dateFrom->setMinimumWidth(100);
dateFrom->setDate(QDate::currentDate().addDays(-7));
layout->addWidget(dateFrom);
layout->addWidget(new QLabel(tr("to")));
dateTo = new QDateTimeEdit(this);
dateTo->setDisplayFormat("dd/MM/yy");
dateTo->setCalendarPopup(true);
dateTo->setMinimumWidth(100);
dateTo->setDate(QDate::currentDate());
layout->addWidget(dateTo);
layout->addStretch();
// Hide by default
dateRangeWidget->setVisible(false);
// Notify on change
connect(dateFrom, SIGNAL(dateChanged(QDate)), this,
SLOT(dateRangeChanged()));
connect(dateTo, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged()));
return dateRangeWidget;
}
void TransactionView::dateRangeChanged() {
if (!transactionProxyModel) {
return;
}
transactionProxyModel->setDateRange(QDateTime(dateFrom->date()),
QDateTime(dateTo->date()).addDays(1));
}
void TransactionView::focusTransaction(const QModelIndex &idx) {
if (!transactionProxyModel) {
return;
}
QModelIndex targetIdx = transactionProxyModel->mapFromSource(idx);
transactionView->scrollTo(targetIdx);
transactionView->setCurrentIndex(targetIdx);
transactionView->setFocus();
}
void TransactionView::focusTransaction(const uint256 &txid) {
if (!transactionProxyModel) {
return;
}
const QModelIndexList results =
this->model->getTransactionTableModel()->match(
this->model->getTransactionTableModel()->index(0, 0),
TransactionTableModel::TxHashRole,
QString::fromStdString(txid.ToString()), -1);
transactionView->setFocus();
transactionView->selectionModel()->clearSelection();
for (const QModelIndex &index : results) {
const QModelIndex targetIndex =
transactionProxyModel->mapFromSource(index);
transactionView->selectionModel()->select(
targetIndex,
QItemSelectionModel::Rows | QItemSelectionModel::Select);
// Called once per destination to ensure all results are in view, unless
// transactions are not ordered by (ascending or descending) date.
transactionView->scrollTo(targetIndex);
// scrollTo() does not scroll far enough the first time when
// transactions are ordered by ascending date.
if (index == results[0]) {
transactionView->scrollTo(targetIndex);
}
}
}
// We override the virtual resizeEvent of the QWidget to adjust tables column
// sizes as the tables width is proportional to the dialogs width.
void TransactionView::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
columnResizingFixer->stretchColumnWidth(TransactionTableModel::ToAddress);
}
// Need to override default Ctrl+C action for amount as default behaviour is
// just to copy DisplayRole text
bool TransactionView::eventFilter(QObject *obj, QEvent *event) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
if (ke->key() == Qt::Key_C &&
ke->modifiers().testFlag(Qt::ControlModifier)) {
GUIUtil::copyEntryData(transactionView, 0,
TransactionTableModel::TxPlainTextRole);
return true;
}
}
return QWidget::eventFilter(obj, event);
}
// show/hide column Watch-only
void TransactionView::updateWatchOnlyColumn(bool fHaveWatchOnly) {
watchOnlyWidget->setVisible(fHaveWatchOnly);
transactionView->setColumnHidden(TransactionTableModel::Watchonly,
!fHaveWatchOnly);
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index e09e3b0e65..57515745da 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -1,659 +1,527 @@
// Copyright (c) 2011-2016 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 "addresstablemodel.h"
-#include "consensus/validation.h"
-#include "guiconstants.h"
-#include "guiutil.h"
-#include "paymentserver.h"
-#include "recentrequeststablemodel.h"
-#include "transactiontablemodel.h"
-
-#include "chain.h"
-#include "config.h"
-#include "dstencode.h"
-#include "keystore.h"
-#include "net.h" // for g_connman
-#include "sync.h"
-#include "ui_interface.h"
-#include "util.h" // for GetBoolArg
-#include "validation.h"
-#include "wallet/coincontrol.h"
-#include "wallet/wallet.h"
-#include "wallet/walletdb.h" // for BackupWallet
-
-#include <cstdint>
+#include <qt/walletmodel.h>
+
+#include <chain.h>
+#include <config.h>
+#include <consensus/validation.h>
+#include <dstencode.h>
+#include <interfaces/handler.h>
+#include <interfaces/node.h>
+#include <keystore.h>
+#include <net.h> // for g_connman
+#include <qt/addresstablemodel.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
+#include <qt/paymentserver.h>
+#include <qt/recentrequeststablemodel.h>
+#include <qt/transactiontablemodel.h>
+#include <sync.h>
+#include <ui_interface.h>
+#include <util.h> // for GetBoolArg
+#include <validation.h>
+#include <wallet/coincontrol.h>
+#include <wallet/wallet.h>
+#include <wallet/walletdb.h> // for BackupWallet
#include <QDebug>
#include <QSet>
#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)
- : QObject(parent), wallet(_wallet), optionsModel(_optionsModel),
- addressTableModel(0), transactionTableModel(0),
- recentRequestsTableModel(0), cachedBalance(), cachedUnconfirmedBalance(),
- cachedImmatureBalance(), cachedEncryptionStatus(Unencrypted),
- cachedNumBlocks(0) {
- fHaveWatchOnly = wallet->HaveWatchOnly();
+ : QObject(parent), m_wallet(std::move(wallet)), m_node(node),
+ cwallet(_wallet), optionsModel(_optionsModel), addressTableModel(0),
+ transactionTableModel(0), recentRequestsTableModel(0),
+ cachedEncryptionStatus(Unencrypted), cachedNumBlocks(0) {
+ fHaveWatchOnly = m_wallet->haveWatchOnly();
fForceCheckBalanceChanged = false;
- addressTableModel = new AddressTableModel(wallet, this);
+ addressTableModel = new AddressTableModel(cwallet, this);
transactionTableModel =
- new TransactionTableModel(platformStyle, wallet, this);
- recentRequestsTableModel = new RecentRequestsTableModel(wallet, this);
+ new TransactionTableModel(platformStyle, cwallet, this);
+ recentRequestsTableModel = new RecentRequestsTableModel(cwallet, 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();
}
-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() {
EncryptionStatus newEncryptionStatus = getEncryptionStatus();
if (cachedEncryptionStatus != newEncryptionStatus) {
Q_EMIT encryptionStatusChanged();
}
}
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.
+ interfaces::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() {
- Amount newBalance(getBalance());
- Amount newUnconfirmedBalance(getUnconfirmedBalance());
- Amount newImmatureBalance(getImmatureBalance());
- Amount newWatchOnlyBalance = Amount::zero();
- Amount newWatchUnconfBalance = Amount::zero();
- Amount newWatchImmatureBalance = Amount::zero();
- 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::checkBalanceChanged(
+ const interfaces::WalletBalances &new_balances) {
+ 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);
}
}
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) {
- return IsValidDestinationString(address.toStdString(),
- GetConfig().GetChainParams());
+ return IsValidDestinationString(address.toStdString(), getChainParams());
}
WalletModel::SendCoinsReturn
WalletModel::prepareTransaction(WalletModelTransaction &transaction,
const CCoinControl &coinControl) {
Amount total = Amount::zero();
bool fSubtractFeeFromAmount = false;
QList<SendCoinsRecipient> recipients = transaction.getRecipients();
std::vector<CRecipient> vecSend;
if (recipients.empty()) {
return OK;
}
// Used to detect duplicates
QSet<QString> setAddress;
int nAddresses = 0;
// Pre-check input data for validity
for (const SendCoinsRecipient &rcp : recipients) {
if (rcp.fSubtractFeeFromAmount) fSubtractFeeFromAmount = true;
// PaymentRequest...
if (rcp.paymentRequest.IsInitialized()) {
Amount subtotal = Amount::zero();
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 += int64_t(out.amount()) * SATOSHI;
const uint8_t *scriptStr = (const uint8_t *)out.script().data();
CScript scriptPubKey(scriptStr,
scriptStr + out.script().size());
Amount nAmount = int64_t(out.amount()) * SATOSHI;
CRecipient recipient = {scriptPubKey, nAmount,
rcp.fSubtractFeeFromAmount};
vecSend.push_back(recipient);
}
if (subtotal <= Amount::zero()) {
return InvalidAmount;
}
total += subtotal;
} else {
// User-entered bitcoin address / amount:
if (!validateAddress(rcp.address)) {
return InvalidAddress;
}
if (rcp.amount <= Amount::zero()) {
return InvalidAmount;
}
setAddress.insert(rcp.address);
++nAddresses;
- CScript scriptPubKey = GetScriptForDestination(DecodeDestination(
- rcp.address.toStdString(), wallet->chainParams));
+ CScript scriptPubKey = GetScriptForDestination(
+ DecodeDestination(rcp.address.toStdString(), getChainParams()));
CRecipient recipient = {scriptPubKey, Amount(rcp.amount),
rcp.fSubtractFeeFromAmount};
vecSend.push_back(recipient);
total += rcp.amount;
}
}
if (setAddress.size() != nAddresses) {
return DuplicateAddress;
}
- Amount nBalance = getBalance(&coinControl);
+ Amount nBalance = m_wallet->getAvailableBalance(coinControl);
if (total > nBalance) {
return AmountExceedsBalance;
}
- {
- LOCK2(cs_main, wallet->cs_wallet);
-
- transaction.newPossibleKeyChange(wallet);
-
- Amount nFeeRequired = Amount::zero();
- 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);
- transaction.setTransactionFee(nFeeRequired);
- if (fSubtractFeeFromAmount && fCreated) {
- transaction.reassignAmounts(nChangePosRet);
- }
+ Amount nFeeRequired = Amount::zero();
+ int nChangePosRet = -1;
+ std::string strFailReason;
+
+ auto &newTx = transaction.getWtx();
+ newTx =
+ m_wallet->createTransaction(vecSend, coinControl, true /* sign */,
+ nChangePosRet, nFeeRequired, strFailReason);
+ transaction.setTransactionFee(nFeeRequired);
+ if (fSubtractFeeFromAmount && newTx) {
+ transaction.reassignAmounts(nChangePosRet);
+ }
- if (!fCreated) {
- if (!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) {
- return SendCoinsReturn(AmountWithFeeExceedsBalance);
- }
- Q_EMIT message(tr("Send Coins"),
- QString::fromStdString(strFailReason),
- CClientUIInterface::MSG_ERROR);
- return TransactionCreationFailed;
+ if (!newTx) {
+ if (!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) {
+ return SendCoinsReturn(AmountWithFeeExceedsBalance);
}
+ Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason),
+ CClientUIInterface::MSG_ERROR);
+ return TransactionCreationFailed;
+ }
- // 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 > Amount(maxTxFee)) {
- return AbsurdFee;
- }
+ // 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 > m_node.getMaxTxFee()) {
+ return AbsurdFee;
}
return SendCoinsReturn(OK);
}
WalletModel::SendCoinsReturn
WalletModel::sendCoins(WalletModelTransaction &transaction) {
/* store serialized transaction */
QByteArray transaction_array;
- {
- LOCK2(cs_main, wallet->cs_wallet);
-
- std::vector<std::pair<std::string, std::string>> vOrderForm;
- for (const SendCoinsRecipient &rcp : transaction.getRecipients()) {
- 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 value;
- rcp.paymentRequest.SerializeToString(&value);
- vOrderForm.emplace_back("PaymentRequest", std::move(value));
- } else if (!rcp.message.isEmpty()) {
- // Message from normal bitcoincash:URI
- // (bitcoincash:123...?message=example)
- vOrderForm.emplace_back("Message", rcp.message.toStdString());
+ std::vector<std::pair<std::string, std::string>> vOrderForm;
+ for (const SendCoinsRecipient &rcp : transaction.getRecipients()) {
+ if (rcp.paymentRequest.IsInitialized()) {
+ // Make sure any payment requests involved are still valid.
+ if (PaymentServer::verifyExpired(rcp.paymentRequest.getDetails())) {
+ return PaymentRequestExpired;
}
- }
- 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()));
+ // Store PaymentRequests in wtx.vOrderForm in wallet.
+ std::string value;
+ rcp.paymentRequest.SerializeToString(&value);
+ vOrderForm.emplace_back("PaymentRequest", std::move(value));
+ } else if (!rcp.message.isEmpty()) {
+ // Message from normal bitcoincash:URI
+ // (bitcoincash:123...?message=example)
+ vOrderForm.emplace_back("Message", rcp.message.toStdString());
}
+ }
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << newTx;
- transaction_array.append(&(ssTx[0]), ssTx.size());
+ 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->get();
+ 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
for (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 =
- DecodeDestination(strAddress, wallet->chainParams);
+ DecodeDestination(strAddress, getChainParams());
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) {
- // "" means don't change purpose
- wallet->SetAddressBook(dest, strLabel, "");
- }
+ // Check if we have a new address or an updated label
+ std::string name;
+ if (!m_wallet->getAddress(dest, &name)) {
+ m_wallet->setAddressBook(dest, strLabel, "send");
+ } else if (name != strLabel) {
+ // "" means don't change purpose
+ m_wallet->setAddressBook(dest, strLabel, "");
}
}
- Q_EMIT coinsSent(wallet, rcp, transaction_array);
+ Q_EMIT coinsSent(cwallet, rcp, transaction_array);
}
// update balance immediately, otherwise there could be a short noticeable
// delay until pollBalanceChanged hits
- checkBalanceChanged();
+ checkBalanceChanged(m_wallet->getBalances());
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()) {
+ if (!m_wallet->isCrypted()) {
return Unencrypted;
- } else if (wallet->IsLocked()) {
+ } else if (m_wallet->isLocked()) {
return Locked;
} else {
return Unlocked;
}
}
bool WalletModel::setWalletEncrypted(bool encrypted,
const SecureString &passphrase) {
if (encrypted) {
// Encrypt
- return wallet->EncryptWallet(passphrase);
+ return m_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();
+ return m_wallet->lock();
} else {
// Unlock
- return wallet->Unlock(passPhrase);
+ return m_wallet->unlock(passPhrase);
}
}
bool WalletModel::changePassphrase(const SecureString &oldPass,
const SecureString &newPass) {
- LOCK(wallet->cs_wallet);
// Make sure wallet is locked before attempting pass change
- wallet->Lock();
- return wallet->ChangeWalletPassphrase(oldPass, newPass);
-}
-
-bool WalletModel::backupWallet(const QString &filename) {
- return wallet->BackupWallet(filename.toLocal8Bit().data());
+ m_wallet->lock();
+ 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) {
QString strAddress = QString::fromStdString(EncodeDestination(address));
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);
+static void NotifyTransactionChanged(WalletModel *walletmodel, const TxId &hash,
+ ChangeType status) {
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(
+ 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
WalletModel::UnlockContext WalletModel::requestUnlock() {
bool was_locked = getEncryptionStatus() == Locked;
if (was_locked) {
// Request UI to unlock wallet
Q_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);
-}
-
-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.GetTxId());
- if (it == wallet->mapWallet.end()) {
+ auto it = cwallet->mapWallet.find(outpoint.GetTxId());
+ if (it == cwallet->mapWallet.end()) {
continue;
}
int nDepth = it->second.GetDepthInMainChain();
if (nDepth < 0) {
continue;
}
COutput out(&it->second, outpoint.GetN(), nDepth, true /* spendable */,
true /* solvable */, true /* safe */);
vOutputs.push_back(out);
}
}
bool WalletModel::isSpent(const COutPoint &outpoint) const {
- LOCK2(cs_main, wallet->cs_wallet);
- return wallet->IsSpent(outpoint.GetTxId(), outpoint.GetN());
+ LOCK2(cs_main, cwallet->cs_wallet);
+ return cwallet->IsSpent(outpoint.GetTxId(), outpoint.GetN());
}
// 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));
}
}
}
-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(
std::vector<std::string> &vReceiveRequests) {
// receive request
- vReceiveRequests = wallet->GetDestValues("rr");
+ vReceiveRequests = m_wallet->getDestValues("rr");
}
bool WalletModel::saveReceiveRequest(const std::string &sAddress,
const int64_t nId,
const std::string &sRequest) {
- CTxDestination dest = DecodeDestination(sAddress, wallet->chainParams);
+ CTxDestination dest = DecodeDestination(sAddress, getChainParams());
std::stringstream ss;
ss << nId;
// "rr" prefix = "receive request" in destdata
std::string key = "rr" + ss.str();
- LOCK(wallet->cs_wallet);
- return sRequest.empty() ? wallet->EraseDestData(dest, key)
- : 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);
+ return sRequest.empty() ? m_wallet->eraseDestData(dest, key)
+ : m_wallet->addDestData(dest, key, sRequest);
}
bool WalletModel::isWalletEnabled() {
return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
}
-bool WalletModel::hdEnabled() const {
- return wallet->IsHDEnabled();
-}
-
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;
}
const CChainParams &WalletModel::getChainParams() const {
- return wallet->chainParams;
+ return GetConfig().GetChainParams();
}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 938f4962f8..77ed02a3e9 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -1,319 +1,305 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_QT_WALLETMODEL_H
#define BITCOIN_QT_WALLETMODEL_H
-#include "chainparams.h"
-#include "paymentrequestplus.h"
-#include "walletmodeltransaction.h"
+#include <chainparams.h>
+#include <interfaces/wallet.h>
+#include <qt/paymentrequestplus.h>
+#include <qt/walletmodeltransaction.h>
+#include <support/allocators/secure.h>
-#include "support/allocators/secure.h"
+#include <QObject>
#include <map>
#include <vector>
-#include <QObject>
-
class AddressTableModel;
class OptionsModel;
class PlatformStyle;
class RecentRequestsTableModel;
class TransactionTableModel;
class WalletModelTransaction;
class CCoinControl;
class CKeyID;
class COutPoint;
class COutput;
class CPubKey;
-class CWallet;
-class uint256;
+
+namespace interfaces {
+class Node;
+} // namespace interface
QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
class SendCoinsRecipient {
public:
explicit SendCoinsRecipient()
: amount(), fSubtractFeeFromAmount(false),
nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
explicit SendCoinsRecipient(const QString &addr, const QString &_label,
const Amount _amount, const QString &_message)
: address(addr), label(_label), amount(_amount), message(_message),
fSubtractFeeFromAmount(false),
nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
// If from an unauthenticated payment request, this is used for storing the
// addresses, e.g. address-A<br />address-B<br />address-C.
// Info: As we don't need to process addresses in here when using payment
// requests, we can abuse it for displaying an address list.
// TOFO: This is a hack, should be replaced with a cleaner solution!
QString address;
QString label;
Amount amount;
// If from a payment request, this is used for storing the memo
QString message;
// If from a payment request, paymentRequest.IsInitialized() will be true
PaymentRequestPlus paymentRequest;
// Empty if no authentication or invalid signature/cert/etc.
QString authenticatedMerchant;
// memory only
bool fSubtractFeeFromAmount;
static const int CURRENT_VERSION = 1;
int nVersion;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
std::string sAddress = address.toStdString();
std::string sLabel = label.toStdString();
std::string sMessage = message.toStdString();
std::string sPaymentRequest;
if (!ser_action.ForRead() && paymentRequest.IsInitialized()) {
paymentRequest.SerializeToString(&sPaymentRequest);
}
std::string sAuthenticatedMerchant =
authenticatedMerchant.toStdString();
READWRITE(this->nVersion);
READWRITE(sAddress);
READWRITE(sLabel);
READWRITE(amount);
READWRITE(sMessage);
READWRITE(sPaymentRequest);
READWRITE(sAuthenticatedMerchant);
if (ser_action.ForRead()) {
address = QString::fromStdString(sAddress);
label = QString::fromStdString(sLabel);
message = QString::fromStdString(sMessage);
if (!sPaymentRequest.empty()) {
paymentRequest.parse(QByteArray::fromRawData(
sPaymentRequest.data(), sPaymentRequest.size()));
}
authenticatedMerchant =
QString::fromStdString(sAuthenticatedMerchant);
}
}
};
/** Interface to Bitcoin wallet from Qt view code. */
class WalletModel : public QObject {
Q_OBJECT
public:
- explicit WalletModel(const PlatformStyle *platformStyle, CWallet *wallet,
+ explicit WalletModel(std::unique_ptr<interfaces::Wallet> wallet,
+ interfaces::Node &node,
+ const PlatformStyle *platformStyle, CWallet *cwallet,
OptionsModel *optionsModel, QObject *parent = nullptr);
~WalletModel();
// Returned by sendCoins
enum StatusCode {
OK,
InvalidAmount,
InvalidAddress,
AmountExceedsBalance,
AmountWithFeeExceedsBalance,
DuplicateAddress,
// Error returned when wallet is still locked
TransactionCreationFailed,
TransactionCommitFailed,
AbsurdFee,
PaymentRequestExpired
};
enum EncryptionStatus {
// !wallet->IsCrypted()
Unencrypted,
// wallet->IsCrypted() && wallet->IsLocked()
Locked,
// wallet->IsCrypted() && !wallet->IsLocked()
Unlocked
};
OptionsModel *getOptionsModel();
AddressTableModel *getAddressTableModel();
TransactionTableModel *getTransactionTableModel();
RecentRequestsTableModel *getRecentRequestsTableModel();
- CWallet *getWallet() const { return wallet; };
-
- Amount getBalance(const CCoinControl *coinControl = nullptr) const;
- Amount getUnconfirmedBalance() const;
- Amount getImmatureBalance() const;
- bool haveWatchOnly() const;
- Amount getWatchBalance() const;
- Amount getWatchUnconfirmedBalance() const;
- Amount getWatchImmatureBalance() const;
EncryptionStatus getEncryptionStatus() const;
// Check address for validity
bool validateAddress(const QString &address);
// Return status record for SendCoins, contains error id + information
struct SendCoinsReturn {
SendCoinsReturn(StatusCode _status = OK,
QString _reasonCommitFailed = "")
: status(_status), reasonCommitFailed(_reasonCommitFailed) {}
StatusCode status;
QString reasonCommitFailed;
};
// prepare transaction for getting txfee before sending coins
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction,
const CCoinControl &coinControl);
// Send coins to a list of recipients
SendCoinsReturn sendCoins(WalletModelTransaction &transaction);
// Wallet encryption
bool setWalletEncrypted(bool encrypted, const SecureString &passphrase);
// Passphrase only needed when unlocking
bool setWalletLocked(bool locked,
const SecureString &passPhrase = SecureString());
bool changePassphrase(const SecureString &oldPass,
const SecureString &newPass);
- // Wallet backup
- bool backupWallet(const QString &filename);
// RAI object for unlocking wallet, returned by requestUnlock()
class UnlockContext {
public:
UnlockContext(WalletModel *wallet, bool valid, bool relock);
~UnlockContext();
bool isValid() const { return valid; }
// Copy operator and constructor transfer the context
UnlockContext(const UnlockContext &obj) { CopyFrom(obj); }
UnlockContext &operator=(const UnlockContext &rhs) {
CopyFrom(rhs);
return *this;
}
private:
WalletModel *wallet;
bool valid;
// mutable, as it can be set to false by copying
mutable bool relock;
void CopyFrom(const UnlockContext &rhs);
};
UnlockContext requestUnlock();
- bool getPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const;
- bool IsSpendable(const CTxDestination &dest) const;
- bool getPrivKey(const CKeyID &address, CKey &vchPrivKeyOut) const;
void getOutputs(const std::vector<COutPoint> &vOutpoints,
std::vector<COutput> &vOutputs);
bool isSpent(const COutPoint &outpoint) const;
void listCoins(std::map<QString, std::vector<COutput>> &mapCoins) const;
- bool isLockedCoin(const TxId &txid, uint32_t n) const;
- void lockCoin(COutPoint &output);
- void unlockCoin(COutPoint &output);
- void listLockedCoins(std::vector<COutPoint> &vOutpts);
-
void loadReceiveRequests(std::vector<std::string> &vReceiveRequests);
bool saveReceiveRequest(const std::string &sAddress, const int64_t nId,
const std::string &sRequest);
- bool transactionCanBeAbandoned(const TxId &txid) const;
- bool abandonTransaction(const TxId &txid) const;
-
static bool isWalletEnabled();
- bool hdEnabled() const;
+ interfaces::Node &node() const { return m_node; }
+ interfaces::Wallet &wallet() const { return *m_wallet; }
const CChainParams &getChainParams() const;
QString getWalletName() const;
- static bool isMultiwallet();
+ bool isMultiwallet();
private:
- CWallet *wallet;
+ std::unique_ptr<interfaces::Wallet> m_wallet;
+ std::unique_ptr<interfaces::Handler> m_handler_status_changed;
+ std::unique_ptr<interfaces::Handler> m_handler_address_book_changed;
+ std::unique_ptr<interfaces::Handler> m_handler_transaction_changed;
+ std::unique_ptr<interfaces::Handler> m_handler_show_progress;
+ std::unique_ptr<interfaces::Handler> m_handler_watch_only_changed;
+ interfaces::Node &m_node;
+
+ CWallet *cwallet;
bool fHaveWatchOnly;
bool fForceCheckBalanceChanged;
// Wallet has an options model for wallet-specific options (transaction fee,
// for example)
OptionsModel *optionsModel;
AddressTableModel *addressTableModel;
TransactionTableModel *transactionTableModel;
RecentRequestsTableModel *recentRequestsTableModel;
// Cache some values to be able to detect changes
- Amount cachedBalance;
- Amount cachedUnconfirmedBalance;
- Amount cachedImmatureBalance;
- Amount cachedWatchOnlyBalance;
- Amount cachedWatchUnconfBalance;
- Amount cachedWatchImmatureBalance;
+ interfaces::WalletBalances m_cached_balances;
EncryptionStatus cachedEncryptionStatus;
int cachedNumBlocks;
QTimer *pollTimer;
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
- void checkBalanceChanged();
+ void checkBalanceChanged(const interfaces::WalletBalances &new_balances);
Q_SIGNALS:
// Signal that balance in wallet changed
void balanceChanged(const Amount balance, const Amount unconfirmedBalance,
const Amount immatureBalance,
const Amount watchOnlyBalance,
const Amount watchUnconfBalance,
const Amount watchImmatureBalance);
// Encryption status of wallet changed
void encryptionStatusChanged();
// Signal emitted when wallet needs to be unlocked
// It is valid behaviour for listeners to keep the wallet locked after this
// signal; this means that the unlocking failed or was cancelled.
void requireUnlock();
// Fired when a message should be reported to the user
void message(const QString &title, const QString &message,
unsigned int style);
// Coins sent: from wallet, to recipient, in (serialized) transaction:
void coinsSent(CWallet *wallet, SendCoinsRecipient recipient,
QByteArray transaction);
// Show progress dialog e.g. for rescan
void showProgress(const QString &title, int nProgress);
// Watch-only address added
void notifyWatchonlyChanged(bool fHaveWatchonly);
public Q_SLOTS:
/** Wallet status might have changed. */
void updateStatus();
/** New transaction, or transaction changed status. */
void updateTransaction();
/** New, updated or removed address book entry. */
void updateAddressBook(const QString &address, const QString &label,
bool isMine, const QString &purpose, int status);
/** Watch-only added. */
void updateWatchOnlyFlag(bool fHaveWatchonly);
/**
* Current, immature or unconfirmed balance might have changed - emit
* 'balanceChanged' if so.
*/
void pollBalanceChanged();
};
#endif // BITCOIN_QT_WALLETMODEL_H
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index a83073206f..460933c43f 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -1,82 +1,74 @@
// Copyright (c) 2011-2016 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 "walletmodeltransaction.h"
+#include <qt/walletmodeltransaction.h>
-#include "policy/policy.h"
-#include "wallet/wallet.h"
+#include <interfaces/node.h>
+#include <policy/policy.h>
WalletModelTransaction::WalletModelTransaction(
const QList<SendCoinsRecipient> &_recipients)
- : recipients(_recipients), walletTransaction(0), fee() {}
+ : recipients(_recipients), fee() {}
QList<SendCoinsRecipient> WalletModelTransaction::getRecipients() const {
return recipients;
}
-CTransactionRef &WalletModelTransaction::getTransaction() {
- return walletTransaction;
+std::unique_ptr<interfaces::PendingWalletTx> &WalletModelTransaction::getWtx() {
+ return wtx;
}
unsigned int WalletModelTransaction::getTransactionSize() {
- return !walletTransaction ? 0
- : CTransaction(*walletTransaction).GetTotalSize();
+ return wtx ? wtx->get().GetTotalSize() : 0;
}
Amount WalletModelTransaction::getTransactionFee() const {
return fee;
}
void WalletModelTransaction::setTransactionFee(const Amount newFee) {
fee = newFee;
}
void WalletModelTransaction::reassignAmounts(int nChangePosRet) {
+ const CTransaction *walletTransaction = &wtx->get();
int i = 0;
for (SendCoinsRecipient &rcp : recipients) {
if (rcp.paymentRequest.IsInitialized()) {
Amount subtotal = Amount::zero();
const payments::PaymentDetails &details =
rcp.paymentRequest.getDetails();
for (int j = 0; j < details.outputs_size(); j++) {
const payments::Output &out = details.outputs(j);
if (out.amount() <= 0) {
continue;
}
if (i == nChangePosRet) {
i++;
}
subtotal += walletTransaction->vout[i].nValue;
i++;
}
rcp.amount = subtotal;
} else {
// normal recipient (no payment request)
if (i == nChangePosRet) {
i++;
}
rcp.amount = walletTransaction->vout[i].nValue;
i++;
}
}
}
Amount WalletModelTransaction::getTotalTransactionAmount() const {
Amount totalTransactionAmount = Amount::zero();
for (const SendCoinsRecipient &rcp : recipients) {
totalTransactionAmount += rcp.amount;
}
return totalTransactionAmount;
}
-
-void WalletModelTransaction::newPossibleKeyChange(CWallet *wallet) {
- keyChange.reset(new CReserveKey(wallet));
-}
-
-CReserveKey *WalletModelTransaction::getPossibleKeyChange() {
- return keyChange.get();
-}
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index c519dd8934..a0b2f812ae 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -1,47 +1,44 @@
// 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.
#ifndef BITCOIN_QT_WALLETMODELTRANSACTION_H
#define BITCOIN_QT_WALLETMODELTRANSACTION_H
-#include "walletmodel.h"
+#include <qt/walletmodel.h>
#include <QObject>
class SendCoinsRecipient;
-class CReserveKey;
-class CWallet;
-class CWalletTx;
+namespace interfaces {
+class Node;
+class PendingWalletTx;
+}
/** Data model for a walletmodel transaction. */
class WalletModelTransaction {
public:
explicit WalletModelTransaction(
const QList<SendCoinsRecipient> &recipients);
QList<SendCoinsRecipient> getRecipients() const;
- CTransactionRef &getTransaction();
+ std::unique_ptr<interfaces::PendingWalletTx> &getWtx();
unsigned int getTransactionSize();
void setTransactionFee(const Amount newFee);
Amount getTransactionFee() const;
Amount getTotalTransactionAmount() const;
- void newPossibleKeyChange(CWallet *wallet);
- CReserveKey *getPossibleKeyChange();
-
// needed for the subtract-fee-from-amount feature
void reassignAmounts(int nChangePosRet);
private:
QList<SendCoinsRecipient> recipients;
- CTransactionRef walletTransaction;
- std::unique_ptr<CReserveKey> keyChange;
+ std::unique_ptr<interfaces::PendingWalletTx> wtx;
Amount fee;
};
#endif // BITCOIN_QT_WALLETMODELTRANSACTION_H
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index a80cb1b430..d9bc934d4b 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -1,370 +1,370 @@
// Copyright (c) 2011-2016 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 "walletview.h"
#include "addressbookpage.h"
#include "askpassphrasedialog.h"
#include "bitcoingui.h"
#include "clientmodel.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "overviewpage.h"
#include "platformstyle.h"
#include "receivecoinsdialog.h"
#include "sendcoinsdialog.h"
#include "signverifymessagedialog.h"
#include "transactiontablemodel.h"
#include "transactionview.h"
#include "walletmodel.h"
#include "interfaces/node.h"
#include "ui_interface.h"
#include <QAction>
#include <QActionGroup>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QProgressDialog>
#include <QPushButton>
#include <QVBoxLayout>
WalletView::WalletView(const PlatformStyle *_platformStyle, const Config *cfg,
QWidget *parent)
: QStackedWidget(parent), clientModel(0), walletModel(0),
platformStyle(_platformStyle) {
// Create tabs
overviewPage = new OverviewPage(platformStyle);
transactionsPage = new QWidget(this);
QVBoxLayout *vbox = new QVBoxLayout();
QHBoxLayout *hbox_buttons = new QHBoxLayout();
transactionView = new TransactionView(platformStyle, this);
vbox->addWidget(transactionView);
QPushButton *exportButton = new QPushButton(tr("&Export"), this);
exportButton->setToolTip(
tr("Export the data in the current tab to a file"));
if (platformStyle->getImagesOnButtons()) {
exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
}
hbox_buttons->addStretch();
hbox_buttons->addWidget(exportButton);
vbox->addLayout(hbox_buttons);
transactionsPage->setLayout(vbox);
receiveCoinsPage = new ReceiveCoinsDialog(platformStyle, cfg);
sendCoinsPage = new SendCoinsDialog(platformStyle);
usedSendingAddressesPage =
new AddressBookPage(platformStyle, AddressBookPage::ForEditing,
AddressBookPage::SendingTab, this);
usedReceivingAddressesPage =
new AddressBookPage(platformStyle, AddressBookPage::ForEditing,
AddressBookPage::ReceivingTab, this);
addWidget(overviewPage);
addWidget(transactionsPage);
addWidget(receiveCoinsPage);
addWidget(sendCoinsPage);
// Clicking on a transaction on the overview pre-selects the transaction on
// the transaction history page
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)),
transactionView, SLOT(focusTransaction(QModelIndex)));
connect(overviewPage, SIGNAL(outOfSyncWarningClicked()), this,
SLOT(requestedSyncWarningInfo()));
// Highlight transaction after send
connect(sendCoinsPage, SIGNAL(coinsSent(uint256)), transactionView,
SLOT(focusTransaction(uint256)));
// Double-clicking on a transaction on the transaction history page shows
// details
connect(transactionView, SIGNAL(doubleClicked(QModelIndex)),
transactionView, SLOT(showDetails()));
// Clicking on "Export" allows to export the transaction list
connect(exportButton, SIGNAL(clicked()), transactionView,
SLOT(exportClicked()));
// Pass through messages from sendCoinsPage
connect(sendCoinsPage, SIGNAL(message(QString, QString, unsigned int)),
this, SIGNAL(message(QString, QString, unsigned int)));
// Pass through messages from transactionView
connect(transactionView, SIGNAL(message(QString, QString, unsigned int)),
this, SIGNAL(message(QString, QString, unsigned int)));
}
WalletView::~WalletView() {}
void WalletView::setBitcoinGUI(BitcoinGUI *gui) {
if (gui) {
// Clicking on a transaction on the overview page simply sends you to
// transaction history page
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), gui,
SLOT(gotoHistoryPage()));
// Navigate to transaction history page after send
connect(sendCoinsPage, SIGNAL(coinsSent(uint256)), gui,
SLOT(gotoHistoryPage()));
// Receive and report messages
connect(this, SIGNAL(message(QString, QString, unsigned int)), gui,
SLOT(message(QString, QString, unsigned int)));
// Pass through encryption status changed signals
connect(this, SIGNAL(encryptionStatusChanged()), gui,
SLOT(updateWalletStatus()));
// Pass through transaction notifications
connect(this,
SIGNAL(incomingTransaction(QString, int, Amount, QString,
QString, QString, QString)),
gui,
SLOT(incomingTransaction(QString, int, Amount, QString, QString,
QString, QString)));
// Connect HD enabled state signal
connect(this, SIGNAL(hdEnabledStatusChanged()), gui,
SLOT(updateWalletStatus()));
}
}
void WalletView::setClientModel(ClientModel *_clientModel) {
this->clientModel = _clientModel;
overviewPage->setClientModel(_clientModel);
sendCoinsPage->setClientModel(_clientModel);
}
void WalletView::setWalletModel(WalletModel *_walletModel) {
this->walletModel = _walletModel;
// Put transaction list in tabs
transactionView->setModel(_walletModel);
overviewPage->setWalletModel(_walletModel);
receiveCoinsPage->setModel(_walletModel);
sendCoinsPage->setModel(_walletModel);
usedReceivingAddressesPage->setModel(
_walletModel ? _walletModel->getAddressTableModel() : nullptr);
usedSendingAddressesPage->setModel(
_walletModel ? _walletModel->getAddressTableModel() : nullptr);
if (_walletModel) {
// Receive and pass through messages from wallet model
connect(_walletModel, SIGNAL(message(QString, QString, unsigned int)),
this, SIGNAL(message(QString, QString, unsigned int)));
// Handle changes in encryption status
connect(_walletModel, SIGNAL(encryptionStatusChanged()), this,
SIGNAL(encryptionStatusChanged()));
updateEncryptionStatus();
// update HD status
Q_EMIT hdEnabledStatusChanged();
// Balloon pop-up for new transaction
connect(_walletModel->getTransactionTableModel(),
SIGNAL(rowsInserted(QModelIndex, int, int)), this,
SLOT(processNewTransaction(QModelIndex, int, int)));
// Ask for passphrase if needed
connect(_walletModel, SIGNAL(requireUnlock()), this,
SLOT(unlockWallet()));
// Show progress dialog
connect(_walletModel, SIGNAL(showProgress(QString, int)), this,
SLOT(showProgress(QString, int)));
}
}
void WalletView::processNewTransaction(const QModelIndex &parent, int start,
int end) {
// Prevent balloon-spam when initial block download is in progress
if (!walletModel || !clientModel ||
clientModel->node().isInitialBlockDownload()) {
return;
}
TransactionTableModel *ttm = walletModel->getTransactionTableModel();
if (!ttm || ttm->processingQueuedTransactions()) {
return;
}
QString date = ttm->index(start, TransactionTableModel::Date, parent)
.data()
.toString();
qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent)
.data(Qt::EditRole)
.toULongLong();
QString type = ttm->index(start, TransactionTableModel::Type, parent)
.data()
.toString();
QModelIndex index = ttm->index(start, 0, parent);
QString address =
ttm->data(index, TransactionTableModel::AddressRole).toString();
QString label =
ttm->data(index, TransactionTableModel::LabelRole).toString();
Q_EMIT incomingTransaction(date,
walletModel->getOptionsModel()->getDisplayUnit(),
int64_t(amount) * SATOSHI, type, address, label,
walletModel->getWalletName());
}
void WalletView::gotoOverviewPage() {
setCurrentWidget(overviewPage);
}
void WalletView::gotoHistoryPage() {
setCurrentWidget(transactionsPage);
}
void WalletView::gotoReceiveCoinsPage() {
setCurrentWidget(receiveCoinsPage);
}
void WalletView::gotoSendCoinsPage(QString addr) {
setCurrentWidget(sendCoinsPage);
if (!addr.isEmpty()) {
sendCoinsPage->setAddress(addr);
}
}
void WalletView::gotoSignMessageTab(QString addr) {
// calls show() in showTab_SM()
SignVerifyMessageDialog *signVerifyMessageDialog =
new SignVerifyMessageDialog(platformStyle, this);
signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
signVerifyMessageDialog->setModel(walletModel);
signVerifyMessageDialog->showTab_SM(true);
if (!addr.isEmpty()) {
signVerifyMessageDialog->setAddress_SM(addr);
}
}
void WalletView::gotoVerifyMessageTab(QString addr) {
// calls show() in showTab_VM()
SignVerifyMessageDialog *signVerifyMessageDialog =
new SignVerifyMessageDialog(platformStyle, this);
signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
signVerifyMessageDialog->setModel(walletModel);
signVerifyMessageDialog->showTab_VM(true);
if (!addr.isEmpty()) {
signVerifyMessageDialog->setAddress_VM(addr);
}
}
bool WalletView::handlePaymentRequest(const SendCoinsRecipient &recipient) {
return sendCoinsPage->handlePaymentRequest(recipient);
}
void WalletView::showOutOfSyncWarning(bool fShow) {
overviewPage->showOutOfSyncWarning(fShow);
}
void WalletView::updateEncryptionStatus() {
Q_EMIT encryptionStatusChanged();
}
void WalletView::encryptWallet(bool status) {
if (!walletModel) {
return;
}
AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt
: AskPassphraseDialog::Decrypt,
this);
dlg.setModel(walletModel);
dlg.exec();
updateEncryptionStatus();
}
void WalletView::backupWallet() {
QString filename =
GUIUtil::getSaveFileName(this, tr("Backup Wallet"), QString(),
tr("Wallet Data (*.dat)"), nullptr);
if (filename.isEmpty()) {
return;
}
- if (!walletModel->backupWallet(filename)) {
+ if (!walletModel->wallet().backupWallet(filename.toLocal8Bit().data())) {
Q_EMIT message(
tr("Backup Failed"),
tr("There was an error trying to save the wallet data to %1.")
.arg(filename),
CClientUIInterface::MSG_ERROR);
} else {
Q_EMIT message(
tr("Backup Successful"),
tr("The wallet data was successfully saved to %1.").arg(filename),
CClientUIInterface::MSG_INFORMATION);
}
}
void WalletView::changePassphrase() {
AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
dlg.setModel(walletModel);
dlg.exec();
}
void WalletView::unlockWallet() {
if (!walletModel) {
return;
}
// Unlock wallet when requested by wallet model
if (walletModel->getEncryptionStatus() == WalletModel::Locked) {
AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
dlg.setModel(walletModel);
dlg.exec();
}
}
void WalletView::usedSendingAddresses() {
if (!walletModel) {
return;
}
usedSendingAddressesPage->show();
usedSendingAddressesPage->raise();
usedSendingAddressesPage->activateWindow();
}
void WalletView::usedReceivingAddresses() {
if (!walletModel) {
return;
}
usedReceivingAddressesPage->show();
usedReceivingAddressesPage->raise();
usedReceivingAddressesPage->activateWindow();
}
void WalletView::showProgress(const QString &title, int nProgress) {
if (nProgress == 0) {
progressDialog = new QProgressDialog(title, "", 0, 100);
progressDialog->setWindowModality(Qt::ApplicationModal);
progressDialog->setMinimumDuration(0);
progressDialog->setCancelButton(0);
progressDialog->setAutoClose(false);
progressDialog->setValue(0);
} else if (nProgress == 100) {
if (progressDialog) {
progressDialog->close();
progressDialog->deleteLater();
}
} else if (progressDialog) {
progressDialog->setValue(nProgress);
}
}
void WalletView::requestedSyncWarningInfo() {
Q_EMIT outOfSyncWarningClicked();
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Mar 2, 09:13 (1 d, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187176
Default Alt Text
(301 KB)

Event Timeline