diff options
59 files changed, 1116 insertions, 890 deletions
diff --git a/doc/bips.md b/doc/bips.md index e587275f0f..5b8eabd6df 100644 --- a/doc/bips.md +++ b/doc/bips.md @@ -1,4 +1,4 @@ -BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.13.0**): +BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.16.0**): * [`BIP 9`](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki): The changes allowing multiple soft-forks to be deployed in parallel have been implemented since **v0.12.1** ([PR #7575](https://github.com/bitcoin/bitcoin/pull/7575)) * [`BIP 11`](https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki): Multisig outputs are standard since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)). @@ -34,4 +34,5 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.13.0**): * [`BIP 147`](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki): NULLDUMMY softfork as of **v0.13.1** ([PR 8636](https://github.com/bitcoin/bitcoin/pull/8636) and [PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)). * [`BIP 152`](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki): Compact block transfer and related optimizations are used as of **v0.13.0** ([PR 8068](https://github.com/bitcoin/bitcoin/pull/8068)). * [`BIP 159`](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki): NODE_NETWORK_LIMITED service bit [signaling only] is supported as of **v0.16.0** ([PR 11740](https://github.com/bitcoin/bitcoin/pull/11740)). +* [`BIP 173`](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki): Bech32 addresses for native Segregated Witness outputs are supported as of **v0.16.0** ([PR 11167](https://github.com/bitcoin/bitcoin/pull/11167)). * [`BIP 176`](https://github.com/bitcoin/bips/blob/master/bip-0176.mediawiki): Bits Denomination [QT only] is supported as of **v0.16.0** ([PR 12035](https://github.com/bitcoin/bitcoin/pull/12035)). diff --git a/src/Makefile.am b/src/Makefile.am index 1bbb92bf42..521687eb45 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -113,6 +113,7 @@ BITCOIN_CORE_H = \ keystore.h \ dbwrapper.h \ limitedmap.h \ + logging.h \ memusage.h \ merkleblock.h \ miner.h \ @@ -364,6 +365,7 @@ libbitcoin_util_a_SOURCES = \ fs.cpp \ interfaces/handler.cpp \ interfaces/node.cpp \ + logging.cpp \ random.cpp \ rpc/protocol.cpp \ rpc/util.cpp \ diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 748c5b7887..3306dcf598 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -25,8 +25,6 @@ bench_bench_bitcoin_SOURCES = \ bench/verify_script.cpp \ bench/base58.cpp \ bench/lockedpool.cpp \ - bench/perf.cpp \ - bench/perf.h \ bench/prevector.cpp nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) diff --git a/src/addrdb.cpp b/src/addrdb.cpp index e4620e63c6..59305ff187 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -49,7 +49,8 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data // Serialize if (!SerializeDB(fileout, data)) return false; - FileCommit(fileout.Get()); + if (!FileCommit(fileout.Get())) + return error("%s: Failed to flush file %s", __func__, pathTmp.string()); fileout.fclose(); // replace existing file, if any, with new file diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp index 21329a5151..de3e57b04f 100644 --- a/src/bench/bench.cpp +++ b/src/bench/bench.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <bench/bench.h> -#include <bench/perf.h> #include <assert.h> #include <iostream> @@ -96,7 +95,6 @@ benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction f void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double scaling, const std::string& filter, bool is_list_only) { - perf_init(); if (!std::ratio_less_equal<benchmark::clock::period, std::micro>::value) { std::cerr << "WARNING: Clock precision is worse than microsecond - benchmarks may be less accurate!\n"; } @@ -126,8 +124,6 @@ void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double } printer.footer(); - - perf_fini(); } bool benchmark::State::UpdateTimer(const benchmark::time_point current_time) diff --git a/src/bench/perf.cpp b/src/bench/perf.cpp deleted file mode 100644 index f92d08c56e..0000000000 --- a/src/bench/perf.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2016-2017 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 <bench/perf.h> - -#if defined(__i386__) || defined(__x86_64__) - -/* These architectures support querying the cycle counter - * from user space, no need for any syscall overhead. - */ -void perf_init(void) { } -void perf_fini(void) { } - -#elif defined(__linux__) - -#include <unistd.h> -#include <sys/syscall.h> -#include <linux/perf_event.h> - -static int fd = -1; -static struct perf_event_attr attr; - -void perf_init(void) -{ - attr.type = PERF_TYPE_HARDWARE; - attr.config = PERF_COUNT_HW_CPU_CYCLES; - fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0); -} - -void perf_fini(void) -{ - if (fd != -1) { - close(fd); - } -} - -uint64_t perf_cpucycles(void) -{ - uint64_t result = 0; - if (fd == -1 || read(fd, &result, sizeof(result)) < (ssize_t)sizeof(result)) { - return 0; - } - return result; -} - -#else /* Unhandled platform */ - -void perf_init(void) { } -void perf_fini(void) { } -uint64_t perf_cpucycles(void) { return 0; } - -#endif diff --git a/src/bench/perf.h b/src/bench/perf.h deleted file mode 100644 index 73ea8b9647..0000000000 --- a/src/bench/perf.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 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. - -/** Functions for measurement of CPU cycles */ -#ifndef BITCOIN_BENCH_PERF_H -#define BITCOIN_BENCH_PERF_H - -#include <stdint.h> - -#if defined(__i386__) - -static inline uint64_t perf_cpucycles(void) -{ - uint64_t x; - __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); - return x; -} - -#elif defined(__x86_64__) - -static inline uint64_t perf_cpucycles(void) -{ - uint32_t hi, lo; - __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); - return ((uint64_t)lo)|(((uint64_t)hi)<<32); -} -#else - -uint64_t perf_cpucycles(void); - -#endif - -void perf_init(void); -void perf_fini(void); - -#endif // BITCOIN_BENCH_PERF_H diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 83d9719df2..0dc2dfbf7d 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -42,12 +42,9 @@ void WaitForShutdown() { - bool fShutdown = ShutdownRequested(); - // Tell the main threads to shutdown. - while (!fShutdown) + while (!ShutdownRequested()) { MilliSleep(200); - fShutdown = ShutdownRequested(); } Interrupt(); } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6067503b0b..121d95af90 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -75,7 +75,7 @@ public: CMainParams() { strNetworkID = "main"; consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP16Height = 173805; // 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 - April 1, 2012 + consensus.BIP16Exception = uint256S("0x00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22"); consensus.BIP34Height = 227931; consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 @@ -190,7 +190,7 @@ public: CTestNetParams() { strNetworkID = "test"; consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP16Height = 514; // 00000000040b4e986385315e14bee30ad876d8b47f748025b26683116d21aa65 + consensus.BIP16Exception = uint256S("0x00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105"); consensus.BIP34Height = 21111; consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 @@ -283,7 +283,7 @@ public: CRegTestParams() { strNetworkID = "regtest"; consensus.nSubsidyHalvingInterval = 150; - consensus.BIP16Height = 0; // always enforce P2SH BIP16 on regtest + consensus.BIP16Exception = uint256(); consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests) consensus.BIP34Hash = uint256(); consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests) diff --git a/src/chainparams.h b/src/chainparams.h index 6b1f813afb..dd029b9d5b 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -25,6 +25,12 @@ struct CCheckpointData { MapCheckpoints mapCheckpoints; }; +/** + * Holds various statistics on transactions within a chain. Used to estimate + * verification progress during chain sync. + * + * See also: CChainParams::TxData, GuessVerificationProgress. + */ struct ChainTxData { int64_t nTime; int64_t nTxCount; diff --git a/src/compat.h b/src/compat.h index 8a0f901304..920b3f776d 100644 --- a/src/compat.h +++ b/src/compat.h @@ -96,6 +96,12 @@ typedef int32_t ssize_t; size_t strnlen( const char *start, size_t max_len); #endif // HAVE_DECL_STRNLEN +#ifndef WIN32 +typedef void* sockopt_arg_type; +#else +typedef char* sockopt_arg_type; +#endif + bool static inline IsSelectableSocket(const SOCKET& s) { #ifdef WIN32 return true; diff --git a/src/compat/endian.h b/src/compat/endian.h index e5c7e50223..4f244c3930 100644 --- a/src/compat/endian.h +++ b/src/compat/endian.h @@ -19,6 +19,51 @@ #include <sys/endian.h> #endif +#ifndef HAVE_CONFIG_H +// While not technically a supported configuration, defaulting to defining these +// DECLs when we were compiled without autotools makes it easier for other build +// systems to build things like libbitcoinconsensus for strange targets. +#ifdef htobe16 +#define HAVE_DECL_HTOBE16 1 +#endif +#ifdef htole16 +#define HAVE_DECL_HTOLE16 1 +#endif +#ifdef be16toh +#define HAVE_DECL_BE16TOH 1 +#endif +#ifdef le16toh +#define HAVE_DECL_LE16TOH 1 +#endif + +#ifdef htobe32 +#define HAVE_DECL_HTOBE32 1 +#endif +#ifdef htole32 +#define HAVE_DECL_HTOLE32 1 +#endif +#ifdef be32toh +#define HAVE_DECL_BE32TOH 1 +#endif +#ifdef le32toh +#define HAVE_DECL_LE32TOH 1 +#endif + +#ifdef htobe64 +#define HAVE_DECL_HTOBE64 1 +#endif +#ifdef htole64 +#define HAVE_DECL_HTOLE64 1 +#endif +#ifdef be64toh +#define HAVE_DECL_BE64TOH 1 +#endif +#ifdef le64toh +#define HAVE_DECL_LE64TOH 1 +#endif + +#endif // HAVE_CONFIG_H + #if defined(WORDS_BIGENDIAN) #if HAVE_DECL_HTOBE16 == 0 diff --git a/src/consensus/params.h b/src/consensus/params.h index 4ef808c856..0559304fc2 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -49,8 +49,8 @@ struct BIP9Deployment { struct Params { uint256 hashGenesisBlock; int nSubsidyHalvingInterval; - /** Block height at which BIP16 becomes active */ - int BIP16Height; + /* Block hash that is excepted from BIP16 enforcement */ + uint256 BIP16Exception; /** Block height and hash at which BIP34 becomes active */ int BIP34Height; uint256 BIP34Hash; diff --git a/src/core_write.cpp b/src/core_write.cpp index 929498ff28..ee6737201b 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -34,7 +34,7 @@ std::string FormatScript(const CScript& script) while (it != script.end()) { CScript::const_iterator it2 = it; std::vector<unsigned char> vch; - if (script.GetOp2(it, op, &vch)) { + if (script.GetOp(it, op, vch)) { if (op == OP_0) { ret += "0 "; continue; diff --git a/src/init.cpp b/src/init.cpp index 8538630d7e..f403f90b08 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -684,7 +684,7 @@ void ThreadImport(std::vector<fs::path> vImportFiles) // scan for better chains in the block chain database, that are not yet connected in the active best chain CValidationState state; if (!ActivateBestChain(state, chainparams)) { - LogPrintf("Failed to connect best block\n"); + LogPrintf("Failed to connect best block (%s)\n", FormatStateMessage(state)); StartShutdown(); return; } diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 919748f942..e55cba3c65 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -216,9 +216,6 @@ class NodeImpl : public Node return result; } CFeeRate getDustRelayFee() override { return ::dustRelayFee; } - CFeeRate getFallbackFee() override { CHECK_WALLET(return CWallet::fallbackFee); } - CFeeRate getPayTxFee() override { CHECK_WALLET(return ::payTxFee); } - void setPayTxFee(CFeeRate rate) override { CHECK_WALLET(::payTxFee = rate); } UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override { JSONRPCRequest req; @@ -239,7 +236,7 @@ class NodeImpl : public Node { #ifdef ENABLE_WALLET std::vector<std::unique_ptr<Wallet>> wallets; - for (CWalletRef wallet : ::vpwallets) { + for (CWallet* wallet : GetWallets()) { wallets.emplace_back(MakeWallet(*wallet)); } return wallets; diff --git a/src/interfaces/node.h b/src/interfaces/node.h index f375af2f19..84e869100a 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -173,15 +173,6 @@ public: //! Get dust relay fee. virtual CFeeRate getDustRelayFee() = 0; - //! Get fallback fee. - virtual CFeeRate getFallbackFee() = 0; - - //! Get pay tx fee. - virtual CFeeRate getPayTxFee() = 0; - - //! Set pay tx fee. - virtual void setPayTxFee(CFeeRate rate) = 0; - //! Execute rpc command. virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0; diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index dfe3d5f711..9d4830d189 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -6,7 +6,7 @@ #define BITCOIN_INTERFACES_WALLET_H #include <amount.h> // For CAmount -#include <pubkey.h> // For CTxDestination (CKeyID and CScriptID) +#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation) #include <script/ismine.h> // For isminefilter, isminetype #include <script/standard.h> // For CTxDestination #include <support/allocators/secure.h> // For SecureString diff --git a/src/logging.cpp b/src/logging.cpp new file mode 100644 index 0000000000..e481582321 --- /dev/null +++ b/src/logging.cpp @@ -0,0 +1,283 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-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 <logging.h> +#include <util.h> +#include <utilstrencodings.h> + +#include <list> +#include <mutex> + +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: 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. + */ +static FILE* fileout = nullptr; +static std::mutex* mutexDebugLog = nullptr; +static std::list<std::string>* vMsgsBeforeOpenLog; + +static int FileWriteStr(const std::string &str, FILE *fp) +{ + return fwrite(str.data(), 1, str.size(), fp); +} + +static void DebugPrintInit() +{ + assert(mutexDebugLog == nullptr); + mutexDebugLog = new std::mutex(); + vMsgsBeforeOpenLog = new std::list<std::string>; +} + +fs::path GetDebugLogPath() +{ + fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + return AbsPathForConfigVal(logfile); +} + +bool OpenDebugLog() +{ + std::call_once(debugPrintInitFlag, &DebugPrintInit); + std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); + + assert(fileout == nullptr); + assert(vMsgsBeforeOpenLog); + fs::path pathDebug = GetDebugLogPath(); + + fileout = fsbridge::fopen(pathDebug, "a"); + if (!fileout) { + return false; + } + + setbuf(fileout, nullptr); // unbuffered + // dump buffered messages from before we opened the log + while (!vMsgsBeforeOpenLog->empty()) { + FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); + vMsgsBeforeOpenLog->pop_front(); + } + + delete vMsgsBeforeOpenLog; + vMsgsBeforeOpenLog = nullptr; + return true; +} + +struct CLogCategoryDesc +{ + uint32_t flag; + std::string category; +}; + +const CLogCategoryDesc LogCategories[] = +{ + {BCLog::NONE, "0"}, + {BCLog::NONE, "none"}, + {BCLog::NET, "net"}, + {BCLog::TOR, "tor"}, + {BCLog::MEMPOOL, "mempool"}, + {BCLog::HTTP, "http"}, + {BCLog::BENCH, "bench"}, + {BCLog::ZMQ, "zmq"}, + {BCLog::DB, "db"}, + {BCLog::RPC, "rpc"}, + {BCLog::ESTIMATEFEE, "estimatefee"}, + {BCLog::ADDRMAN, "addrman"}, + {BCLog::SELECTCOINS, "selectcoins"}, + {BCLog::REINDEX, "reindex"}, + {BCLog::CMPCTBLOCK, "cmpctblock"}, + {BCLog::RAND, "rand"}, + {BCLog::PRUNE, "prune"}, + {BCLog::PROXY, "proxy"}, + {BCLog::MEMPOOLREJ, "mempoolrej"}, + {BCLog::LIBEVENT, "libevent"}, + {BCLog::COINDB, "coindb"}, + {BCLog::QT, "qt"}, + {BCLog::LEVELDB, "leveldb"}, + {BCLog::ALL, "1"}, + {BCLog::ALL, "all"}, +}; + +bool GetLogCategory(uint32_t *f, const std::string *str) +{ + if (f && str) { + if (*str == "") { + *f = BCLog::ALL; + return true; + } + for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + if (LogCategories[i].category == *str) { + *f = LogCategories[i].flag; + return true; + } + } + } + return false; +} + +std::string ListLogCategories() +{ + std::string ret; + int outcount = 0; + for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + // Omit the special cases. + if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { + if (outcount != 0) ret += ", "; + ret += LogCategories[i].category; + outcount++; + } + } + return ret; +} + +std::vector<CLogCategoryActive> ListActiveLogCategories() +{ + std::vector<CLogCategoryActive> ret; + for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + // Omit the special cases. + if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { + CLogCategoryActive catActive; + catActive.category = LogCategories[i].category; + catActive.active = LogAcceptCategory(LogCategories[i].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 strStamped; + + if (!fLogTimestamps) + return str; + + if (*fStartedNewLine) { + int64_t nTimeMicros = GetTimeMicros(); + strStamped = FormatISO8601DateTime(nTimeMicros/1000000); + if (fLogTimeMicros) { + strStamped.pop_back(); + strStamped += strprintf(".%06dZ", nTimeMicros%1000000); + } + int64_t mocktime = GetMockTime(); + if (mocktime) { + strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")"; + } + strStamped += ' ' + str; + } else + strStamped = str; + + if (!str.empty() && str[str.size()-1] == '\n') + *fStartedNewLine = true; + else + *fStartedNewLine = false; + + return strStamped; +} + +int 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); + + if (fPrintToConsole) { + // 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); + + // buffer if we haven't opened the log yet + if (fileout == nullptr) { + assert(vMsgsBeforeOpenLog); + ret = strTimestamped.length(); + vMsgsBeforeOpenLog->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 + } + + ret = FileWriteStr(strTimestamped, fileout); + } + } + return ret; +} + +void ShrinkDebugFile() +{ + // Amount of debug.log to save at end when shrinking (must fit in memory) + constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; + // Scroll debug.log if it's getting too big + fs::path pathLog = GetDebugLogPath(); + FILE* file = fsbridge::fopen(pathLog, "r"); + + // Special files (e.g. device nodes) may not have a size. + size_t log_size = 0; + try { + log_size = fs::file_size(pathLog); + } catch (boost::filesystem::filesystem_error &) {} + + // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE + // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes + if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) + { + // Restart the file with some of the end + std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0); + fseek(file, -((long)vch.size()), SEEK_END); + int nBytes = fread(vch.data(), 1, vch.size(), file); + fclose(file); + + file = fsbridge::fopen(pathLog, "w"); + if (file) + { + fwrite(vch.data(), 1, nBytes, file); + fclose(file); + } + } + else if (file != nullptr) + fclose(file); +} diff --git a/src/logging.h b/src/logging.h new file mode 100644 index 0000000000..4053f75acf --- /dev/null +++ b/src/logging.h @@ -0,0 +1,125 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-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_LOGGING_H +#define BITCOIN_LOGGING_H + +#include <fs.h> +#include <tinyformat.h> + +#include <atomic> +#include <cstdint> +#include <string> +#include <vector> + +static const bool DEFAULT_LOGTIMEMICROS = false; +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 +{ + std::string category; + bool active; +}; + +namespace BCLog { + enum LogFlags : uint32_t { + NONE = 0, + NET = (1 << 0), + TOR = (1 << 1), + MEMPOOL = (1 << 2), + HTTP = (1 << 3), + BENCH = (1 << 4), + ZMQ = (1 << 5), + DB = (1 << 6), + RPC = (1 << 7), + ESTIMATEFEE = (1 << 8), + ADDRMAN = (1 << 9), + SELECTCOINS = (1 << 10), + REINDEX = (1 << 11), + CMPCTBLOCK = (1 << 12), + RAND = (1 << 13), + PRUNE = (1 << 14), + PROXY = (1 << 15), + MEMPOOLREJ = (1 << 16), + LIBEVENT = (1 << 17), + COINDB = (1 << 18), + QT = (1 << 19), + LEVELDB = (1 << 20), + ALL = ~(uint32_t)0, + }; +} +/** Return true if log accepts specified category */ +static inline bool LogAcceptCategory(uint32_t category) +{ + return (logCategories.load(std::memory_order_relaxed) & category) != 0; +} + +/** Returns a string with the log categories. */ +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); + +/** Get format string from VA_ARGS for error reporting */ +template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; } + +static inline void MarkUsed() {} +template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args) +{ + (void)t; + MarkUsed(args...); +} + +// Be conservative when using LogPrintf/error or other things which +// unconditionally log to debug.log! It should not be the case that an inbound +// peer can fill up a user's disk with debug.log entries. + +#ifdef USE_COVERAGE +#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0) +#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) +#else +#define LogPrintf(...) do { \ + if (fPrintToConsole || fPrintToDebugLog) { \ + std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ + try { \ + _log_msg_ = tfm::format(__VA_ARGS__); \ + } catch (tinyformat::format_error &fmterr) { \ + /* 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_); \ + } \ +} while(0) + +#define LogPrint(category, ...) do { \ + if (LogAcceptCategory((category))) { \ + LogPrintf(__VA_ARGS__); \ + } \ +} while(0) +#endif + +fs::path GetDebugLogPath(); +bool OpenDebugLog(); +void ShrinkDebugFile(); + +#endif // BITCOIN_LOGGING_H diff --git a/src/net.cpp b/src/net.cpp index 356a66563f..cd076c1ce2 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1923,23 +1923,25 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() for (const std::string& strAddNode : lAddresses) { CService service(LookupNumeric(strAddNode.c_str(), Params().GetDefaultPort())); + AddedNodeInfo addedNode{strAddNode, CService(), false, false}; if (service.IsValid()) { // strAddNode is an IP:port auto it = mapConnected.find(service); if (it != mapConnected.end()) { - ret.push_back(AddedNodeInfo{strAddNode, service, true, it->second}); - } else { - ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false}); + addedNode.resolvedAddress = service; + addedNode.fConnected = true; + addedNode.fInbound = it->second; } } else { // strAddNode is a name auto it = mapConnectedByName.find(strAddNode); if (it != mapConnectedByName.end()) { - ret.push_back(AddedNodeInfo{strAddNode, it->second.second, true, it->second.first}); - } else { - ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false}); + addedNode.resolvedAddress = it->second.second; + addedNode.fConnected = true; + addedNode.fInbound = it->second.first; } } + ret.emplace_back(std::move(addedNode)); } return ret; @@ -2088,23 +2090,16 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b LogPrintf("%s\n", strError); return false; } -#ifndef WIN32 + // Allow binding if the port is still in TIME_WAIT state after // the program was closed and restarted. - setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); -#else - setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int)); -#endif + setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)); // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option // and enable it by default or not. Try to enable it, if possible. if (addrBind.IsIPv6()) { #ifdef IPV6_V6ONLY -#ifdef WIN32 - setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int)); -#else - setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int)); -#endif + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)); #endif #ifdef WIN32 int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index dbdae705de..ee4e9e61bc 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1100,8 +1100,10 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus } } // release cs_main before calling ActivateBestChain if (need_activate_chain) { - CValidationState dummy; - ActivateBestChain(dummy, Params(), a_recent_block); + CValidationState state; + if (!ActivateBestChain(state, Params(), a_recent_block)) { + LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); + } } LOCK(cs_main); @@ -1992,8 +1994,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr LOCK(cs_most_recent_block); a_recent_block = most_recent_block; } - CValidationState dummy; - ActivateBestChain(dummy, Params(), a_recent_block); + CValidationState state; + if (!ActivateBestChain(state, Params(), a_recent_block)) { + LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); + } } LOCK(cs_main); diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 4f231d73c8..18d5948f85 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -14,7 +14,7 @@ static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; // 0xFD + sha256("bitcoin")[0:5] static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 }; -void CNetAddr::Init() +CNetAddr::CNetAddr() { memset(ip, 0, sizeof(ip)); scopeId = 0; @@ -67,11 +67,6 @@ bool CNetAddr::SetSpecial(const std::string &strName) return false; } -CNetAddr::CNetAddr() -{ - Init(); -} - CNetAddr::CNetAddr(const struct in_addr& ipv4Addr) { SetRaw(NET_IPV4, (const uint8_t*)&ipv4Addr); @@ -290,11 +285,6 @@ bool operator==(const CNetAddr& a, const CNetAddr& b) return (memcmp(a.ip, b.ip, 16) == 0); } -bool operator!=(const CNetAddr& a, const CNetAddr& b) -{ - return (memcmp(a.ip, b.ip, 16) != 0); -} - bool operator<(const CNetAddr& a, const CNetAddr& b) { return (memcmp(a.ip, b.ip, 16) < 0); @@ -469,14 +459,8 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const } } -void CService::Init() +CService::CService() : port(0) { - port = 0; -} - -CService::CService() -{ - Init(); } CService::CService(const CNetAddr& cip, unsigned short portIn) : CNetAddr(cip), port(portIn) @@ -525,11 +509,6 @@ bool operator==(const CService& a, const CService& b) return static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port == b.port; } -bool operator!=(const CService& a, const CService& b) -{ - return static_cast<CNetAddr>(a) != static_cast<CNetAddr>(b) || a.port != b.port; -} - bool operator<(const CService& a, const CService& b) { return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) || (static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port < b.port); @@ -663,16 +642,16 @@ bool CSubNet::Match(const CNetAddr &addr) const static inline int NetmaskBits(uint8_t x) { switch(x) { - case 0x00: return 0; break; - case 0x80: return 1; break; - case 0xc0: return 2; break; - case 0xe0: return 3; break; - case 0xf0: return 4; break; - case 0xf8: return 5; break; - case 0xfc: return 6; break; - case 0xfe: return 7; break; - case 0xff: return 8; break; - default: return -1; break; + case 0x00: return 0; + case 0x80: return 1; + case 0xc0: return 2; + case 0xe0: return 3; + case 0xf0: return 4; + case 0xf8: return 5; + case 0xfc: return 6; + case 0xfe: return 7; + case 0xff: return 8; + default: return -1; } } @@ -724,11 +703,6 @@ bool operator==(const CSubNet& a, const CSubNet& b) return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16); } -bool operator!=(const CSubNet& a, const CSubNet& b) -{ - return !(a==b); -} - bool operator<(const CSubNet& a, const CSubNet& b) { return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0)); diff --git a/src/netaddress.h b/src/netaddress.h index 38f8709257..f8f2ab99ff 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -38,15 +38,16 @@ class CNetAddr public: CNetAddr(); explicit CNetAddr(const struct in_addr& ipv4Addr); - void Init(); void SetIP(const CNetAddr& ip); + private: /** * Set raw IPv4 or IPv6 address (in network byte order) * @note Only NET_IPV4 and NET_IPV6 are allowed for network. */ void SetRaw(Network network, const uint8_t *data); + public: /** * Transform an arbitrary string into a non-routable ipv6 address. * Useful for mapping resolved addresses back to their source. @@ -87,7 +88,7 @@ class CNetAddr bool GetIn6Addr(struct in6_addr* pipv6Addr) const; friend bool operator==(const CNetAddr& a, const CNetAddr& b); - friend bool operator!=(const CNetAddr& a, const CNetAddr& b); + friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); } friend bool operator<(const CNetAddr& a, const CNetAddr& b); ADD_SERIALIZE_METHODS; @@ -124,7 +125,7 @@ class CSubNet bool IsValid() const; friend bool operator==(const CSubNet& a, const CSubNet& b); - friend bool operator!=(const CSubNet& a, const CSubNet& b); + friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); } friend bool operator<(const CSubNet& a, const CSubNet& b); ADD_SERIALIZE_METHODS; @@ -148,12 +149,11 @@ class CService : public CNetAddr CService(const CNetAddr& ip, unsigned short port); CService(const struct in_addr& ipv4Addr, unsigned short port); explicit CService(const struct sockaddr_in& addr); - void Init(); unsigned short GetPort() const; bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const; bool SetSockAddr(const struct sockaddr* paddr); friend bool operator==(const CService& a, const CService& b); - friend bool operator!=(const CService& a, const CService& b); + friend bool operator!=(const CService& a, const CService& b) { return !(a == b); } friend bool operator<(const CService& a, const CService& b); std::vector<unsigned char> GetKey() const; std::string ToString() const; diff --git a/src/netbase.cpp b/src/netbase.cpp index 5d3d2f25c8..57835b5427 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -513,11 +513,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i return false; } socklen_t nRetSize = sizeof(nRet); -#ifdef WIN32 - if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR) -#else - if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR) -#endif + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&nRet, &nRetSize) == SOCKET_ERROR) { LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); return false; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 599c3c0985..57fe4552a1 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -403,6 +403,10 @@ void BitcoinApplication::startThread() void BitcoinApplication::parameterSetup() { + // Default printtoconsole to false for the GUI. GUI programs should not + // print to the console unnecessarily. + gArgs.SoftSetBoolArg("-printtoconsole", false); + m_node.initLogging(); m_node.initParameterInteraction(); } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bfa8844a09..aed5374a7d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -968,6 +968,11 @@ void BitcoinGUI::changeEvent(QEvent *e) QTimer::singleShot(0, this, SLOT(hide())); e->ignore(); } + else if((wsevt->oldState() & Qt::WindowMinimized) && !isMinimized()) + { + QTimer::singleShot(0, this, SLOT(show())); + e->ignore(); + } } } #endif diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index dcc834c352..56d2d38194 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -180,9 +180,9 @@ void TestGUI() TransactionView transactionView(platformStyle.get()); auto node = interfaces::MakeNode(); OptionsModel optionsModel(*node); - vpwallets.insert(vpwallets.begin(), &wallet); - WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &optionsModel); - vpwallets.erase(vpwallets.begin()); + AddWallet(&wallet); + WalletModel walletModel(std::move(node->getWallets().back()), *node, platformStyle.get(), &optionsModel); + RemoveWallet(&wallet); sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 9a7c4b8e6d..b4bb78e689 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -116,7 +116,7 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen } unsigned int nExtraNonce = 0; UniValue blockHashes(UniValue::VARR); - while (nHeight < nHeightEnd) + while (nHeight < nHeightEnd && !ShutdownRequested()) { std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript)); if (!pblocktemplate.get()) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 49e865a64a..6754407dbd 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -69,7 +69,7 @@ UniValue validateaddress(const JSONRPCRequest& request) { #ifdef ENABLE_WALLET - if (!::vpwallets.empty() && IsDeprecatedRPCEnabled("validateaddress")) { + if (HasWallets() && IsDeprecatedRPCEnabled("validateaddress")) { ret.pushKVs(getaddressinfo(request)); } #endif diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 338e07e24e..e0d193fa38 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -250,6 +250,34 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) { return true; } +int FindAndDelete(CScript& script, const CScript& b) +{ + int nFound = 0; + if (b.empty()) + return nFound; + CScript result; + CScript::const_iterator pc = script.begin(), pc2 = script.begin(), end = script.end(); + opcodetype opcode; + do + { + result.insert(result.end(), pc2, pc); + while (static_cast<size_t>(end - pc) >= b.size() && std::equal(b.begin(), b.end(), pc)) + { + pc = pc + b.size(); + ++nFound; + } + pc2 = pc; + } + while (script.GetOp(pc, opcode)); + + if (nFound > 0) { + result.insert(result.end(), pc2, end); + script = std::move(result); + } + + return nFound; +} + bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror) { static const CScriptNum bnZero(0); @@ -891,7 +919,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& // Drop the signature in pre-segwit scripts but not segwit scripts if (sigversion == SigVersion::BASE) { - scriptCode.FindAndDelete(CScript(vchSig)); + FindAndDelete(scriptCode, CScript(vchSig)); } if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { @@ -955,7 +983,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& { valtype& vchSig = stacktop(-isig-k); if (sigversion == SigVersion::BASE) { - scriptCode.FindAndDelete(CScript(vchSig)); + FindAndDelete(scriptCode, CScript(vchSig)); } } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 601a4a866d..50c747900a 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -189,4 +189,6 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags); +int FindAndDelete(CScript& script, const CScript& b); + #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/script.cpp b/src/script/script.cpp index 65e5405ebd..7f25d915a8 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -280,3 +280,55 @@ bool CScript::HasValidOps() const } return true; } + +bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) +{ + opcodeRet = OP_INVALIDOPCODE; + if (pvchRet) + pvchRet->clear(); + if (pc >= end) + return false; + + // Read instruction + if (end - pc < 1) + return false; + unsigned int opcode = *pc++; + + // Immediate operand + if (opcode <= OP_PUSHDATA4) + { + unsigned int nSize = 0; + if (opcode < OP_PUSHDATA1) + { + nSize = opcode; + } + else if (opcode == OP_PUSHDATA1) + { + if (end - pc < 1) + return false; + nSize = *pc++; + } + else if (opcode == OP_PUSHDATA2) + { + if (end - pc < 2) + return false; + nSize = ReadLE16(&pc[0]); + pc += 2; + } + else if (opcode == OP_PUSHDATA4) + { + if (end - pc < 4) + return false; + nSize = ReadLE32(&pc[0]); + pc += 4; + } + if (end - pc < 0 || (unsigned int)(end - pc) < nSize) + return false; + if (pvchRet) + pvchRet->assign(pc, pc + nSize); + pc += nSize; + } + + opcodeRet = static_cast<opcodetype>(opcode); + return true; +} diff --git a/src/script/script.h b/src/script/script.h index 59ceff247c..d8b7c06013 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -385,6 +385,8 @@ private: */ typedef prevector<28, unsigned char> CScriptBase; +bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet); + /** Serialized script, used inside transaction inputs and outputs */ class CScript : public CScriptBase { @@ -493,84 +495,16 @@ public: } - bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) - { - // Wrapper so it can be called with either iterator or const_iterator - const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, &vchRet); - pc = begin() + (pc2 - begin()); - return fRet; - } - - bool GetOp(iterator& pc, opcodetype& opcodeRet) - { - const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, nullptr); - pc = begin() + (pc2 - begin()); - return fRet; - } - bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) const { - return GetOp2(pc, opcodeRet, &vchRet); + return GetScriptOp(pc, end(), opcodeRet, &vchRet); } bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const { - return GetOp2(pc, opcodeRet, nullptr); + return GetScriptOp(pc, end(), opcodeRet, nullptr); } - bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) const - { - opcodeRet = OP_INVALIDOPCODE; - if (pvchRet) - pvchRet->clear(); - if (pc >= end()) - return false; - - // Read instruction - if (end() - pc < 1) - return false; - unsigned int opcode = *pc++; - - // Immediate operand - if (opcode <= OP_PUSHDATA4) - { - unsigned int nSize = 0; - if (opcode < OP_PUSHDATA1) - { - nSize = opcode; - } - else if (opcode == OP_PUSHDATA1) - { - if (end() - pc < 1) - return false; - nSize = *pc++; - } - else if (opcode == OP_PUSHDATA2) - { - if (end() - pc < 2) - return false; - nSize = ReadLE16(&pc[0]); - pc += 2; - } - else if (opcode == OP_PUSHDATA4) - { - if (end() - pc < 4) - return false; - nSize = ReadLE32(&pc[0]); - pc += 4; - } - if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize) - return false; - if (pvchRet) - pvchRet->assign(pc, pc + nSize); - pc += nSize; - } - - opcodeRet = static_cast<opcodetype>(opcode); - return true; - } /** Encode/decode small integers: */ static int DecodeOP_N(opcodetype opcode) @@ -588,34 +522,6 @@ public: return (opcodetype)(OP_1+n-1); } - int FindAndDelete(const CScript& b) - { - int nFound = 0; - if (b.empty()) - return nFound; - CScript result; - iterator pc = begin(), pc2 = begin(); - opcodetype opcode; - do - { - result.insert(result.end(), pc2, pc); - while (static_cast<size_t>(end() - pc) >= b.size() && std::equal(b.begin(), b.end(), pc)) - { - pc = pc + b.size(); - ++nFound; - } - pc2 = pc; - } - while (GetOp(pc, opcode)); - - if (nFound > 0) { - result.insert(result.end(), pc2, end()); - *this = result; - } - - return nFound; - } - /** * Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs * as 20 sigops. With pay-to-script-hash, that changed: diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 242e1fb7ff..da591547d7 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -247,14 +247,14 @@ BOOST_AUTO_TEST_CASE(rpc_ban) ar = r.get_array(); BOOST_CHECK_EQUAL(ar.size(), 0U); - BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 1607731200 true"))); + BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 9907731200 true"))); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); adr = find_value(o1, "address"); UniValue banned_until = find_value(o1, "banned_until"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); - BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check + BOOST_CHECK_EQUAL(banned_until.get_int64(), 9907731200); // absolute time check BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned"))); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index a06b573b37..068f1e66f4 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1349,43 +1349,43 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete) s = CScript() << OP_1 << OP_2; d = CScript(); // delete nothing should be a no-op expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); s = CScript() << OP_1 << OP_2 << OP_3; d = CScript() << OP_2; expect = CScript() << OP_1 << OP_3; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = CScript() << OP_3 << OP_1 << OP_3 << OP_3 << OP_4 << OP_3; d = CScript() << OP_3; expect = CScript() << OP_1 << OP_4; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 4); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 4); BOOST_CHECK(s == expect); s = ScriptFromHex("0302ff03"); // PUSH 0x02ff03 onto stack d = ScriptFromHex("0302ff03"); expect = CScript(); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = ScriptFromHex("0302ff030302ff03"); // PUSH 0x2ff03 PUSH 0x2ff03 d = ScriptFromHex("0302ff03"); expect = CScript(); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2); BOOST_CHECK(s == expect); s = ScriptFromHex("0302ff030302ff03"); d = ScriptFromHex("02"); expect = s; // FindAndDelete matches entire opcodes - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); s = ScriptFromHex("0302ff030302ff03"); d = ScriptFromHex("ff"); expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); // This is an odd edge case: strip of the push-three-bytes @@ -1393,44 +1393,44 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete) s = ScriptFromHex("0302ff030302ff03"); d = ScriptFromHex("03"); expect = CScript() << ParseHex("ff03") << ParseHex("ff03"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2); BOOST_CHECK(s == expect); // Byte sequence that spans multiple opcodes: s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY d = ScriptFromHex("feed51"); expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); // doesn't match 'inside' opcodes + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); // doesn't match 'inside' opcodes BOOST_CHECK(s == expect); s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY d = ScriptFromHex("02feed51"); expect = ScriptFromHex("69"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = ScriptFromHex("516902feed5169"); d = ScriptFromHex("feed51"); expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); s = ScriptFromHex("516902feed5169"); d = ScriptFromHex("02feed51"); expect = ScriptFromHex("516969"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = CScript() << OP_0 << OP_0 << OP_1 << OP_1; d = CScript() << OP_0 << OP_1; expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = CScript() << OP_0 << OP_0 << OP_1 << OP_0 << OP_1 << OP_1; d = CScript() << OP_0 << OP_1; expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2); BOOST_CHECK(s == expect); // Another weird edge case: @@ -1438,13 +1438,13 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete) s = ScriptFromHex("0003feed"); d = ScriptFromHex("03feed"); // ... can remove the invalid push expect = ScriptFromHex("00"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = ScriptFromHex("0003feed"); d = ScriptFromHex("00"); expect = ScriptFromHex("03feed"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); } diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index a2bd8998b1..6b8856ef47 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -35,7 +35,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un // In case concatenating two scripts ends up with two codeseparators, // or an extra one at the end, this prevents all those possible incompatibilities. - scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + FindAndDelete(scriptCode, CScript(OP_CODESEPARATOR)); // Blank out other inputs' signatures for (unsigned int i = 0; i < txTmp.vin.size(); i++) diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index b72df1604f..e9873f4526 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -85,7 +85,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha { CValidationState state; if (!ActivateBestChain(state, chainparams)) { - throw std::runtime_error("ActivateBestChain failed."); + throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state))); } } nScriptCheckThreads = 3; diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 344113b60c..1c3acfb1a5 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -572,7 +572,7 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); // check setting the network to test (and thus making - // [test] regtest=1 potentially relevent) doesn't break things + // [test] regtest=1 potentially relevant) doesn't break things test_args.SelectConfigNetwork("test"); test_args.ParseParameters(0, (char**)argv_testnet); diff --git a/src/util.cpp b/src/util.cpp index 6b0bffa35a..9a3067259f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -84,21 +84,11 @@ const int64_t nStartupTime = GetTime(); const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf"; const char * const BITCOIN_PID_FILENAME = "bitcoind.pid"; -const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; ArgsManager gArgs; -bool fPrintToConsole = false; -bool fPrintToDebugLog = true; -bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; -bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; -bool fLogIPs = DEFAULT_LOGIPS; -std::atomic<bool> fReopenDebugLog(false); CTranslationInterface translationInterface; -/** Log categories bitfield. */ -std::atomic<uint32_t> logCategories(0); - /** Init OpenSSL library multithreading support */ static std::unique_ptr<CCriticalSection[]> ppmutexOpenSSL; void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS @@ -147,231 +137,6 @@ public: } instance_of_cinit; -/** - * 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: 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. - */ -static FILE* fileout = nullptr; -static std::mutex* mutexDebugLog = nullptr; -static std::list<std::string>* vMsgsBeforeOpenLog; - -static int FileWriteStr(const std::string &str, FILE *fp) -{ - return fwrite(str.data(), 1, str.size(), fp); -} - -static void DebugPrintInit() -{ - assert(mutexDebugLog == nullptr); - mutexDebugLog = new std::mutex(); - vMsgsBeforeOpenLog = new std::list<std::string>; -} - -fs::path GetDebugLogPath() -{ - fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); - return AbsPathForConfigVal(logfile); -} - -bool OpenDebugLog() -{ - std::call_once(debugPrintInitFlag, &DebugPrintInit); - std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); - - assert(fileout == nullptr); - assert(vMsgsBeforeOpenLog); - fs::path pathDebug = GetDebugLogPath(); - - fileout = fsbridge::fopen(pathDebug, "a"); - if (!fileout) { - return false; - } - - setbuf(fileout, nullptr); // unbuffered - // dump buffered messages from before we opened the log - while (!vMsgsBeforeOpenLog->empty()) { - FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); - vMsgsBeforeOpenLog->pop_front(); - } - - delete vMsgsBeforeOpenLog; - vMsgsBeforeOpenLog = nullptr; - return true; -} - -struct CLogCategoryDesc -{ - uint32_t flag; - std::string category; -}; - -const CLogCategoryDesc LogCategories[] = -{ - {BCLog::NONE, "0"}, - {BCLog::NONE, "none"}, - {BCLog::NET, "net"}, - {BCLog::TOR, "tor"}, - {BCLog::MEMPOOL, "mempool"}, - {BCLog::HTTP, "http"}, - {BCLog::BENCH, "bench"}, - {BCLog::ZMQ, "zmq"}, - {BCLog::DB, "db"}, - {BCLog::RPC, "rpc"}, - {BCLog::ESTIMATEFEE, "estimatefee"}, - {BCLog::ADDRMAN, "addrman"}, - {BCLog::SELECTCOINS, "selectcoins"}, - {BCLog::REINDEX, "reindex"}, - {BCLog::CMPCTBLOCK, "cmpctblock"}, - {BCLog::RAND, "rand"}, - {BCLog::PRUNE, "prune"}, - {BCLog::PROXY, "proxy"}, - {BCLog::MEMPOOLREJ, "mempoolrej"}, - {BCLog::LIBEVENT, "libevent"}, - {BCLog::COINDB, "coindb"}, - {BCLog::QT, "qt"}, - {BCLog::LEVELDB, "leveldb"}, - {BCLog::ALL, "1"}, - {BCLog::ALL, "all"}, -}; - -bool GetLogCategory(uint32_t *f, const std::string *str) -{ - if (f && str) { - if (*str == "") { - *f = BCLog::ALL; - return true; - } - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { - if (LogCategories[i].category == *str) { - *f = LogCategories[i].flag; - return true; - } - } - } - return false; -} - -std::string ListLogCategories() -{ - std::string ret; - int outcount = 0; - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { - // Omit the special cases. - if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { - if (outcount != 0) ret += ", "; - ret += LogCategories[i].category; - outcount++; - } - } - return ret; -} - -std::vector<CLogCategoryActive> ListActiveLogCategories() -{ - std::vector<CLogCategoryActive> ret; - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { - // Omit the special cases. - if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { - CLogCategoryActive catActive; - catActive.category = LogCategories[i].category; - catActive.active = LogAcceptCategory(LogCategories[i].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 strStamped; - - if (!fLogTimestamps) - return str; - - if (*fStartedNewLine) { - int64_t nTimeMicros = GetTimeMicros(); - strStamped = FormatISO8601DateTime(nTimeMicros/1000000); - if (fLogTimeMicros) { - strStamped.pop_back(); - strStamped += strprintf(".%06dZ", nTimeMicros%1000000); - } - int64_t mocktime = GetMockTime(); - if (mocktime) { - strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")"; - } - strStamped += ' ' + str; - } else - strStamped = str; - - if (!str.empty() && str[str.size()-1] == '\n') - *fStartedNewLine = true; - else - *fStartedNewLine = false; - - return strStamped; -} - -int 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); - - if (fPrintToConsole) { - // 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); - - // buffer if we haven't opened the log yet - if (fileout == nullptr) { - assert(vMsgsBeforeOpenLog); - ret = strTimestamped.length(); - vMsgsBeforeOpenLog->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 - } - - ret = FileWriteStr(strTimestamped, fileout); - } - } - return ret; -} - /** A map that contains all the currently held directory locks. After * successful locking, these will be held here until the global destructor * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks @@ -1023,21 +788,37 @@ bool TryCreateDirectories(const fs::path& p) return false; } -void FileCommit(FILE *file) +bool FileCommit(FILE *file) { - fflush(file); // harmless if redundantly called + if (fflush(file) != 0) { // harmless if redundantly called + LogPrintf("%s: fflush failed: %d\n", __func__, errno); + return false; + } #ifdef WIN32 HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); - FlushFileBuffers(hFile); + if (FlushFileBuffers(hFile) == 0) { + LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError()); + return false; + } #else #if defined(__linux__) || defined(__NetBSD__) - fdatasync(fileno(file)); + if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync + LogPrintf("%s: fdatasync failed: %d\n", __func__, errno); + return false; + } #elif defined(__APPLE__) && defined(F_FULLFSYNC) - fcntl(fileno(file), F_FULLFSYNC, 0); + if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success + LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno); + return false; + } #else - fsync(fileno(file)); + if (fsync(fileno(file)) != 0 && errno != EINVAL) { + LogPrintf("%s: fsync failed: %d\n", __func__, errno); + return false; + } #endif #endif + return true; } bool TruncateFile(FILE *file, unsigned int length) { @@ -1117,41 +898,6 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { #endif } -void ShrinkDebugFile() -{ - // Amount of debug.log to save at end when shrinking (must fit in memory) - constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; - // Scroll debug.log if it's getting too big - fs::path pathLog = GetDebugLogPath(); - FILE* file = fsbridge::fopen(pathLog, "r"); - - // Special files (e.g. device nodes) may not have a size. - size_t log_size = 0; - try { - log_size = fs::file_size(pathLog); - } catch (boost::filesystem::filesystem_error &) {} - - // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE - // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes - if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) - { - // Restart the file with some of the end - std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0); - fseek(file, -((long)vch.size()), SEEK_END); - int nBytes = fread(vch.data(), 1, vch.size(), file); - fclose(file); - - file = fsbridge::fopen(pathLog, "w"); - if (file) - { - fwrite(vch.data(), 1, nBytes, file); - fclose(file); - } - } - else if (file != nullptr) - fclose(file); -} - #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate) { diff --git a/src/util.h b/src/util.h index ffdee99d27..ce94f396af 100644 --- a/src/util.h +++ b/src/util.h @@ -5,7 +5,7 @@ /** * Server/client environment: argument handling, config file parsing, - * logging, thread wrappers, startup time + * thread wrappers, startup time */ #ifndef BITCOIN_UTIL_H #define BITCOIN_UTIL_H @@ -16,6 +16,7 @@ #include <compat.h> #include <fs.h> +#include <logging.h> #include <sync.h> #include <tinyformat.h> #include <utiltime.h> @@ -36,11 +37,6 @@ // Application startup time (used for uptime calculation) int64_t GetStartupTime(); -static const bool DEFAULT_LOGTIMEMICROS = false; -static const bool DEFAULT_LOGIPS = false; -static const bool DEFAULT_LOGTIMESTAMPS = true; -extern const char * const DEFAULT_DEBUGLOGFILE; - /** Signals for translation. */ class CTranslationInterface { @@ -49,20 +45,11 @@ public: boost::signals2::signal<std::string (const char* psz)> Translate; }; -extern bool fPrintToConsole; -extern bool fPrintToDebugLog; - -extern bool fLogTimestamps; -extern bool fLogTimeMicros; -extern bool fLogIPs; -extern std::atomic<bool> fReopenDebugLog; extern CTranslationInterface translationInterface; extern const char * const BITCOIN_CONF_FILENAME; extern const char * const BITCOIN_PID_FILENAME; -extern std::atomic<uint32_t> logCategories; - /** * Translation function: Call Translate signal on UI interface, which returns a boost::optional result. * If no translation slot is registered, nothing is returned, and simply return the input. @@ -76,95 +63,6 @@ inline std::string _(const char* psz) void SetupEnvironment(); bool SetupNetworking(); -struct CLogCategoryActive -{ - std::string category; - bool active; -}; - -namespace BCLog { - enum LogFlags : uint32_t { - NONE = 0, - NET = (1 << 0), - TOR = (1 << 1), - MEMPOOL = (1 << 2), - HTTP = (1 << 3), - BENCH = (1 << 4), - ZMQ = (1 << 5), - DB = (1 << 6), - RPC = (1 << 7), - ESTIMATEFEE = (1 << 8), - ADDRMAN = (1 << 9), - SELECTCOINS = (1 << 10), - REINDEX = (1 << 11), - CMPCTBLOCK = (1 << 12), - RAND = (1 << 13), - PRUNE = (1 << 14), - PROXY = (1 << 15), - MEMPOOLREJ = (1 << 16), - LIBEVENT = (1 << 17), - COINDB = (1 << 18), - QT = (1 << 19), - LEVELDB = (1 << 20), - ALL = ~(uint32_t)0, - }; -} -/** Return true if log accepts specified category */ -static inline bool LogAcceptCategory(uint32_t category) -{ - return (logCategories.load(std::memory_order_relaxed) & category) != 0; -} - -/** Returns a string with the log categories. */ -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); - -/** Get format string from VA_ARGS for error reporting */ -template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; } - -static inline void MarkUsed() {} -template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args) -{ - (void)t; - MarkUsed(args...); -} - -// Be conservative when using LogPrintf/error or other things which -// unconditionally log to debug.log! It should not be the case that an inbound -// peer can fill up a user's disk with debug.log entries. - -#ifdef USE_COVERAGE -#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0) -#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) -#else -#define LogPrintf(...) do { \ - if (fPrintToConsole || fPrintToDebugLog) { \ - std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ - try { \ - _log_msg_ = tfm::format(__VA_ARGS__); \ - } catch (tinyformat::format_error &fmterr) { \ - /* 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_); \ - } \ -} while(0) - -#define LogPrint(category, ...) do { \ - if (LogAcceptCategory((category))) { \ - LogPrintf(__VA_ARGS__); \ - } \ -} while(0) -#endif - template<typename... Args> bool error(const char* fmt, const Args&... args) { @@ -173,7 +71,7 @@ bool error(const char* fmt, const Args&... args) } void PrintExceptionContinue(const std::exception *pex, const char* pszThread); -void FileCommit(FILE *file); +bool FileCommit(FILE *file); bool TruncateFile(FILE *file, unsigned int length); int RaiseFileDescriptorLimit(int nMinFD); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); @@ -199,9 +97,6 @@ void CreatePidFile(const fs::path &path, pid_t pid); #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif -fs::path GetDebugLogPath(); -bool OpenDebugLog(); -void ShrinkDebugFile(); void runCommand(const std::string& strCommand); /** diff --git a/src/validation.cpp b/src/validation.cpp index 3726cb3b14..bce8c4f9e9 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -142,7 +142,7 @@ private: * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, * instead of putting things in this set. */ - std::set<CBlockIndex*> g_failed_blocks; + std::set<CBlockIndex*> m_failed_blocks; public: CChain chainActive; @@ -154,6 +154,10 @@ public: bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock); + /** + * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure + * that it doesn't descend from an invalid block, and then add it to mapBlockIndex. + */ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex); bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock); @@ -185,6 +189,11 @@ private: CBlockIndex* AddToBlockIndex(const CBlockHeader& block); /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(const uint256& hash); + /** + * Make various assertions about the state of the block index. + * + * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex. + */ void CheckBlockIndex(const Consensus::Params& consensusParams); void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state); @@ -720,7 +729,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // No transactions are allowed below minRelayTxFee except from disconnected blocks if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met"); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", false, strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize))); } if (nAbsurdFee && nFees > nAbsurdFee) @@ -1286,7 +1295,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { if (!state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; - g_failed_blocks.insert(pindex); + m_failed_blocks.insert(pindex); setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); InvalidChainFound(pindex); @@ -1615,22 +1624,27 @@ void static FlushBlockFile(bool fFinalize = false) LOCK(cs_LastBlockFile); CDiskBlockPos posOld(nLastBlockFile, 0); + bool status = true; FILE *fileOld = OpenBlockFile(posOld); if (fileOld) { if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); - FileCommit(fileOld); + status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); + status &= FileCommit(fileOld); fclose(fileOld); } fileOld = OpenUndoFile(posOld); if (fileOld) { if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); - FileCommit(fileOld); + status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); + status &= FileCommit(fileOld); fclose(fileOld); } + + if (!status) { + AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error."); + } } static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); @@ -1726,16 +1740,38 @@ public: // Protected by cs_main static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; +// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for +// mainnet. We no longer need to support disabling the segwit deployment +// except for testing purposes, due to limitations of the functional test +// environment. See test/functional/p2p-segwit.py. +static bool IsScriptWitnessEnabled(const Consensus::Params& params) +{ + return params.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0; +} + static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) { AssertLockHeld(cs_main); unsigned int flags = SCRIPT_VERIFY_NONE; - // Start enforcing P2SH (BIP16) - if (pindex->nHeight >= consensusparams.BIP16Height) { + // BIP16 didn't become active until Apr 1 2012 (on mainnet, and + // retroactively applied to testnet) + // However, only one historical block violated the P2SH rules (on both + // mainnet and testnet), so for simplicity, always leave P2SH + // on except for the one violating block. + if (consensusparams.BIP16Exception.IsNull() || // no bip16 exception on this chain + pindex->phashBlock == nullptr || // this is a new candidate block, eg from TestBlockValidity() + *pindex->phashBlock != consensusparams.BIP16Exception) // this block isn't the historical exception + { flags |= SCRIPT_VERIFY_P2SH; } + // Enforce WITNESS rules whenever P2SH is in effect (and the segwit + // deployment is defined). + if (flags & SCRIPT_VERIFY_P2SH && IsScriptWitnessEnabled(consensusparams)) { + flags |= SCRIPT_VERIFY_WITNESS; + } + // Start enforcing the DERSIG (BIP66) rule if (pindex->nHeight >= consensusparams.BIP66Height) { flags |= SCRIPT_VERIFY_DERSIG; @@ -1751,9 +1787,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; } - // Start enforcing WITNESS rules using versionbits logic. - if (IsWitnessEnabled(pindex->pprev, consensusparams)) { - flags |= SCRIPT_VERIFY_WITNESS; + if (IsNullDummyEnabled(pindex->pprev, consensusparams)) { flags |= SCRIPT_VERIFY_NULLDUMMY; } @@ -2181,14 +2215,18 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & void FlushStateToDisk() { CValidationState state; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } void PruneAndFlush() { CValidationState state; fCheckForPruning = true; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::NONE); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } static void DoWarning(const std::string& strWarning) @@ -2625,6 +2663,10 @@ static void NotifyHeaderTip() { * Make the best chain active, in multiple steps. The result is either failure * or an activated best chain. pblock is either nullptr or a pointer to a block * that is already loaded (to avoid loading it again from disk). + * + * ActivateBestChain is split into steps (see ActivateBestChainStep) so that + * we avoid holding cs_main for an extended period of time; the length of this + * call may be quite long during reindexing or a substantial reorg. */ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) { // Note that while we're often called here from ProcessNewBlock, this is @@ -2783,7 +2825,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); - g_failed_blocks.insert(pindex); + m_failed_blocks.insert(pindex); // DisconnectTip will add transactions to disconnectpool; try to add these // back to the mempool. @@ -2829,7 +2871,7 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { // Reset invalid block marker if it was pointing to one of those. pindexBestInvalid = nullptr; } - g_failed_blocks.erase(it->second); + m_failed_blocks.erase(it->second); } it++; } @@ -3099,6 +3141,12 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE); } +bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE); +} + // Compute at which vout of the block's coinbase transaction the witness // commitment occurs, or -1 if not found. static int GetWitnessCommitmentIndex(const CBlock& block) @@ -3322,8 +3370,11 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState& if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); + // If the previous block index isn't valid, determine if it descends from any block which + // has been found invalid (g_failed_blocks), then mark pindexPrev and any blocks + // between them as failed. if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) { - for (const CBlockIndex* failedit : g_failed_blocks) { + for (const CBlockIndex* failedit : m_failed_blocks) { if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { assert(failedit->nStatus & BLOCK_FAILED_VALID); CBlockIndex* invalid_walk = pindexPrev; @@ -3489,7 +3540,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons } if (!ret) { GetMainSignals().BlockChecked(*pblock, state); - return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage()); + return error("%s: AcceptBlock FAILED (%s)", __func__, FormatStateMessage(state)); } } @@ -3497,7 +3548,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons CValidationState state; // Only used to report errors, not invalidity - ignore it if (!g_chainstate.ActivateBestChain(state, chainparams, pblock)) - return error("%s: ActivateBestChain failed", __func__); + return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state)); return true; } @@ -3615,7 +3666,9 @@ void PruneBlockFilesManual(int nManualPruneHeight) { CValidationState state; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } /** @@ -3869,6 +3922,7 @@ bool LoadChainTip(const CChainParams& chainparams) LogPrintf("%s: Connecting genesis block...\n", __func__); CValidationState state; if (!ActivateBestChain(state, chainparams)) { + LogPrintf("%s: failed to activate chain (%s)\n", __func__, FormatStateMessage(state)); return false; } } @@ -3983,7 +4037,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams)) - return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); } } @@ -4091,6 +4145,9 @@ bool CChainState::RewindBlockIndex(const CChainParams& params) int nHeight = 1; while (nHeight <= chainActive.Height()) { + // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all + // blocks in ConnectBlock, we don't need to go back and + // re-download/re-verify blocks from before segwit actually activated. if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) { break; } @@ -4110,11 +4167,13 @@ bool CChainState::RewindBlockIndex(const CChainParams& params) break; } if (!DisconnectTip(state, params, nullptr)) { - return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); + return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", pindex->nHeight, FormatStateMessage(state)); } // Occasionally flush state to disk. - if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) + if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) { + LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); return false; + } } // Reduce validity flag and have-data flags. @@ -4180,6 +4239,7 @@ bool RewindBlockIndex(const CChainParams& params) { // it'll get called a bunch real soon. CValidationState state; if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) { + LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); return false; } } @@ -4189,7 +4249,7 @@ bool RewindBlockIndex(const CChainParams& params) { void CChainState::UnloadBlockIndex() { nBlockSequenceId = 1; - g_failed_blocks.clear(); + m_failed_blocks.clear(); setBlockIndexCandidates.clear(); } @@ -4266,7 +4326,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) CBlockIndex *pindex = AddToBlockIndex(block); CValidationState state; if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus())) - return error("%s: genesis block not accepted", __func__); + return error("%s: genesis block not accepted (%s)", __func__, FormatStateMessage(state)); } catch (const std::runtime_error& e) { return error("%s: failed to write genesis block: %s", __func__, e.what()); } @@ -4731,7 +4791,8 @@ bool DumpMempool(void) } file << mapDeltas; - FileCommit(file.Get()); + if (!FileCommit(file.Get())) + throw std::runtime_error("FileCommit failed"); file.fclose(); RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat"); int64_t last = GetTimeMicros(); diff --git a/src/validation.h b/src/validation.h index 3668484696..b415a85053 100644 --- a/src/validation.h +++ b/src/validation.h @@ -411,6 +411,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, /** Check whether witness commitments are required for block. */ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params); +/** Check whether NULLDUMMY (BIP 147) has activated. */ +bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params); + /** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */ bool RewindBlockIndex(const CChainParams& params); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 2fd9aa1a6f..5091477dfd 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -118,19 +118,19 @@ bool WalletInit::ParameterInteraction() const } } - int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); + bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false); // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); + if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) { + LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -persistmempool=0\n", __func__); } // -zapwallettxes implies a rescan - if (zapwallettxes != 0) { + if (zapwallettxes) { if (is_multiwallet) { return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); } if (gArgs.SoftSetBoolArg("-rescan", true)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes); + LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__); } } @@ -315,7 +315,7 @@ bool WalletInit::Open() const if (!pwallet) { return false; } - vpwallets.push_back(pwallet); + AddWallet(pwallet); } return true; @@ -323,29 +323,29 @@ bool WalletInit::Open() const void WalletInit::Start(CScheduler& scheduler) const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { pwallet->postInitProcess(scheduler); } } void WalletInit::Flush() const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { pwallet->Flush(false); } } void WalletInit::Stop() const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { pwallet->Flush(true); } } void WalletInit::Close() const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { + RemoveWallet(pwallet); delete pwallet; } - vpwallets.clear(); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 56bdc0695c..9b5fb0b062 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -46,14 +46,13 @@ CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request) if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) { // wallet endpoint was used std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size())); - for (CWalletRef pwallet : ::vpwallets) { - if (pwallet->GetName() == requestedWallet) { - return pwallet; - } - } - throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); + CWallet* pwallet = GetWallet(requestedWallet); + if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); + return pwallet; } - return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) ? ::vpwallets[0] : nullptr; + + std::vector<CWallet*> wallets = GetWallets(); + return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr; } std::string HelpRequiringPassphrase(CWallet * const pwallet) @@ -67,7 +66,7 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException) { if (pwallet) return true; if (avoidException) return false; - if (::vpwallets.empty()) { + if (!HasWallets()) { // Note: It isn't currently possible to trigger this error because // wallet RPC methods aren't registered unless a wallet is loaded. But // this error is being kept as a precaution, because it's possible in @@ -2862,8 +2861,7 @@ UniValue listwallets(const JSONRPCRequest& request) UniValue obj(UniValue::VARR); - for (CWalletRef pwallet : vpwallets) { - + for (CWallet* pwallet : GetWallets()) { if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 57705926a3..99c963a348 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -74,7 +74,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // after. { CWallet wallet("dummy", WalletDatabase::CreateDummy()); - vpwallets.insert(vpwallets.begin(), &wallet); + AddWallet(&wallet); UniValue keys; keys.setArray(); UniValue key; @@ -105,7 +105,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) "downloading and rescanning the relevant blocks (see -reindex and -rescan " "options).\"}},{\"success\":true}]", 0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW)); - vpwallets.erase(vpwallets.begin()); + RemoveWallet(&wallet); } } @@ -140,9 +140,9 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) JSONRPCRequest request; request.params.setArray(); request.params.push_back((pathTemp / "wallet.backup").string()); - vpwallets.insert(vpwallets.begin(), &wallet); + AddWallet(&wallet); ::dumpwallet(request); - vpwallets.erase(vpwallets.begin()); + RemoveWallet(&wallet); } // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME @@ -153,9 +153,9 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) JSONRPCRequest request; request.params.setArray(); request.params.push_back((pathTemp / "wallet.backup").string()); - vpwallets.insert(vpwallets.begin(), &wallet); + AddWallet(&wallet); ::importwallet(request); - vpwallets.erase(vpwallets.begin()); + RemoveWallet(&wallet); LOCK(wallet.cs_wallet); BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3U); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 45c85a7912..8c392434fc 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -28,12 +28,50 @@ #include <utilmoneystr.h> #include <wallet/fees.h> +#include <algorithm> #include <assert.h> #include <future> #include <boost/algorithm/string/replace.hpp> -std::vector<CWalletRef> vpwallets; +static std::vector<CWallet*> vpwallets; + +bool AddWallet(CWallet* wallet) +{ + assert(wallet); + std::vector<CWallet*>::const_iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); + if (i != vpwallets.end()) return false; + vpwallets.push_back(wallet); + return true; +} + +bool RemoveWallet(CWallet* wallet) +{ + assert(wallet); + std::vector<CWallet*>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); + if (i == vpwallets.end()) return false; + vpwallets.erase(i); + return true; +} + +bool HasWallets() +{ + return !vpwallets.empty(); +} + +std::vector<CWallet*> GetWallets() +{ + return vpwallets; +} + +CWallet* GetWallet(const std::string& name) +{ + for (CWallet* wallet : vpwallets) { + if (wallet->GetName() == name) return wallet; + } + return nullptr; +} + /** Transaction fee set by the user */ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index b85f374a06..dd165de825 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -32,8 +32,11 @@ #include <utility> #include <vector> -typedef CWallet* CWalletRef; -extern std::vector<CWalletRef> vpwallets; +bool AddWallet(CWallet* wallet); +bool RemoveWallet(CWallet* wallet); +bool HasWallets(); +std::vector<CWallet*> GetWallets(); +CWallet* GetWallet(const std::string& name); /** * Settings @@ -268,7 +271,7 @@ public: //Get the marginal bytes of spending the specified output int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet); -/** +/** * A transaction with a bunch of additional info that only the owner cares about. * It includes any unrecorded transactions needed to link it back to the block chain. */ @@ -653,7 +656,7 @@ struct CoinEligibilityFilter }; class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime -/** +/** * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. */ @@ -903,7 +906,7 @@ public: void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const; unsigned int ComputeTimeSmart(const CWalletTx& wtx) const; - /** + /** * Increment the next transaction order id * @return next transaction order id */ @@ -1032,7 +1035,7 @@ public: } void GetScriptForMining(std::shared_ptr<CReserveScript> &script); - + unsigned int GetKeyPoolSize() { AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool @@ -1057,7 +1060,7 @@ public: //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); - /** + /** * Address book entry changed. * @note called with lock cs_wallet held. */ @@ -1066,7 +1069,7 @@ public: const std::string &purpose, ChangeType status)> NotifyAddressBookChanged; - /** + /** * Wallet transaction added, removed or updated. * @note called with lock cs_wallet held. */ @@ -1113,7 +1116,7 @@ public: /* Generates a new HD master key (will not be activated) */ CPubKey GenerateNewHDMasterKey(); - + /* Set the current HD master key (will reset the chain child index counters) Sets the master key's version based on the current wallet version (so the caller must ensure the current wallet version is correct before calling @@ -1184,7 +1187,7 @@ public: }; -/** +/** * DEPRECATED Account information. * Stored in wallet with key "acc"+string account name. */ @@ -1230,10 +1233,10 @@ std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key); class WalletRescanReserver { private: - CWalletRef m_wallet; + CWallet* m_wallet; bool m_could_reserve; public: - explicit WalletRescanReserver(CWalletRef w) : m_wallet(w), m_could_reserve(false) {} + explicit WalletRescanReserver(CWallet* w) : m_wallet(w), m_could_reserve(false) {} bool reserve() { diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index bcc7cf877d..5b275131af 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -756,7 +756,7 @@ void MaybeCompactWalletDB() return; } - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { WalletDatabase& dbh = pwallet->GetDBHandle(); unsigned int nUpdateCounter = dbh.nUpdateCounter; diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 181c7f3369..38b76239e5 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -1306,7 +1306,13 @@ class FullBlockTest(BitcoinTestFramework): self.nodes[0].disconnect_p2ps() self.nodes[0].add_p2p_connection(P2PDataStore()) network_thread_start() - self.nodes[0].p2p.wait_for_verack() + # We need to wait for the initial getheaders from the peer before we + # start populating our blockstore. If we don't, then we may run ahead + # to the next subtest before we receive the getheaders. We'd then send + # an INV for the next block and receive two getheaders - one for the + # IBD and one for the INV. We'd respond to both and could get + # unexpectedly disconnected if the DoS score for that error is 50. + self.nodes[0].p2p.wait_for_getheaders(timeout=5) def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True, reconnect=False, timeout=60): """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block. diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index fa1732c4c5..e835b9d777 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -150,19 +150,11 @@ class SegWitTest(BitcoinTestFramework): self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][0], True) #block 426 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][0], True) #block 427 - # TODO: An old node would see these txs without witnesses and be able to mine them - - self.log.info("Verify unsigned bare witness txs in versionbits-setting blocks are valid before the fork") - self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][1], False) #block 428 - self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][1], False) #block 429 - self.log.info("Verify unsigned p2sh witness txs without a redeem script are invalid") self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V0][1], False) self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V1][1], False) - self.log.info("Verify unsigned p2sh witness txs with a redeem script in versionbits-settings blocks are valid before the fork") - self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][1], False, witness_script(False, self.pubkey[2])) #block 430 - self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][1], False, witness_script(True, self.pubkey[2])) #block 431 + self.nodes[2].generate(4) # blocks 428-431 self.log.info("Verify previous witness txs skipped for mining can now be mined") assert_equal(len(self.nodes[2].getrawmempool()), 4) diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py index e754dd31ad..b433f11aa5 100755 --- a/test/functional/mining_prioritisetransaction.py +++ b/test/functional/mining_prioritisetransaction.py @@ -120,7 +120,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework): tx_id = self.nodes[0].decoderawtransaction(tx_hex)["txid"] # This will raise an exception due to min relay fee not being met - assert_raises_rpc_error(-26, "min relay fee not met (code 66)", self.nodes[0].sendrawtransaction, tx_hex) + assert_raises_rpc_error(-26, "min relay fee not met, 0 < 134 (code 66)", self.nodes[0].sendrawtransaction, tx_hex) assert(tx_id not in self.nodes[0].getrawmempool()) # This is a less than 1000-byte transaction, so just set the fee diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index f8afe22eaf..4fecd4ffee 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -48,7 +48,7 @@ def test_transaction_acceptance(rpc, p2p, tx, with_witness, accepted, reason=Non with mininode_lock: assert_equal(p2p.last_message["reject"].reason, reason) -def test_witness_block(rpc, p2p, block, accepted, with_witness=True): +def test_witness_block(rpc, p2p, block, accepted, with_witness=True, reason=None): """Send a block to the node and check that it's accepted - Submit the block over the p2p interface @@ -59,6 +59,10 @@ def test_witness_block(rpc, p2p, block, accepted, with_witness=True): p2p.send_message(msg_block(block)) p2p.sync_with_ping() assert_equal(rpc.getbestblockhash() == block.hash, accepted) + if (reason != None and not accepted): + # Check the rejection reason as well. + with mininode_lock: + assert_equal(p2p.last_message["reject"].reason, reason) class TestP2PConn(P2PInterface): def __init__(self): @@ -272,6 +276,80 @@ class SegWitTest(BitcoinTestFramework): self.utxo.pop(0) self.utxo.append(UTXO(tx4.sha256, 0, tx4.vout[0].nValue)) + # ~6 months after segwit activation, the SCRIPT_VERIFY_WITNESS flag was + # backdated so that it applies to all blocks, going back to the genesis + # block. + # + # Consequently, version 0 witness outputs are never spendable without + # witness, and so can't be spent before segwit activation (the point at which + # blocks are permitted to contain witnesses). + def test_v0_outputs_arent_spendable(self): + self.log.info("Testing that v0 witness program outputs aren't spendable before activation") + + assert len(self.utxo), "self.utxo is empty" + + # Create two outputs, a p2wsh and p2sh-p2wsh + witness_program = CScript([OP_TRUE]) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + + p2sh_pubkey = hash160(scriptPubKey) + p2sh_scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) + + value = self.utxo[0].nValue // 3 + + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b'')] + tx.vout = [CTxOut(value, scriptPubKey), CTxOut(value, p2sh_scriptPubKey)] + tx.vout.append(CTxOut(value, CScript([OP_TRUE]))) + tx.rehash() + txid = tx.sha256 + + # Add it to a block + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + # Verify that segwit isn't activated. A block serialized with witness + # should be rejected prior to activation. + test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason = b'unexpected-witness') + # Now send the block without witness. It should be accepted + test_witness_block(self.nodes[0], self.test_node, block, accepted=True, with_witness=False) + + # Now try to spend the outputs. This should fail since SCRIPT_VERIFY_WITNESS is always enabled. + p2wsh_tx = CTransaction() + p2wsh_tx.vin = [CTxIn(COutPoint(txid, 0), b'')] + p2wsh_tx.vout = [CTxOut(value, CScript([OP_TRUE]))] + p2wsh_tx.wit.vtxinwit.append(CTxInWitness()) + p2wsh_tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] + p2wsh_tx.rehash() + + p2sh_p2wsh_tx = CTransaction() + p2sh_p2wsh_tx.vin = [CTxIn(COutPoint(txid, 1), CScript([scriptPubKey]))] + p2sh_p2wsh_tx.vout = [CTxOut(value, CScript([OP_TRUE]))] + p2sh_p2wsh_tx.wit.vtxinwit.append(CTxInWitness()) + p2sh_p2wsh_tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] + p2sh_p2wsh_tx.rehash() + + for tx in [p2wsh_tx, p2sh_p2wsh_tx]: + + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + + # When the block is serialized with a witness, the block will be rejected because witness + # data isn't allowed in blocks that don't commit to witness data. + test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason=b'unexpected-witness') + + # When the block is serialized without witness, validation fails because the transaction is + # invalid (transactions are always validated with SCRIPT_VERIFY_WITNESS so a segwit v0 transaction + # without a witness is invalid). + # Note: The reject reason for this failure could be + # 'block-validation-failed' (if script check threads > 1) or + # 'non-mandatory-script-verify-flag (Witness program was passed an + # empty witness)' (otherwise). + # TODO: support multiple acceptable reject reasons. + test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False) + + self.utxo.pop(0) + self.utxo.append(UTXO(txid, 2, value)) # Mine enough blocks for segwit's vb state to be 'started'. def advance_to_segwit_started(self): @@ -1479,9 +1557,10 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() self.update_witness_block_with_transactions(block, [spend_tx]) - # If we're before activation, then sending this without witnesses - # should be valid. If we're after activation, then sending this with - # witnesses should be valid. + # If we're after activation, then sending this with witnesses should be valid. + # This no longer works before activation, because SCRIPT_VERIFY_WITNESS + # is always set. + # TODO: rewrite this test to make clear that it only works after activation. if segwit_activated: test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) else: @@ -1900,6 +1979,7 @@ class SegWitTest(BitcoinTestFramework): self.test_witness_services() # Verifies NODE_WITNESS self.test_non_witness_transaction() # non-witness tx's are accepted self.test_unnecessary_witness_before_segwit_activation() + self.test_v0_outputs_arent_spendable() self.test_block_relay(segwit_activated=False) # Advance to segwit being 'started' @@ -1917,7 +1997,6 @@ class SegWitTest(BitcoinTestFramework): self.test_unnecessary_witness_before_segwit_activation() self.test_witness_tx_relay_before_segwit_activation() self.test_block_relay(segwit_activated=False) - self.test_p2sh_witness(segwit_activated=False) self.test_standardness_v0(segwit_activated=False) sync_blocks(self.nodes) diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py index 5b50520d3f..343e162058 100755 --- a/test/functional/rpc_bind.py +++ b/test/functional/rpc_bind.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test running bitcoind with the -rpcbind and -rpcallowip options.""" -import socket import sys from test_framework.test_framework import BitcoinTestFramework, SkipTest @@ -20,6 +19,11 @@ class RPCBindTest(BitcoinTestFramework): def setup_network(self): self.add_nodes(self.num_nodes, None) + def add_options(self, parser): + parser.add_option("--ipv4", action='store_true', dest="run_ipv4", help="Run ipv4 tests only", default=False) + parser.add_option("--ipv6", action='store_true', dest="run_ipv6", help="Run ipv6 tests only", default=False) + parser.add_option("--nonloopback", action='store_true', dest="run_nonloopback", help="Run non-loopback tests only", default=False) + def run_bind_test(self, allow_ips, connect_to, addresses, expected): ''' Start a node with requested rpcallowip and rpcbind parameters, @@ -54,55 +58,69 @@ class RPCBindTest(BitcoinTestFramework): def run_test(self): # due to OS-specific network stats queries, this test works only on Linux + if sum([self.options.run_ipv4, self.options.run_ipv6, self.options.run_nonloopback]) > 1: + raise AssertionError("Only one of --ipv4, --ipv6 and --nonloopback can be set") + + self.log.info("Check for linux") if not sys.platform.startswith('linux'): - raise SkipTest("This test can only be run on Linux.") - # find the first non-loopback interface for testing - non_loopback_ip = None + raise SkipTest("This test can only be run on linux.") + + self.log.info("Check for ipv6") + have_ipv6 = test_ipv6_local() + if not have_ipv6 and not self.options.run_ipv4: + raise SkipTest("This test requires ipv6 support.") + + self.log.info("Check for non-loopback interface") + self.non_loopback_ip = None for name,ip in all_interfaces(): if ip != '127.0.0.1': - non_loopback_ip = ip + self.non_loopback_ip = ip break - if non_loopback_ip is None: - raise SkipTest("This test requires at least one non-loopback IPv4 interface.") - try: - s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) - s.connect(("::1",1)) - s.close - except OSError: - raise SkipTest("This test requires IPv6 support.") - - self.log.info("Using interface %s for testing" % non_loopback_ip) - - defaultport = rpc_port(0) - - # check default without rpcallowip (IPv4 and IPv6 localhost) - self.run_bind_test(None, '127.0.0.1', [], - [('127.0.0.1', defaultport), ('::1', defaultport)]) - # check default with rpcallowip (IPv6 any) - self.run_bind_test(['127.0.0.1'], '127.0.0.1', [], - [('::0', defaultport)]) - # check only IPv4 localhost (explicit) - self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1'], - [('127.0.0.1', defaultport)]) - # check only IPv4 localhost (explicit) with alternative port - self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'], - [('127.0.0.1', 32171)]) - # check only IPv4 localhost (explicit) with multiple alternative ports on same host - self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'], - [('127.0.0.1', 32171), ('127.0.0.1', 32172)]) - # check only IPv6 localhost (explicit) - self.run_bind_test(['[::1]'], '[::1]', ['[::1]'], - [('::1', defaultport)]) - # check both IPv4 and IPv6 localhost (explicit) - self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'], - [('127.0.0.1', defaultport), ('::1', defaultport)]) + if self.non_loopback_ip is None and self.options.run_nonloopback: + raise SkipTest("This test requires a non-loopback ip address.") + + self.defaultport = rpc_port(0) + + if not self.options.run_nonloopback: + self._run_loopback_tests() + if not self.options.run_ipv4 and not self.options.run_ipv6: + self._run_nonloopback_tests() + + def _run_loopback_tests(self): + if self.options.run_ipv4: + # check only IPv4 localhost (explicit) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1'], + [('127.0.0.1', self.defaultport)]) + # check only IPv4 localhost (explicit) with alternative port + self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'], + [('127.0.0.1', 32171)]) + # check only IPv4 localhost (explicit) with multiple alternative ports on same host + self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'], + [('127.0.0.1', 32171), ('127.0.0.1', 32172)]) + else: + # check default without rpcallowip (IPv4 and IPv6 localhost) + self.run_bind_test(None, '127.0.0.1', [], + [('127.0.0.1', self.defaultport), ('::1', self.defaultport)]) + # check default with rpcallowip (IPv6 any) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', [], + [('::0', self.defaultport)]) + # check only IPv6 localhost (explicit) + self.run_bind_test(['[::1]'], '[::1]', ['[::1]'], + [('::1', self.defaultport)]) + # check both IPv4 and IPv6 localhost (explicit) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'], + [('127.0.0.1', self.defaultport), ('::1', self.defaultport)]) + + def _run_nonloopback_tests(self): + self.log.info("Using interface %s for testing" % self.non_loopback_ip) + # check only non-loopback interface - self.run_bind_test([non_loopback_ip], non_loopback_ip, [non_loopback_ip], - [(non_loopback_ip, defaultport)]) + self.run_bind_test([self.non_loopback_ip], self.non_loopback_ip, [self.non_loopback_ip], + [(self.non_loopback_ip, self.defaultport)]) # Check that with invalid rpcallowip, we are denied - self.run_allowip_test([non_loopback_ip], non_loopback_ip, defaultport) - assert_raises_rpc_error(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], non_loopback_ip, defaultport) + self.run_allowip_test([self.non_loopback_ip], self.non_loopback_ip, self.defaultport) + assert_raises_rpc_error(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], self.non_loopback_ip, self.defaultport) if __name__ == '__main__': RPCBindTest().main() diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 658782e82a..48b4a4a9db 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -14,13 +14,10 @@ Test the following RPCs: from collections import OrderedDict from io import BytesIO +from test_framework.messages import CTransaction, ToHex from test_framework.test_framework import BitcoinTestFramework -from test_framework.messages import ( - CTransaction, -) from test_framework.util import * - class multidict(dict): """Dictionary that allows duplicate keys. @@ -363,5 +360,23 @@ class RawTransactionsTest(BitcoinTestFramework): decrawtx= self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 4294967294) + #################################### + # TRANSACTION VERSION NUMBER TESTS # + #################################### + + # Test the minimum transaction version number that fits in a signed 32-bit integer. + tx = CTransaction() + tx.nVersion = -0x80000000 + rawtx = ToHex(tx) + decrawtx = self.nodes[0].decoderawtransaction(rawtx) + assert_equal(decrawtx['version'], -0x80000000) + + # Test the maximum transaction version number that fits in a signed 32-bit integer. + tx = CTransaction() + tx.nVersion = 0x7fffffff + rawtx = ToHex(tx) + decrawtx = self.nodes[0].decoderawtransaction(rawtx) + assert_equal(decrawtx['version'], 0x7fffffff) + if __name__ == '__main__': RawTransactionsTest().main() diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index 43982cd09a..5c2b1815e5 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -10,7 +10,24 @@ from .address import ( script_to_p2sh_p2wsh, script_to_p2wsh, ) -from .mininode import * +from .messages import ( + CBlock, + COIN, + COutPoint, + CTransaction, + CTxIn, + CTxInWitness, + CTxOut, + FromHex, + ToHex, + bytes_to_hex_str, + hash256, + hex_str_to_bytes, + ser_string, + ser_uint256, + sha256, + uint256_from_str, +) from .script import ( CScript, OP_0, @@ -23,34 +40,34 @@ from .script import ( ) from .util import assert_equal -# Create a block (with regtest difficulty) -def create_block(hashprev, coinbase, nTime=None): +# From BIP141 +WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed" + +def create_block(hashprev, coinbase, ntime=None): + """Create a block (with regtest difficulty).""" block = CBlock() - if nTime is None: + if ntime is None: import time - block.nTime = int(time.time()+600) + block.nTime = int(time.time() + 600) else: - block.nTime = nTime + block.nTime = ntime block.hashPrevBlock = hashprev - block.nBits = 0x207fffff # Will break after a difficulty adjustment... + block.nBits = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams block.vtx.append(coinbase) block.hashMerkleRoot = block.calc_merkle_root() block.calc_sha256() return block -# From BIP141 -WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed" - - def get_witness_script(witness_root, witness_nonce): - witness_commitment = uint256_from_str(hash256(ser_uint256(witness_root)+ser_uint256(witness_nonce))) + witness_commitment = uint256_from_str(hash256(ser_uint256(witness_root) + ser_uint256(witness_nonce))) output_data = WITNESS_COMMITMENT_HEADER + ser_uint256(witness_commitment) return CScript([OP_RETURN, output_data]) - -# According to BIP141, blocks with witness rules active must commit to the -# hash of all in-block transactions including witness. def add_witness_commitment(block, nonce=0): + """Add a witness commitment to the block's coinbase transaction. + + According to BIP141, blocks with witness rules active must commit to the + hash of all in-block transactions including witness.""" # First calculate the merkle root of the block's # transactions, with witnesses. witness_nonce = nonce @@ -65,7 +82,6 @@ def add_witness_commitment(block, nonce=0): block.hashMerkleRoot = block.calc_merkle_root() block.rehash() - def serialize_script_num(value): r = bytearray(0) if value == 0: @@ -81,55 +97,59 @@ def serialize_script_num(value): r[-1] |= 0x80 return r -# Create a coinbase transaction, assuming no miner fees. -# If pubkey is passed in, the coinbase output will be a P2PK output; -# otherwise an anyone-can-spend output. -def create_coinbase(height, pubkey = None): +def create_coinbase(height, pubkey=None): + """Create a coinbase transaction, assuming no miner fees. + + If pubkey is passed in, the coinbase output will be a P2PK output; + otherwise an anyone-can-spend output.""" coinbase = CTransaction() - coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), - ser_string(serialize_script_num(height)), 0xffffffff)) + coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), + ser_string(serialize_script_num(height)), 0xffffffff)) coinbaseoutput = CTxOut() coinbaseoutput.nValue = 50 * COIN - halvings = int(height/150) # regtest + halvings = int(height / 150) # regtest coinbaseoutput.nValue >>= halvings - if (pubkey != None): + if (pubkey is not None): coinbaseoutput.scriptPubKey = CScript([pubkey, OP_CHECKSIG]) else: coinbaseoutput.scriptPubKey = CScript([OP_TRUE]) - coinbase.vout = [ coinbaseoutput ] + coinbase.vout = [coinbaseoutput] coinbase.calc_sha256() return coinbase -# Create a transaction. -# If the scriptPubKey is not specified, make it anyone-can-spend. -def create_transaction(prevtx, n, sig, value, scriptPubKey=CScript()): +def create_transaction(prevtx, n, sig, value, script_pub_key=CScript()): + """Create a transaction. + + If the script_pub_key is not specified, make it anyone-can-spend.""" tx = CTransaction() assert(n < len(prevtx.vout)) tx.vin.append(CTxIn(COutPoint(prevtx.sha256, n), sig, 0xffffffff)) - tx.vout.append(CTxOut(value, scriptPubKey)) + tx.vout.append(CTxOut(value, script_pub_key)) tx.calc_sha256() return tx -def get_legacy_sigopcount_block(block, fAccurate=True): +def get_legacy_sigopcount_block(block, accurate=True): count = 0 for tx in block.vtx: - count += get_legacy_sigopcount_tx(tx, fAccurate) + count += get_legacy_sigopcount_tx(tx, accurate) return count -def get_legacy_sigopcount_tx(tx, fAccurate=True): +def get_legacy_sigopcount_tx(tx, accurate=True): count = 0 for i in tx.vout: - count += i.scriptPubKey.GetSigOpCount(fAccurate) + count += i.scriptPubKey.GetSigOpCount(accurate) for j in tx.vin: # scriptSig might be of type bytes, so convert to CScript for the moment - count += CScript(j.scriptSig).GetSigOpCount(fAccurate) + count += CScript(j.scriptSig).GetSigOpCount(accurate) return count -# Create a scriptPubKey corresponding to either a P2WPKH output for the -# given pubkey, or a P2WSH output of a 1-of-1 multisig for the given -# pubkey. Returns the hex encoding of the scriptPubKey. def witness_script(use_p2wsh, pubkey): - if (use_p2wsh == False): + """Create a scriptPubKey for a pay-to-wtiness TxOut. + + This is either a P2WPKH output for the given pubkey, or a P2WSH output of a + 1-of-1 multisig for the given pubkey. Returns the hex encoding of the + scriptPubKey.""" + if not use_p2wsh: # P2WPKH instead pubkeyhash = hash160(hex_str_to_bytes(pubkey)) pkscript = CScript([OP_0, pubkeyhash]) @@ -140,9 +160,10 @@ def witness_script(use_p2wsh, pubkey): pkscript = CScript([OP_0, scripthash]) return bytes_to_hex_str(pkscript) -# Return a transaction (in hex) that spends the given utxo to a segwit output, -# optionally wrapping the segwit output using P2SH. def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount): + """Return a transaction (in hex) that spends the given utxo to a segwit output. + + Optionally wrap the segwit output using P2SH.""" if use_p2wsh: program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG]) addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program) @@ -152,12 +173,13 @@ def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount): assert_equal(node.getaddressinfo(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey)) return node.createrawtransaction([utxo], {addr: amount}) -# Create a transaction spending a given utxo to a segwit output corresponding -# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH; -# encode_p2sh determines whether to wrap in P2SH. -# sign=True will have the given node sign the transaction. -# insert_redeem_script will be added to the scriptSig, if given. def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""): + """Create a transaction spending a given utxo to a segwit output. + + The output corresponds to the given pubkey: use_p2wsh determines whether to + use P2WPKH or P2WSH; encode_p2sh determines whether to wrap in P2SH. + sign=True will have the given node sign the transaction. + insert_redeem_script will be added to the scriptSig, if given.""" tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount) if (sign): signed = node.signrawtransactionwithwallet(tx_to_witness) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 04d1de8d91..54e533d6f6 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -19,7 +19,6 @@ import time from .authproxy import JSONRPCException from .util import ( append_config, - assert_equal, delete_cookie_file, get_rpc_proxy, rpc_url, @@ -103,6 +102,14 @@ class TestNode(): self.p2ps = [] + def _node_msg(self, msg: str) -> str: + """Return a modified msg that identifies this node by its index as a debugging aid.""" + return "[node %d] %s" % (self.index, msg) + + def _raise_assertion_error(self, msg: str): + """Raise an AssertionError with msg modified to identify this node.""" + raise AssertionError(self._node_msg(msg)) + def __del__(self): # Ensure that we don't leave any bitcoind processes lying around after # the test ends @@ -110,7 +117,7 @@ class TestNode(): # Should only happen on test failure # Avoid using logger, as that may have already been shutdown when # this destructor is called. - print("Cleaning up leftover process") + print(self._node_msg("Cleaning up leftover process")) self.process.kill() def __getattr__(self, name): @@ -118,7 +125,7 @@ class TestNode(): if self.use_cli: return getattr(self.cli, name) else: - assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection" + assert self.rpc_connected and self.rpc is not None, self._node_msg("Error: no RPC connection") return getattr(self.rpc, name) def start(self, extra_args=None, stderr=None, *args, **kwargs): @@ -141,7 +148,8 @@ class TestNode(): poll_per_s = 4 for _ in range(poll_per_s * self.rpc_timeout): if self.process.poll() is not None: - raise FailedToStartError('bitcoind exited with status {} during initialization'.format(self.process.returncode)) + raise FailedToStartError(self._node_msg( + 'bitcoind exited with status {} during initialization'.format(self.process.returncode))) try: self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir) self.rpc.getblockcount() @@ -160,14 +168,13 @@ class TestNode(): if "No RPC credentials" not in str(e): raise time.sleep(1.0 / poll_per_s) - raise AssertionError("Unable to connect to bitcoind") + self._raise_assertion_error("Unable to connect to bitcoind") def get_wallet_rpc(self, wallet_name): if self.use_cli: return self.cli("-rpcwallet={}".format(wallet_name)) else: - assert self.rpc_connected - assert self.rpc + assert self.rpc_connected and self.rpc, self._node_msg("RPC not connected") wallet_path = "wallet/%s" % wallet_name return self.rpc / wallet_path @@ -194,7 +201,8 @@ class TestNode(): return False # process has stopped. Assert that it didn't return an error code. - assert_equal(return_code, 0) + assert return_code == 0, self._node_msg( + "Node returned non-zero exit code (%d) when stopping" % return_code) self.running = False self.process = None self.rpc_connected = False @@ -229,19 +237,22 @@ class TestNode(): stderr = log_stderr.read().decode('utf-8').strip() if match == ErrorMatch.PARTIAL_REGEX: if re.search(expected_msg, stderr, flags=re.MULTILINE) is None: - raise AssertionError('Expected message "{}" does not partially match stderr:\n"{}"'.format(expected_msg, stderr)) + self._raise_assertion_error( + 'Expected message "{}" does not partially match stderr:\n"{}"'.format(expected_msg, stderr)) elif match == ErrorMatch.FULL_REGEX: if re.fullmatch(expected_msg, stderr) is None: - raise AssertionError('Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) + self._raise_assertion_error( + 'Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) elif match == ErrorMatch.FULL_TEXT: if expected_msg != stderr: - raise AssertionError('Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) + self._raise_assertion_error( + 'Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) else: if expected_msg is None: assert_msg = "bitcoind should have exited with an error" else: assert_msg = "bitcoind should have exited with expected error " + expected_msg - raise AssertionError(assert_msg) + self._raise_assertion_error(assert_msg) def node_encrypt_wallet(self, passphrase): """"Encrypts the wallet. @@ -272,7 +283,7 @@ class TestNode(): Convenience property - most tests only use a single p2p connection to each node, so this saves having to write node.p2ps[0] many times.""" - assert self.p2ps, "No p2p connection" + assert self.p2ps, self._node_msg("No p2p connection") return self.p2ps[0] def disconnect_p2ps(self): diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 518c16b5f1..da43280ced 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -120,6 +120,9 @@ BASE_SCRIPTS = [ 'feature_nulldummy.py', 'mempool_accept.py', 'wallet_import_rescan.py', + 'rpc_bind.py --ipv4', + 'rpc_bind.py --ipv6', + 'rpc_bind.py --nonloopback', 'mining_basic.py', 'wallet_bumpfee.py', 'rpc_named_arguments.py', @@ -160,7 +163,6 @@ EXTENDED_SCRIPTS = [ 'p2p_timeouts.py', # vv Tests less than 60s vv 'p2p_feefilter.py', - 'rpc_bind.py', # vv Tests less than 30s vv 'feature_assumevalid.py', 'example_test.py', diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 0a24d34398..60bf2e7e13 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -91,6 +91,19 @@ class MultiWalletTest(BitcoinTestFramework): open(not_a_dir, 'a').close() self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory') + self.log.info("Do not allow -zapwallettxes with multiwallet") + self.nodes[0].assert_start_raises_init_error(['-zapwallettxes', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=1', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=2', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + + self.log.info("Do not allow -salvagewallet with multiwallet") + self.nodes[0].assert_start_raises_init_error(['-salvagewallet', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-salvagewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file") + + self.log.info("Do not allow -upgradewallet with multiwallet") + self.nodes[0].assert_start_raises_init_error(['-upgradewallet', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-upgradewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file") + # if wallets/ doesn't exist, datadir should be the default wallet dir wallet_dir2 = data_dir('walletdir') os.rename(wallet_dir(), wallet_dir2) |