Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13115008
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
301 KB
Subscribers
None
View Options
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 ¶ms,
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 ¶ms,
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
Details
Attached
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)
Attached To
rSTAGING Bitcoin ABC staging
Event Timeline
Log In to Comment