diff options
45 files changed, 910 insertions, 573 deletions
diff --git a/doc/README.md b/doc/README.md index 344b1be5c4..c91cca5cb4 100644 --- a/doc/README.md +++ b/doc/README.md @@ -5,7 +5,7 @@ Setup --------------------- Bitcoin Core is the original Bitcoin client and it builds the backbone of the network. It downloads and, by default, stores the entire history of Bitcoin transactions, which requires a few hundred gigabytes of disk space. Depending on the speed of your computer and network connection, the synchronization process can take anywhere from a few hours to a day or more. -To download Bitcoin Core, visit [bitcoincore.org](https://bitcoincore.org/en/releases/). +To download Bitcoin Core, visit [bitcoincore.org](https://bitcoincore.org/en/download/). Running --------------------- diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 445849e3d8..f4f84e2a99 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -120,6 +120,7 @@ QT_MOC_CPP = \ qt/moc_bantablemodel.cpp \ qt/moc_bitcoinaddressvalidator.cpp \ qt/moc_bitcoinamountfield.cpp \ + qt/moc_bitcoin.cpp \ qt/moc_bitcoingui.cpp \ qt/moc_bitcoinunits.cpp \ qt/moc_clientmodel.cpp \ @@ -166,7 +167,6 @@ BITCOIN_MM = \ qt/macos_appnap.mm QT_MOC = \ - qt/bitcoin.moc \ qt/bitcoinamountfield.moc \ qt/intro.moc \ qt/overviewpage.moc \ @@ -194,6 +194,7 @@ BITCOIN_QT_H = \ qt/bantablemodel.h \ qt/bitcoinaddressvalidator.h \ qt/bitcoinamountfield.h \ + qt/bitcoin.h \ qt/bitcoingui.h \ qt/bitcoinunits.h \ qt/clientmodel.h \ @@ -302,6 +303,7 @@ RES_ICONS = \ BITCOIN_QT_BASE_CPP = \ qt/bantablemodel.cpp \ + qt/bitcoin.cpp \ qt/bitcoinaddressvalidator.cpp \ qt/bitcoinamountfield.cpp \ qt/bitcoingui.cpp \ @@ -382,6 +384,9 @@ qt_libbitcoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS) qt_libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \ $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) +if TARGET_DARWIN + qt_libbitcoinqt_a_SOURCES += $(BITCOIN_MM) +endif nodist_qt_libbitcoinqt_a_SOURCES = $(QT_MOC_CPP) $(QT_MOC) $(PROTOBUF_CC) \ $(PROTOBUF_H) $(QT_QRC_CPP) $(QT_QRC_LOCALE_CPP) @@ -404,10 +409,7 @@ qt_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDE $(QT_INCLUDES) $(PROTOBUF_CFLAGS) $(QR_CFLAGS) qt_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) -qt_bitcoin_qt_SOURCES = qt/bitcoin.cpp -if TARGET_DARWIN - qt_bitcoin_qt_SOURCES += $(BITCOIN_MM) -endif +qt_bitcoin_qt_SOURCES = qt/main.cpp if TARGET_WINDOWS qt_bitcoin_qt_SOURCES += $(BITCOIN_RC) endif diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index db7873e8b7..61977b50cd 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -6,6 +6,7 @@ bin_PROGRAMS += qt/test/test_bitcoin-qt TESTS += qt/test/test_bitcoin-qt TEST_QT_MOC_CPP = \ + qt/test/moc_apptests.cpp \ qt/test/moc_compattests.cpp \ qt/test/moc_rpcnestedtests.cpp \ qt/test/moc_uritests.cpp @@ -22,6 +23,7 @@ endif # ENABLE_WALLET TEST_QT_H = \ qt/test/addressbooktests.h \ + qt/test/apptests.h \ qt/test/compattests.h \ qt/test/rpcnestedtests.h \ qt/test/uritests.h \ @@ -40,6 +42,7 @@ qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_ $(QT_INCLUDES) $(QT_TEST_INCLUDES) $(PROTOBUF_CFLAGS) qt_test_test_bitcoin_qt_SOURCES = \ + qt/test/apptests.cpp \ qt/test/compattests.cpp \ qt/test/rpcnestedtests.cpp \ qt/test/test_main.cpp \ diff --git a/src/addrman.h b/src/addrman.h index af5a1d3b23..003bd059f8 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -23,33 +23,31 @@ */ class CAddrInfo : public CAddress { - - public: //! last try whatsoever by us (memory only) - int64_t nLastTry; + int64_t nLastTry{0}; //! last counted attempt (memory only) - int64_t nLastCountAttempt; + int64_t nLastCountAttempt{0}; private: //! where knowledge about this address first came from CNetAddr source; //! last successful connection by us - int64_t nLastSuccess; + int64_t nLastSuccess{0}; //! connection attempts since last successful attempt - int nAttempts; + int nAttempts{0}; //! reference count in new sets (memory only) - int nRefCount; + int nRefCount{0}; //! in tried set? (memory only) - bool fInTried; + bool fInTried{false}; //! position in vRandom - int nRandomPos; + int nRandomPos{-1}; friend class CAddrMan; @@ -65,25 +63,12 @@ public: READWRITE(nAttempts); } - void Init() - { - nLastSuccess = 0; - nLastTry = 0; - nLastCountAttempt = 0; - nAttempts = 0; - nRefCount = 0; - fInTried = false; - nRandomPos = -1; - } - CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource) { - Init(); } CAddrInfo() : CAddress(), source() { - Init(); } //! Calculate in which "tried" bucket this entry belongs @@ -106,7 +91,6 @@ public: //! Calculate the relative chance this entry should be given when selecting nodes to connect to double GetChance(int64_t nNow = GetAdjustedTime()) const; - }; /** Stochastic address manager diff --git a/src/httpserver.cpp b/src/httpserver.cpp index cb8578927a..ca60ea43a7 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -124,7 +124,6 @@ public: struct HTTPPathHandler { - HTTPPathHandler() {} HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler): prefix(_prefix), exactMatch(_exactMatch), handler(_handler) { diff --git a/src/index/base.cpp b/src/index/base.cpp index 4d4a7e1502..f6f59572ce 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -60,7 +60,11 @@ bool BaseIndex::Init() } LOCK(cs_main); - m_best_block_index = FindForkInGlobalIndex(chainActive, locator); + if (locator.IsNull()) { + m_best_block_index = nullptr; + } else { + m_best_block_index = FindForkInGlobalIndex(chainActive, locator); + } m_synced = m_best_block_index.load() == chainActive.Tip(); return true; } diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp index ba1c44765f..10bc8419dd 100644 --- a/src/index/txindex.cpp +++ b/src/index/txindex.cpp @@ -245,6 +245,9 @@ bool TxIndex::Init() bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) { + // Exclude genesis block transaction because outputs are not spendable. + if (pindex->nHeight == 0) return true; + CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector<std::pair<uint256, CDiskTxPos>> vPos; vPos.reserve(block.vtx.size()); diff --git a/src/init.cpp b/src/init.cpp index 18c145a023..f4f00ea691 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1631,8 +1631,14 @@ bool AppInitMain(InitInterfaces& interfaces) // ********************************************************* Step 11: import blocks - if (!CheckDiskSpace() && !CheckDiskSpace(0, true)) + if (!CheckDiskSpace(/* additional_bytes */ 0, /* blocks_dir */ false)) { + InitError(strprintf(_("Error: Disk space is low for %s"), GetDataDir())); return false; + } + if (!CheckDiskSpace(/* additional_bytes */ 0, /* blocks_dir */ true)) { + InitError(strprintf(_("Error: Disk space is low for %s"), GetBlocksDir())); + return false; + } // Either install a handler to notify us when genesis activates, or set fHaveGenesis directly. // No locking, as this happens before any background thread is started. diff --git a/src/net.cpp b/src/net.cpp index 902830f79c..86e5225839 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -295,15 +295,13 @@ bool IsLocal(const CService& addr) /** check whether a given network is one we can probably connect to */ bool IsReachable(enum Network net) { - LOCK(cs_mapLocalHost); - return !vfLimited[net]; + return !IsLimited(net); } /** check whether a given address is in a network we can probably connect to */ bool IsReachable(const CNetAddr& addr) { - enum Network net = addr.GetNetwork(); - return IsReachable(net); + return IsReachable(addr.GetNetwork()); } @@ -2316,14 +2314,6 @@ void CConnman::SetNetworkActive(bool active) CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSeed1(nSeed1In) { - fNetworkActive = true; - setBannedIsDirty = false; - fAddressesInitialized = false; - nLastNodeId = 0; - nPrevNodeCount = 0; - nSendBufferMaxSize = 0; - nReceiveFloodSize = 0; - flagInterruptMsgProc = false; SetTryNewOutboundPeer(false); Options connOptions; @@ -404,15 +404,15 @@ private: // whitelisted (as well as those connecting to whitelisted binds). std::vector<CSubNet> vWhitelistedRange; - unsigned int nSendBufferMaxSize; - unsigned int nReceiveFloodSize; + unsigned int nSendBufferMaxSize{0}; + unsigned int nReceiveFloodSize{0}; std::vector<ListenSocket> vhListenSocket; - std::atomic<bool> fNetworkActive; + std::atomic<bool> fNetworkActive{true}; banmap_t setBanned GUARDED_BY(cs_setBanned); CCriticalSection cs_setBanned; - bool setBannedIsDirty GUARDED_BY(cs_setBanned); - bool fAddressesInitialized; + bool setBannedIsDirty GUARDED_BY(cs_setBanned){false}; + bool fAddressesInitialized{false}; CAddrMan addrman; std::deque<std::string> vOneShots GUARDED_BY(cs_vOneShots); CCriticalSection cs_vOneShots; @@ -421,8 +421,8 @@ private: std::vector<CNode*> vNodes; std::list<CNode*> vNodesDisconnected; mutable CCriticalSection cs_vNodes; - std::atomic<NodeId> nLastNodeId; - unsigned int nPrevNodeCount; + std::atomic<NodeId> nLastNodeId{0}; + unsigned int nPrevNodeCount{0}; /** Services this instance offers */ ServiceFlags nLocalServices; @@ -446,7 +446,7 @@ private: std::condition_variable condMsgProc; Mutex mutexMsgProc; - std::atomic<bool> flagInterruptMsgProc; + std::atomic<bool> flagInterruptMsgProc{false}; CThreadInterrupt interruptNet; diff --git a/src/protocol.h b/src/protocol.h index 50d197415b..a790a06906 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -402,7 +402,6 @@ public: std::string GetCommand() const; std::string ToString() const; - // TODO: make private (improves encapsulation) public: int type; uint256 hash; diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index dcfe3dcc57..713db595d5 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -40,8 +40,8 @@ class BanTablePriv public: /** Local cache of peer information */ QList<CCombinedBan> cachedBanlist; - /** Column to sort nodes by */ - int sortColumn; + /** Column to sort nodes by (default to unsorted) */ + int sortColumn{-1}; /** Order (ascending or descending) to sort nodes by */ Qt::SortOrder sortOrder; @@ -87,8 +87,6 @@ BanTableModel::BanTableModel(interfaces::Node& node, ClientModel *parent) : { columns << tr("IP/Netmask") << tr("Banned Until"); priv.reset(new BanTablePriv()); - // default to unsorted - priv->sortColumn = -1; // load initial data refresh(); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 922987b7bc..6e08dae3c4 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -6,6 +6,7 @@ #include <config/bitcoin-config.h> #endif +#include <qt/bitcoin.h> #include <qt/bitcoingui.h> #include <chainparams.h> @@ -71,11 +72,6 @@ Q_DECLARE_METATYPE(bool*) Q_DECLARE_METATYPE(CAmount) Q_DECLARE_METATYPE(uint256) -/** Translate string to current locale using Qt. */ -const std::function<std::string(const char*)> G_TRANSLATION_FUN = [](const char* psz) { - return QCoreApplication::translate("bitcoin-core", psz).toStdString(); -}; - static QString GetLangTerritory() { QSettings settings; @@ -140,101 +136,6 @@ void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons } } -/** Class encapsulating Bitcoin Core startup and shutdown. - * Allows running startup and shutdown in a different thread from the UI thread. - */ -class BitcoinCore: public QObject -{ - Q_OBJECT -public: - explicit BitcoinCore(interfaces::Node& node); - -public Q_SLOTS: - void initialize(); - 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 NetworkStyle *networkStyle); - /// Create splash screen - void createSplashScreen(const NetworkStyle *networkStyle); - - /// Request core initialization - void requestInitialize(); - /// Request core shutdown - void requestShutdown(); - - /// Get process return value - int getReturnValue() const { return returnValue; } - - /// Get window identifier of QMainWindow (BitcoinGUI) - WId getMainWinId() const; - - /// Setup platform style - void setupPlatformStyle(); - -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); - void addWallet(WalletModel* walletModel); - void removeWallet(); - -Q_SIGNALS: - void requestedInitialize(); - void requestedShutdown(); - void stopThread(); - void splashFinished(); - -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; - std::unique_ptr<interfaces::Handler> m_handler_load_wallet; -#endif - int returnValue; - const PlatformStyle *platformStyle; - std::unique_ptr<QWidget> shutdownWindow; - - void startThread(); -}; - -#include <qt/bitcoin.moc> - BitcoinCore::BitcoinCore(interfaces::Node& node) : QObject(), m_node(node) { @@ -358,6 +259,11 @@ void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) connect(this, &BitcoinApplication::requestedShutdown, splash, &QWidget::close); } +bool BitcoinApplication::baseInitialize() +{ + return m_node.baseInitialize(); +} + void BitcoinApplication::startThread() { if(coreThread) @@ -467,7 +373,7 @@ void BitcoinApplication::initializeResult(bool success) #ifdef ENABLE_BIP70 PaymentServer::LoadRootCAs(); #endif - paymentServer->setOptionsModel(optionsModel); + if (paymentServer) paymentServer->setOptionsModel(optionsModel); #endif clientModel = new ClientModel(m_node, optionsModel); @@ -486,26 +392,28 @@ void BitcoinApplication::initializeResult(bool success) } #endif - // If -min option passed, start window minimized. - if(gArgs.GetBoolArg("-min", false)) - { - window->showMinimized(); - } - else - { + // If -min option passed, start window minimized (iconified) or minimized to tray + if (!gArgs.GetBoolArg("-min", false)) { window->show(); + } else if (clientModel->getOptionsModel()->getMinimizeToTray() && window->hasTrayIcon()) { + // do nothing as the window is managed by the tray icon + } else { + window->showMinimized(); } Q_EMIT splashFinished(); + Q_EMIT windowShown(window); #ifdef ENABLE_WALLET // Now that initialization/startup is done, process any command-line // bitcoin: URIs or payment requests: - connect(paymentServer, &PaymentServer::receivedPaymentRequest, window, &BitcoinGUI::handlePaymentRequest); - connect(window, &BitcoinGUI::receivedURI, paymentServer, &PaymentServer::handleURIOrFile); - connect(paymentServer, &PaymentServer::message, [this](const QString& title, const QString& message, unsigned int style) { - window->message(title, message, style); - }); - QTimer::singleShot(100, paymentServer, &PaymentServer::uiReady); + if (paymentServer) { + connect(paymentServer, &PaymentServer::receivedPaymentRequest, window, &BitcoinGUI::handlePaymentRequest); + connect(window, &BitcoinGUI::receivedURI, paymentServer, &PaymentServer::handleURIOrFile); + connect(paymentServer, &PaymentServer::message, [this](const QString& title, const QString& message, unsigned int style) { + window->message(title, message, style); + }); + QTimer::singleShot(100, paymentServer, &PaymentServer::uiReady); + } #endif pollShutdownTimer->start(200); } else { @@ -548,7 +456,7 @@ static void SetupUIArgs() } #ifndef BITCOIN_QT_TEST -int main(int argc, char *argv[]) +int GuiMain(int argc, char* argv[]) { #ifdef WIN32 util::WinCmdLineArgs winArgs; @@ -706,7 +614,7 @@ int main(int argc, char *argv[]) // 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()) { + if (app.baseInitialize()) { app.requestInitialize(); #if defined(Q_OS_WIN) WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId()); diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h new file mode 100644 index 0000000000..48b5907570 --- /dev/null +++ b/src/qt/bitcoin.h @@ -0,0 +1,127 @@ +// 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_BITCOIN_H +#define BITCOIN_QT_BITCOIN_H + +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif + +#include <QApplication> +#include <memory> +#include <vector> + +class BitcoinGUI; +class ClientModel; +class NetworkStyle; +class OptionsModel; +class PaymentServer; +class PlatformStyle; +class WalletModel; + +namespace interfaces { +class Handler; +class Node; +} // namespace interfaces + +/** Class encapsulating Bitcoin Core startup and shutdown. + * Allows running startup and shutdown in a different thread from the UI thread. + */ +class BitcoinCore: public QObject +{ + Q_OBJECT +public: + explicit BitcoinCore(interfaces::Node& node); + +public Q_SLOTS: + void initialize(); + 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 NetworkStyle *networkStyle); + /// Create splash screen + void createSplashScreen(const NetworkStyle *networkStyle); + /// Basic initialization, before starting initialization/shutdown thread. Return true on success. + bool baseInitialize(); + + /// Request core initialization + void requestInitialize(); + /// Request core shutdown + void requestShutdown(); + + /// Get process return value + int getReturnValue() const { return returnValue; } + + /// Get window identifier of QMainWindow (BitcoinGUI) + WId getMainWinId() const; + + /// Setup platform style + void setupPlatformStyle(); + +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); + void addWallet(WalletModel* walletModel); + void removeWallet(); + +Q_SIGNALS: + void requestedInitialize(); + void requestedShutdown(); + void stopThread(); + void splashFinished(); + void windowShown(BitcoinGUI* 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; + std::unique_ptr<interfaces::Handler> m_handler_load_wallet; +#endif + int returnValue; + const PlatformStyle *platformStyle; + std::unique_ptr<QWidget> shutdownWindow; + + void startThread(); +}; + +int GuiMain(int argc, char* argv[]); + +#endif // BITCOIN_QT_BITCOIN_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 0ca5b5fcdc..155f8efe7f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -110,6 +110,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty * the central widget is the rpc console. */ setCentralWidget(rpcConsole); + Q_EMIT consoleShown(rpcConsole); } // Accept D&D of URIs @@ -324,6 +325,7 @@ void BitcoinGUI::createActions() openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); // initially disable the debug window menu item openRPCConsoleAction->setEnabled(false); + openRPCConsoleAction->setObjectName("openRPCConsoleAction"); usedSendingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Sending addresses"), this); usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels")); @@ -640,9 +642,11 @@ void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle) assert(QSystemTrayIcon::isSystemTrayAvailable()); #ifndef Q_OS_MAC - trayIcon = new QSystemTrayIcon(networkStyle->getTrayAndWindowIcon(), this); - QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " + networkStyle->getTitleAddText(); - trayIcon->setToolTip(toolTip); + if (QSystemTrayIcon::isSystemTrayAvailable()) { + trayIcon = new QSystemTrayIcon(networkStyle->getTrayAndWindowIcon(), this); + QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " + networkStyle->getTitleAddText(); + trayIcon->setToolTip(toolTip); + } #endif } @@ -722,6 +726,7 @@ void BitcoinGUI::aboutClicked() void BitcoinGUI::showDebugWindow() { GUIUtil::bringToFront(rpcConsole); + Q_EMIT consoleShown(rpcConsole); } void BitcoinGUI::showDebugWindowActivateConsole() diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 1324dd6625..9ca9e4c926 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -86,6 +86,11 @@ public: #endif // ENABLE_WALLET bool enableWallet = false; + /** Get the tray icon status. + Some systems have not "system tray" or "notification area" available. + */ + bool hasTrayIcon() const { return trayIcon; } + protected: void changeEvent(QEvent *e); void closeEvent(QCloseEvent *event); @@ -187,6 +192,8 @@ private: Q_SIGNALS: /** Signal raised when a URI was entered or dragged to the GUI */ void receivedURI(const QString &uri); + /** Signal raised when RPC console shown */ + void consoleShown(RPCConsole* console); public Q_SLOTS: /** Set number of connections shown in the UI */ diff --git a/src/qt/main.cpp b/src/qt/main.cpp new file mode 100644 index 0000000000..6a3c2249d1 --- /dev/null +++ b/src/qt/main.cpp @@ -0,0 +1,17 @@ +// 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 <qt/bitcoin.h> + +#include <QCoreApplication> + +#include <functional> +#include <string> + +/** Translate string to current locale using Qt. */ +extern const std::function<std::string(const char*)> G_TRANSLATION_FUN = [](const char* psz) { + return QCoreApplication::translate("bitcoin-core", psz).toStdString(); +}; + +int main(int argc, char* argv[]) { return GuiMain(argc, argv); } diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 8cfedca57f..e2e78fe81b 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -49,8 +49,8 @@ class PeerTablePriv public: /** Local cache of peer information */ QList<CNodeCombinedStats> cachedNodeStats; - /** Column to sort nodes by */ - int sortColumn; + /** Column to sort nodes by (default to unsorted) */ + int sortColumn{-1}; /** Order (ascending or descending) to sort nodes by */ Qt::SortOrder sortOrder; /** Index of rows by node ID */ @@ -108,8 +108,6 @@ PeerTableModel::PeerTableModel(interfaces::Node& node, ClientModel *parent) : { columns << tr("NodeId") << tr("Node/Service") << tr("Ping") << tr("Sent") << tr("Received") << tr("User Agent"); priv.reset(new PeerTablePriv()); - // default to unsorted - priv->sortColumn = -1; // set up timer for auto refresh timer = new QTimer(this); diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 82ab48ac20..aa746017f3 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -15,8 +15,6 @@ RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) : QAbstractTableModel(parent), walletModel(parent) { - nReceiveRequestsMaxId = 0; - // Load entries from wallet std::vector<std::string> vReceiveRequests; parent->loadReceiveRequests(vReceiveRequests); diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index ff2c0c8098..8a1140e952 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -94,7 +94,7 @@ private: WalletModel *walletModel; QStringList columns; QList<RecentRequestEntry> list; - int64_t nReceiveRequestsMaxId; + int64_t nReceiveRequestsMaxId{0}; /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ void updateAmountColumnTitle(); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index a6187685a9..d062ea49bd 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -18,6 +18,7 @@ #include <netbase.h> #include <rpc/server.h> #include <rpc/client.h> +#include <util/strencodings.h> #include <util/system.h> #include <openssl/crypto.h> @@ -226,7 +227,7 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes if (lastResult.isArray()) { for(char argch: curarg) - if (!std::isdigit(argch)) + if (!IsDigit(argch)) throw std::runtime_error("Invalid result query"); subelement = lastResult[atoi(curarg.c_str())]; } diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp new file mode 100644 index 0000000000..2c477a2e98 --- /dev/null +++ b/src/qt/test/apptests.cpp @@ -0,0 +1,117 @@ +// 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 <qt/test/apptests.h> + +#include <chainparams.h> +#include <init.h> +#include <qt/bitcoin.h> +#include <qt/bitcoingui.h> +#include <qt/networkstyle.h> +#include <qt/rpcconsole.h> +#include <shutdown.h> +#include <validation.h> + +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif +#ifdef ENABLE_WALLET +#include <wallet/db.h> +#endif + +#include <QAction> +#include <QEventLoop> +#include <QLineEdit> +#include <QScopedPointer> +#include <QTest> +#include <QTextEdit> +#include <QtGlobal> +#if QT_VERSION >= 0x050000 +#include <QtTest/QtTestWidgets> +#endif +#include <QtTest/QtTestGui> +#include <new> +#include <string> +#include <univalue.h> + +namespace { +//! Call getblockchaininfo RPC and check first field of JSON output. +void TestRpcCommand(RPCConsole* console) +{ + QEventLoop loop; + QTextEdit* messagesWidget = console->findChild<QTextEdit*>("messagesWidget"); + QObject::connect(messagesWidget, &QTextEdit::textChanged, &loop, &QEventLoop::quit); + QLineEdit* lineEdit = console->findChild<QLineEdit*>("lineEdit"); + QTest::keyClicks(lineEdit, "getblockchaininfo"); + QTest::keyClick(lineEdit, Qt::Key_Return); + loop.exec(); + QString output = messagesWidget->toPlainText(); + UniValue value; + value.read(output.right(output.size() - output.lastIndexOf(QChar::ObjectReplacementCharacter) - 1).toStdString()); + QCOMPARE(value["chain"].get_str(), std::string("regtest")); +} +} // namespace + +//! Entry point for BitcoinApplication tests. +void AppTests::appTests() +{ +#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 AppTests 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 + + m_app.parameterSetup(); + m_app.createOptionsModel(true /* reset settings */); + QScopedPointer<const NetworkStyle> style( + NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString()))); + m_app.setupPlatformStyle(); + m_app.createWindow(style.data()); + connect(&m_app, &BitcoinApplication::windowShown, this, &AppTests::guiTests); + expectCallback("guiTests"); + m_app.baseInitialize(); + m_app.requestInitialize(); + m_app.exec(); + m_app.requestShutdown(); + m_app.exec(); + + // Reset global state to avoid interfering with later tests. + AbortShutdown(); + UnloadBlockIndex(); +} + +//! Entry point for BitcoinGUI tests. +void AppTests::guiTests(BitcoinGUI* window) +{ + HandleCallback callback{"guiTests", *this}; + connect(window, &BitcoinGUI::consoleShown, this, &AppTests::consoleTests); + expectCallback("consoleTests"); + QAction* action = window->findChild<QAction*>("openRPCConsoleAction"); + action->activate(QAction::Trigger); +} + +//! Entry point for RPCConsole tests. +void AppTests::consoleTests(RPCConsole* console) +{ + HandleCallback callback{"consoleTests", *this}; + TestRpcCommand(console); +} + +//! Destructor to shut down after the last expected callback completes. +AppTests::HandleCallback::~HandleCallback() +{ + auto& callbacks = m_app_tests.m_callbacks; + auto it = callbacks.find(m_callback); + assert(it != callbacks.end()); + callbacks.erase(it); + if (callbacks.empty()) { + m_app_tests.m_app.quit(); + } +} diff --git a/src/qt/test/apptests.h b/src/qt/test/apptests.h new file mode 100644 index 0000000000..83bf56f1e4 --- /dev/null +++ b/src/qt/test/apptests.h @@ -0,0 +1,50 @@ +// 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_QT_TEST_APPTESTS_H +#define BITCOIN_QT_TEST_APPTESTS_H + +#include <QObject> +#include <set> +#include <string> +#include <utility> + +class BitcoinApplication; +class BitcoinGUI; +class RPCConsole; + +class AppTests : public QObject +{ + Q_OBJECT +public: + explicit AppTests(BitcoinApplication& app) : m_app(app) {} + +private Q_SLOTS: + void appTests(); + void guiTests(BitcoinGUI* window); + void consoleTests(RPCConsole* console); + +private: + //! Add expected callback name to list of pending callbacks. + void expectCallback(std::string callback) { m_callbacks.emplace(std::move(callback)); } + + //! RAII helper to remove no-longer-pending callback. + struct HandleCallback + { + std::string m_callback; + AppTests& m_app_tests; + ~HandleCallback(); + }; + + //! Bitcoin application. + BitcoinApplication& m_app; + + //! Set of pending callback names. Used to track expected callbacks and shut + //! down the app after the last callback has been handled and all tests have + //! either run or thrown exceptions. This could be a simple int counter + //! instead of a set of names, but the names might be useful for debugging. + std::multiset<std::string> m_callbacks; +}; + +#endif // BITCOIN_QT_TEST_APPTESTS_H diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index ed453336da..173c814f1e 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -41,7 +41,7 @@ void RPCNestedTests::rpcNestedTests() TestingSetup test; - SetRPCWarmupFinished(); + if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished(); std::string result; std::string result2; diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index b6523604fd..a2bf53973b 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -7,6 +7,9 @@ #endif #include <chainparams.h> +#include <interfaces/node.h> +#include <qt/bitcoin.h> +#include <qt/test/apptests.h> #include <qt/test/rpcnestedtests.h> #include <util/system.h> #include <qt/test/uritests.h> @@ -47,12 +50,13 @@ int main(int argc, char *argv[]) { SetupEnvironment(); SetupNetworking(); - SelectParams(CBaseChainParams::MAIN); + SelectParams(CBaseChainParams::REGTEST); noui_connect(); ClearDatadirCache(); fs::path pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin-qt_%lu_%i", (unsigned long)GetTime(), (int)GetRand(100000)); fs::create_directories(pathTemp); gArgs.ForceSetArg("-datadir", pathTemp.string()); + auto node = interfaces::MakeNode(); bool fInvalid = false; @@ -67,11 +71,15 @@ int main(int argc, char *argv[]) // Don't remove this, it's needed to access // QApplication:: and QCoreApplication:: in the tests - QApplication app(argc, argv); + BitcoinApplication app(*node, argc, argv); app.setApplicationName("Bitcoin-Qt-test"); SSL_library_init(); + AppTests app_tests(app); + if (QTest::qExec(&app_tests) != 0) { + fInvalid = true; + } URITests test1; if (QTest::qExec(&test1) != 0) { fInvalid = true; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index b7fa636494..0a5b21f997 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -40,8 +40,6 @@ WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, interfaces: cachedNumBlocks(0) { fHaveWatchOnly = m_wallet->haveWatchOnly(); - fForceCheckBalanceChanged = false; - addressTableModel = new AddressTableModel(this); transactionTableModel = new TransactionTableModel(platformStyle, this); recentRequestsTableModel = new RecentRequestsTableModel(this); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 2988682533..6a6c538157 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -235,7 +235,7 @@ private: interfaces::Node& m_node; bool fHaveWatchOnly; - bool fForceCheckBalanceChanged; + bool fForceCheckBalanceChanged{false}; // Wallet has an options model for wallet-specific options // (transaction fee, for example) diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h index 7cd0df135d..57f5b1f733 100644 --- a/src/support/allocators/secure.h +++ b/src/support/allocators/secure.h @@ -40,7 +40,11 @@ struct secure_allocator : public std::allocator<T> { T* allocate(std::size_t n, const void* hint = 0) { - return static_cast<T*>(LockedPoolManager::Instance().alloc(sizeof(T) * n)); + T* allocation = static_cast<T*>(LockedPoolManager::Instance().alloc(sizeof(T) * n)); + if (!allocation) { + throw std::bad_alloc(); + } + return allocation; } void deallocate(T* p, std::size_t n) diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index 8d577cf521..627018083e 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -248,6 +248,9 @@ void *PosixLockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess) void *addr; len = align_up(len, page_size); addr = mmap(nullptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (addr == MAP_FAILED) { + return nullptr; + } if (addr) { *lockingSuccess = mlock(addr, len) == 0; } diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index 48ffd7b307..b420c909fc 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -22,7 +22,7 @@ public: virtual ~LockedPageAllocator() {} /** Allocate and lock memory pages. * If len is not a multiple of the system page size, it is rounded up. - * Returns 0 in case of allocation failure. + * Returns nullptr in case of allocation failure. * * If locking the memory pages could not be accomplished it will still * return the memory, however the lockingSuccess flag will be false. diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 234da5ae4d..22347fbc57 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -12,13 +12,9 @@ class CAddrManTest : public CAddrMan { - uint64_t state; - public: explicit CAddrManTest(bool makeDeterministic = true) { - state = 1; - if (makeDeterministic) { // Set addrman addr placement to be deterministic. MakeDeterministic(); diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 14ddf4d10e..8048238028 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <util/strencodings.h> #include <util/system.h> #include <test/test_bitcoin.h> @@ -17,7 +18,7 @@ static void ResetArgs(const std::string& strArg) { std::vector<std::string> vecArg; if (strArg.size()) - boost::split(vecArg, strArg, boost::is_space(), boost::token_compress_on); + boost::split(vecArg, strArg, IsSpace, boost::token_compress_on); // Insert dummy executable name: vecArg.insert(vecArg.begin(), "testbitcoin"); diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index e7a3c96343..4dc394b86d 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -227,4 +227,93 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test) BOOST_CHECK(1); } + +BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network) +{ + SetLimited(NET_IPV4, true); + SetLimited(NET_IPV6, true); + SetLimited(NET_ONION, true); + + BOOST_CHECK_EQUAL(IsLimited(NET_IPV4), true); + BOOST_CHECK_EQUAL(IsLimited(NET_IPV6), true); + BOOST_CHECK_EQUAL(IsLimited(NET_ONION), true); + + BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), false); + BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), false); + BOOST_CHECK_EQUAL(IsReachable(NET_ONION), false); + + + SetLimited(NET_IPV4, false); + SetLimited(NET_IPV6, false); + SetLimited(NET_ONION, false); + + BOOST_CHECK_EQUAL(IsLimited(NET_IPV4), false); + BOOST_CHECK_EQUAL(IsLimited(NET_IPV6), false); + BOOST_CHECK_EQUAL(IsLimited(NET_ONION), false); + + BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true); + BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true); + BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true); +} + +BOOST_AUTO_TEST_CASE(LimitedAndReachable_NetworkCaseUnroutableAndInternal) +{ + BOOST_CHECK_EQUAL(IsLimited(NET_UNROUTABLE), false); + BOOST_CHECK_EQUAL(IsLimited(NET_INTERNAL), false); + + BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true); + BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true); + + SetLimited(NET_UNROUTABLE, true); + SetLimited(NET_INTERNAL, true); + + BOOST_CHECK_EQUAL(IsLimited(NET_UNROUTABLE), false); // Ignored for both networks + BOOST_CHECK_EQUAL(IsLimited(NET_INTERNAL), false); + + BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true); + BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true); +} + +CNetAddr UtilBuildAddress(unsigned char p1, unsigned char p2, unsigned char p3, unsigned char p4) +{ + unsigned char ip[] = {p1, p2, p3, p4}; + + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sockaddr_in)); // initialize the memory block + memcpy(&(sa.sin_addr), &ip, sizeof(ip)); + return CNetAddr(sa.sin_addr); +} + + +BOOST_AUTO_TEST_CASE(LimitedAndReachable_CNetAddr) +{ + CNetAddr addr = UtilBuildAddress(0x001, 0x001, 0x001, 0x001); // 1.1.1.1 + + SetLimited(NET_IPV4, false); + BOOST_CHECK_EQUAL(IsLimited(addr), false); + BOOST_CHECK_EQUAL(IsReachable(addr), true); + + SetLimited(NET_IPV4, true); + BOOST_CHECK_EQUAL(IsLimited(addr), true); + BOOST_CHECK_EQUAL(IsReachable(addr), false); + + SetLimited(NET_IPV4, false); // have to reset this, because this is stateful. +} + + +BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle) +{ + CService addr = CService(UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000); // 2.1.1.1:1000 + + SetLimited(NET_IPV4, false); + + BOOST_CHECK_EQUAL(IsLocal(addr), false); + BOOST_CHECK_EQUAL(AddLocal(addr, 1000), true); + BOOST_CHECK_EQUAL(IsLocal(addr), true); + + RemoveLocal(addr); + BOOST_CHECK_EQUAL(IsLocal(addr), false); +} + + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp index 43e025c58f..0301901bf0 100644 --- a/src/test/txindex_tests.cpp +++ b/src/test/txindex_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <chainparams.h> #include <index/txindex.h> #include <script/standard.h> #include <test/test_bitcoin.h> @@ -38,6 +39,12 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup) MilliSleep(100); } + // Check that txindex excludes genesis block transactions. + const CBlock& genesis_block = Params().GenesisBlock(); + for (const auto& txn : genesis_block.vtx) { + BOOST_CHECK(!txindex.FindTx(txn->GetHash(), block_hash, tx_disk)); + } + // Check that txindex has all txs that were in the chain before it started. for (const auto& txn : m_coinbase_txns) { if (!txindex.FindTx(txn->GetHash(), block_hash, tx_disk)) { diff --git a/src/uint256.cpp b/src/uint256.cpp index d9da668036..b723f9ee54 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -33,7 +33,7 @@ void base_blob<BITS>::SetHex(const char* psz) psz++; // skip 0x - if (psz[0] == '0' && tolower(psz[1]) == 'x') + if (psz[0] == '0' && ToLower((unsigned char)psz[1]) == 'x') psz += 2; // hex string to uint diff --git a/src/util/moneystr.cpp b/src/util/moneystr.cpp index 4c4de7b729..f4e41eea4f 100644 --- a/src/util/moneystr.cpp +++ b/src/util/moneystr.cpp @@ -20,7 +20,7 @@ std::string FormatMoney(const CAmount& n) // Right-trim excess zeros before the decimal point: int nTrim = 0; - for (int i = str.size()-1; (str[i] == '0' && isdigit(str[i-2])); --i) + for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i) ++nTrim; if (nTrim) str.erase(str.size()-nTrim, nTrim); @@ -49,7 +49,7 @@ bool ParseMoney(const char* pszIn, CAmount& nRet) { p++; int64_t nMult = COIN / 10; - while (isdigit(*p) && (nMult > 0)) + while (IsDigit(*p) && (nMult > 0)) { nUnits += nMult * (*p++ - '0'); nMult /= 10; @@ -58,7 +58,7 @@ bool ParseMoney(const char* pszIn, CAmount& nRet) } if (IsSpace(*p)) break; - if (!isdigit(*p)) + if (!IsDigit(*p)) return false; strWhole.insert(strWhole.end(), *p); } diff --git a/src/util/system.cpp b/src/util/system.cpp index 8e201ec590..a4bf126900 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -443,7 +443,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin key.erase(is_index); } #ifdef WIN32 - std::transform(key.begin(), key.end(), key.begin(), ::tolower); + std::transform(key.begin(), key.end(), key.begin(), ToLower); if (key[0] == '/') key[0] = '-'; #endif @@ -865,7 +865,7 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect } else if ((pos = str.find('=')) != std::string::npos) { std::string name = prefix + TrimString(str.substr(0, pos), pattern); std::string value = TrimString(str.substr(pos + 1), pattern); - if (used_hash && name == "rpcpassword") { + if (used_hash && name.find("rpcpassword") != std::string::npos) { error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr); return false; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c1cdd0b2ee..38397a3940 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1020,15 +1020,12 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) struct tallyitem { - CAmount nAmount; - int nConf; + CAmount nAmount{0}; + int nConf{std::numeric_limits<int>::max()}; std::vector<uint256> txids; - bool fIsWatchonly; + bool fIsWatchonly{false}; tallyitem() { - nAmount = 0; - nConf = std::numeric_limits<int>::max(); - fIsWatchonly = false; } }; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 95a2c833f8..3a3ec43c9b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1178,18 +1178,16 @@ class CReserveKey final : public CReserveScript { protected: CWallet* pwallet; - int64_t nIndex; + int64_t nIndex{-1}; CPubKey vchPubKey; - bool fInternal; + bool fInternal{false}; + public: explicit CReserveKey(CWallet* pwalletIn) { - nIndex = -1; pwallet = pwalletIn; - fInternal = false; } - CReserveKey() = default; CReserveKey(const CReserveKey&) = delete; CReserveKey& operator=(const CReserveKey&) = delete; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 09a33f252c..6e037808e3 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -153,21 +153,17 @@ bool WalletBatch::WriteMinVersion(int nVersion) class CWalletScanState { public: - unsigned int nKeys; - unsigned int nCKeys; - unsigned int nWatchKeys; - unsigned int nKeyMeta; - unsigned int m_unknown_records; - bool fIsEncrypted; - bool fAnyUnordered; - int nFileVersion; + unsigned int nKeys{0}; + unsigned int nCKeys{0}; + unsigned int nWatchKeys{0}; + unsigned int nKeyMeta{0}; + unsigned int m_unknown_records{0}; + bool fIsEncrypted{false}; + bool fAnyUnordered{false}; + int nFileVersion{0}; std::vector<uint256> vWalletUpgrade; CWalletScanState() { - nKeys = nCKeys = nWatchKeys = nKeyMeta = m_unknown_records = 0; - fIsEncrypted = false; - fAnyUnordered = false; - nFileVersion = 0; } }; diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index d87eabaa6d..4b3f6603a2 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -34,6 +34,14 @@ class ConfArgsTest(BitcoinTestFramework): self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided') with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: + conf.write('server=1\nrpcuser=someuser\nmain.rpcpassword=some#pass') + self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided') + + with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: + conf.write('server=1\nrpcuser=someuser\n[main]\nrpcpassword=some#pass') + self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 4, using # in rpcpassword can be ambiguous and should be avoided') + + with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: conf.write('testnot.datadir=1\n[testnet]\n') self.restart_node(0) self.nodes[0].stop_node(expected_stderr='Warning: Section [testnet] is not recognized.' + os.linesep + 'Warning: Section [testnot] is not recognized.') diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py new file mode 100755 index 0000000000..c0dfa4c3f0 --- /dev/null +++ b/test/functional/test_framework/wallet_util.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# 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. +"""Useful util functions for testing the wallet""" +from collections import namedtuple + +from test_framework.address import ( + key_to_p2pkh, + key_to_p2sh_p2wpkh, + key_to_p2wpkh, + script_to_p2sh, + script_to_p2sh_p2wsh, + script_to_p2wsh, +) +from test_framework.script import ( + CScript, + OP_0, + OP_2, + OP_3, + OP_CHECKMULTISIG, + OP_CHECKSIG, + OP_DUP, + OP_EQUAL, + OP_EQUALVERIFY, + OP_HASH160, + hash160, + sha256, +) +from test_framework.util import hex_str_to_bytes + +Key = namedtuple('Key', ['privkey', + 'pubkey', + 'p2pkh_script', + 'p2pkh_addr', + 'p2wpkh_script', + 'p2wpkh_addr', + 'p2sh_p2wpkh_script', + 'p2sh_p2wpkh_redeem_script', + 'p2sh_p2wpkh_addr']) + +Multisig = namedtuple('Multisig', ['privkeys', + 'pubkeys', + 'p2sh_script', + 'p2sh_addr', + 'redeem_script', + 'p2wsh_script', + 'p2wsh_addr', + 'p2sh_p2wsh_script', + 'p2sh_p2wsh_addr']) + +def get_key(node): + """Generate a fresh key on node + + Returns a named tuple of privkey, pubkey and all address and scripts.""" + addr = node.getnewaddress() + pubkey = node.getaddressinfo(addr)['pubkey'] + pkh = hash160(hex_str_to_bytes(pubkey)) + return Key(privkey=node.dumpprivkey(addr), + pubkey=pubkey, + p2pkh_script=CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG]).hex(), + p2pkh_addr=key_to_p2pkh(pubkey), + p2wpkh_script=CScript([OP_0, pkh]).hex(), + p2wpkh_addr=key_to_p2wpkh(pubkey), + p2sh_p2wpkh_script=CScript([OP_HASH160, hash160(CScript([OP_0, pkh])), OP_EQUAL]).hex(), + p2sh_p2wpkh_redeem_script=CScript([OP_0, pkh]).hex(), + p2sh_p2wpkh_addr=key_to_p2sh_p2wpkh(pubkey)) + +def get_multisig(node): + """Generate a fresh 2-of-3 multisig on node + + Returns a named tuple of privkeys, pubkeys and all address and scripts.""" + addrs = [] + pubkeys = [] + for _ in range(3): + addr = node.getaddressinfo(node.getnewaddress()) + addrs.append(addr['address']) + pubkeys.append(addr['pubkey']) + script_code = CScript([OP_2] + [hex_str_to_bytes(pubkey) for pubkey in pubkeys] + [OP_3, OP_CHECKMULTISIG]) + witness_script = CScript([OP_0, sha256(script_code)]) + return Multisig(privkeys=[node.dumpprivkey(addr) for addr in addrs], + pubkeys=pubkeys, + p2sh_script=CScript([OP_HASH160, hash160(script_code), OP_EQUAL]).hex(), + p2sh_addr=script_to_p2sh(script_code), + redeem_script=script_code.hex(), + p2wsh_script=witness_script.hex(), + p2wsh_addr=script_to_p2wsh(script_code), + p2sh_p2wsh_script=CScript([OP_HASH160, witness_script, OP_EQUAL]).hex(), + p2sh_p2wsh_addr=script_to_p2sh_p2wsh(script_code)) + +def test_address(node, address, **kwargs): + """Get address info for `address` and test whether the returned values are as expected.""" + addr_info = node.getaddressinfo(address) + for key, value in kwargs.items(): + if value is None: + if key in addr_info.keys(): + raise AssertionError("key {} unexpectedly returned in getaddressinfo.".format(key)) + elif addr_info[key] != value: + raise AssertionError("key {} value {} did not match expected value {}".format(key, addr_info[key], value)) diff --git a/test/functional/wallet_import_with_label.py b/test/functional/wallet_import_with_label.py index 95acaa752e..a623b75606 100755 --- a/test/functional/wallet_import_with_label.py +++ b/test/functional/wallet_import_with_label.py @@ -11,7 +11,7 @@ with and without a label. """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal +from test_framework.wallet_util import test_address class ImportWithLabel(BitcoinTestFramework): @@ -32,11 +32,11 @@ class ImportWithLabel(BitcoinTestFramework): address = self.nodes[0].getnewaddress() label = "Test Label" self.nodes[1].importaddress(address, label) - address_assert = self.nodes[1].getaddressinfo(address) - - assert_equal(address_assert["iswatchonly"], True) - assert_equal(address_assert["ismine"], False) - assert_equal(address_assert["label"], label) + test_address(self.nodes[1], + address, + iswatchonly=True, + ismine=False, + label=label) self.log.info( "Import the watch-only address's private key without a " @@ -45,7 +45,9 @@ class ImportWithLabel(BitcoinTestFramework): priv_key = self.nodes[0].dumpprivkey(address) self.nodes[1].importprivkey(priv_key) - assert_equal(label, self.nodes[1].getaddressinfo(address)["label"]) + test_address(self.nodes[1], + address, + label=label) self.log.info( "Test importaddress without label and importprivkey with label." @@ -53,11 +55,11 @@ class ImportWithLabel(BitcoinTestFramework): self.log.info("Import a watch-only address without a label.") address2 = self.nodes[0].getnewaddress() self.nodes[1].importaddress(address2) - address_assert2 = self.nodes[1].getaddressinfo(address2) - - assert_equal(address_assert2["iswatchonly"], True) - assert_equal(address_assert2["ismine"], False) - assert_equal(address_assert2["label"], "") + test_address(self.nodes[1], + address2, + iswatchonly=True, + ismine=False, + label="") self.log.info( "Import the watch-only address's private key with a " @@ -67,18 +69,20 @@ class ImportWithLabel(BitcoinTestFramework): label2 = "Test Label 2" self.nodes[1].importprivkey(priv_key2, label2) - assert_equal(label2, self.nodes[1].getaddressinfo(address2)["label"]) + test_address(self.nodes[1], + address2, + label=label2) self.log.info("Test importaddress with label and importprivkey with label.") self.log.info("Import a watch-only address with a label.") address3 = self.nodes[0].getnewaddress() label3_addr = "Test Label 3 for importaddress" self.nodes[1].importaddress(address3, label3_addr) - address_assert3 = self.nodes[1].getaddressinfo(address3) - - assert_equal(address_assert3["iswatchonly"], True) - assert_equal(address_assert3["ismine"], False) - assert_equal(address_assert3["label"], label3_addr) + test_address(self.nodes[1], + address3, + iswatchonly=True, + ismine=False, + label=label3_addr) self.log.info( "Import the watch-only address's private key with a " @@ -88,7 +92,9 @@ class ImportWithLabel(BitcoinTestFramework): label3_priv = "Test Label 3 for importprivkey" self.nodes[1].importprivkey(priv_key3, label3_priv) - assert_equal(label3_priv, self.nodes[1].getaddressinfo(address3)["label"]) + test_address(self.nodes[1], + address3, + label=label3_priv) self.log.info( "Test importprivkey won't label new dests with the same " @@ -98,15 +104,12 @@ class ImportWithLabel(BitcoinTestFramework): address4 = self.nodes[0].getnewaddress() label4_addr = "Test Label 4 for importaddress" self.nodes[1].importaddress(address4, label4_addr) - address_assert4 = self.nodes[1].getaddressinfo(address4) - - assert_equal(address_assert4["iswatchonly"], True) - assert_equal(address_assert4["ismine"], False) - assert_equal(address_assert4["label"], label4_addr) - - self.log.info("Asserts address has no embedded field with dests.") - - assert_equal(address_assert4.get("embedded"), None) + test_address(self.nodes[1], + address4, + iswatchonly=True, + ismine=False, + label=label4_addr, + embedded=None) self.log.info( "Import the watch-only address's private key without a " @@ -116,16 +119,14 @@ class ImportWithLabel(BitcoinTestFramework): ) priv_key4 = self.nodes[0].dumpprivkey(address4) self.nodes[1].importprivkey(priv_key4) - address_assert4 = self.nodes[1].getaddressinfo(address4) - - assert address_assert4.get("embedded") - - bcaddress_assert = self.nodes[1].getaddressinfo( - address_assert4["embedded"]["address"] - ) - - assert_equal(address_assert4["label"], label4_addr) - assert_equal(bcaddress_assert["label"], "") + embedded_addr = self.nodes[1].getaddressinfo(address4)['embedded']['address'] + + test_address(self.nodes[1], + embedded_addr, + label="") + test_address(self.nodes[1], + address4, + label=label4_addr) self.stop_nodes() diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py index 3492075694..f122f19e3a 100755 --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -14,30 +14,10 @@ variants. success, and (if unsuccessful) test the error code and error message returned. - `test_address()` is called to call getaddressinfo for an address on node1 and test the values returned.""" -from collections import namedtuple - -from test_framework.address import ( - key_to_p2pkh, - key_to_p2sh_p2wpkh, - key_to_p2wpkh, - script_to_p2sh, - script_to_p2sh_p2wsh, - script_to_p2wsh, -) + from test_framework.script import ( CScript, - OP_0, - OP_2, - OP_3, - OP_CHECKMULTISIG, - OP_CHECKSIG, - OP_DUP, - OP_EQUAL, - OP_EQUALVERIFY, - OP_HASH160, OP_NOP, - hash160, - sha256, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( @@ -45,28 +25,12 @@ from test_framework.util import ( assert_greater_than, assert_raises_rpc_error, bytes_to_hex_str, - hex_str_to_bytes ) - -Key = namedtuple('Key', ['privkey', - 'pubkey', - 'p2pkh_script', - 'p2pkh_addr', - 'p2wpkh_script', - 'p2wpkh_addr', - 'p2sh_p2wpkh_script', - 'p2sh_p2wpkh_redeem_script', - 'p2sh_p2wpkh_addr']) - -Multisig = namedtuple('Multisig', ['privkeys', - 'pubkeys', - 'p2sh_script', - 'p2sh_addr', - 'redeem_script', - 'p2wsh_script', - 'p2wsh_addr', - 'p2sh_p2wsh_script', - 'p2sh_p2wsh_addr']) +from test_framework.wallet_util import ( + get_key, + get_multisig, + test_address, +) class ImportMultiTest(BitcoinTestFramework): def set_test_params(self): @@ -80,45 +44,6 @@ class ImportMultiTest(BitcoinTestFramework): def setup_network(self): self.setup_nodes() - def get_key(self): - """Generate a fresh key on node0 - - Returns a named tuple of privkey, pubkey and all address and scripts.""" - addr = self.nodes[0].getnewaddress() - pubkey = self.nodes[0].getaddressinfo(addr)['pubkey'] - pkh = hash160(hex_str_to_bytes(pubkey)) - return Key(self.nodes[0].dumpprivkey(addr), - pubkey, - CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG]).hex(), # p2pkh - key_to_p2pkh(pubkey), # p2pkh addr - CScript([OP_0, pkh]).hex(), # p2wpkh - key_to_p2wpkh(pubkey), # p2wpkh addr - CScript([OP_HASH160, hash160(CScript([OP_0, pkh])), OP_EQUAL]).hex(), # p2sh-p2wpkh - CScript([OP_0, pkh]).hex(), # p2sh-p2wpkh redeem script - key_to_p2sh_p2wpkh(pubkey)) # p2sh-p2wpkh addr - - def get_multisig(self): - """Generate a fresh multisig on node0 - - Returns a named tuple of privkeys, pubkeys and all address and scripts.""" - addrs = [] - pubkeys = [] - for _ in range(3): - addr = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) - addrs.append(addr['address']) - pubkeys.append(addr['pubkey']) - script_code = CScript([OP_2] + [hex_str_to_bytes(pubkey) for pubkey in pubkeys] + [OP_3, OP_CHECKMULTISIG]) - witness_script = CScript([OP_0, sha256(script_code)]) - return Multisig([self.nodes[0].dumpprivkey(addr) for addr in addrs], - pubkeys, - CScript([OP_HASH160, hash160(script_code), OP_EQUAL]).hex(), # p2sh - script_to_p2sh(script_code), # p2sh addr - script_code.hex(), # redeem script - witness_script.hex(), # p2wsh - script_to_p2wsh(script_code), # p2wsh addr - CScript([OP_HASH160, witness_script, OP_EQUAL]).hex(), # p2sh-p2wsh - script_to_p2sh_p2wsh(script_code)) # p2sh-p2wsh addr - def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=[]): """Run importmulti and assert success""" result = self.nodes[1].importmulti([req]) @@ -131,16 +56,6 @@ class ImportMultiTest(BitcoinTestFramework): assert_equal(result[0]['error']['code'], error_code) assert_equal(result[0]['error']['message'], error_message) - def test_address(self, address, **kwargs): - """Get address info for `address` and test whether the returned values are as expected.""" - addr_info = self.nodes[1].getaddressinfo(address) - for key, value in kwargs.items(): - if value is None: - if key in addr_info.keys(): - raise AssertionError("key {} unexpectedly returned in getaddressinfo.".format(key)) - elif addr_info[key] != value: - raise AssertionError("key {} value {} did not match expected value {}".format(key, addr_info[key], value)) - def run_test(self): self.log.info("Mining blocks...") self.nodes[0].generate(1) @@ -164,177 +79,178 @@ class ImportMultiTest(BitcoinTestFramework): # Bitcoin Address (implicit non-internal) self.log.info("Should import an address") - key = self.get_key() - address = key.p2pkh_addr - self.test_importmulti({"scriptPubKey": {"address": address}, + key = get_key(self.nodes[0]) + self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr}, "timestamp": "now"}, - True) - self.test_address(address, - iswatchonly=True, - ismine=False, - timestamp=timestamp, - ischange=False) - watchonly_address = address + success=True) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=True, + ismine=False, + timestamp=timestamp, + ischange=False) + watchonly_address = key.p2pkh_addr watchonly_timestamp = timestamp self.log.info("Should not import an invalid address") self.test_importmulti({"scriptPubKey": {"address": "not valid address"}, "timestamp": "now"}, - False, + success=False, error_code=-5, error_message='Invalid address \"not valid address\"') # ScriptPubKey + internal self.log.info("Should import a scriptPubKey with internal flag") - key = self.get_key() + key = get_key(self.nodes[0]) self.test_importmulti({"scriptPubKey": key.p2pkh_script, "timestamp": "now", "internal": True}, - True) - self.test_address(key.p2pkh_addr, - iswatchonly=True, - ismine=False, - timestamp=timestamp, - ischange=True) + success=True) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=True, + ismine=False, + timestamp=timestamp, + ischange=True) # ScriptPubKey + internal + label self.log.info("Should not allow a label to be specified when internal is true") - key = self.get_key() + key = get_key(self.nodes[0]) self.test_importmulti({"scriptPubKey": key.p2pkh_script, "timestamp": "now", "internal": True, "label": "Example label"}, - False, + success=False, error_code=-8, error_message='Internal addresses should not have a label') # Nonstandard scriptPubKey + !internal self.log.info("Should not import a nonstandard scriptPubKey without internal flag") nonstandardScriptPubKey = key.p2pkh_script + bytes_to_hex_str(CScript([OP_NOP])) - key = self.get_key() - address = key.p2pkh_addr + key = get_key(self.nodes[0]) self.test_importmulti({"scriptPubKey": nonstandardScriptPubKey, "timestamp": "now"}, - False, + success=False, error_code=-8, error_message='Internal must be set to true for nonstandard scriptPubKey imports.') - self.test_address(address, - iswatchonly=False, - ismine=False, - timestamp=None) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=False, + ismine=False, + timestamp=None) # Address + Public key + !Internal(explicit) self.log.info("Should import an address with public key") - key = self.get_key() - address = key.p2pkh_addr - self.test_importmulti({"scriptPubKey": {"address": address}, + key = get_key(self.nodes[0]) + self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr}, "timestamp": "now", "pubkeys": [key.pubkey], "internal": False}, - True, + success=True, warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(address, - iswatchonly=True, - ismine=False, - timestamp=timestamp) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=True, + ismine=False, + timestamp=timestamp) # ScriptPubKey + Public key + internal self.log.info("Should import a scriptPubKey with internal and with public key") - key = self.get_key() - address = key.p2pkh_addr + key = get_key(self.nodes[0]) self.test_importmulti({"scriptPubKey": key.p2pkh_script, "timestamp": "now", "pubkeys": [key.pubkey], "internal": True}, - True, + success=True, warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(address, - iswatchonly=True, - ismine=False, - timestamp=timestamp) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=True, + ismine=False, + timestamp=timestamp) # Nonstandard scriptPubKey + Public key + !internal self.log.info("Should not import a nonstandard scriptPubKey without internal and with public key") - key = self.get_key() - address = key.p2pkh_addr + key = get_key(self.nodes[0]) self.test_importmulti({"scriptPubKey": nonstandardScriptPubKey, "timestamp": "now", "pubkeys": [key.pubkey]}, - False, + success=False, error_code=-8, error_message='Internal must be set to true for nonstandard scriptPubKey imports.') - self.test_address(address, - iswatchonly=False, - ismine=False, - timestamp=None) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=False, + ismine=False, + timestamp=None) # Address + Private key + !watchonly self.log.info("Should import an address with private key") - key = self.get_key() - address = key.p2pkh_addr - self.test_importmulti({"scriptPubKey": {"address": address}, + key = get_key(self.nodes[0]) + self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr}, "timestamp": "now", "keys": [key.privkey]}, - True) - self.test_address(address, - iswatchonly=False, - ismine=True, - timestamp=timestamp) + success=True) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=False, + ismine=True, + timestamp=timestamp) self.log.info("Should not import an address with private key if is already imported") - self.test_importmulti({"scriptPubKey": {"address": address}, + self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr}, "timestamp": "now", "keys": [key.privkey]}, - False, + success=False, error_code=-4, error_message='The wallet already contains the private key for this address or script') # Address + Private key + watchonly self.log.info("Should import an address with private key and with watchonly") - key = self.get_key() - address = key.p2pkh_addr - self.test_importmulti({"scriptPubKey": {"address": address}, + key = get_key(self.nodes[0]) + self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr}, "timestamp": "now", "keys": [key.privkey], "watchonly": True}, - True, + success=True, warnings=["All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag."]) - self.test_address(address, - iswatchonly=False, - ismine=True, - timestamp=timestamp) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=False, + ismine=True, + timestamp=timestamp) # ScriptPubKey + Private key + internal self.log.info("Should import a scriptPubKey with internal and with private key") - key = self.get_key() - address = key.p2pkh_addr + key = get_key(self.nodes[0]) self.test_importmulti({"scriptPubKey": key.p2pkh_script, "timestamp": "now", "keys": [key.privkey], "internal": True}, - True) - self.test_address(address, - iswatchonly=False, - ismine=True, - timestamp=timestamp) + success=True) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=False, + ismine=True, + timestamp=timestamp) # Nonstandard scriptPubKey + Private key + !internal self.log.info("Should not import a nonstandard scriptPubKey without internal and with private key") - key = self.get_key() - address = key.p2pkh_addr + key = get_key(self.nodes[0]) self.test_importmulti({"scriptPubKey": nonstandardScriptPubKey, "timestamp": "now", "keys": [key.privkey]}, - False, + success=False, error_code=-8, error_message='Internal must be set to true for nonstandard scriptPubKey imports.') - self.test_address(address, - iswatchonly=False, - ismine=False, - timestamp=None) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=False, + ismine=False, + timestamp=None) # P2SH address - multisig = self.get_multisig() + multisig = get_multisig(self.nodes[0]) self.nodes[1].generate(100) self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00) self.nodes[1].generate(1) @@ -343,17 +259,18 @@ class ImportMultiTest(BitcoinTestFramework): self.log.info("Should import a p2sh") self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr}, "timestamp": "now"}, - True) - self.test_address(multisig.p2sh_addr, - isscript=True, - iswatchonly=True, - timestamp=timestamp) + success=True) + test_address(self.nodes[1], + multisig.p2sh_addr, + isscript=True, + iswatchonly=True, + timestamp=timestamp) p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0] assert_equal(p2shunspent['spendable'], False) assert_equal(p2shunspent['solvable'], False) # P2SH + Redeem script - multisig = self.get_multisig() + multisig = get_multisig(self.nodes[0]) self.nodes[1].generate(100) self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00) self.nodes[1].generate(1) @@ -363,16 +280,17 @@ class ImportMultiTest(BitcoinTestFramework): self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr}, "timestamp": "now", "redeemscript": multisig.redeem_script}, - True, + success=True, warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(multisig.p2sh_addr, timestamp=timestamp, iswatchonly=True, ismine=False, solvable=True) + test_address(self.nodes[1], + multisig.p2sh_addr, timestamp=timestamp, iswatchonly=True, ismine=False, solvable=True) p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0] assert_equal(p2shunspent['spendable'], False) assert_equal(p2shunspent['solvable'], True) # P2SH + Redeem script + Private Keys + !Watchonly - multisig = self.get_multisig() + multisig = get_multisig(self.nodes[0]) self.nodes[1].generate(100) self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00) self.nodes[1].generate(1) @@ -383,20 +301,21 @@ class ImportMultiTest(BitcoinTestFramework): "timestamp": "now", "redeemscript": multisig.redeem_script, "keys": multisig.privkeys[0:2]}, - True, + success=True, warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(multisig.p2sh_addr, - timestamp=timestamp, - ismine=False, - iswatchonly=True, - solvable=True) + test_address(self.nodes[1], + multisig.p2sh_addr, + timestamp=timestamp, + ismine=False, + iswatchonly=True, + solvable=True) p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0] assert_equal(p2shunspent['spendable'], False) assert_equal(p2shunspent['solvable'], True) # P2SH + Redeem script + Private Keys + Watchonly - multisig = self.get_multisig() + multisig = get_multisig(self.nodes[0]) self.nodes[1].generate(100) self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00) self.nodes[1].generate(1) @@ -408,98 +327,101 @@ class ImportMultiTest(BitcoinTestFramework): "redeemscript": multisig.redeem_script, "keys": multisig.privkeys[0:2], "watchonly": True}, - True) - self.test_address(multisig.p2sh_addr, - iswatchonly=True, - ismine=False, - solvable=True, - timestamp=timestamp) + success=True) + test_address(self.nodes[1], + multisig.p2sh_addr, + iswatchonly=True, + ismine=False, + solvable=True, + timestamp=timestamp) # Address + Public key + !Internal + Wrong pubkey self.log.info("Should not import an address with the wrong public key as non-solvable") - key = self.get_key() - address = key.p2pkh_addr - wrong_key = self.get_key().pubkey - self.test_importmulti({"scriptPubKey": {"address": address}, + key = get_key(self.nodes[0]) + wrong_key = get_key(self.nodes[0]).pubkey + self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr}, "timestamp": "now", "pubkeys": [wrong_key]}, - True, + success=True, warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(address, - iswatchonly=True, - ismine=False, - solvable=False, - timestamp=timestamp) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=True, + ismine=False, + solvable=False, + timestamp=timestamp) # ScriptPubKey + Public key + internal + Wrong pubkey self.log.info("Should import a scriptPubKey with internal and with a wrong public key as non-solvable") - key = self.get_key() - address = key.p2pkh_addr - wrong_key = self.get_key().pubkey + key = get_key(self.nodes[0]) + wrong_key = get_key(self.nodes[0]).pubkey self.test_importmulti({"scriptPubKey": key.p2pkh_script, "timestamp": "now", "pubkeys": [wrong_key], "internal": True}, - True, + success=True, warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(address, - iswatchonly=True, - ismine=False, - solvable=False, - timestamp=timestamp) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=True, + ismine=False, + solvable=False, + timestamp=timestamp) # Address + Private key + !watchonly + Wrong private key self.log.info("Should import an address with a wrong private key as non-solvable") - key = self.get_key() - address = key.p2pkh_addr - wrong_privkey = self.get_key().privkey - self.test_importmulti({"scriptPubKey": {"address": address}, + key = get_key(self.nodes[0]) + wrong_privkey = get_key(self.nodes[0]).privkey + self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr}, "timestamp": "now", "keys": [wrong_privkey]}, - True, + success=True, warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(address, - iswatchonly=True, - ismine=False, - solvable=False, - timestamp=timestamp) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=True, + ismine=False, + solvable=False, + timestamp=timestamp) # ScriptPubKey + Private key + internal + Wrong private key self.log.info("Should import a scriptPubKey with internal and with a wrong private key as non-solvable") - key = self.get_key() - address = key.p2pkh_addr - wrong_privkey = self.get_key().privkey + key = get_key(self.nodes[0]) + wrong_privkey = get_key(self.nodes[0]).privkey self.test_importmulti({"scriptPubKey": key.p2pkh_script, "timestamp": "now", "keys": [wrong_privkey], "internal": True}, - True, + success=True, warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(address, - iswatchonly=True, - ismine=False, - solvable=False, - timestamp=timestamp) + test_address(self.nodes[1], + key.p2pkh_addr, + iswatchonly=True, + ismine=False, + solvable=False, + timestamp=timestamp) # Importing existing watch only address with new timestamp should replace saved timestamp. assert_greater_than(timestamp, watchonly_timestamp) self.log.info("Should replace previously saved watch only timestamp.") self.test_importmulti({"scriptPubKey": {"address": watchonly_address}, "timestamp": "now"}, - True) - self.test_address(watchonly_address, - iswatchonly=True, - ismine=False, - timestamp=timestamp) + success=True) + test_address(self.nodes[1], + watchonly_address, + iswatchonly=True, + ismine=False, + timestamp=timestamp) watchonly_timestamp = timestamp # restart nodes to check for proper serialization/deserialization of watch only address self.stop_nodes() self.start_nodes() - self.test_address(watchonly_address, - iswatchonly=True, - ismine=False, - timestamp=watchonly_timestamp) + test_address(self.nodes[1], + watchonly_address, + iswatchonly=True, + ismine=False, + timestamp=watchonly_timestamp) # Bad or missing timestamps self.log.info("Should throw on invalid or missing timestamp values") @@ -513,48 +435,49 @@ class ImportMultiTest(BitcoinTestFramework): # Import P2WPKH address as watch only self.log.info("Should import a P2WPKH address as watch only") - key = self.get_key() - address = key.p2wpkh_addr - self.test_importmulti({"scriptPubKey": {"address": address}, + key = get_key(self.nodes[0]) + self.test_importmulti({"scriptPubKey": {"address": key.p2wpkh_addr}, "timestamp": "now"}, - True) - self.test_address(address, - iswatchonly=True, - solvable=False) + success=True) + test_address(self.nodes[1], + key.p2wpkh_addr, + iswatchonly=True, + solvable=False) # Import P2WPKH address with public key but no private key self.log.info("Should import a P2WPKH address and public key as solvable but not spendable") - key = self.get_key() - address = key.p2wpkh_addr - self.test_importmulti({"scriptPubKey": {"address": address}, + key = get_key(self.nodes[0]) + self.test_importmulti({"scriptPubKey": {"address": key.p2wpkh_addr}, "timestamp": "now", "pubkeys": [key.pubkey]}, - True, + success=True, warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(address, - ismine=False, - solvable=True) + test_address(self.nodes[1], + key.p2wpkh_addr, + ismine=False, + solvable=True) # Import P2WPKH address with key and check it is spendable self.log.info("Should import a P2WPKH address with key") - key = self.get_key() - address = key.p2wpkh_addr - self.test_importmulti({"scriptPubKey": {"address": address}, + key = get_key(self.nodes[0]) + self.test_importmulti({"scriptPubKey": {"address": key.p2wpkh_addr}, "timestamp": "now", "keys": [key.privkey]}, - True) - self.test_address(address, - iswatchonly=False, - ismine=True) + success=True) + test_address(self.nodes[1], + key.p2wpkh_addr, + iswatchonly=False, + ismine=True) # P2WSH multisig address without scripts or keys - multisig = self.get_multisig() + multisig = get_multisig(self.nodes[0]) self.log.info("Should import a p2wsh multisig as watch only without respective redeem script and private keys") self.test_importmulti({"scriptPubKey": {"address": multisig.p2wsh_addr}, "timestamp": "now"}, - True) - self.test_address(multisig.p2sh_addr, - solvable=False) + success=True) + test_address(self.nodes[1], + multisig.p2sh_addr, + solvable=False) # Same P2WSH multisig address as above, but now with witnessscript + private keys self.log.info("Should import a p2wsh with respective witness script and private keys") @@ -562,61 +485,63 @@ class ImportMultiTest(BitcoinTestFramework): "timestamp": "now", "witnessscript": multisig.redeem_script, "keys": multisig.privkeys}, - True) - self.test_address(multisig.p2sh_addr, - solvable=True, - ismine=True, - sigsrequired=2) + success=True) + test_address(self.nodes[1], + multisig.p2sh_addr, + solvable=True, + ismine=True, + sigsrequired=2) # P2SH-P2WPKH address with no redeemscript or public or private key - key = self.get_key() - address = key.p2sh_p2wpkh_addr + key = get_key(self.nodes[0]) self.log.info("Should import a p2sh-p2wpkh without redeem script or keys") - self.test_importmulti({"scriptPubKey": {"address": address}, + self.test_importmulti({"scriptPubKey": {"address": key.p2sh_p2wpkh_addr}, "timestamp": "now"}, - True) - self.test_address(address, - solvable=False, - ismine=False) + success=True) + test_address(self.nodes[1], + key.p2sh_p2wpkh_addr, + solvable=False, + ismine=False) # P2SH-P2WPKH address + redeemscript + public key with no private key self.log.info("Should import a p2sh-p2wpkh with respective redeem script and pubkey as solvable") - self.test_importmulti({"scriptPubKey": {"address": address}, + self.test_importmulti({"scriptPubKey": {"address": key.p2sh_p2wpkh_addr}, "timestamp": "now", "redeemscript": key.p2sh_p2wpkh_redeem_script, "pubkeys": [key.pubkey]}, - True, + success=True, warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(address, - solvable=True, - ismine=False) + test_address(self.nodes[1], + key.p2sh_p2wpkh_addr, + solvable=True, + ismine=False) # P2SH-P2WPKH address + redeemscript + private key - key = self.get_key() - address = key.p2sh_p2wpkh_addr + key = get_key(self.nodes[0]) self.log.info("Should import a p2sh-p2wpkh with respective redeem script and private keys") - self.test_importmulti({"scriptPubKey": {"address": address}, + self.test_importmulti({"scriptPubKey": {"address": key.p2sh_p2wpkh_addr}, "timestamp": "now", "redeemscript": key.p2sh_p2wpkh_redeem_script, "keys": [key.privkey]}, - True) - self.test_address(address, - solvable=True, - ismine=True) + success=True) + test_address(self.nodes[1], + key.p2sh_p2wpkh_addr, + solvable=True, + ismine=True) # P2SH-P2WSH multisig + redeemscript with no private key - multisig = self.get_multisig() - address = multisig.p2sh_p2wsh_addr + multisig = get_multisig(self.nodes[0]) self.log.info("Should import a p2sh-p2wsh with respective redeem script but no private key") - self.test_importmulti({"scriptPubKey": {"address": address}, + self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_p2wsh_addr}, "timestamp": "now", "redeemscript": multisig.p2wsh_script, "witnessscript": multisig.redeem_script}, - True, + success=True, warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) - self.test_address(address, - solvable=True, - ismine=False) + test_address(self.nodes[1], + multisig.p2sh_p2wsh_addr, + solvable=True, + ismine=False) if __name__ == '__main__': ImportMultiTest().main() diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh index 44170a6b5a..1534d5ef68 100755 --- a/test/lint/lint-locale-dependence.sh +++ b/test/lint/lint-locale-dependence.sh @@ -4,30 +4,21 @@ export LC_ALL=C KNOWN_VIOLATIONS=( "src/bitcoin-tx.cpp.*stoul" "src/bitcoin-tx.cpp.*trim_right" - "src/bitcoin-tx.cpp:.*atoi" - "src/core_read.cpp.*is_digit" "src/dbwrapper.cpp.*stoul" "src/dbwrapper.cpp:.*vsnprintf" "src/httprpc.cpp.*trim" "src/init.cpp:.*atoi" "src/qt/rpcconsole.cpp:.*atoi" - "src/qt/rpcconsole.cpp:.*isdigit" "src/rest.cpp:.*strtol" "src/test/dbwrapper_tests.cpp:.*snprintf" - "src/test/getarg_tests.cpp.*split" "src/torcontrol.cpp:.*atoi" "src/torcontrol.cpp:.*strtol" - "src/uint256.cpp:.*tolower" - "src/util/system.cpp:.*atoi" - "src/util/system.cpp:.*fprintf" - "src/util/system.cpp:.*tolower" - "src/util/moneystr.cpp:.*isdigit" "src/util/strencodings.cpp:.*atoi" "src/util/strencodings.cpp:.*strtol" - "src/util/strencodings.cpp:.*strtoll" "src/util/strencodings.cpp:.*strtoul" - "src/util/strencodings.cpp:.*strtoull" "src/util/strencodings.h:.*atoi" + "src/util/system.cpp:.*atoi" + "src/util/system.cpp:.*fprintf" ) REGEXP_IGNORE_EXTERNAL_DEPENDENCIES="^src/(crypto/ctaes/|leveldb/|secp256k1/|tinyformat.h|univalue/)" |