diff options
32 files changed, 508 insertions, 484 deletions
diff --git a/.travis.yml b/.travis.yml index 7f8bc638ff..48bcdf601b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -80,7 +80,7 @@ script: - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib - if [ "$RUN_TESTS" = "true" ]; then travis_wait 50 make $MAKEJOBS check VERBOSE=1; fi - if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then extended="--extended --exclude feature_pruning,feature_dbcrash"; fi - - if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --combinedlogslen=4000 --coverage --quiet ${extended}; fi + - if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --combinedlogslen=4000 --coverage --quiet --failfast ${extended}; fi after_script: - echo $TRAVIS_COMMIT_RANGE - echo $TRAVIS_COMMIT_LOG diff --git a/configure.ac b/configure.ac index b68fd8fc58..ce9e683eda 100644 --- a/configure.ac +++ b/configure.ac @@ -84,7 +84,7 @@ AC_PATH_TOOL(STRIP, strip) AC_PATH_TOOL(GCOV, gcov) AC_PATH_PROG(LCOV, lcov) dnl Python 3.x is supported from 3.4 on (see https://github.com/bitcoin/bitcoin/issues/7893) -AC_PATH_PROGS([PYTHON], [python3.6 python3.5 python3.4 python3 python2.7 python2 python]) +AC_PATH_PROGS([PYTHON], [python3.7 python3.6 python3.5 python3.4 python3 python]) AC_PATH_PROG(GENHTML, genhtml) AC_PATH_PROG([GIT], [git]) AC_PATH_PROG(CCACHE,ccache) diff --git a/contrib/tidy_datadir.sh b/contrib/tidy_datadir.sh deleted file mode 100755 index b845b34e41..0000000000 --- a/contrib/tidy_datadir.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash -# Copyright (c) 2013 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 [ -d "$1" ]; then - cd "$1" || exit 1 -else - echo "Usage: $0 <datadir>" >&2 - echo "Removes obsolete Bitcoin database files" >&2 - exit 1 -fi - -LEVEL=0 -if [ -f wallet.dat -a -f addr.dat -a -f blkindex.dat -a -f blk0001.dat ]; then LEVEL=1; fi -if [ -f wallet.dat -a -f peers.dat -a -f blkindex.dat -a -f blk0001.dat ]; then LEVEL=2; fi -if [ -f wallet.dat -a -f peers.dat -a -f coins/CURRENT -a -f blktree/CURRENT -a -f blocks/blk00000.dat ]; then LEVEL=3; fi -if [ -f wallet.dat -a -f peers.dat -a -f chainstate/CURRENT -a -f blocks/index/CURRENT -a -f blocks/blk00000.dat ]; then LEVEL=4; fi - -case $LEVEL in - 0) - echo "Error: no Bitcoin datadir detected." - exit 1 - ;; - 1) - echo "Detected old Bitcoin datadir (before 0.7)." - echo "Nothing to do." - exit 0 - ;; - 2) - echo "Detected Bitcoin 0.7 datadir." - ;; - 3) - echo "Detected Bitcoin pre-0.8 datadir." - ;; - 4) - echo "Detected Bitcoin 0.8 datadir." - ;; -esac - -FILES="" -DIRS="" - -if [ $LEVEL -ge 3 ]; then FILES=$(echo $FILES blk????.dat blkindex.dat); fi -if [ $LEVEL -ge 2 ]; then FILES=$(echo $FILES addr.dat); fi -if [ $LEVEL -ge 4 ]; then DIRS=$(echo $DIRS coins blktree); fi - -for FILE in $FILES; do - if [ -f $FILE ]; then - echo "Deleting: $FILE" - rm -f $FILE - fi -done - -for DIR in $DIRS; do - if [ -d $DIR ]; then - echo "Deleting: $DIR/" - rm -rf $DIR - fi -done - -echo "Done." diff --git a/doc/release-notes.md b/doc/release-notes.md index 1554272195..99ef6b2b59 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -72,8 +72,8 @@ RPC changes `getrawtransaction` RPC commands. - New `fees` field introduced in `getrawmempool`, `getmempoolancestors`, `getmempooldescendants` and `getmempoolentry` when verbosity is set to `true` with sub-fields `ancestor`, `base`, `modified` - and `descendent` denominated in BTC. This new field deprecates previous fee fields, such as - `fee`, `modifiedfee`, `ancestorfee` and `descendentfee`. + and `descendant` denominated in BTC. This new field deprecates previous fee fields, such as + `fee`, `modifiedfee`, `ancestorfee` and `descendantfee`. External wallet files --------------------- diff --git a/doc/tor.md b/doc/tor.md index 931c83abdd..f0f98b7d12 100644 --- a/doc/tor.md +++ b/doc/tor.md @@ -1,5 +1,4 @@ -TOR SUPPORT IN BITCOIN -====================== +# TOR SUPPORT IN BITCOIN It is possible to run Bitcoin as a Tor hidden service, and connect to such services. @@ -7,8 +6,7 @@ The following directions assume you have a Tor proxy running on port 9050. Many configure Tor. -1. Run bitcoin behind a Tor proxy ---------------------------------- +## 1. Run bitcoin behind a Tor proxy The first step is running Bitcoin behind a Tor proxy. This will already make all outgoing connections be anonymized, but more is possible. @@ -34,12 +32,12 @@ In a typical situation, this suffices to run behind a Tor proxy: ./bitcoind -proxy=127.0.0.1:9050 -2. Run a bitcoin hidden server ------------------------------- +## 2. Run a bitcoin hidden server If you configure your Tor system accordingly, it is possible to make your node also reachable from the Tor network. Add these lines to your /etc/tor/torrc (or equivalent -config file): +config file): *Needed for Tor version 0.2.7.0 and older versions of Tor only. For newer +versions of Tor see [Section 3](#3-automatically-listen-on-tor).* HiddenServiceDir /var/lib/tor/bitcoin-service/ HiddenServicePort 8333 127.0.0.1:8333 @@ -88,8 +86,7 @@ for normal IPv4/IPv6 communication, use: ./bitcoind -onion=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -discover -3. Automatically listen on Tor --------------------------------- +## 3. Automatically listen on Tor Starting with Tor version 0.2.7.1 it is possible, through Tor's control socket API, to create and destroy 'ephemeral' hidden services programmatically. @@ -115,8 +112,7 @@ which has the appropriate permissions. An alternative authentication method is t of the `-torpassword` flag and a `hash-password` which can be enabled and specified in Tor configuration. -4. Privacy recommendations ---------------------------- +## 4. Privacy recommendations - Do not add anything but bitcoin ports to the hidden service created in section 2. If you run a web service too, create a new hidden service for that. diff --git a/share/rpcauth/README.md b/share/rpcauth/README.md index 389278a125..20d16f0a97 100644 --- a/share/rpcauth/README.md +++ b/share/rpcauth/README.md @@ -8,3 +8,7 @@ Create login credentials for a JSON-RPC user. Usage: ./rpcauth.py <username> + +in which case the script will generate a password. To specify a custom password do: + + ./rpcauth.py <username> <password> diff --git a/share/rpcauth/rpcauth.py b/share/rpcauth/rpcauth.py index da84deb5e2..566c55aba9 100755 --- a/share/rpcauth/rpcauth.py +++ b/share/rpcauth/rpcauth.py @@ -17,24 +17,27 @@ def generate_salt(): salt_sequence = [cryptogen.randrange(256) for _ in range(16)] return ''.join([format(r, 'x') for r in salt_sequence]) -def generate_password(salt): +def generate_password(): """Create 32 byte b64 password""" - password = base64.urlsafe_b64encode(os.urandom(32)).decode('utf-8') + return base64.urlsafe_b64encode(os.urandom(32)).decode('utf-8') +def password_to_hmac(salt, password): m = hmac.new(bytearray(salt, 'utf-8'), bytearray(password, 'utf-8'), 'SHA256') - password_hmac = m.hexdigest() - - return password, password_hmac + return m.hexdigest() def main(): if len(sys.argv) < 2: - sys.stderr.write('Please include username as an argument.\n') + sys.stderr.write('Please include username (and an optional password, will generate one if not provided) as an argument.\n') sys.exit(0) username = sys.argv[1] salt = generate_salt() - password, password_hmac = generate_password(salt) + if len(sys.argv) > 2: + password = sys.argv[2] + else: + password = generate_password() + password_hmac = password_to_hmac(salt, password) print('String to be appended to bitcoin.conf:') print('rpcauth={0}:{1}${2}'.format(username, salt, password_hmac)) diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index 1d87883522..c1f3339830 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -46,7 +46,6 @@ main(int argc, char** argv) RandomInit(); ECC_Start(); SetupEnvironment(); - fPrintToDebugLog = false; // don't want to write to debug.log file int64_t evaluations = gArgs.GetArg("-evals", DEFAULT_BENCH_EVALUATIONS); std::string regex_filter = gArgs.GetArg("-filter", DEFAULT_BENCH_FILTER); diff --git a/src/httpserver.cpp b/src/httpserver.cpp index b8b338dfbc..bd08b04c0f 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -364,8 +364,8 @@ bool InitHTTPServer() // Update libevent's log handling. Returns false if our version of // libevent doesn't support debug logging, in which case we should // clear the BCLog::LIBEVENT flag. - if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) { - logCategories &= ~BCLog::LIBEVENT; + if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) { + g_logger->DisableCategory(BCLog::LIBEVENT); } #ifdef WIN32 diff --git a/src/httpserver.h b/src/httpserver.h index f17be65962..8132c887b5 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -32,7 +32,7 @@ void InterruptHTTPServer(); /** Stop HTTP server */ void StopHTTPServer(); -/** Change logging level for libevent. Removes BCLog::LIBEVENT from logCategories if +/** Change logging level for libevent. Removes BCLog::LIBEVENT from log categories if * libevent doesn't support debug logging.*/ bool UpdateHTTPServerLogging(bool enable); diff --git a/src/init.cpp b/src/init.cpp index 99dab605ac..6423d87026 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -305,7 +305,7 @@ static void HandleSIGTERM(int) static void HandleSIGHUP(int) { - fReopenDebugLog = true; + g_logger->m_reopen_file = true; } #ifndef WIN32 @@ -826,15 +826,18 @@ static std::string ResolveErrMsg(const char * const optname, const std::string& */ void InitLogging() { + g_logger->m_print_to_file = !gArgs.IsArgNegated("-debuglogfile"); + g_logger->m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + // Add newlines to the logfile to distinguish this execution from the last // one; called before console logging is set up, so this is only sent to // debug.log. LogPrintf("\n\n\n\n\n"); - fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false)); - fPrintToDebugLog = !gArgs.IsArgNegated("-debuglogfile"); - fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); - fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); + g_logger->m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false)); + g_logger->m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); + g_logger->m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); + fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS); std::string version_string = FormatFullVersion(); @@ -962,24 +965,18 @@ bool AppInitParameterInteraction() if (std::none_of(categories.begin(), categories.end(), [](std::string cat){return cat == "0" || cat == "none";})) { for (const auto& cat : categories) { - uint32_t flag = 0; - if (!GetLogCategory(&flag, &cat)) { + if (!g_logger->EnableCategory(cat)) { InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat)); - continue; } - logCategories |= flag; } } } // Now remove the logging categories which were explicitly excluded for (const std::string& cat : gArgs.GetArgs("-debugexclude")) { - uint32_t flag = 0; - if (!GetLogCategory(&flag, &cat)) { + if (!g_logger->DisableCategory(cat)) { InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat)); - continue; } - logCategories &= ~flag; } // Check for -debugnet @@ -1230,18 +1227,19 @@ bool AppInitMain() #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif - if (fPrintToDebugLog) { - if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) { + if (g_logger->m_print_to_file) { + if (gArgs.GetBoolArg("-shrinkdebugfile", g_logger->DefaultShrinkDebugFile())) { // Do this first since it both loads a bunch of debug.log into memory, // and because this needs to happen before any other debug.log printing - ShrinkDebugFile(); + g_logger->ShrinkDebugFile(); } - if (!OpenDebugLog()) { - return InitError(strprintf("Could not open debug log file %s", GetDebugLogPath().string())); + if (!g_logger->OpenDebugLog()) { + return InitError(strprintf("Could not open debug log file %s", + g_logger->m_file_path.string())); } } - if (!fLogTimestamps) + if (!g_logger->m_log_timestamps) LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime())); LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); LogPrintf("Using data directory %s\n", GetDataDir().string()); diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 55786c807f..53d2359caf 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -60,7 +60,7 @@ class NodeImpl : public Node void initLogging() override { InitLogging(); } void initParameterInteraction() override { InitParameterInteraction(); } std::string getWarnings(const std::string& type) override { return GetWarnings(type); } - uint32_t getLogCategories() override { return ::logCategories; } + uint32_t getLogCategories() override { return g_logger->GetCategoryMask(); } bool baseInitialize() override { return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() && diff --git a/src/keystore.h b/src/keystore.h index fa912cb195..c56e4751de 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -54,7 +54,7 @@ protected: ScriptMap mapScripts; WatchOnlySet setWatchOnly; - void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey); + void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); public: bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; diff --git a/src/logging.cpp b/src/logging.cpp index e481582321..10a3b18958 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -4,97 +4,93 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <logging.h> -#include <util.h> -#include <utilstrencodings.h> - -#include <list> -#include <mutex> +#include <utiltime.h> const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; -bool fPrintToConsole = false; -bool fPrintToDebugLog = true; - -bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; -bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; -bool fLogIPs = DEFAULT_LOGIPS; -std::atomic<bool> fReopenDebugLog(false); - -/** Log categories bitfield. */ -std::atomic<uint32_t> logCategories(0); /** - * LogPrintf() has been broken a couple of times now - * by well-meaning people adding mutexes in the most straightforward way. - * It breaks because it may be called by global destructors during shutdown. - * Since the order of destruction of static/global objects is undefined, - * defining a mutex as a global object doesn't work (the mutex gets - * destroyed, and then some later destructor calls OutputDebugStringF, - * maybe indirectly, and you get a core dump at shutdown trying to lock - * the mutex). - */ - -static std::once_flag debugPrintInitFlag; - -/** - * We use std::call_once() to make sure mutexDebugLog and - * vMsgsBeforeOpenLog are initialized in a thread-safe manner. + * NOTE: the logger instances is leaked on exit. This is ugly, but will be + * cleaned up by the OS/libc. Defining a logger as a global object doesn't work + * since the order of destruction of static/global objects is undefined. + * Consider if the logger gets destroyed, and then some later destructor calls + * LogPrintf, maybe indirectly, and you get a core dump at shutdown trying to + * access the logger. When the shutdown sequence is fully audited and tested, + * explicit destruction of these objects can be implemented by changing this + * from a raw pointer to a std::unique_ptr. * - * NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog - * are leaked on exit. This is ugly, but will be cleaned up by - * the OS/libc. When the shutdown sequence is fully audited and - * tested, explicit destruction of these objects can be implemented. + * This method of initialization was originally introduced in + * ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c. */ -static FILE* fileout = nullptr; -static std::mutex* mutexDebugLog = nullptr; -static std::list<std::string>* vMsgsBeforeOpenLog; +BCLog::Logger* const g_logger = new BCLog::Logger(); + +bool fLogIPs = DEFAULT_LOGIPS; static int FileWriteStr(const std::string &str, FILE *fp) { return fwrite(str.data(), 1, str.size(), fp); } -static void DebugPrintInit() +bool BCLog::Logger::OpenDebugLog() { - assert(mutexDebugLog == nullptr); - mutexDebugLog = new std::mutex(); - vMsgsBeforeOpenLog = new std::list<std::string>; + std::lock_guard<std::mutex> scoped_lock(m_file_mutex); + + assert(m_fileout == nullptr); + assert(!m_file_path.empty()); + + m_fileout = fsbridge::fopen(m_file_path, "a"); + if (!m_fileout) { + return false; + } + + setbuf(m_fileout, nullptr); // unbuffered + // dump buffered messages from before we opened the log + while (!m_msgs_before_open.empty()) { + FileWriteStr(m_msgs_before_open.front(), m_fileout); + m_msgs_before_open.pop_front(); + } + + return true; } -fs::path GetDebugLogPath() +void BCLog::Logger::EnableCategory(BCLog::LogFlags flag) { - fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); - return AbsPathForConfigVal(logfile); + m_categories |= flag; } -bool OpenDebugLog() +bool BCLog::Logger::EnableCategory(const std::string& str) { - std::call_once(debugPrintInitFlag, &DebugPrintInit); - std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); + BCLog::LogFlags flag; + if (!GetLogCategory(flag, str)) return false; + EnableCategory(flag); + return true; +} - assert(fileout == nullptr); - assert(vMsgsBeforeOpenLog); - fs::path pathDebug = GetDebugLogPath(); +void BCLog::Logger::DisableCategory(BCLog::LogFlags flag) +{ + m_categories &= ~flag; +} - fileout = fsbridge::fopen(pathDebug, "a"); - if (!fileout) { - return false; - } +bool BCLog::Logger::DisableCategory(const std::string& str) +{ + BCLog::LogFlags flag; + if (!GetLogCategory(flag, str)) return false; + DisableCategory(flag); + return true; +} - setbuf(fileout, nullptr); // unbuffered - // dump buffered messages from before we opened the log - while (!vMsgsBeforeOpenLog->empty()) { - FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); - vMsgsBeforeOpenLog->pop_front(); - } +bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const +{ + return (m_categories.load(std::memory_order_relaxed) & category) != 0; +} - delete vMsgsBeforeOpenLog; - vMsgsBeforeOpenLog = nullptr; - return true; +bool BCLog::Logger::DefaultShrinkDebugFile() const +{ + return m_categories == BCLog::NONE; } struct CLogCategoryDesc { - uint32_t flag; + BCLog::LogFlags flag; std::string category; }; @@ -127,19 +123,17 @@ const CLogCategoryDesc LogCategories[] = {BCLog::ALL, "all"}, }; -bool GetLogCategory(uint32_t *f, const std::string *str) +bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str) { - if (f && str) { - if (*str == "") { - *f = BCLog::ALL; + if (str == "") { + flag = BCLog::ALL; + return true; + } + for (const CLogCategoryDesc& category_desc : LogCategories) { + if (category_desc.category == str) { + flag = category_desc.flag; return true; } - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { - if (LogCategories[i].category == *str) { - *f = LogCategories[i].flag; - return true; - } - } } return false; } @@ -148,11 +142,11 @@ std::string ListLogCategories() { std::string ret; int outcount = 0; - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + for (const CLogCategoryDesc& category_desc : LogCategories) { // Omit the special cases. - if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { + if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) { if (outcount != 0) ret += ", "; - ret += LogCategories[i].category; + ret += category_desc.category; outcount++; } } @@ -162,34 +156,29 @@ std::string ListLogCategories() std::vector<CLogCategoryActive> ListActiveLogCategories() { std::vector<CLogCategoryActive> ret; - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + for (const CLogCategoryDesc& category_desc : LogCategories) { // Omit the special cases. - if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { + if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) { CLogCategoryActive catActive; - catActive.category = LogCategories[i].category; - catActive.active = LogAcceptCategory(LogCategories[i].flag); + catActive.category = category_desc.category; + catActive.active = LogAcceptCategory(category_desc.flag); ret.push_back(catActive); } } return ret; } -/** - * fStartedNewLine is a state variable held by the calling context that will - * suppress printing of the timestamp when multiple calls are made that don't - * end in a newline. Initialize it to true, and hold it, in the calling context. - */ -static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine) +std::string BCLog::Logger::LogTimestampStr(const std::string &str) { std::string strStamped; - if (!fLogTimestamps) + if (!m_log_timestamps) return str; - if (*fStartedNewLine) { + if (m_started_new_line) { int64_t nTimeMicros = GetTimeMicros(); strStamped = FormatISO8601DateTime(nTimeMicros/1000000); - if (fLogTimeMicros) { + if (m_log_time_micros) { strStamped.pop_back(); strStamped += strprintf(".%06dZ", nTimeMicros%1000000); } @@ -202,63 +191,61 @@ static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fSt strStamped = str; if (!str.empty() && str[str.size()-1] == '\n') - *fStartedNewLine = true; + m_started_new_line = true; else - *fStartedNewLine = false; + m_started_new_line = false; return strStamped; } -int LogPrintStr(const std::string &str) +int BCLog::Logger::LogPrintStr(const std::string &str) { int ret = 0; // Returns total number of characters written - static std::atomic_bool fStartedNewLine(true); - std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine); + std::string strTimestamped = LogTimestampStr(str); - if (fPrintToConsole) { + if (m_print_to_console) { // print to console ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); fflush(stdout); } - if (fPrintToDebugLog) { - std::call_once(debugPrintInitFlag, &DebugPrintInit); - std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); + if (m_print_to_file) { + std::lock_guard<std::mutex> scoped_lock(m_file_mutex); // buffer if we haven't opened the log yet - if (fileout == nullptr) { - assert(vMsgsBeforeOpenLog); + if (m_fileout == nullptr) { ret = strTimestamped.length(); - vMsgsBeforeOpenLog->push_back(strTimestamped); + m_msgs_before_open.push_back(strTimestamped); } else { // reopen the log file, if requested - if (fReopenDebugLog) { - fReopenDebugLog = false; - fs::path pathDebug = GetDebugLogPath(); - if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr) - setbuf(fileout, nullptr); // unbuffered + if (m_reopen_file) { + m_reopen_file = false; + if (fsbridge::freopen(m_file_path,"a",m_fileout) != nullptr) + setbuf(m_fileout, nullptr); // unbuffered } - ret = FileWriteStr(strTimestamped, fileout); + ret = FileWriteStr(strTimestamped, m_fileout); } } return ret; } -void ShrinkDebugFile() +void BCLog::Logger::ShrinkDebugFile() { // Amount of debug.log to save at end when shrinking (must fit in memory) constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; + + assert(!m_file_path.empty()); + // Scroll debug.log if it's getting too big - fs::path pathLog = GetDebugLogPath(); - FILE* file = fsbridge::fopen(pathLog, "r"); + FILE* file = fsbridge::fopen(m_file_path, "r"); // Special files (e.g. device nodes) may not have a size. size_t log_size = 0; try { - log_size = fs::file_size(pathLog); + log_size = fs::file_size(m_file_path); } catch (boost::filesystem::filesystem_error &) {} // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE @@ -271,7 +258,7 @@ void ShrinkDebugFile() int nBytes = fread(vch.data(), 1, vch.size(), file); fclose(file); - file = fsbridge::fopen(pathLog, "w"); + file = fsbridge::fopen(m_file_path, "w"); if (file) { fwrite(vch.data(), 1, nBytes, file); diff --git a/src/logging.h b/src/logging.h index 4053f75acf..1f2be6016a 100644 --- a/src/logging.h +++ b/src/logging.h @@ -11,6 +11,8 @@ #include <atomic> #include <cstdint> +#include <list> +#include <mutex> #include <string> #include <vector> @@ -19,15 +21,7 @@ static const bool DEFAULT_LOGIPS = false; static const bool DEFAULT_LOGTIMESTAMPS = true; extern const char * const DEFAULT_DEBUGLOGFILE; -extern bool fPrintToConsole; -extern bool fPrintToDebugLog; - -extern bool fLogTimestamps; -extern bool fLogTimeMicros; extern bool fLogIPs; -extern std::atomic<bool> fReopenDebugLog; - -extern std::atomic<uint32_t> logCategories; struct CLogCategoryActive { @@ -61,11 +55,65 @@ namespace BCLog { LEVELDB = (1 << 20), ALL = ~(uint32_t)0, }; -} + + class Logger + { + private: + FILE* m_fileout = nullptr; + std::mutex m_file_mutex; + std::list<std::string> m_msgs_before_open; + + /** + * m_started_new_line is a state variable that will suppress printing of + * the timestamp when multiple calls are made that don't end in a + * newline. + */ + std::atomic_bool m_started_new_line{true}; + + /** Log categories bitfield. */ + std::atomic<uint32_t> m_categories{0}; + + std::string LogTimestampStr(const std::string& str); + + public: + bool m_print_to_console = false; + bool m_print_to_file = false; + + bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS; + bool m_log_time_micros = DEFAULT_LOGTIMEMICROS; + + fs::path m_file_path; + std::atomic<bool> m_reopen_file{false}; + + /** Send a string to the log output */ + int LogPrintStr(const std::string &str); + + /** Returns whether logs will be written to any output */ + bool Enabled() const { return m_print_to_console || m_print_to_file; } + + bool OpenDebugLog(); + void ShrinkDebugFile(); + + uint32_t GetCategoryMask() const { return m_categories.load(); } + + void EnableCategory(LogFlags flag); + bool EnableCategory(const std::string& str); + void DisableCategory(LogFlags flag); + bool DisableCategory(const std::string& str); + + bool WillLogCategory(LogFlags category) const; + + bool DefaultShrinkDebugFile() const; + }; + +} // namespace BCLog + +extern BCLog::Logger* const g_logger; + /** Return true if log accepts specified category */ -static inline bool LogAcceptCategory(uint32_t category) +static inline bool LogAcceptCategory(BCLog::LogFlags category) { - return (logCategories.load(std::memory_order_relaxed) & category) != 0; + return g_logger->WillLogCategory(category); } /** Returns a string with the log categories. */ @@ -74,11 +122,8 @@ std::string ListLogCategories(); /** Returns a vector of the active log categories. */ std::vector<CLogCategoryActive> ListActiveLogCategories(); -/** Return true if str parses as a log category and set the flags in f */ -bool GetLogCategory(uint32_t *f, const std::string *str); - -/** Send a string to the log output */ -int LogPrintStr(const std::string &str); +/** Return true if str parses as a log category and set the flag */ +bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str); /** Get format string from VA_ARGS for error reporting */ template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; } @@ -99,7 +144,7 @@ template<typename T, typename... Args> static inline void MarkUsed(const T& t, c #define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) #else #define LogPrintf(...) do { \ - if (fPrintToConsole || fPrintToDebugLog) { \ + if (g_logger->Enabled()) { \ std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ try { \ _log_msg_ = tfm::format(__VA_ARGS__); \ @@ -107,7 +152,7 @@ template<typename T, typename... Args> static inline void MarkUsed(const T& t, c /* Original format string will have newline so don't add one here */ \ _log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \ } \ - LogPrintStr(_log_msg_); \ + g_logger->LogPrintStr(_log_msg_); \ } \ } while(0) @@ -118,8 +163,4 @@ template<typename T, typename... Args> static inline void MarkUsed(const T& t, c } while(0) #endif -fs::path GetDebugLogPath(); -bool OpenDebugLog(); -void ShrinkDebugFile(); - #endif // BITCOIN_LOGGING_H diff --git a/src/qt/README.md b/src/qt/README.md index 7c23ccadcc..d8acf96ceb 100644 --- a/src/qt/README.md +++ b/src/qt/README.md @@ -16,7 +16,7 @@ To run: ### forms -Contains [Designer UI](http://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#use-qt-Creator-as IDE), but can be edited using any text editor. +Contains [Designer UI](http://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#using-qt-creator-as-ide), but can be edited using any text editor. ### locale diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 03c097197f..c24a3de060 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -373,7 +373,7 @@ std::string EntryDescriptionString() " \"base\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" " \"modified\" : n, (numeric) transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT + "\n" " \"ancestor\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT + "\n" - " \"descendent\" : n, (numeric) number of in-mempool ancestor transactions (including this one) in " + CURRENCY_UNIT + "\n" + " \"descendant\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT + "\n" " }\n" " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 6754407dbd..0c93108bce 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -346,21 +346,22 @@ UniValue getmemoryinfo(const JSONRPCRequest& request) } } -uint32_t getCategoryMask(UniValue cats) { +void EnableOrDisableLogCategories(UniValue cats, bool enable) { cats = cats.get_array(); - uint32_t mask = 0; for (unsigned int i = 0; i < cats.size(); ++i) { - uint32_t flag = 0; std::string cat = cats[i].get_str(); - if (!GetLogCategory(&flag, &cat)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat); + + bool success; + if (enable) { + success = g_logger->EnableCategory(cat); + } else { + success = g_logger->DisableCategory(cat); } - if (flag == BCLog::NONE) { - return 0; + + if (!success) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat); } - mask |= flag; } - return mask; } UniValue logging(const JSONRPCRequest& request) @@ -399,25 +400,25 @@ UniValue logging(const JSONRPCRequest& request) ); } - uint32_t originalLogCategories = logCategories; + uint32_t original_log_categories = g_logger->GetCategoryMask(); if (request.params[0].isArray()) { - logCategories |= getCategoryMask(request.params[0]); + EnableOrDisableLogCategories(request.params[0], true); } - if (request.params[1].isArray()) { - logCategories &= ~getCategoryMask(request.params[1]); + EnableOrDisableLogCategories(request.params[1], false); } + uint32_t updated_log_categories = g_logger->GetCategoryMask(); + uint32_t changed_log_categories = original_log_categories ^ updated_log_categories; // Update libevent logging if BCLog::LIBEVENT has changed. // If the library version doesn't allow it, UpdateHTTPServerLogging() returns false, // in which case we should clear the BCLog::LIBEVENT flag. // Throw an error if the user has explicitly asked to change only the libevent // flag and it failed. - uint32_t changedLogCategories = originalLogCategories ^ logCategories; - if (changedLogCategories & BCLog::LIBEVENT) { - if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) { - logCategories &= ~BCLog::LIBEVENT; - if (changedLogCategories == BCLog::LIBEVENT) { + if (changed_log_categories & BCLog::LIBEVENT) { + if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) { + g_logger->DisableCategory(BCLog::LIBEVENT); + if (changed_log_categories == BCLog::LIBEVENT) { throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1."); } } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index e9873f4526..fe816a6f79 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -47,7 +47,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) SetupNetworking(); InitSignatureCache(); InitScriptExecutionCache(); - fPrintToDebugLog = false; // don't want to write to debug.log file fCheckBlockIndex = true; SelectParams(chainName); noui_connect(); diff --git a/src/util.h b/src/util.h index ce94f396af..2da8023285 100644 --- a/src/util.h +++ b/src/util.h @@ -66,7 +66,7 @@ bool SetupNetworking(); template<typename... Args> bool error(const char* fmt, const Args&... args) { - LogPrintStr("ERROR: " + tfm::format(fmt, args...) + "\n"); + LogPrintf("ERROR: %s\n", tfm::format(fmt, args...)); return false; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 123252fdce..c0aa748073 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -34,10 +34,12 @@ #include <boost/algorithm/string/replace.hpp> -static std::vector<CWallet*> vpwallets; +static CCriticalSection cs_wallets; +static std::vector<CWallet*> vpwallets GUARDED_BY(cs_wallets); bool AddWallet(CWallet* wallet) { + LOCK(cs_wallets); assert(wallet); std::vector<CWallet*>::const_iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); if (i != vpwallets.end()) return false; @@ -47,6 +49,7 @@ bool AddWallet(CWallet* wallet) bool RemoveWallet(CWallet* wallet) { + LOCK(cs_wallets); assert(wallet); std::vector<CWallet*>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); if (i == vpwallets.end()) return false; @@ -56,16 +59,19 @@ bool RemoveWallet(CWallet* wallet) bool HasWallets() { + LOCK(cs_wallets); return !vpwallets.empty(); } std::vector<CWallet*> GetWallets() { + LOCK(cs_wallets); return vpwallets; } CWallet* GetWallet(const std::string& name) { + LOCK(cs_wallets); for (CWallet* wallet : vpwallets) { if (wallet->GetName() == name) return wallet; } diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py index 7b7c596506..2e0500e7c4 100755 --- a/test/functional/rpc_deprecated.py +++ b/test/functional/rpc_deprecated.py @@ -49,6 +49,7 @@ class DeprecatedRpcTest(BitcoinTestFramework): # address0 = self.nodes[0].getnewaddress() self.nodes[0].generatetoaddress(101, address0) + self.sync_all() address1 = self.nodes[1].getnewaddress() self.nodes[1].generatetoaddress(101, address1) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index f5e1f3d4f7..ff4b480165 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -201,6 +201,7 @@ def main(): parser.add_argument('--keepcache', '-k', action='store_true', help='the default behavior is to flush the cache directory on startup. --keepcache retains the cache from the previous testrun.') parser.add_argument('--quiet', '-q', action='store_true', help='only print results summary and failure logs') parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs") + parser.add_argument('--failfast', action='store_true', help='stop execution after the first test failure') args, unknown_args = parser.parse_known_args() # args to be passed on always start with two dashes; tests are the remaining unknown args @@ -283,9 +284,21 @@ def main(): if not args.keepcache: shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True) - run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], tmpdir, args.jobs, args.coverage, passon_args, args.combinedlogslen) + run_tests( + test_list, + config["environment"]["SRCDIR"], + config["environment"]["BUILDDIR"], + tmpdir, + jobs=args.jobs, + enable_coverage=args.coverage, + args=passon_args, + combined_logs_len=args.combinedlogslen, + failfast=args.failfast, + ) + +def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False): + args = args or [] -def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=[], combined_logs_len=0): # Warn if bitcoind is already running (unix only) try: if subprocess.check_output(["pidof", "bitcoind"]) is not None: @@ -346,6 +359,10 @@ def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=Fal combined_logs, _ = subprocess.Popen([sys.executable, os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate() print("\n".join(deque(combined_logs.splitlines(), combined_logs_len))) + if failfast: + logging.debug("Early exiting after test failure") + break + print_results(test_results, max_len_name, (int(time.time() - start_time))) if coverage: @@ -360,6 +377,10 @@ def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=Fal all_passed = all(map(lambda test_result: test_result.was_successful, test_results)) + # This will be a no-op unless failfast is True in which case there may be dangling + # processes which need to be killed. + job_queue.kill_and_join() + sys.exit(not all_passed) def print_results(test_results, max_len_name, runtime): @@ -450,6 +471,17 @@ class TestHandler: return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr print('.', end='', flush=True) + def kill_and_join(self): + """Send SIGKILL to all jobs and block until all have ended.""" + procs = [i[2] for i in self.jobs] + + for proc in procs: + proc.kill() + + for proc in procs: + proc.wait() + + class TestResult(): def __init__(self, name, status, time): self.name = name diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index bfe90957a7..0e095a6132 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -3,8 +3,20 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the wallet.""" +from decimal import Decimal +import time + from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_array_result, + assert_equal, + assert_fee_amount, + assert_raises_rpc_error, + connect_nodes_bi, + sync_blocks, + sync_mempools, + wait_until, +) class WalletTest(BitcoinTestFramework): def set_test_params(self): @@ -17,9 +29,9 @@ class WalletTest(BitcoinTestFramework): self.start_node(0) self.start_node(1) self.start_node(2) - connect_nodes_bi(self.nodes,0,1) - connect_nodes_bi(self.nodes,1,2) - connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 1, 2) + connect_nodes_bi(self.nodes, 0, 2) self.sync_all([self.nodes[0:3]]) def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): @@ -112,11 +124,11 @@ class WalletTest(BitcoinTestFramework): self.nodes[2].lockunspent(True, [unspent_0]) assert_equal(len(self.nodes[2].listlockunspent()), 0) assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction", - self.nodes[2].lockunspent, False, - [{"txid": "0000000000000000000000000000000000", "vout": 0}]) + self.nodes[2].lockunspent, False, + [{"txid": "0000000000000000000000000000000000", "vout": 0}]) assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds", - self.nodes[2].lockunspent, False, - [{"txid": unspent_0["txid"], "vout": 999}]) + self.nodes[2].lockunspent, False, + [{"txid": unspent_0["txid"], "vout": 999}]) # Have node1 generate 100 blocks (so node0 can recover the fee) self.nodes[1].generate(100) @@ -124,7 +136,7 @@ class WalletTest(BitcoinTestFramework): # node0 should end up with 100 btc in block rewards plus fees, but # minus the 21 plus fees sent to node2 - assert_equal(self.nodes[0].getbalance(), 100-21) + assert_equal(self.nodes[0].getbalance(), 100 - 21) assert_equal(self.nodes[2].getbalance(), 21) # Node0 should have two unspent outputs. @@ -138,7 +150,7 @@ class WalletTest(BitcoinTestFramework): for utxo in node0utxos: inputs = [] outputs = {} - inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]}) + inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]}) outputs[self.nodes[2].getnewaddress("from1")] = utxo["amount"] - 3 raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) txns_to_send.append(self.nodes[0].signrawtransactionwithwallet(raw_tx)) @@ -153,7 +165,7 @@ class WalletTest(BitcoinTestFramework): assert_equal(self.nodes[0].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 94) - assert_equal(self.nodes[2].getbalance("from1"), 94-21) + assert_equal(self.nodes[2].getbalance("from1"), 94 - 21) # Verify that a spent output cannot be locked anymore spent_0 = {"txid": node0utxos[0]["txid"], "vout": node0utxos[0]["vout"]} @@ -215,91 +227,90 @@ class WalletTest(BitcoinTestFramework): assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], 1) assert_equal(self.nodes[0].getunconfirmedbalance(), 1) - #check if we can list zero value tx as available coins - #1. create rawtx - #2. hex-changed one output to 0.0 - #3. sign and send - #4. check if recipient (node0) can list the zero value tx + # check if we can list zero value tx as available coins + # 1. create raw_tx + # 2. hex-changed one output to 0.0 + # 3. sign and send + # 4. check if recipient (node0) can list the zero value tx usp = self.nodes[1].listunspent() - inputs = [{"txid":usp[0]['txid'], "vout":usp[0]['vout']}] + inputs = [{"txid": usp[0]['txid'], "vout": usp[0]['vout']}] outputs = {self.nodes[1].getnewaddress(): 49.998, self.nodes[0].getnewaddress(): 11.11} - rawTx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") #replace 11.11 with 0.0 (int32) - decRawTx = self.nodes[1].decoderawtransaction(rawTx) - signedRawTx = self.nodes[1].signrawtransactionwithwallet(rawTx) - decRawTx = self.nodes[1].decoderawtransaction(signedRawTx['hex']) - zeroValueTxid= decRawTx['txid'] - self.nodes[1].sendrawtransaction(signedRawTx['hex']) + raw_tx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") # replace 11.11 with 0.0 (int32) + signed_raw_tx = self.nodes[1].signrawtransactionwithwallet(raw_tx) + decoded_raw_tx = self.nodes[1].decoderawtransaction(signed_raw_tx['hex']) + zero_value_txid = decoded_raw_tx['txid'] + self.nodes[1].sendrawtransaction(signed_raw_tx['hex']) self.sync_all() - self.nodes[1].generate(1) #mine a block + self.nodes[1].generate(1) # mine a block self.sync_all() - unspentTxs = self.nodes[0].listunspent() #zero value tx must be in listunspents output + unspent_txs = self.nodes[0].listunspent() # zero value tx must be in listunspents output found = False - for uTx in unspentTxs: - if uTx['txid'] == zeroValueTxid: + for uTx in unspent_txs: + if uTx['txid'] == zero_value_txid: found = True assert_equal(uTx['amount'], Decimal('0')) assert(found) - #do some -walletbroadcast tests + # do some -walletbroadcast tests self.stop_nodes() self.start_node(0, ["-walletbroadcast=0"]) self.start_node(1, ["-walletbroadcast=0"]) self.start_node(2, ["-walletbroadcast=0"]) - connect_nodes_bi(self.nodes,0,1) - connect_nodes_bi(self.nodes,1,2) - connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 1, 2) + connect_nodes_bi(self.nodes, 0, 2) self.sync_all([self.nodes[0:3]]) - txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) - txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) - self.nodes[1].generate(1) #mine a block, tx should not be in there + txid_not_broadcast = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) + tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) + self.nodes[1].generate(1) # mine a block, tx should not be in there self.sync_all([self.nodes[0:3]]) - assert_equal(self.nodes[2].getbalance(), node_2_bal) #should not be changed because tx was not broadcasted + assert_equal(self.nodes[2].getbalance(), node_2_bal) # should not be changed because tx was not broadcasted - #now broadcast from another node, mine a block, sync, and check the balance - self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex']) + # now broadcast from another node, mine a block, sync, and check the balance + self.nodes[1].sendrawtransaction(tx_obj_not_broadcast['hex']) self.nodes[1].generate(1) self.sync_all([self.nodes[0:3]]) node_2_bal += 2 - txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) + tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) assert_equal(self.nodes[2].getbalance(), node_2_bal) - #create another tx - txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) + # create another tx + self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) - #restart the nodes with -walletbroadcast=1 + # restart the nodes with -walletbroadcast=1 self.stop_nodes() self.start_node(0) self.start_node(1) self.start_node(2) - connect_nodes_bi(self.nodes,0,1) - connect_nodes_bi(self.nodes,1,2) - connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 1, 2) + connect_nodes_bi(self.nodes, 0, 2) sync_blocks(self.nodes[0:3]) self.nodes[0].generate(1) sync_blocks(self.nodes[0:3]) node_2_bal += 2 - #tx should be added to balance because after restarting the nodes tx should be broadcast + # tx should be added to balance because after restarting the nodes tx should be broadcast assert_equal(self.nodes[2].getbalance(), node_2_bal) - #send a tx with value in a string (PR#6380 +) - txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2") - txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-2')) + # send a tx with value in a string (PR#6380 +) + txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2") + tx_obj = self.nodes[0].gettransaction(txid) + assert_equal(tx_obj['amount'], Decimal('-2')) - txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001") - txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-0.0001')) + txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001") + tx_obj = self.nodes[0].gettransaction(txid) + assert_equal(tx_obj['amount'], Decimal('-0.0001')) - #check if JSON parser can handle scientific notation in strings - txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4") - txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-0.0001')) + # check if JSON parser can handle scientific notation in strings + txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4") + tx_obj = self.nodes[0].gettransaction(txid) + assert_equal(tx_obj['amount'], Decimal('-0.0001')) # This will raise an exception because the amount type is wrong assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4") @@ -322,8 +333,8 @@ class WalletTest(BitcoinTestFramework): # 4. Check that the unspents after import are not spendable assert_array_result(self.nodes[1].listunspent(), - {"address": address_to_import}, - {"spendable": False}) + {"address": address_to_import}, + {"spendable": False}) # 5. Import private key of the previously imported address on node1 priv_key = self.nodes[2].dumpprivkey(address_to_import) @@ -331,17 +342,17 @@ class WalletTest(BitcoinTestFramework): # 6. Check that the unspents are now spendable on node1 assert_array_result(self.nodes[1].listunspent(), - {"address": address_to_import}, - {"spendable": True}) + {"address": address_to_import}, + {"spendable": True}) # Mine a block from node0 to an address from node1 - cbAddr = self.nodes[1].getnewaddress() - blkHash = self.nodes[0].generatetoaddress(1, cbAddr)[0] - cbTxId = self.nodes[0].getblock(blkHash)['tx'][0] + coinbase_addr = self.nodes[1].getnewaddress() + block_hash = self.nodes[0].generatetoaddress(1, coinbase_addr)[0] + coinbase_txid = self.nodes[0].getblock(block_hash)['tx'][0] self.sync_all([self.nodes[0:3]]) # Check that the txid and balance is found by node1 - self.nodes[1].gettransaction(cbTxId) + self.nodes[1].gettransaction(coinbase_txid) # check if wallet or blockchain maintenance changes the balance self.sync_all([self.nodes[0:3]]) @@ -361,7 +372,7 @@ class WalletTest(BitcoinTestFramework): label = self.nodes[0].getaccount(addr) assert_equal(label, s) assert(s in self.nodes[0].listaccounts().keys()) - self.nodes[0].ensure_ascii = True # restore to default + self.nodes[0].ensure_ascii = True # restore to default # maintenance tests maintenance = [ @@ -377,9 +388,9 @@ class WalletTest(BitcoinTestFramework): self.log.info("check " + m) self.stop_nodes() # set lower ancestor limit for later - self.start_node(0, [m, "-deprecatedrpc=accounts", "-limitancestorcount="+str(chainlimit)]) - self.start_node(1, [m, "-deprecatedrpc=accounts", "-limitancestorcount="+str(chainlimit)]) - self.start_node(2, [m, "-deprecatedrpc=accounts", "-limitancestorcount="+str(chainlimit)]) + self.start_node(0, [m, "-deprecatedrpc=accounts", "-limitancestorcount=" + str(chainlimit)]) + self.start_node(1, [m, "-deprecatedrpc=accounts", "-limitancestorcount=" + str(chainlimit)]) + self.start_node(2, [m, "-deprecatedrpc=accounts", "-limitancestorcount=" + str(chainlimit)]) if m == '-reindex': # reindex will leave rpc warm up "early"; Wait for it to finish wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)]) @@ -400,7 +411,7 @@ class WalletTest(BitcoinTestFramework): self.nodes[0].generate(1) node0_balance = self.nodes[0].getbalance() # Split into two chains - rawtx = self.nodes[0].createrawtransaction([{"txid":singletxid, "vout":0}], {chain_addrs[0]:node0_balance/2-Decimal('0.01'), chain_addrs[1]:node0_balance/2-Decimal('0.01')}) + rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], {chain_addrs[0]: node0_balance / 2 - Decimal('0.01'), chain_addrs[1]: node0_balance / 2 - Decimal('0.01')}) signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) singletxid = self.nodes[0].sendrawtransaction(signedtx["hex"]) self.nodes[0].generate(1) @@ -411,10 +422,10 @@ class WalletTest(BitcoinTestFramework): # So we should be able to generate exactly chainlimit txs for each original output sending_addr = self.nodes[1].getnewaddress() txid_list = [] - for i in range(chainlimit*2): + for i in range(chainlimit * 2): txid_list.append(self.nodes[0].sendtoaddress(sending_addr, Decimal('0.0001'))) - assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit*2) - assert_equal(len(txid_list), chainlimit*2) + assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit * 2) + assert_equal(len(txid_list), chainlimit * 2) # Without walletrejectlongchains, we will still generate a txid # The tx will be stored in the wallet but not accepted to the mempool @@ -422,26 +433,26 @@ class WalletTest(BitcoinTestFramework): assert(extra_txid not in self.nodes[0].getrawmempool()) assert(extra_txid in [tx["txid"] for tx in self.nodes[0].listtransactions()]) self.nodes[0].abandontransaction(extra_txid) - total_txs = len(self.nodes[0].listtransactions("*",99999)) + total_txs = len(self.nodes[0].listtransactions("*", 99999)) # Try with walletrejectlongchains # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf self.stop_node(0) - self.start_node(0, extra_args=["-deprecatedrpc=accounts", "-walletrejectlongchains", "-limitancestorcount="+str(2*chainlimit)]) + self.start_node(0, extra_args=["-deprecatedrpc=accounts", "-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)]) # wait for loadmempool timeout = 10 - while (timeout > 0 and len(self.nodes[0].getrawmempool()) < chainlimit*2): + while (timeout > 0 and len(self.nodes[0].getrawmempool()) < chainlimit * 2): time.sleep(0.5) timeout -= 0.5 - assert_equal(len(self.nodes[0].getrawmempool()), chainlimit*2) + assert_equal(len(self.nodes[0].getrawmempool()), chainlimit * 2) node0_balance = self.nodes[0].getbalance() # With walletrejectlongchains we will not create the tx and store it in our wallet. assert_raises_rpc_error(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01')) # Verify nothing new in wallet - assert_equal(total_txs, len(self.nodes[0].listtransactions("*",99999))) + assert_equal(total_txs, len(self.nodes[0].listtransactions("*", 99999))) # Test getaddressinfo. Note that these addresses are taken from disablewallet.py assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy") diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index 6775d8b46d..baf933f079 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -46,10 +46,10 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): if self.call == Call.single: if self.data == Data.address: response = self.try_rpc(self.node.importaddress, self.address["address"], self.label, - self.rescan == Rescan.yes) + self.rescan == Rescan.yes) elif self.data == Data.pub: response = self.try_rpc(self.node.importpubkey, self.address["pubkey"], self.label, - self.rescan == Rescan.yes) + self.rescan == Rescan.yes) elif self.data == Data.priv: response = self.try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes) assert_equal(response, None) diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py index d0ec290f36..9cee9aa49a 100755 --- a/test/functional/wallet_importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -3,8 +3,13 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the importprunedfunds and removeprunedfunds RPCs.""" +from decimal import Decimal + from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error, +) class ImportPrunedFundsTest(BitcoinTestFramework): def set_test_params(self): @@ -24,18 +29,18 @@ class ImportPrunedFundsTest(BitcoinTestFramework): address2 = self.nodes[0].getnewaddress() # privkey address3 = self.nodes[0].getnewaddress() - address3_privkey = self.nodes[0].dumpprivkey(address3) # Using privkey + address3_privkey = self.nodes[0].dumpprivkey(address3) # Using privkey - #Check only one address + # Check only one address address_info = self.nodes[0].getaddressinfo(address1) assert_equal(address_info['ismine'], True) self.sync_all() - #Node 1 sync test - assert_equal(self.nodes[1].getblockcount(),101) + # Node 1 sync test + assert_equal(self.nodes[1].getblockcount(), 101) - #Address Test - before import + # Address Test - before import address_info = self.nodes[1].getaddressinfo(address1) assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) @@ -48,7 +53,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) - #Send funds to self + # Send funds to self txnid1 = self.nodes[0].sendtoaddress(address1, 0.1) self.nodes[0].generate(1) rawtxn1 = self.nodes[0].gettransaction(txnid1)['hex'] @@ -66,19 +71,19 @@ class ImportPrunedFundsTest(BitcoinTestFramework): self.sync_all() - #Import with no affiliated address + # Import with no affiliated address assert_raises_rpc_error(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1) balance1 = self.nodes[1].getbalance("", 0, True) assert_equal(balance1, Decimal(0)) - #Import with affiliated address with no rescan + # Import with affiliated address with no rescan self.nodes[1].importaddress(address2, "add2", False) self.nodes[1].importprunedfunds(rawtxn2, proof2) balance2 = self.nodes[1].getbalance("add2", 0, True) assert_equal(balance2, Decimal('0.05')) - #Import with private key with no rescan + # Import with private key with no rescan self.nodes[1].importprivkey(privkey=address3_privkey, label="add3", rescan=False) self.nodes[1].importprunedfunds(rawtxn3, proof3) balance3 = self.nodes[1].getbalance("add3", 0, False) @@ -86,7 +91,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): balance3 = self.nodes[1].getbalance("*", 0, True) assert_equal(balance3, Decimal('0.075')) - #Addresses Test - after import + # Addresses Test - after import address_info = self.nodes[1].getaddressinfo(address1) assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) @@ -97,7 +102,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], True) - #Remove transactions + # Remove transactions assert_raises_rpc_error(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1) balance1 = self.nodes[1].getbalance("*", 0, True) diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py index aba5d642bc..e0e20cc9a3 100755 --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -51,37 +51,37 @@ class ReceivedByTest(BitcoinTestFramework): {"address": empty_addr}, {"address": empty_addr, "label": "", "amount": 0, "confirmations": 0, "txids": []}) - #Test Address filtering - #Only on addr - expected = {"address":addr, "label":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]} + # Test Address filtering + # Only on addr + expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]} res = self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True, address_filter=addr) - assert_array_result(res, {"address":addr}, expected) + assert_array_result(res, {"address": addr}, expected) assert_equal(len(res), 1) - #Error on invalid address + # Error on invalid address assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, minconf=0, include_empty=True, include_watchonly=True, address_filter="bamboozling") - #Another address receive money + # Another address receive money res = self.nodes[1].listreceivedbyaddress(0, True, True) - assert_equal(len(res), 2) #Right now 2 entries + assert_equal(len(res), 2) # Right now 2 entries other_addr = self.nodes[1].getnewaddress() txid2 = self.nodes[0].sendtoaddress(other_addr, 0.1) self.nodes[0].generate(1) self.sync_all() - #Same test as above should still pass - expected = {"address":addr, "label":"", "amount":Decimal("0.1"), "confirmations":11, "txids":[txid,]} + # Same test as above should still pass + expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 11, "txids": [txid, ]} res = self.nodes[1].listreceivedbyaddress(0, True, True, addr) - assert_array_result(res, {"address":addr}, expected) + assert_array_result(res, {"address": addr}, expected) assert_equal(len(res), 1) - #Same test as above but with other_addr should still pass - expected = {"address":other_addr, "label":"", "amount":Decimal("0.1"), "confirmations":1, "txids":[txid2,]} + # Same test as above but with other_addr should still pass + expected = {"address": other_addr, "label": "", "amount": Decimal("0.1"), "confirmations": 1, "txids": [txid2, ]} res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr) - assert_array_result(res, {"address":other_addr}, expected) + assert_array_result(res, {"address": other_addr}, expected) assert_equal(len(res), 1) - #Should be two entries though without filter + # Should be two entries though without filter res = self.nodes[1].listreceivedbyaddress(0, True, True) - assert_equal(len(res), 3) #Became 3 entries + assert_equal(len(res), 3) # Became 3 entries - #Not on random addr - other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr + # Not on random addr + other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr) assert_equal(len(res), 0) @@ -112,8 +112,8 @@ class ReceivedByTest(BitcoinTestFramework): self.log.info("listreceivedbylabel + getreceivedbylabel Test") # set pre-state - addrArr = self.nodes[1].getnewaddress() - label = self.nodes[1].getaccount(addrArr) + address = self.nodes[1].getnewaddress() + label = self.nodes[1].getaccount(address) received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel() if r["label"] == label][0] balance_by_label = self.nodes[1].getreceivedbylabel(label) diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py index 930bfcd7b0..50a3313e2f 100755 --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -150,26 +150,26 @@ class ListSinceBlockTest (BitcoinTestFramework): # send from nodes[1] using utxo to nodes[0] change = '%.8f' % (float(utxo['amount']) - 1.0003) - recipientDict = { + recipient_dict = { self.nodes[0].getnewaddress(): 1, self.nodes[1].getnewaddress(): change, } - utxoDicts = [{ + utxo_dicts = [{ 'txid': utxo['txid'], 'vout': utxo['vout'], }] txid1 = self.nodes[1].sendrawtransaction( self.nodes[1].signrawtransactionwithwallet( - self.nodes[1].createrawtransaction(utxoDicts, recipientDict))['hex']) + self.nodes[1].createrawtransaction(utxo_dicts, recipient_dict))['hex']) # send from nodes[2] using utxo to nodes[3] - recipientDict2 = { + recipient_dict2 = { self.nodes[3].getnewaddress(): 1, self.nodes[2].getnewaddress(): change, } self.nodes[2].sendrawtransaction( self.nodes[2].signrawtransactionwithwallet( - self.nodes[2].createrawtransaction(utxoDicts, recipientDict2))['hex']) + self.nodes[2].createrawtransaction(utxo_dicts, recipient_dict2))['hex']) # generate on both sides lastblockhash = self.nodes[1].generate(3)[2] @@ -225,16 +225,16 @@ class ListSinceBlockTest (BitcoinTestFramework): utxos = self.nodes[2].listunspent() utxo = utxos[0] change = '%.8f' % (float(utxo['amount']) - 1.0003) - recipientDict = { + recipient_dict = { self.nodes[0].getnewaddress(): 1, self.nodes[2].getnewaddress(): change, } - utxoDicts = [{ + utxo_dicts = [{ 'txid': utxo['txid'], 'vout': utxo['vout'], }] signedtxres = self.nodes[2].signrawtransactionwithwallet( - self.nodes[2].createrawtransaction(utxoDicts, recipientDict)) + self.nodes[2].createrawtransaction(utxo_dicts, recipient_dict)) assert signedtxres['complete'] signedtx = signedtxres['hex'] diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py index 5466bbf18b..883942cc19 100755 --- a/test/functional/wallet_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -3,13 +3,20 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the listtransactions API.""" - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from test_framework.mininode import CTransaction, COIN +from decimal import Decimal from io import BytesIO -def txFromHex(hexstring): +from test_framework.mininode import CTransaction, COIN +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_array_result, + assert_equal, + bytes_to_hex_str, + hex_str_to_bytes, + sync_mempools, +) + +def tx_from_hex(hexstring): tx = CTransaction() f = BytesIO(hex_str_to_bytes(hexstring)) tx.deserialize(f) @@ -26,61 +33,61 @@ class ListTransactionsTest(BitcoinTestFramework): txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1) self.sync_all() assert_array_result(self.nodes[0].listtransactions(), - {"txid":txid}, - {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0}) + {"txid": txid}, + {"category": "send", "account": "", "amount": Decimal("-0.1"), "confirmations": 0}) assert_array_result(self.nodes[1].listtransactions(), - {"txid":txid}, - {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0}) + {"txid": txid}, + {"category": "receive", "account": "", "amount": Decimal("0.1"), "confirmations": 0}) # mine a block, confirmations should change: self.nodes[0].generate(1) self.sync_all() assert_array_result(self.nodes[0].listtransactions(), - {"txid":txid}, - {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1}) + {"txid": txid}, + {"category": "send", "account": "", "amount": Decimal("-0.1"), "confirmations": 1}) assert_array_result(self.nodes[1].listtransactions(), - {"txid":txid}, - {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1}) + {"txid": txid}, + {"category": "receive", "account": "", "amount": Decimal("0.1"), "confirmations": 1}) # send-to-self: txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) assert_array_result(self.nodes[0].listtransactions(), - {"txid":txid, "category":"send"}, - {"amount":Decimal("-0.2")}) + {"txid": txid, "category": "send"}, + {"amount": Decimal("-0.2")}) assert_array_result(self.nodes[0].listtransactions(), - {"txid":txid, "category":"receive"}, - {"amount":Decimal("0.2")}) + {"txid": txid, "category": "receive"}, + {"amount": Decimal("0.2")}) # sendmany from node1: twice to self, twice to node2: - send_to = { self.nodes[0].getnewaddress() : 0.11, - self.nodes[1].getnewaddress() : 0.22, - self.nodes[0].getaccountaddress("from1") : 0.33, - self.nodes[1].getaccountaddress("toself") : 0.44 } + send_to = {self.nodes[0].getnewaddress(): 0.11, + self.nodes[1].getnewaddress(): 0.22, + self.nodes[0].getaccountaddress("from1"): 0.33, + self.nodes[1].getaccountaddress("toself"): 0.44} txid = self.nodes[1].sendmany("", send_to) self.sync_all() assert_array_result(self.nodes[1].listtransactions(), - {"category":"send","amount":Decimal("-0.11")}, - {"txid":txid} ) + {"category": "send", "amount": Decimal("-0.11")}, + {"txid": txid}) assert_array_result(self.nodes[0].listtransactions(), - {"category":"receive","amount":Decimal("0.11")}, - {"txid":txid} ) + {"category": "receive", "amount": Decimal("0.11")}, + {"txid": txid}) assert_array_result(self.nodes[1].listtransactions(), - {"category":"send","amount":Decimal("-0.22")}, - {"txid":txid} ) + {"category": "send", "amount": Decimal("-0.22")}, + {"txid": txid}) assert_array_result(self.nodes[1].listtransactions(), - {"category":"receive","amount":Decimal("0.22")}, - {"txid":txid} ) + {"category": "receive", "amount": Decimal("0.22")}, + {"txid": txid}) assert_array_result(self.nodes[1].listtransactions(), - {"category":"send","amount":Decimal("-0.33")}, - {"txid":txid} ) + {"category": "send", "amount": Decimal("-0.33")}, + {"txid": txid}) assert_array_result(self.nodes[0].listtransactions(), - {"category":"receive","amount":Decimal("0.33")}, - {"txid":txid, "account" : "from1"} ) + {"category": "receive", "amount": Decimal("0.33")}, + {"txid": txid, "account": "from1"}) assert_array_result(self.nodes[1].listtransactions(), - {"category":"send","amount":Decimal("-0.44")}, - {"txid":txid, "account" : ""} ) + {"category": "send", "amount": Decimal("-0.44")}, + {"txid": txid, "account": ""}) assert_array_result(self.nodes[1].listtransactions(), - {"category":"receive","amount":Decimal("0.44")}, - {"txid":txid, "account" : "toself"} ) + {"category": "receive", "amount": Decimal("0.44")}, + {"txid": txid, "account": "toself"}) pubkey = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['pubkey'] multisig = self.nodes[1].createmultisig(1, [pubkey]) @@ -90,8 +97,8 @@ class ListTransactionsTest(BitcoinTestFramework): self.sync_all() assert(len(self.nodes[0].listtransactions("watchonly", 100, 0, False)) == 0) assert_array_result(self.nodes[0].listtransactions("watchonly", 100, 0, True), - {"category":"receive","amount":Decimal("0.1")}, - {"txid":txid, "account" : "watchonly"} ) + {"category": "receive", "amount": Decimal("0.1")}, + {"txid": txid, "account": "watchonly"}) self.run_rbf_opt_in_test() @@ -117,9 +124,9 @@ class ListTransactionsTest(BitcoinTestFramework): # 1. Chain a few transactions that don't opt-in. txid_1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) assert(not is_opt_in(self.nodes[0], txid_1)) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"}) + assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable": "no"}) sync_mempools(self.nodes) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"}) + assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable": "no"}) # Tx2 will build off txid_1, still not opting in to RBF. utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_1) @@ -129,7 +136,7 @@ class ListTransactionsTest(BitcoinTestFramework): assert_equal(utxo_to_use["safe"], False) # Create tx2 using createrawtransaction - inputs = [{"txid":utxo_to_use["txid"], "vout":utxo_to_use["vout"]}] + inputs = [{"txid": utxo_to_use["txid"], "vout": utxo_to_use["vout"]}] outputs = {self.nodes[0].getnewaddress(): 0.999} tx2 = self.nodes[1].createrawtransaction(inputs, outputs) tx2_signed = self.nodes[1].signrawtransactionwithwallet(tx2)["hex"] @@ -137,51 +144,51 @@ class ListTransactionsTest(BitcoinTestFramework): # ...and check the result assert(not is_opt_in(self.nodes[1], txid_2)) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"}) + assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_2}, {"bip125-replaceable": "no"}) sync_mempools(self.nodes) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"}) + assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable": "no"}) # Tx3 will opt-in to RBF utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_2) - inputs = [{"txid": txid_2, "vout":utxo_to_use["vout"]}] + inputs = [{"txid": txid_2, "vout": utxo_to_use["vout"]}] outputs = {self.nodes[1].getnewaddress(): 0.998} tx3 = self.nodes[0].createrawtransaction(inputs, outputs) - tx3_modified = txFromHex(tx3) + tx3_modified = tx_from_hex(tx3) tx3_modified.vin[0].nSequence = 0 tx3 = bytes_to_hex_str(tx3_modified.serialize()) tx3_signed = self.nodes[0].signrawtransactionwithwallet(tx3)['hex'] txid_3 = self.nodes[0].sendrawtransaction(tx3_signed) assert(is_opt_in(self.nodes[0], txid_3)) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"}) + assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_3}, {"bip125-replaceable": "yes"}) sync_mempools(self.nodes) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"}) + assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_3}, {"bip125-replaceable": "yes"}) # Tx4 will chain off tx3. Doesn't signal itself, but depends on one # that does. utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_3) - inputs = [{"txid": txid_3, "vout":utxo_to_use["vout"]}] + inputs = [{"txid": txid_3, "vout": utxo_to_use["vout"]}] outputs = {self.nodes[0].getnewaddress(): 0.997} tx4 = self.nodes[1].createrawtransaction(inputs, outputs) tx4_signed = self.nodes[1].signrawtransactionwithwallet(tx4)["hex"] txid_4 = self.nodes[1].sendrawtransaction(tx4_signed) assert(not is_opt_in(self.nodes[1], txid_4)) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"}) + assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "yes"}) sync_mempools(self.nodes) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"}) + assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "yes"}) # Replace tx3, and check that tx4 becomes unknown tx3_b = tx3_modified - tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee + tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee tx3_b = bytes_to_hex_str(tx3_b.serialize()) tx3_b_signed = self.nodes[0].signrawtransactionwithwallet(tx3_b)['hex'] txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, True) assert(is_opt_in(self.nodes[0], txid_3b)) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"}) + assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "unknown"}) sync_mempools(self.nodes) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"}) + assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "unknown"}) # Check gettransaction as well: for n in self.nodes[0:2]: @@ -197,7 +204,5 @@ class ListTransactionsTest(BitcoinTestFramework): assert_equal(self.nodes[0].gettransaction(txid_3b)["bip125-replaceable"], "no") assert_equal(self.nodes[0].gettransaction(txid_4)["bip125-replaceable"], "unknown") - if __name__ == '__main__': ListTransactionsTest().main() - diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py index aee84f7e90..b4e4cb1686 100755 --- a/test/functional/wallet_txn_clone.py +++ b/test/functional/wallet_txn_clone.py @@ -5,7 +5,12 @@ """Test the wallet accounts properly when there are cloned transactions with malleated scriptsigs.""" from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_equal, + connect_nodes, + disconnect_nodes, + sync_blocks, +) class TxnMallTest(BitcoinTestFramework): def set_test_params(self): @@ -26,9 +31,9 @@ class TxnMallTest(BitcoinTestFramework): def run_test(self): if self.options.segwit: - output_type="p2sh-segwit" + output_type = "p2sh-segwit" else: - output_type="legacy" + output_type = "legacy" # All nodes should start with 1,250 BTC: starting_balance = 1250 @@ -53,28 +58,27 @@ class TxnMallTest(BitcoinTestFramework): # Coins are sent to node1_address node1_address = self.nodes[1].getnewaddress("from0") - # Send tx1, and another transaction tx2 that won't be cloned + # Send tx1, and another transaction tx2 that won't be cloned txid1 = self.nodes[0].sendfrom("foo", node1_address, 40, 0) txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0) - # Construct a clone of tx1, to be malleated - rawtx1 = self.nodes[0].getrawtransaction(txid1,1) - clone_inputs = [{"txid":rawtx1["vin"][0]["txid"],"vout":rawtx1["vin"][0]["vout"]}] - clone_outputs = {rawtx1["vout"][0]["scriptPubKey"]["addresses"][0]:rawtx1["vout"][0]["value"], - rawtx1["vout"][1]["scriptPubKey"]["addresses"][0]:rawtx1["vout"][1]["value"]} + # Construct a clone of tx1, to be malleated + rawtx1 = self.nodes[0].getrawtransaction(txid1, 1) + clone_inputs = [{"txid": rawtx1["vin"][0]["txid"], "vout": rawtx1["vin"][0]["vout"]}] + clone_outputs = {rawtx1["vout"][0]["scriptPubKey"]["addresses"][0]: rawtx1["vout"][0]["value"], + rawtx1["vout"][1]["scriptPubKey"]["addresses"][0]: rawtx1["vout"][1]["value"]} clone_locktime = rawtx1["locktime"] clone_raw = self.nodes[0].createrawtransaction(clone_inputs, clone_outputs, clone_locktime) # createrawtransaction randomizes the order of its outputs, so swap them if necessary. # output 0 is at version+#inputs+input+sigstub+sequence+#outputs # 40 BTC serialized is 00286bee00000000 - pos0 = 2*(4+1+36+1+4+1) + pos0 = 2 * (4 + 1 + 36 + 1 + 4 + 1) hex40 = "00286bee00000000" - output_len = 16 + 2 + 2 * int("0x" + clone_raw[pos0 + 16 : pos0 + 16 + 2], 0) - if (rawtx1["vout"][0]["value"] == 40 and clone_raw[pos0 : pos0 + 16] != hex40 or - rawtx1["vout"][0]["value"] != 40 and clone_raw[pos0 : pos0 + 16] == hex40): - output0 = clone_raw[pos0 : pos0 + output_len] - output1 = clone_raw[pos0 + output_len : pos0 + 2 * output_len] + output_len = 16 + 2 + 2 * int("0x" + clone_raw[pos0 + 16:pos0 + 16 + 2], 0) + if (rawtx1["vout"][0]["value"] == 40 and clone_raw[pos0:pos0 + 16] != hex40 or rawtx1["vout"][0]["value"] != 40 and clone_raw[pos0:pos0 + 16] == hex40): + output0 = clone_raw[pos0:pos0 + output_len] + output1 = clone_raw[pos0 + output_len:pos0 + 2 * output_len] clone_raw = clone_raw[:pos0] + output1 + output0 + clone_raw[pos0 + 2 * output_len:] # Use a different signature hash type to sign. This creates an equivalent but malleated clone. @@ -142,7 +146,7 @@ class TxnMallTest(BitcoinTestFramework): # Check node0's total balance; should be same as before the clone, + 100 BTC for 2 matured, # less possible orphaned matured subsidy expected += 100 - if (self.options.mine_block): + if (self.options.mine_block): expected -= 50 assert_equal(self.nodes[0].getbalance(), expected) assert_equal(self.nodes[0].getbalance("*", 0), expected) @@ -153,16 +157,11 @@ class TxnMallTest(BitcoinTestFramework): # "bar" should have been debited by (possibly unconfirmed) tx2 assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"]) # "" should have starting balance, less funding txes, plus subsidies - assert_equal(self.nodes[0].getbalance("", 0), starting_balance - - 1219 - + fund_foo_tx["fee"] - - 29 - + fund_bar_tx["fee"] - + 100) + assert_equal(self.nodes[0].getbalance("", 0), + starting_balance - 1219 + fund_foo_tx["fee"] - 29 + fund_bar_tx["fee"] + 100) # Node1's "from0" account balance assert_equal(self.nodes[1].getbalance("from0", 0), -(tx1["amount"] + tx2["amount"])) if __name__ == '__main__': TxnMallTest().main() - diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py index d644a94c73..d8d91132d1 100755 --- a/test/functional/wallet_txn_doublespend.py +++ b/test/functional/wallet_txn_doublespend.py @@ -3,9 +3,16 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the wallet accounts properly when there is a double-spend conflict.""" +from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_equal, + connect_nodes, + disconnect_nodes, + find_output, + sync_blocks, +) class TxnMallTest(BitcoinTestFramework): def set_test_params(self): @@ -84,14 +91,14 @@ class TxnMallTest(BitcoinTestFramework): assert_equal(self.nodes[0].getbalance(), expected) # foo and bar accounts should be debited: - assert_equal(self.nodes[0].getbalance("foo", 0), 1219+tx1["amount"]+tx1["fee"]) - assert_equal(self.nodes[0].getbalance("bar", 0), 29+tx2["amount"]+tx2["fee"]) + assert_equal(self.nodes[0].getbalance("foo", 0), 1219 + tx1["amount"] + tx1["fee"]) + assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"]) if self.options.mine_block: assert_equal(tx1["confirmations"], 1) assert_equal(tx2["confirmations"], 1) # Node1's "from0" balance should be both transaction amounts: - assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"]+tx2["amount"])) + assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"] + tx2["amount"])) else: assert_equal(tx1["confirmations"], 0) assert_equal(tx2["confirmations"], 0) @@ -117,7 +124,7 @@ class TxnMallTest(BitcoinTestFramework): assert_equal(tx1["confirmations"], -2) assert_equal(tx2["confirmations"], -2) - # Node0's total balance should be starting balance, plus 100BTC for + # Node0's total balance should be starting balance, plus 100BTC for # two more matured blocks, minus 1240 for the double-spend, plus fees (which are # negative): expected = starting_balance + 100 - 1240 + fund_foo_tx["fee"] + fund_bar_tx["fee"] + doublespend_fee @@ -128,18 +135,11 @@ class TxnMallTest(BitcoinTestFramework): # fees (which are negative) assert_equal(self.nodes[0].getbalance("foo"), 1219) assert_equal(self.nodes[0].getbalance("bar"), 29) - assert_equal(self.nodes[0].getbalance(""), starting_balance - -1219 - - 29 - -1240 - + 100 - + fund_foo_tx["fee"] - + fund_bar_tx["fee"] - + doublespend_fee) + assert_equal(self.nodes[0].getbalance(""), + starting_balance - 1219 - 29 - 1240 + 100 + fund_foo_tx["fee"] + fund_bar_tx["fee"] + doublespend_fee) # Node1's "from0" account balance should be just the doublespend: assert_equal(self.nodes[1].getbalance("from0"), 1240) if __name__ == '__main__': TxnMallTest().main() - diff --git a/test/util/rpcauth-test.py b/test/util/rpcauth-test.py index dfbb5ea3a7..2456feb102 100755 --- a/test/util/rpcauth-test.py +++ b/test/util/rpcauth-test.py @@ -28,16 +28,15 @@ class TestRPCAuth(unittest.TestCase): self.assertGreaterEqual(len(self.rpcauth.generate_salt()), 16) def test_generate_password(self): - salt = self.rpcauth.generate_salt() - password, password_hmac = self.rpcauth.generate_password(salt) - + password = self.rpcauth.generate_password() expected_password = base64.urlsafe_b64encode( base64.urlsafe_b64decode(password)).decode('utf-8') self.assertEqual(expected_password, password) def test_check_password_hmac(self): salt = self.rpcauth.generate_salt() - password, password_hmac = self.rpcauth.generate_password(salt) + password = self.rpcauth.generate_password() + password_hmac = self.rpcauth.password_to_hmac(salt, password) m = hmac.new(bytearray(salt, 'utf-8'), bytearray(password, 'utf-8'), 'SHA256') |