diff options
71 files changed, 932 insertions, 458 deletions
diff --git a/configure.ac b/configure.ac index b38e480f27..c4bc061d74 100644 --- a/configure.ac +++ b/configure.ac @@ -630,11 +630,7 @@ if test x$use_hardening != xno; then AX_CHECK_LINK_FLAG([[-Wl,--high-entropy-va]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--high-entropy-va"]) AX_CHECK_LINK_FLAG([[-Wl,-z,relro]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"]) AX_CHECK_LINK_FLAG([[-Wl,-z,now]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"]) - - if test x$TARGET_OS != xwindows; then - AX_CHECK_COMPILE_FLAG([-fPIE],[PIE_FLAGS="-fPIE"]) - AX_CHECK_LINK_FLAG([[-pie]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"]) - fi + AX_CHECK_LINK_FLAG([[-fPIE -pie]], [PIE_FLAGS="-fPIE"; HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"],, [[$CXXFLAG_WERROR]]) case $host in *mingw*) diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py index c817e794b9..e7cccaab03 100755 --- a/contrib/devtools/copyright_header.py +++ b/contrib/devtools/copyright_header.py @@ -286,7 +286,7 @@ Arguments: def report_cmd(argv): if len(argv) == 2: sys.exit(REPORT_USAGE) - + base_directory = argv[2] if not os.path.exists(base_directory): sys.exit("*** bad <base_directory>: %s" % base_directory) @@ -444,7 +444,7 @@ def print_file_action_message(filename, action): def update_cmd(argv): if len(argv) != 3: sys.exit(UPDATE_USAGE) - + base_directory = argv[2] if not os.path.exists(base_directory): sys.exit("*** bad base_directory: %s" % base_directory) @@ -570,13 +570,13 @@ def insert_cmd(argv): _, extension = os.path.splitext(filename) if extension not in ['.h', '.cpp', '.cc', '.c', '.py']: sys.exit("*** cannot insert for file extension %s" % extension) - + if extension == '.py': style = 'python' else: style = 'cpp' exec_insert_header(filename, style) - + ################################################################################ # UI ################################################################################ diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py index 9c666673cf..187ef75fb7 100755 --- a/contrib/devtools/github-merge.py +++ b/contrib/devtools/github-merge.py @@ -21,7 +21,8 @@ import argparse import hashlib import subprocess import sys -import json,codecs +import json +import codecs try: from urllib.request import Request,urlopen except: diff --git a/contrib/devtools/lint-python-shebang.sh b/contrib/devtools/lint-python-shebang.sh new file mode 100755 index 0000000000..f5c5971c03 --- /dev/null +++ b/contrib/devtools/lint-python-shebang.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Shebang must use python3 (not python or python2) +EXIT_CODE=0 +for PYTHON_FILE in $(git ls-files -- "*.py"); do + if [[ $(head -c 2 "${PYTHON_FILE}") == "#!" && + $(head -n 1 "${PYTHON_FILE}") != "#!/usr/bin/env python3" ]]; then + echo "Missing shebang \"#!/usr/bin/env python3\" in ${PYTHON_FILE} (do not use python or python2)" + EXIT_CODE=1 + fi +done +exit ${EXIT_CODE} diff --git a/contrib/devtools/lint-python.sh b/contrib/devtools/lint-python.sh index 1469ce1640..239337000d 100755 --- a/contrib/devtools/lint-python.sh +++ b/contrib/devtools/lint-python.sh @@ -15,6 +15,8 @@ # E133 closing bracket is missing indentation # E223 tab before operator # E224 tab after operator +# E242 tab after ',' +# E266 too many leading '#' for block comment # E271 multiple spaces after keyword # E272 multiple spaces before keyword # E273 tab after keyword @@ -22,7 +24,10 @@ # E275 missing whitespace after keyword # E304 blank lines found after function decorator # E306 expected 1 blank line before a nested definition +# E401 multiple imports on one line +# E402 module level import not at top of file # E502 the backslash is redundant between brackets +# E701 multiple statements on one line (colon) # E702 multiple statements on one line (semicolon) # E703 statement ends with a semicolon # E714 test for object identity should be "is not" @@ -30,6 +35,8 @@ # E741 do not use variables named "l", "O", or "I" # E742 do not define classes named "l", "O", or "I" # E743 do not define functions named "l", "O", or "I" +# E901 SyntaxError: invalid syntax +# E902 TokenError: EOF in multi-line string # F401 module imported but unused # F402 import module from line N shadowed by loop variable # F404 future import(s) name after other statements @@ -49,16 +56,19 @@ # F707 an except: block as not the last exception handler # F811 redefinition of unused name from line N # F812 list comprehension redefines 'foo' from line N +# F821 undefined name 'Foo' # F822 undefined name name in __all__ # F823 local variable name … referenced before assignment # F831 duplicate argument name in function definition # F841 local variable 'foo' is assigned to but never used # W292 no newline at end of file +# W293 blank line contains whitespace # W504 line break after binary operator # W601 .has_key() is deprecated, use "in" # W602 deprecated form of raising exception # W603 "<>" is deprecated, use "!=" # W604 backticks are deprecated, use "repr()" # W605 invalid escape sequence "x" +# W606 'async' and 'await' are reserved keywords starting with Python 3.7 -flake8 --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E271,E272,E273,E274,E275,E304,E306,E502,E702,E703,E714,E721,E741,E742,E743,F401,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F822,F823,F831,F841,W292,W504,W601,W602,W603,W604,W605 . +flake8 --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 . diff --git a/contrib/devtools/optimize-pngs.py b/contrib/devtools/optimize-pngs.py index 565b199125..a75731ef76 100755 --- a/contrib/devtools/optimize-pngs.py +++ b/contrib/devtools/optimize-pngs.py @@ -41,19 +41,19 @@ for folder in folders: file_path = os.path.join(absFolder, file) fileMetaMap = {'file' : file, 'osize': os.path.getsize(file_path), 'sha256Old' : file_hash(file_path)} fileMetaMap['contentHashPre'] = content_hash(file_path) - + try: subprocess.call([pngcrush, "-brute", "-ow", "-rem", "gAMA", "-rem", "cHRM", "-rem", "iCCP", "-rem", "sRGB", "-rem", "alla", "-rem", "text", file_path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except: print("pngcrush is not installed, aborting...") sys.exit(0) - + #verify if "Not a PNG file" in subprocess.check_output([pngcrush, "-n", "-v", file_path], stderr=subprocess.STDOUT, universal_newlines=True): print("PNG file "+file+" is corrupted after crushing, check out pngcursh version") sys.exit(1) - + fileMetaMap['sha256New'] = file_hash(file_path) fileMetaMap['contentHashPost'] = content_hash(file_path) @@ -72,5 +72,5 @@ for fileDict in outputArray: totalSaveBytes += fileDict['osize'] - fileDict['psize'] noHashChange = noHashChange and (oldHash == newHash) print(fileDict['file']+"\n size diff from: "+str(fileDict['osize'])+" to: "+str(fileDict['psize'])+"\n old sha256: "+oldHash+"\n new sha256: "+newHash+"\n") - + print("completed. Checksum stable: "+str(noHashChange)+". Total reduction: "+str(totalSaveBytes)+" bytes") diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py index 22f5ee20f7..ee87c8bab4 100755 --- a/contrib/devtools/test-security-check.py +++ b/contrib/devtools/test-security-check.py @@ -1,11 +1,10 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # Copyright (c) 2015-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. ''' Test script for security-check.py ''' -from __future__ import division,print_function import subprocess import unittest @@ -22,7 +21,7 @@ def write_testcode(filename): def call_security_check(cc, source, executable, options): subprocess.check_call([cc,source,'-o',executable] + options) - p = subprocess.Popen(['./security-check.py',executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + p = subprocess.Popen(['./security-check.py',executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() return (p.returncode, stdout.rstrip()) diff --git a/contrib/linearize/linearize-data.py b/contrib/linearize/linearize-data.py index 4969e96827..c609e9b336 100755 --- a/contrib/linearize/linearize-data.py +++ b/contrib/linearize/linearize-data.py @@ -21,7 +21,6 @@ from binascii import hexlify, unhexlify settings = {} -##### Switch endian-ness ##### def hex_switchEndian(s): """ Switches the endianness of a hex string (in pairs of hex chars) """ pairList = [s[i:i+2].encode() for i in range(0, len(s), 2)] diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py index 6b69c5b3a3..e1304e26d0 100755 --- a/contrib/linearize/linearize-hashes.py +++ b/contrib/linearize/linearize-hashes.py @@ -21,7 +21,6 @@ import os.path settings = {} -##### Switch endian-ness ##### def hex_switchEndian(s): """ Switches the endianness of a hex string (in pairs of hex chars) """ pairList = [s[i:i+2].encode() for i in range(0, len(s), 2)] diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index 2790ef4acd..72eb7255f3 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -34,7 +34,8 @@ These should be pasted into `src/chainparamsseeds.h`. from base64 import b32decode from binascii import a2b_hex -import sys, os +import sys +import os import re # ipv4 in ipv6 prefix @@ -46,7 +47,7 @@ def name_to_ipv6(addr): if len(addr)>6 and addr.endswith('.onion'): vchAddr = b32decode(addr[0:-6], True) if len(vchAddr) != 16-len(pchOnionCat): - raise ValueError('Invalid onion %s' % s) + raise ValueError('Invalid onion %s' % vchAddr) return pchOnionCat + vchAddr elif '.' in addr: # IPv4 return pchIPv4 + bytearray((int(x) for x in addr.split('.'))) @@ -132,7 +133,7 @@ def main(): with open(os.path.join(indir,'nodes_test.txt'),'r') as f: process_nodes(g, f, 'pnSeed6_test', 18333) g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n') - + if __name__ == '__main__': main() diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py index 6e253c994d..59044e701a 100755 --- a/contrib/seeds/makeseeds.py +++ b/contrib/seeds/makeseeds.py @@ -6,6 +6,11 @@ # Generate seeds.txt from Pieter's DNS seeder # +import re +import sys +import dns.resolver +import collections + NSEEDS=512 MAX_SEEDS_PER_ASN=2 @@ -22,11 +27,6 @@ SUSPICIOUS_HOSTS = { "54.94.195.96", "54.94.200.247" } -import re -import sys -import dns.resolver -import collections - PATTERN_IPV4 = re.compile(r"^((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})):(\d+)$") PATTERN_IPV6 = re.compile(r"^\[([0-9a-z:]+)\]:(\d+)$") PATTERN_ONION = re.compile(r"^([abcdefghijklmnopqrstuvwxyz234567]{16}\.onion):(\d+)$") diff --git a/contrib/testgen/base58.py b/contrib/testgen/base58.py index 0dbb79a707..071bc722b0 100644 --- a/contrib/testgen/base58.py +++ b/contrib/testgen/base58.py @@ -43,8 +43,10 @@ def b58encode(v): # leading 0-bytes in the input become leading-1s nPad = 0 for c in v: - if c == 0: nPad += 1 - else: break + if c == 0: + nPad += 1 + else: + break return (__b58chars[0]*nPad) + result @@ -98,7 +100,8 @@ def b58decode_chk(v): def get_bcaddress_version(strAddress): """ Returns None if strAddress is invalid. Otherwise returns integer version of address. """ addr = b58decode_chk(strAddress) - if addr is None or len(addr)!=21: return None + if addr is None or len(addr)!=21: + return None version = addr[0] return ord(version) diff --git a/contrib/testgen/gen_base58_test_vectors.py b/contrib/testgen/gen_base58_test_vectors.py index 4351605786..de15657d27 100755 --- a/contrib/testgen/gen_base58_test_vectors.py +++ b/contrib/testgen/gen_base58_test_vectors.py @@ -74,12 +74,12 @@ def gen_invalid_vector(template, corrupt_prefix, randomize_payload_size, corrupt prefix = os.urandom(1) else: prefix = bytearray(template[0]) - + if randomize_payload_size: payload = os.urandom(max(int(random.expovariate(0.5)), 50)) else: payload = os.urandom(template[1]) - + if corrupt_suffix: suffix = os.urandom(len(template[2])) else: @@ -114,7 +114,8 @@ def gen_invalid_vectors(): yield val, if __name__ == '__main__': - import sys, json + import sys + import json iters = {'valid':gen_valid_vectors, 'invalid':gen_invalid_vectors} try: uiter = iters[sys.argv[1]] @@ -124,7 +125,7 @@ if __name__ == '__main__': count = int(sys.argv[2]) except IndexError: count = 0 - + data = list(islice(uiter(), count)) json.dump(data, sys.stdout, sort_keys=True, indent=4) sys.stdout.write('\n') diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 8491927552..745c9e1157 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -8,7 +8,8 @@ $(package)_dependencies=openssl zlib $(package)_linux_dependencies=freetype fontconfig libxcb libX11 xproto libXext $(package)_build_subdir=qtbase $(package)_qt_libs=corelib network widgets gui plugins testlib -$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch fix_qt_pkgconfig.patch fix-cocoahelpers-macos.patch +$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch +$(package)_patches+=fix_qt_pkgconfig.patch fix-cocoahelpers-macos.patch qfixed-coretext.patch $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) $(package)_qttranslations_sha256_hash=3a15aebd523c6d89fb97b2d3df866c94149653a26d27a00aac9b6d3020bc5a1d @@ -141,6 +142,7 @@ define $(package)_preprocess_cmds patch -p1 < $($(package)_patch_dir)/fix-xcb-include-order.patch && \ patch -p1 < $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \ patch -p1 < $($(package)_patch_dir)/fix-cocoahelpers-macos.patch && \ + patch -p1 < $($(package)_patch_dir)/qfixed-coretext.patch && \ echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ diff --git a/depends/patches/qt/qfixed-coretext.patch b/depends/patches/qt/qfixed-coretext.patch new file mode 100644 index 0000000000..aa56f1e1de --- /dev/null +++ b/depends/patches/qt/qfixed-coretext.patch @@ -0,0 +1,34 @@ +From dbdd5f0ffbce52c8b789ed09f1aa3f1da6c02e23 Mon Sep 17 00:00:00 2001 +From: Gabriel de Dietrich <gabriel.dedietrich@qt.io> +Date: Fri, 30 Mar 2018 11:58:16 -0700 +Subject: [PATCH] QCoreTextFontEngine: Fix build with Xcode 9.3 + +Apple LLVM version 9.1.0 (clang-902.0.39.1) + +Error message: + +.../qfontengine_coretext.mm:827:20: error: qualified reference to + 'QFixed' is a constructor name rather than a type in this context + return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); + +Change-Id: Iebe26b3b087a16b10664208fc8851cbddb47f043 +Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com> +--- + src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git old/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm new/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +index 25ff69d877d..98b753eff96 100644 +--- old/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm ++++ new/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +@@ -824,7 +824,7 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gl + + QFixed QCoreTextFontEngine::emSquareSize() const + { +- return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); ++ return QFixed(int(CTFontGetUnitsPerEm(ctfont))); + } + + QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const +-- +2.16.3
\ No newline at end of file diff --git a/doc/release-notes-pr12823.md b/doc/release-notes-pr12823.md new file mode 100644 index 0000000000..b493908716 --- /dev/null +++ b/doc/release-notes-pr12823.md @@ -0,0 +1,20 @@ +Configuration sections for testnet and regtest +---------------------------------------------- + +It is now possible for a single configuration file to set different +options for different networks. This is done by using sections or by +prefixing the option with the network, such as: + + main.uacomment=bitcoin + test.uacomment=bitcoin-testnet + regtest.uacomment=regtest + [main] + mempoolsize=300 + [test] + mempoolsize=100 + [regtest] + mempoolsize=20 + +The `addnode=`, `connect=`, `port=`, `bind=`, `rpcport=`, `rpcbind=` +and `wallet=` options will only apply to mainnet when specified in the +configuration file, unless a network is specified. diff --git a/share/rpcauth/rpcauth.py b/share/rpcauth/rpcauth.py index d6580281d4..f9b9787514 100755 --- a/share/rpcauth/rpcauth.py +++ b/share/rpcauth/rpcauth.py @@ -3,7 +3,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -import hashlib import sys import os from random import SystemRandom @@ -25,15 +24,9 @@ hexseq = list(map(hex, salt_sequence)) salt = "".join([x[2:] for x in hexseq]) #Create 32 byte b64 password -password = base64.urlsafe_b64encode(os.urandom(32)) +password = base64.urlsafe_b64encode(os.urandom(32)).decode("utf-8") -digestmod = hashlib.sha256 - -if sys.version_info.major >= 3: - password = password.decode('utf-8') - digestmod = 'SHA256' - -m = hmac.new(bytearray(salt, 'utf-8'), bytearray(password, 'utf-8'), digestmod) +m = hmac.new(bytearray(salt, 'utf-8'), bytearray(password, 'utf-8'), "SHA256") result = m.hexdigest() print("String to be appended to bitcoin.conf:") diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index deb8212a8f..07ad09ea7b 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -545,12 +545,10 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) if (!findSighashFlags(nHashType, flagStr)) throw std::runtime_error("unknown sighash flag/sign option"); - std::vector<CTransaction> txVariants; - txVariants.push_back(tx); - // mergedTx will end up with all the signatures; it // starts as a clone of the raw tx: - CMutableTransaction mergedTx(txVariants[0]); + CMutableTransaction mergedTx{tx}; + const CTransaction txv{tx}; CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); @@ -633,7 +631,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { - CTxIn& txin = mergedTx.vin[i]; + const CTxIn& txin = mergedTx.vin[i]; const Coin& coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { continue; @@ -644,11 +642,10 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata); + ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata); // ... and merge in other signatures: - for (const CTransaction& txv : txVariants) - sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); + sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); UpdateTransaction(mergedTx, i, sigdata); } diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index e840a2ed30..3ef9c2cfe5 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -47,4 +47,5 @@ std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain void SelectBaseParams(const std::string& chain) { globalChainBaseParams = CreateBaseChainParams(chain); + gArgs.SelectConfigNetwork(chain); } diff --git a/src/init.cpp b/src/init.cpp index 9edd93000f..8538630d7e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -76,19 +76,18 @@ std::unique_ptr<PeerLogicValidation> peerLogic; class DummyWalletInit : public WalletInitInterface { public: - std::string GetHelpString(bool showDebug) override {return std::string{};} - bool ParameterInteraction() override {return true;} - void RegisterRPC(CRPCTable &) override {} - bool Verify() override {return true;} - bool Open() override {LogPrintf("No wallet support compiled in!\n"); return true;} - void Start(CScheduler& scheduler) override {} - void Flush() override {} - void Stop() override {} - void Close() override {} + std::string GetHelpString(bool showDebug) const override {return std::string{};} + bool ParameterInteraction() const override {return true;} + void RegisterRPC(CRPCTable &) const override {} + bool Verify() const override {return true;} + bool Open() const override {LogPrintf("No wallet support compiled in!\n"); return true;} + void Start(CScheduler& scheduler) const override {} + void Flush() const override {} + void Stop() const override {} + void Close() const override {} }; -static DummyWalletInit g_dummy_wallet_init; -WalletInitInterface* const g_wallet_init_interface = &g_dummy_wallet_init; +const WalletInitInterface& g_wallet_init_interface = DummyWalletInit(); #endif #if ENABLE_ZMQ @@ -204,7 +203,7 @@ void Shutdown() StopREST(); StopRPC(); StopHTTPServer(); - g_wallet_init_interface->Flush(); + g_wallet_init_interface.Flush(); StopMapPort(); // Because these depend on each-other, we make sure that neither can be @@ -262,7 +261,7 @@ void Shutdown() pcoinsdbview.reset(); pblocktree.reset(); } - g_wallet_init_interface->Stop(); + g_wallet_init_interface.Stop(); #if ENABLE_ZMQ if (pzmqNotificationInterface) { @@ -282,7 +281,7 @@ void Shutdown() UnregisterAllValidationInterfaces(); GetMainSignals().UnregisterBackgroundSignalScheduler(); GetMainSignals().UnregisterWithMempoolSignals(mempool); - g_wallet_init_interface->Close(); + g_wallet_init_interface.Close(); globalVerifyHandle.reset(); ECC_Stop(); LogPrintf("%s: done\n", __func__); @@ -323,7 +322,7 @@ void OnRPCStopped() { uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange); RPCNotifyBlockChange(false, nullptr); - cvBlockChange.notify_all(); + g_best_block_cv.notify_all(); LogPrint(BCLog::RPC, "RPC stopped.\n"); } @@ -425,7 +424,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-whitelist=<IP address or network>", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.") + " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway")); - strUsage += g_wallet_init_interface->GetHelpString(showDebug); + strUsage += g_wallet_init_interface.GetHelpString(showDebug); #if ENABLE_ZMQ strUsage += HelpMessageGroup(_("ZeroMQ notification options:")); @@ -803,6 +802,11 @@ void InitParameterInteraction() if (gArgs.SoftSetBoolArg("-whitelistrelay", true)) LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__); } + + // Warn if network-specific options (-addnode, -connect, etc) are + // specified in default section of config file, but not overridden + // on the command line or in this network's section of the config file. + gArgs.WarnForSectionOnlyArgs(); } static std::string ResolveErrMsg(const char * const optname, const std::string& strBind) @@ -810,14 +814,25 @@ static std::string ResolveErrMsg(const char * const optname, const std::string& return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind); } +/** + * Initialize global loggers. + * + * Note that this is called very early in the process lifetime, so you should be + * careful about what global state you rely on here. + */ void InitLogging() { - fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false); + // Add newlines to the logfile to distinguish this execution from the last + // one; called before console logging is set up, so this is only sent to + // debug.log. + LogPrintf("\n\n\n\n\n"); + + fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false)); + fPrintToDebugLog = !gArgs.IsArgNegated("-debuglogfile"); fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS); - LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); std::string version_string = FormatFullVersion(); #ifdef DEBUG version_string += " (debug build)"; @@ -1093,7 +1108,7 @@ bool AppInitParameterInteraction() return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); - if (!g_wallet_init_interface->ParameterInteraction()) return false; + if (!g_wallet_init_interface.ParameterInteraction()) return false; fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); @@ -1211,13 +1226,12 @@ bool AppInitMain() #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif - if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) { - // Do this first since it both loads a bunch of debug.log into memory, - // and because this needs to happen before any other debug.log printing - ShrinkDebugFile(); - } - if (fPrintToDebugLog) { + if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) { + // Do this first since it both loads a bunch of debug.log into memory, + // and because this needs to happen before any other debug.log printing + ShrinkDebugFile(); + } if (!OpenDebugLog()) { return InitError(strprintf("Could not open debug log file %s", GetDebugLogPath().string())); } @@ -1259,7 +1273,7 @@ bool AppInitMain() * available in the GUI RPC console even if external calls are disabled. */ RegisterAllCoreRPCCommands(tableRPC); - g_wallet_init_interface->RegisterRPC(tableRPC); + g_wallet_init_interface.RegisterRPC(tableRPC); /* Start the RPC server already. It will be started in "warmup" mode * and not really process calls already (but it will signify connections @@ -1276,7 +1290,7 @@ bool AppInitMain() int64_t nStart; // ********************************************************* Step 5: verify wallet database integrity - if (!g_wallet_init_interface->Verify()) return false; + if (!g_wallet_init_interface.Verify()) return false; // ********************************************************* Step 6: network initialization // Note that we absolutely cannot open any actual connections @@ -1595,7 +1609,7 @@ bool AppInitMain() fFeeEstimatesInitialized = true; // ********************************************************* Step 8: load wallet - if (!g_wallet_init_interface->Open()) return false; + if (!g_wallet_init_interface.Open()) return false; // ********************************************************* Step 9: data directory maintenance @@ -1741,7 +1755,7 @@ bool AppInitMain() SetRPCWarmupFinished(); uiInterface.InitMessage(_("Done loading")); - g_wallet_init_interface->Start(scheduler); + g_wallet_init_interface.Start(scheduler); return true; } diff --git a/src/init.h b/src/init.h index 829c110112..000c8c95e4 100644 --- a/src/init.h +++ b/src/init.h @@ -13,7 +13,7 @@ class CScheduler; class CWallet; class WalletInitInterface; -extern WalletInitInterface* const g_wallet_init_interface; +extern const WalletInitInterface& g_wallet_init_interface; namespace boost { diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index fbf631f7da..3e6f0d6728 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -130,6 +130,7 @@ public: { return m_wallet.ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase); } + void abortRescan() override { m_wallet.AbortRescan(); } bool backupWallet(const std::string& filename) override { return m_wallet.BackupWallet(filename); } std::string getWalletName() override { return m_wallet.GetName(); } bool getKeyFromPool(bool internal, CPubKey& pub_key) override diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 3e27242c2c..dfe3d5f711 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -65,6 +65,9 @@ public: virtual bool changeWalletPassphrase(const SecureString& old_wallet_passphrase, const SecureString& new_wallet_passphrase) = 0; + //! Abort a rescan. + virtual void abortRescan() = 0; + //! Back up wallet. virtual bool backupWallet(const std::string& filename) = 0; diff --git a/src/net.cpp b/src/net.cpp index 342dfbaeb9..356a66563f 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -124,7 +124,7 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer) return nBestScore >= 0; } -//! Convert the pnSeeds6 array into usable address objects. +//! Convert the pnSeed6 array into usable address objects. static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn) { // It'll only connect to one or two seed nodes because once it connects, @@ -368,7 +368,7 @@ static CAddress GetBindAddress(SOCKET sock) return addr_bind; } -CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) +CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection) { if (pszDest == nullptr) { if (IsLocal(addrConnect)) @@ -432,7 +432,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo if (hSocket == INVALID_SOCKET) { return nullptr; } - connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout); + connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout, manual_connection); } if (!proxyConnectionFailed) { // If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to @@ -1992,7 +1992,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai } else if (FindNode(std::string(pszDest))) return; - CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure); + CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection); if (!pnode) return; @@ -338,7 +338,7 @@ private: CNode* FindNode(const CService& addr); bool AttemptToEvictConnection(); - CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure); + CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection); bool IsWhitelistedRange(const CNetAddr &addr); void DeleteNode(CNode* pnode); diff --git a/src/netbase.cpp b/src/netbase.cpp index 92ac1c4c85..5d3d2f25c8 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -9,6 +9,7 @@ #include <sync.h> #include <uint256.h> #include <random.h> +#include <tinyformat.h> #include <util.h> #include <utilstrencodings.h> @@ -468,7 +469,17 @@ SOCKET CreateSocket(const CService &addrConnect) return hSocket; } -bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout) +template<typename... Args> +static void LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args) { + std::string error_message = tfm::format(fmt, args...); + if (manual_connection) { + LogPrintf("%s\n", error_message); + } else { + LogPrint(BCLog::NET, "%s\n", error_message); + } +} + +bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); @@ -513,7 +524,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i } if (nRet != 0) { - LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString(), NetworkErrorString(nRet)); + LogConnectFailure(manual_connection, "connect() to %s failed after select(): %s", addrConnect.ToString(), NetworkErrorString(nRet)); return false; } } @@ -523,7 +534,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i else #endif { - LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); + LogConnectFailure(manual_connection, "connect() to %s failed: %s", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); return false; } } @@ -581,7 +592,7 @@ bool IsProxy(const CNetAddr &addr) { bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocket, int nTimeout, bool *outProxyConnectionFailed) { // first connect to proxy server - if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout)) { + if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout, true)) { if (outProxyConnectionFailed) *outProxyConnectionFailed = true; return false; @@ -601,6 +612,7 @@ bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int } return true; } + bool LookupSubNet(const char* pszName, CSubNet& ret) { std::string strSubnet(pszName); diff --git a/src/netbase.h b/src/netbase.h index c0921b6441..50d4bc54fa 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -52,7 +52,7 @@ bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, CService LookupNumeric(const char *pszName, int portDefault = 0); bool LookupSubNet(const char *pszName, CSubNet& subnet); SOCKET CreateSocket(const CService &addrConnect); -bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout); +bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout, bool manual_connection); bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed); /** Return readable error string for a network error code */ std::string NetworkErrorString(int err); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index c3f65fb2ab..5963bf371a 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -230,7 +230,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) return false; // Check P2WSH standard limits - if (witnessversion == 0 && witnessprogram.size() == 32) { + if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { if (tx.vin[i].scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return false; size_t sizeWitnessStack = tx.vin[i].scriptWitness.stack.size() - 1; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 8b9b85c8c9..d529595dec 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -315,9 +315,9 @@ void WalletView::showProgress(const QString &title, int nProgress) progressDialog = new QProgressDialog(title, "", 0, 100); progressDialog->setWindowModality(Qt::ApplicationModal); progressDialog->setMinimumDuration(0); - progressDialog->setCancelButton(0); progressDialog->setAutoClose(false); progressDialog->setValue(0); + progressDialog->setCancelButtonText(tr("Cancel")); } else if (nProgress == 100) { @@ -327,8 +327,13 @@ void WalletView::showProgress(const QString &title, int nProgress) progressDialog->deleteLater(); } } - else if (progressDialog) - progressDialog->setValue(nProgress); + else if (progressDialog) { + if (progressDialog->wasCanceled()) { + getWalletModel()->wallet().abortRescan(); + } else { + progressDialog->setValue(nProgress); + } + } } void WalletView::requestedSyncWarningInfo() diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 75bc983200..9a7c4b8e6d 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -471,10 +471,10 @@ UniValue getblocktemplate(const JSONRPCRequest& request) { checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); - WaitableLock lock(csBestBlock); - while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) + WaitableLock lock(g_best_block_mutex); + while (g_best_block == hashWatchedChain && IsRPCRunning()) { - if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout) + if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout) { // Timeout: Check transactions for update if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index cc616ce5e6..543e4fc358 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -828,7 +828,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mtx.vout.size())) { - ProduceSignature(MutableTransactionSignatureCreator(keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata); + ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata); } sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i)); diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 182f4a3327..338e07e24e 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1361,7 +1361,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, CScript scriptPubKey; if (witversion == 0) { - if (program.size() == 32) { + if (program.size() == WITNESS_V0_SCRIPTHASH_SIZE) { // Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness if (witness.stack.size() == 0) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY); @@ -1373,7 +1373,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); } - } else if (program.size() == 20) { + } else if (program.size() == WITNESS_V0_KEYHASH_SIZE) { // Special case for pay-to-pubkeyhash; signature + pubkey in witness if (witness.stack.size() != 2) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness @@ -1530,10 +1530,10 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C size_t static WitnessSigOps(int witversion, const std::vector<unsigned char>& witprogram, const CScriptWitness& witness, int flags) { if (witversion == 0) { - if (witprogram.size() == 20) + if (witprogram.size() == WITNESS_V0_KEYHASH_SIZE) return 1; - if (witprogram.size() == 32 && witness.stack.size() > 0) { + if (witprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE && witness.stack.size() > 0) { CScript subscript(witness.stack.back().begin(), witness.stack.back().end()); return subscript.GetSigOpCount(true); } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index bb7750d783..601a4a866d 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -129,6 +129,10 @@ enum class SigVersion WITNESS_V0 = 1, }; +/** Signature hash sizes */ +static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32; +static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20; + uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); class BaseSignatureChecker diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 05bc5e9bd6..b826bcfe20 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -146,7 +146,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& if (keystore.HaveWatchOnly(scriptPubKey)) { // TODO: This could be optimized some by doing some work after the above solver SignatureData sigs; - return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; + return ProduceSignature(keystore, DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; } return ISMINE_NO; } diff --git a/src/script/script.h b/src/script/script.h index 8e5a792c7d..59ceff247c 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -615,15 +615,6 @@ public: return nFound; } - int Find(opcodetype op) const - { - int nFound = 0; - opcodetype opcode; - for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);) - if (opcode == op) - ++nFound; - return nFound; - } /** * Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 910bb39ce6..ac35f17f3e 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -14,12 +14,12 @@ typedef std::vector<unsigned char> valtype; -TransactionSignatureCreator::TransactionSignatureCreator(const SigningProvider* provider, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(provider), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} +TransactionSignatureCreator::TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} -bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const +bool TransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const { CKey key; - if (!m_provider->GetKey(address, key)) + if (!provider.GetKey(address, key)) return false; // Signing with uncompressed keys is disabled in witness scripts @@ -33,16 +33,16 @@ bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, return true; } -static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) +static bool Sign1(const SigningProvider& provider, const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) { std::vector<unsigned char> vchSig; - if (!creator.CreateSig(vchSig, address, scriptCode, sigversion)) + if (!creator.CreateSig(provider, vchSig, address, scriptCode, sigversion)) return false; ret.push_back(vchSig); return true; } -static bool SignN(const std::vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) +static bool SignN(const SigningProvider& provider, const std::vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) { int nSigned = 0; int nRequired = multisigdata.front()[0]; @@ -50,7 +50,7 @@ static bool SignN(const std::vector<valtype>& multisigdata, const BaseSignatureC { const valtype& pubkey = multisigdata[i]; CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(keyID, creator, scriptCode, ret, sigversion)) + if (Sign1(provider, keyID, creator, scriptCode, ret, sigversion)) ++nSigned; } return nSigned==nRequired; @@ -62,7 +62,7 @@ static bool SignN(const std::vector<valtype>& multisigdata, const BaseSignatureC * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. * Returns false if scriptPubKey could not be completely satisfied. */ -static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, +static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion) { CScript scriptRet; @@ -82,20 +82,20 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, creator, scriptPubKey, ret, sigversion); + return Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) + if (!Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion)) return false; else { CPubKey vch; - creator.Provider().GetPubKey(keyID, vch); + provider.GetPubKey(keyID, vch); ret.push_back(ToByteVector(vch)); } return true; case TX_SCRIPTHASH: - if (creator.Provider().GetCScript(uint160(vSolutions[0]), scriptRet)) { + if (provider.GetCScript(uint160(vSolutions[0]), scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } @@ -103,7 +103,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP case TX_MULTISIG: ret.push_back(valtype()); // workaround CHECKMULTISIG bug - return (SignN(vSolutions, creator, scriptPubKey, ret, sigversion)); + return (SignN(provider, vSolutions, creator, scriptPubKey, ret, sigversion)); case TX_WITNESS_V0_KEYHASH: ret.push_back(vSolutions[0]); @@ -111,7 +111,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP case TX_WITNESS_V0_SCRIPTHASH: CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin()); - if (creator.Provider().GetCScript(h160, scriptRet)) { + if (provider.GetCScript(h160, scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } @@ -137,11 +137,11 @@ static CScript PushAll(const std::vector<valtype>& values) return result; } -bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) +bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) { std::vector<valtype> result; txnouttype whichType; - bool solved = SignStep(creator, fromPubKey, result, whichType, SigVersion::BASE); + bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); @@ -152,7 +152,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu // the final scriptSig is the signatures from that // and then the serialized subscript: subscript = CScript(result[0].begin(), result[0].end()); - solved = solved && SignStep(creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH; + solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH; P2SH = true; } @@ -161,7 +161,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu CScript witnessscript; witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG; txnouttype subType; - solved = solved && SignStep(creator, witnessscript, result, subType, SigVersion::WITNESS_V0); + solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0); sigdata.scriptWitness.stack = result; result.clear(); } @@ -169,7 +169,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu { CScript witnessscript(result[0].begin(), result[0].end()); txnouttype subType; - solved = solved && SignStep(creator, witnessscript, result, subType, SigVersion::WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; + solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end())); sigdata.scriptWitness.stack = result; result.clear(); @@ -210,10 +210,10 @@ bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, C assert(nIn < txTo.vin.size()); CTransaction txToConst(txTo); - TransactionSignatureCreator creator(&provider, &txToConst, nIn, amount, nHashType); + TransactionSignatureCreator creator(&txToConst, nIn, amount, nHashType); SignatureData sigdata; - bool ret = ProduceSignature(creator, fromPubKey, sigdata); + bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata); UpdateTransaction(txTo, nIn, sigdata); return ret; } @@ -392,39 +392,37 @@ SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignature namespace { /** Dummy signature checker which accepts all signatures. */ -class DummySignatureChecker : public BaseSignatureChecker +class DummySignatureChecker final : public BaseSignatureChecker { public: DummySignatureChecker() {} + bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; } +}; +const DummySignatureChecker DUMMY_CHECKER; - bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override +class DummySignatureCreator final : public BaseSignatureCreator { +public: + DummySignatureCreator() {} + const BaseSignatureChecker& Checker() const override { return DUMMY_CHECKER; } + bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override { + // Create a dummy signature that is a valid DER-encoding + vchSig.assign(72, '\000'); + vchSig[0] = 0x30; + vchSig[1] = 69; + vchSig[2] = 0x02; + vchSig[3] = 33; + vchSig[4] = 0x01; + vchSig[4 + 33] = 0x02; + vchSig[5 + 33] = 32; + vchSig[6 + 33] = 0x01; + vchSig[6 + 33 + 32] = SIGHASH_ALL; return true; } }; -const DummySignatureChecker dummyChecker; -} // namespace - -const BaseSignatureChecker& DummySignatureCreator::Checker() const -{ - return dummyChecker; } -bool DummySignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const -{ - // Create a dummy signature that is a valid DER-encoding - vchSig.assign(72, '\000'); - vchSig[0] = 0x30; - vchSig[1] = 69; - vchSig[2] = 0x02; - vchSig[3] = 33; - vchSig[4] = 0x01; - vchSig[4 + 33] = 0x02; - vchSig[5 + 33] = 32; - vchSig[6 + 33] = 0x01; - vchSig[6 + 33 + 32] = SIGHASH_ALL; - return true; -} +const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(); bool IsSolvable(const SigningProvider& provider, const CScript& script) { @@ -432,14 +430,13 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script) // if we were to have the private keys. This is just to make sure that the script is valid and that, // if found in a transaction, we would still accept and relay that transaction. In particular, // it will reject witness outputs that require signing with an uncompressed public key. - DummySignatureCreator creator(&provider); SignatureData sigs; // Make sure that STANDARD_SCRIPT_VERIFY_FLAGS includes SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, the most // important property this function is designed to test for. static_assert(STANDARD_SCRIPT_VERIFY_FLAGS & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, "IsSolvable requires standard script flags to include WITNESS_PUBKEYTYPE"); - if (ProduceSignature(creator, script, sigs)) { + if (ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, script, sigs)) { // VerifyScript check is just defensive, and should never fail. - assert(VerifyScript(sigs.scriptSig, script, &sigs.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker())); + assert(VerifyScript(sigs.scriptSig, script, &sigs.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, DUMMY_CHECKER)); return true; } return false; diff --git a/src/script/sign.h b/src/script/sign.h index c301f0544f..cf3651c1de 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -26,19 +26,14 @@ public: virtual bool GetKey(const CKeyID &address, CKey& key) const =0; }; -/** Virtual base class for signature creators. */ +/** Interface for signature creators. */ class BaseSignatureCreator { -protected: - const SigningProvider* m_provider; - public: - explicit BaseSignatureCreator(const SigningProvider* provider) : m_provider(provider) {} - const SigningProvider& Provider() const { return *m_provider; } virtual ~BaseSignatureCreator() {} virtual const BaseSignatureChecker& Checker() const =0; /** Create a singular (non-script) signature. */ - virtual bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0; + virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0; }; /** A signature creator for transactions. */ @@ -50,25 +45,20 @@ class TransactionSignatureCreator : public BaseSignatureCreator { const TransactionSignatureChecker checker; public: - TransactionSignatureCreator(const SigningProvider* provider, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); + TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); const BaseSignatureChecker& Checker() const override { return checker; } - bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; + bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; }; class MutableTransactionSignatureCreator : public TransactionSignatureCreator { CTransaction tx; public: - MutableTransactionSignatureCreator(const SigningProvider* provider, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : TransactionSignatureCreator(provider, &tx, nInIn, amountIn, nHashTypeIn), tx(*txToIn) {} + MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : TransactionSignatureCreator(&tx, nInIn, amountIn, nHashTypeIn), tx(*txToIn) {} }; /** A signature creator that just produces 72-byte empty signatures. */ -class DummySignatureCreator : public BaseSignatureCreator { -public: - explicit DummySignatureCreator(const SigningProvider* provider) : BaseSignatureCreator(provider) {} - const BaseSignatureChecker& Checker() const override; - bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; -}; +extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR; struct SignatureData { CScript scriptSig; @@ -79,7 +69,7 @@ struct SignatureData { }; /** Produce a script signature using a generic signature creator. */ -bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); +bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); /** Produce a script signature for a transaction. */ bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType); diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 0b9053d7fc..76778112aa 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -66,12 +66,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v int witnessversion; std::vector<unsigned char> witnessprogram; if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { - if (witnessversion == 0 && witnessprogram.size() == 20) { + if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) { typeRet = TX_WITNESS_V0_KEYHASH; vSolutionsRet.push_back(witnessprogram); return true; } - if (witnessversion == 0 && witnessprogram.size() == 32) { + if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { typeRet = TX_WITNESS_V0_SCRIPTHASH; vSolutionsRet.push_back(witnessprogram); return true; diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 87976a9a07..71f3d727b1 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -314,7 +314,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // Sign SignatureData sigdata; - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata); + ProduceSignature(keystore, MutableTransactionSignatureCreator(&valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata); UpdateTransaction(valid_with_witness_tx, 0, sigdata); // This should be valid under all script flags. @@ -342,7 +342,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // Sign for (int i=0; i<2; ++i) { SignatureData sigdata; - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata); + ProduceSignature(keystore, MutableTransactionSignatureCreator(&tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata); UpdateTransaction(tx, i, sigdata); } diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index d41c43a795..344113b60c 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -159,17 +159,6 @@ BOOST_AUTO_TEST_CASE(util_HexStr) } -BOOST_AUTO_TEST_CASE(util_DateTimeStrFormat) -{ - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0), "1970-01-01 00:00:00"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0x7FFFFFFF), "2038-01-19 03:14:07"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 1317425777), "2011-09-30 23:36:17"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", 1317425777), "2011-09-30T23:36:17Z"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%H:%M:%SZ", 1317425777), "23:36:17Z"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M", 1317425777), "2011-09-30 23:36"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", 1317425777), "Fri, 30 Sep 2011 23:36:17 +0000"); -} - BOOST_AUTO_TEST_CASE(util_FormatISO8601DateTime) { BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777), "2011-09-30T23:36:17Z"); @@ -187,13 +176,22 @@ BOOST_AUTO_TEST_CASE(util_FormatISO8601Time) struct TestArgsManager : public ArgsManager { - std::map<std::string, std::string>& GetMapArgs() { return mapArgs; } - const std::map<std::string, std::vector<std::string> >& GetMapMultiArgs() { return mapMultiArgs; } - const std::unordered_set<std::string>& GetNegatedArgs() { return m_negated_args; } + TestArgsManager() { m_network_only_args.clear(); } + std::map<std::string, std::vector<std::string> >& GetOverrideArgs() { return m_override_args; } + std::map<std::string, std::vector<std::string> >& GetConfigArgs() { return m_config_args; } void ReadConfigString(const std::string str_config) { - std::istringstream stream(str_config); - ReadConfigStream(stream); + std::istringstream streamConfig(str_config); + { + LOCK(cs_args); + m_config_args.clear(); + } + ReadConfigStream(streamConfig); + } + void SetNetworkOnlyArg(const std::string arg) + { + LOCK(cs_args); + m_network_only_args.insert(arg); } }; @@ -203,22 +201,26 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters) const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"}; testArgs.ParseParameters(0, (char**)argv_test); - BOOST_CHECK(testArgs.GetMapArgs().empty() && testArgs.GetMapMultiArgs().empty()); + BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); testArgs.ParseParameters(1, (char**)argv_test); - BOOST_CHECK(testArgs.GetMapArgs().empty() && testArgs.GetMapMultiArgs().empty()); + BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); testArgs.ParseParameters(7, (char**)argv_test); // expectation: -ignored is ignored (program name argument), // -a, -b and -ccc end up in map, -d ignored because it is after // a non-option argument (non-GNU option parsing) - BOOST_CHECK(testArgs.GetMapArgs().size() == 3 && testArgs.GetMapMultiArgs().size() == 3); + BOOST_CHECK(testArgs.GetOverrideArgs().size() == 3 && testArgs.GetConfigArgs().empty()); BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc") && !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d")); - BOOST_CHECK(testArgs.GetMapMultiArgs().count("-a") && testArgs.GetMapMultiArgs().count("-b") && testArgs.GetMapMultiArgs().count("-ccc") - && !testArgs.GetMapMultiArgs().count("f") && !testArgs.GetMapMultiArgs().count("-d")); - - BOOST_CHECK(testArgs.GetMapArgs()["-a"] == "" && testArgs.GetMapArgs()["-ccc"] == "multiple"); + BOOST_CHECK(testArgs.GetOverrideArgs().count("-a") && testArgs.GetOverrideArgs().count("-b") && testArgs.GetOverrideArgs().count("-ccc") + && !testArgs.GetOverrideArgs().count("f") && !testArgs.GetOverrideArgs().count("-d")); + + BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].size() == 1); + BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].front() == ""); + BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].size() == 2); + BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].front() == "argument"); + BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].back() == "multiple"); BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2); } @@ -234,15 +236,14 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg) BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt); // Nothing else should be in the map - BOOST_CHECK(testArgs.GetMapArgs().size() == 6 && - testArgs.GetMapMultiArgs().size() == 6); + BOOST_CHECK(testArgs.GetOverrideArgs().size() == 6 && + testArgs.GetConfigArgs().empty()); // The -no prefix should get stripped on the way in. BOOST_CHECK(!testArgs.IsArgSet("-nob")); // The -b option is flagged as negated, and nothing else is BOOST_CHECK(testArgs.IsArgNegated("-b")); - BOOST_CHECK(testArgs.GetNegatedArgs().size() == 1); BOOST_CHECK(!testArgs.IsArgNegated("-a")); // Check expected values. @@ -267,8 +268,8 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) BOOST_CHECK(!testArgs.IsArgNegated("-foo")); BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == ""); - // A double negative is a positive. - BOOST_CHECK(testArgs.IsArgNegated("-bar")); + // A double negative is a positive, and not marked as negated. + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1"); // Config test @@ -277,12 +278,12 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) testArgs.ReadConfigString(conf_test); // This was passed twice, second one overrides the negative setting, - // but not the value. + // and the value. BOOST_CHECK(!testArgs.IsArgNegated("-foo")); - BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "0"); + BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "1"); - // A double negative is a positive. - BOOST_CHECK(testArgs.IsArgNegated("-bar")); + // A double negative is a positive, and does not count as negated. + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1"); // Combined test @@ -292,18 +293,15 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) testArgs.ReadConfigString(combo_test_conf); // Command line overrides, but doesn't erase old setting - BOOST_CHECK(!testArgs.IsArgNegated("-foo")); + BOOST_CHECK(testArgs.IsArgNegated("-foo")); BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "0"); - BOOST_CHECK(testArgs.GetArgs("-foo").size() == 2 - && testArgs.GetArgs("-foo").front() == "0" - && testArgs.GetArgs("-foo").back() == "1"); + BOOST_CHECK(testArgs.GetArgs("-foo").size() == 0); // Command line overrides, but doesn't erase old setting - BOOST_CHECK(testArgs.IsArgNegated("-bar")); + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == ""); - BOOST_CHECK(testArgs.GetArgs("-bar").size() == 2 - && testArgs.GetArgs("-bar").front() == "" - && testArgs.GetArgs("-bar").back() == "0"); + BOOST_CHECK(testArgs.GetArgs("-bar").size() == 1 + && testArgs.GetArgs("-bar").front() == ""); } BOOST_AUTO_TEST_CASE(util_ReadConfigStream) @@ -319,24 +317,39 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) "h=1\n" "noh=1\n" "noi=1\n" - "i=1\n"; + "i=1\n" + "sec1.ccc=extend1\n" + "\n" + "[sec1]\n" + "ccc=extend2\n" + "d=eee\n" + "h=1\n" + "[sec2]\n" + "ccc=extend3\n" + "iii=2\n"; TestArgsManager test_args; test_args.ReadConfigString(str_config); // expectation: a, b, ccc, d, fff, ggg, h, i end up in map - - BOOST_CHECK(test_args.GetMapArgs().size() == 8); - BOOST_CHECK(test_args.GetMapMultiArgs().size() == 8); - - BOOST_CHECK(test_args.GetMapArgs().count("-a") - && test_args.GetMapArgs().count("-b") - && test_args.GetMapArgs().count("-ccc") - && test_args.GetMapArgs().count("-d") - && test_args.GetMapArgs().count("-fff") - && test_args.GetMapArgs().count("-ggg") - && test_args.GetMapArgs().count("-h") - && test_args.GetMapArgs().count("-i") + // so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii + + BOOST_CHECK(test_args.GetOverrideArgs().empty()); + BOOST_CHECK(test_args.GetConfigArgs().size() == 13); + + BOOST_CHECK(test_args.GetConfigArgs().count("-a") + && test_args.GetConfigArgs().count("-b") + && test_args.GetConfigArgs().count("-ccc") + && test_args.GetConfigArgs().count("-d") + && test_args.GetConfigArgs().count("-fff") + && test_args.GetConfigArgs().count("-ggg") + && test_args.GetConfigArgs().count("-h") + && test_args.GetConfigArgs().count("-i") + ); + BOOST_CHECK(test_args.GetConfigArgs().count("-sec1.ccc") + && test_args.GetConfigArgs().count("-sec1.h") + && test_args.GetConfigArgs().count("-sec2.ccc") + && test_args.GetConfigArgs().count("-sec2.iii") ); BOOST_CHECK(test_args.IsArgSet("-a") @@ -348,6 +361,7 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) && test_args.IsArgSet("-h") && test_args.IsArgSet("-i") && !test_args.IsArgSet("-zzz") + && !test_args.IsArgSet("-iii") ); BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" @@ -356,9 +370,10 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) && test_args.GetArg("-d", "xxx") == "e" && test_args.GetArg("-fff", "xxx") == "0" && test_args.GetArg("-ggg", "xxx") == "1" - && test_args.GetArg("-h", "xxx") == "1" // 1st value takes precedence - && test_args.GetArg("-i", "xxx") == "0" // 1st value takes precedence + && test_args.GetArg("-h", "xxx") == "0" + && test_args.GetArg("-i", "xxx") == "1" && test_args.GetArg("-zzz", "xxx") == "xxx" + && test_args.GetArg("-iii", "xxx") == "xxx" ); for (bool def : {false, true}) { @@ -368,9 +383,10 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) && !test_args.GetBoolArg("-d", def) && !test_args.GetBoolArg("-fff", def) && test_args.GetBoolArg("-ggg", def) - && test_args.GetBoolArg("-h", def) - && !test_args.GetBoolArg("-i", def) + && !test_args.GetBoolArg("-h", def) + && test_args.GetBoolArg("-i", def) && test_args.GetBoolArg("-zzz", def) == def + && test_args.GetBoolArg("-iii", def) == def ); } @@ -381,19 +397,15 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2 && test_args.GetArgs("-ccc").front() == "argument" && test_args.GetArgs("-ccc").back() == "multiple"); - BOOST_CHECK(test_args.GetArgs("-fff").size() == 1 - && test_args.GetArgs("-fff").front() == "0"); + BOOST_CHECK(test_args.GetArgs("-fff").size() == 0); BOOST_CHECK(test_args.GetArgs("-nofff").size() == 0); BOOST_CHECK(test_args.GetArgs("-ggg").size() == 1 && test_args.GetArgs("-ggg").front() == "1"); BOOST_CHECK(test_args.GetArgs("-noggg").size() == 0); - BOOST_CHECK(test_args.GetArgs("-h").size() == 2 - && test_args.GetArgs("-h").front() == "1" - && test_args.GetArgs("-h").back() == "0"); + BOOST_CHECK(test_args.GetArgs("-h").size() == 0); BOOST_CHECK(test_args.GetArgs("-noh").size() == 0); - BOOST_CHECK(test_args.GetArgs("-i").size() == 2 - && test_args.GetArgs("-i").front() == "0" - && test_args.GetArgs("-i").back() == "1"); + BOOST_CHECK(test_args.GetArgs("-i").size() == 1 + && test_args.GetArgs("-i").front() == "1"); BOOST_CHECK(test_args.GetArgs("-noi").size() == 0); BOOST_CHECK(test_args.GetArgs("-zzz").size() == 0); @@ -402,25 +414,98 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) BOOST_CHECK(!test_args.IsArgNegated("-ccc")); BOOST_CHECK(!test_args.IsArgNegated("-d")); BOOST_CHECK(test_args.IsArgNegated("-fff")); - BOOST_CHECK(test_args.IsArgNegated("-ggg")); // IsArgNegated==true when noggg=0 + BOOST_CHECK(!test_args.IsArgNegated("-ggg")); BOOST_CHECK(test_args.IsArgNegated("-h")); // last setting takes precedence BOOST_CHECK(!test_args.IsArgNegated("-i")); // last setting takes precedence BOOST_CHECK(!test_args.IsArgNegated("-zzz")); + + // Test sections work + test_args.SelectConfigNetwork("sec1"); + + // same as original + BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" + && test_args.GetArg("-b", "xxx") == "1" + && test_args.GetArg("-fff", "xxx") == "0" + && test_args.GetArg("-ggg", "xxx") == "1" + && test_args.GetArg("-zzz", "xxx") == "xxx" + && test_args.GetArg("-iii", "xxx") == "xxx" + ); + // d is overridden + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee"); + // section-specific setting + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); + // section takes priority for multiple values + BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend1"); + // check multiple values works + const std::vector<std::string> sec1_ccc_expected = {"extend1","extend2","argument","multiple"}; + const auto& sec1_ccc_res = test_args.GetArgs("-ccc"); + BOOST_CHECK_EQUAL_COLLECTIONS(sec1_ccc_res.begin(), sec1_ccc_res.end(), sec1_ccc_expected.begin(), sec1_ccc_expected.end()); + + test_args.SelectConfigNetwork("sec2"); + + // same as original + BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" + && test_args.GetArg("-b", "xxx") == "1" + && test_args.GetArg("-d", "xxx") == "e" + && test_args.GetArg("-fff", "xxx") == "0" + && test_args.GetArg("-ggg", "xxx") == "1" + && test_args.GetArg("-zzz", "xxx") == "xxx" + && test_args.GetArg("-h", "xxx") == "0" + ); + // section-specific setting + BOOST_CHECK(test_args.GetArg("-iii", "xxx") == "2"); + // section takes priority for multiple values + BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend3"); + // check multiple values works + const std::vector<std::string> sec2_ccc_expected = {"extend3","argument","multiple"}; + const auto& sec2_ccc_res = test_args.GetArgs("-ccc"); + BOOST_CHECK_EQUAL_COLLECTIONS(sec2_ccc_res.begin(), sec2_ccc_res.end(), sec2_ccc_expected.begin(), sec2_ccc_expected.end()); + + // Test section only options + + test_args.SetNetworkOnlyArg("-d"); + test_args.SetNetworkOnlyArg("-ccc"); + test_args.SetNetworkOnlyArg("-h"); + + test_args.SelectConfigNetwork(CBaseChainParams::MAIN); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e"); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); + + test_args.SelectConfigNetwork("sec1"); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee"); + BOOST_CHECK(test_args.GetArgs("-d").size() == 1); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); + + test_args.SelectConfigNetwork("sec2"); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "xxx"); + BOOST_CHECK(test_args.GetArgs("-d").size() == 0); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 1); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); } BOOST_AUTO_TEST_CASE(util_GetArg) { TestArgsManager testArgs; - testArgs.GetMapArgs().clear(); - testArgs.GetMapArgs()["strtest1"] = "string..."; + testArgs.GetOverrideArgs().clear(); + testArgs.GetOverrideArgs()["strtest1"] = {"string..."}; // strtest2 undefined on purpose - testArgs.GetMapArgs()["inttest1"] = "12345"; - testArgs.GetMapArgs()["inttest2"] = "81985529216486895"; + testArgs.GetOverrideArgs()["inttest1"] = {"12345"}; + testArgs.GetOverrideArgs()["inttest2"] = {"81985529216486895"}; // inttest3 undefined on purpose - testArgs.GetMapArgs()["booltest1"] = ""; + testArgs.GetOverrideArgs()["booltest1"] = {""}; // booltest2 undefined on purpose - testArgs.GetMapArgs()["booltest3"] = "0"; - testArgs.GetMapArgs()["booltest4"] = "1"; + testArgs.GetOverrideArgs()["booltest3"] = {"0"}; + testArgs.GetOverrideArgs()["booltest4"] = {"1"}; + + // priorities + testArgs.GetOverrideArgs()["pritest1"] = {"a", "b"}; + testArgs.GetConfigArgs()["pritest2"] = {"a", "b"}; + testArgs.GetOverrideArgs()["pritest3"] = {"a"}; + testArgs.GetConfigArgs()["pritest3"] = {"b"}; + testArgs.GetOverrideArgs()["pritest4"] = {"a","b"}; + testArgs.GetConfigArgs()["pritest4"] = {"c","d"}; BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string..."); BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default"); @@ -431,6 +516,11 @@ BOOST_AUTO_TEST_CASE(util_GetArg) BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest2", false), false); BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest3", false), false); BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest4", false), true); + + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest1", "default"), "b"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest2", "default"), "a"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest3", "default"), "a"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest4", "default"), "b"); } BOOST_AUTO_TEST_CASE(util_GetChainName) @@ -443,7 +533,8 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) const char* argv_both[] = {"cmd", "-testnet", "-regtest"}; // equivalent to "-testnet" - const char* testnetconf = "testnet=1\nregtest=0\n"; + // regtest in testnet section is ignored + const char* testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1"; test_args.ParseParameters(0, (char**)argv_testnet); BOOST_CHECK_EQUAL(test_args.GetChainName(), "main"); @@ -479,6 +570,30 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) test_args.ParseParameters(3, (char**)argv_both); test_args.ReadConfigString(testnetconf); 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_args.SelectConfigNetwork("test"); + + test_args.ParseParameters(0, (char**)argv_testnet); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(2, (char**)argv_testnet); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(2, (char**)argv_regtest); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + test_args.ParseParameters(2, (char**)argv_test_no_reg); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(3, (char**)argv_both); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); } BOOST_AUTO_TEST_CASE(util_FormatMoney) diff --git a/src/util.cpp b/src/util.cpp index f55c9c8c34..6b0bffa35a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -341,14 +341,12 @@ int LogPrintStr(const std::string &str) std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine); - if (fPrintToConsole) - { + if (fPrintToConsole) { // print to console ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); fflush(stdout); } - else if (fPrintToDebugLog) - { + if (fPrintToDebugLog) { std::call_once(debugPrintInitFlag, &DebugPrintInit); std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); @@ -455,39 +453,204 @@ static bool InterpretBool(const std::string& strValue) return (atoi(strValue) != 0); } +/** Internal helper functions for ArgsManager */ +class ArgsManagerHelper { +public: + typedef std::map<std::string, std::vector<std::string>> MapArgs; + + /** Determine whether to use config settings in the default section, + * See also comments around ArgsManager::ArgsManager() below. */ + static inline bool UseDefaultSection(const ArgsManager& am, const std::string& arg) + { + return (am.m_network == CBaseChainParams::MAIN || am.m_network_only_args.count(arg) == 0); + } + + /** Convert regular argument into the network-specific setting */ + static inline std::string NetworkArg(const ArgsManager& am, const std::string& arg) + { + assert(arg.length() > 1 && arg[0] == '-'); + return "-" + am.m_network + "." + arg.substr(1); + } + + /** Find arguments in a map and add them to a vector */ + static inline void AddArgs(std::vector<std::string>& res, const MapArgs& map_args, const std::string& arg) + { + auto it = map_args.find(arg); + if (it != map_args.end()) { + res.insert(res.end(), it->second.begin(), it->second.end()); + } + } + + /** Return true/false if an argument is set in a map, and also + * return the first (or last) of the possibly multiple values it has + */ + static inline std::pair<bool,std::string> GetArgHelper(const MapArgs& map_args, const std::string& arg, bool getLast = false) + { + auto it = map_args.find(arg); + + if (it == map_args.end() || it->second.empty()) { + return std::make_pair(false, std::string()); + } + + if (getLast) { + return std::make_pair(true, it->second.back()); + } else { + return std::make_pair(true, it->second.front()); + } + } + + /* Get the string value of an argument, returning a pair of a boolean + * indicating the argument was found, and the value for the argument + * if it was found (or the empty string if not found). + */ + static inline std::pair<bool,std::string> GetArg(const ArgsManager &am, const std::string& arg) + { + LOCK(am.cs_args); + std::pair<bool,std::string> found_result(false, std::string()); + + // We pass "true" to GetArgHelper in order to return the last + // argument value seen from the command line (so "bitcoind -foo=bar + // -foo=baz" gives GetArg(am,"foo")=={true,"baz"} + found_result = GetArgHelper(am.m_override_args, arg, true); + if (found_result.first) { + return found_result; + } + + // But in contrast we return the first argument seen in a config file, + // so "foo=bar \n foo=baz" in the config file gives + // GetArg(am,"foo")={true,"bar"} + if (!am.m_network.empty()) { + found_result = GetArgHelper(am.m_config_args, NetworkArg(am, arg)); + if (found_result.first) { + return found_result; + } + } + + if (UseDefaultSection(am, arg)) { + found_result = GetArgHelper(am.m_config_args, arg); + if (found_result.first) { + return found_result; + } + } + + return found_result; + } + + /* Special test for -testnet and -regtest args, because we + * don't want to be confused by craziness like "[regtest] testnet=1" + */ + static inline bool GetNetBoolArg(const ArgsManager &am, const std::string& net_arg) + { + std::pair<bool,std::string> found_result(false,std::string()); + found_result = GetArgHelper(am.m_override_args, net_arg, true); + if (!found_result.first) { + found_result = GetArgHelper(am.m_config_args, net_arg, true); + if (!found_result.first) { + return false; // not set + } + } + return InterpretBool(found_result.second); // is set, so evaluate + } +}; + /** * Interpret -nofoo as if the user supplied -foo=0. * - * This method also tracks when the -no form was supplied, and treats "-foo" as - * a negated option when this happens. This can be later checked using the + * This method also tracks when the -no form was supplied, and if so, + * checks whether there was a double-negative (-nofoo=0 -> -foo=1). + * + * If there was not a double negative, it removes the "no" from the key, + * and returns true, indicating the caller should clear the args vector + * to indicate a negated option. + * + * If there was a double negative, it removes "no" from the key, sets the + * value to "1" and returns false. + * + * If there was no "no", it leaves key and value untouched and returns + * false. + * + * Where an option was negated can be later checked using the * IsArgNegated() method. One use case for this is to have a way to disable * options that are not normally boolean (e.g. using -nodebuglogfile to request * that debug log output is not sent to any file at all). */ -void ArgsManager::InterpretNegatedOption(std::string& key, std::string& val) +static bool InterpretNegatedOption(std::string& key, std::string& val) { - if (key.substr(0, 3) == "-no") { + assert(key[0] == '-'); + + size_t option_index = key.find('.'); + if (option_index == std::string::npos) { + option_index = 1; + } else { + ++option_index; + } + if (key.substr(option_index, 2) == "no") { bool bool_val = InterpretBool(val); + key.erase(option_index, 2); if (!bool_val ) { // Double negatives like -nofoo=0 are supported (but discouraged) LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val); + val = "1"; + } else { + return true; } - key.erase(1, 2); - m_negated_args.insert(key); - val = bool_val ? "0" : "1"; - } else { - // In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo - // as negated when we see the second option. - m_negated_args.erase(key); } + return false; +} + +ArgsManager::ArgsManager() : + /* These options would cause cross-contamination if values for + * mainnet were used while running on regtest/testnet (or vice-versa). + * Setting them as section_only_args ensures that sharing a config file + * between mainnet and regtest/testnet won't cause problems due to these + * parameters by accident. */ + m_network_only_args{ + "-addnode", "-connect", + "-port", "-bind", + "-rpcport", "-rpcbind", + "-wallet", + } +{ + // nothing to do +} + +void ArgsManager::WarnForSectionOnlyArgs() +{ + // if there's no section selected, don't worry + if (m_network.empty()) return; + + // if it's okay to use the default section for this network, don't worry + if (m_network == CBaseChainParams::MAIN) return; + + for (const auto& arg : m_network_only_args) { + std::pair<bool, std::string> found_result; + + // if this option is overridden it's fine + found_result = ArgsManagerHelper::GetArgHelper(m_override_args, arg); + if (found_result.first) continue; + + // if there's a network-specific value for this option, it's fine + found_result = ArgsManagerHelper::GetArgHelper(m_config_args, ArgsManagerHelper::NetworkArg(*this, arg)); + if (found_result.first) continue; + + // if there isn't a default value for this option, it's fine + found_result = ArgsManagerHelper::GetArgHelper(m_config_args, arg); + if (!found_result.first) continue; + + // otherwise, issue a warning + LogPrintf("Warning: Config setting for %s only applied on %s network when in [%s] section.\n", arg, m_network, m_network); + } +} + +void ArgsManager::SelectConfigNetwork(const std::string& network) +{ + m_network = network; } void ArgsManager::ParseParameters(int argc, const char* const argv[]) { LOCK(cs_args); - mapArgs.clear(); - mapMultiArgs.clear(); - m_negated_args.clear(); + m_override_args.clear(); for (int i = 1; i < argc; i++) { std::string key(argv[i]); @@ -510,55 +673,79 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[]) if (key.length() > 1 && key[1] == '-') key.erase(0, 1); - // Transform -nofoo to -foo=0 - InterpretNegatedOption(key, val); - - mapArgs[key] = val; - mapMultiArgs[key].push_back(val); + // Check for -nofoo + if (InterpretNegatedOption(key, val)) { + m_override_args[key].clear(); + } else { + m_override_args[key].push_back(val); + } } } std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const { + std::vector<std::string> result = {}; + if (IsArgNegated(strArg)) return result; // special case + LOCK(cs_args); - auto it = mapMultiArgs.find(strArg); - if (it != mapMultiArgs.end()) return it->second; - return {}; + + ArgsManagerHelper::AddArgs(result, m_override_args, strArg); + if (!m_network.empty()) { + ArgsManagerHelper::AddArgs(result, m_config_args, ArgsManagerHelper::NetworkArg(*this, strArg)); + } + + if (ArgsManagerHelper::UseDefaultSection(*this, strArg)) { + ArgsManagerHelper::AddArgs(result, m_config_args, strArg); + } + + return result; } bool ArgsManager::IsArgSet(const std::string& strArg) const { - LOCK(cs_args); - return mapArgs.count(strArg); + if (IsArgNegated(strArg)) return true; // special case + return ArgsManagerHelper::GetArg(*this, strArg).first; } bool ArgsManager::IsArgNegated(const std::string& strArg) const { LOCK(cs_args); - return m_negated_args.find(strArg) != m_negated_args.end(); + + const auto& ov = m_override_args.find(strArg); + if (ov != m_override_args.end()) return ov->second.empty(); + + if (!m_network.empty()) { + const auto& cfs = m_config_args.find(ArgsManagerHelper::NetworkArg(*this, strArg)); + if (cfs != m_config_args.end()) return cfs->second.empty(); + } + + const auto& cf = m_config_args.find(strArg); + if (cf != m_config_args.end()) return cf->second.empty(); + + return false; } std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const { - LOCK(cs_args); - auto it = mapArgs.find(strArg); - if (it != mapArgs.end()) return it->second; + if (IsArgNegated(strArg)) return "0"; + std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); + if (found_res.first) return found_res.second; return strDefault; } int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const { - LOCK(cs_args); - auto it = mapArgs.find(strArg); - if (it != mapArgs.end()) return atoi64(it->second); + if (IsArgNegated(strArg)) return 0; + std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); + if (found_res.first) return atoi64(found_res.second); return nDefault; } bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const { - LOCK(cs_args); - auto it = mapArgs.find(strArg); - if (it != mapArgs.end()) return InterpretBool(it->second); + if (IsArgNegated(strArg)) return false; + std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); + if (found_res.first) return InterpretBool(found_res.second); return fDefault; } @@ -581,8 +768,7 @@ bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue) void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue) { LOCK(cs_args); - mapArgs[strArg] = strValue; - mapMultiArgs[strArg] = {strValue}; + m_override_args[strArg] = {strValue}; } bool HelpRequested(const ArgsManager& args) @@ -745,18 +931,23 @@ void ArgsManager::ReadConfigStream(std::istream& stream) for (boost::program_options::detail::config_file_iterator it(stream, setOptions), end; it != end; ++it) { - // Don't overwrite existing settings so command line settings override bitcoin.conf std::string strKey = std::string("-") + it->string_key; std::string strValue = it->value[0]; - InterpretNegatedOption(strKey, strValue); - if (mapArgs.count(strKey) == 0) - mapArgs[strKey] = strValue; - mapMultiArgs[strKey].push_back(strValue); + if (InterpretNegatedOption(strKey, strValue)) { + m_config_args[strKey].clear(); + } else { + m_config_args[strKey].push_back(strValue); + } } } void ArgsManager::ReadConfigFile(const std::string& confPath) { + { + LOCK(cs_args); + m_config_args.clear(); + } + fs::ifstream stream(GetConfigFile(confPath)); // ok to not have a config file @@ -773,8 +964,8 @@ void ArgsManager::ReadConfigFile(const std::string& confPath) std::string ArgsManager::GetChainName() const { - bool fRegTest = GetBoolArg("-regtest", false); - bool fTestNet = GetBoolArg("-testnet", false); + bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest"); + bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet"); if (fTestNet && fRegTest) throw std::runtime_error("Invalid combination of -regtest and -testnet."); @@ -933,9 +1124,16 @@ void ShrinkDebugFile() // 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 && fs::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) + 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); diff --git a/src/util.h b/src/util.h index 3952461e48..ffdee99d27 100644 --- a/src/util.h +++ b/src/util.h @@ -24,6 +24,7 @@ #include <exception> #include <map> #include <memory> +#include <set> #include <stdint.h> #include <string> #include <unordered_set> @@ -145,14 +146,16 @@ template<typename T, typename... Args> static inline void MarkUsed(const T& t, c #define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) #else #define LogPrintf(...) do { \ - 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__); \ + 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_); \ } \ - LogPrintStr(_log_msg_); \ } while(0) #define LogPrint(category, ...) do { \ @@ -223,18 +226,36 @@ inline bool IsSwitchChar(char c) class ArgsManager { protected: + friend class ArgsManagerHelper; + mutable CCriticalSection cs_args; - std::map<std::string, std::string> mapArgs; - std::map<std::string, std::vector<std::string>> mapMultiArgs; - std::unordered_set<std::string> m_negated_args; + std::map<std::string, std::vector<std::string>> m_override_args; + std::map<std::string, std::vector<std::string>> m_config_args; + std::string m_network; + std::set<std::string> m_network_only_args; void ReadConfigStream(std::istream& stream); public: + ArgsManager(); + + /** + * Select the network in use + */ + void SelectConfigNetwork(const std::string& network); + void ParseParameters(int argc, const char*const argv[]); void ReadConfigFile(const std::string& confPath); /** + * Log warnings for options in m_section_only_args when + * they are specified in the default section but not overridden + * on the command line or in a network-specific section in the + * config file. + */ + void WarnForSectionOnlyArgs(); + + /** * Return a vector of strings of the given argument * * @param strArg Argument to get (e.g. "-foo") @@ -313,11 +334,6 @@ public: * @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given. */ std::string GetChainName() const; - -private: - - // Munge -nofoo into -foo=0 and track the value as negated. - void InterpretNegatedOption(std::string &key, std::string &val); }; extern ArgsManager gArgs; diff --git a/src/utiltime.cpp b/src/utiltime.cpp index 8a861039b3..34800c7b6d 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -10,9 +10,10 @@ #include <utiltime.h> #include <atomic> - #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread.hpp> +#include <ctime> +#include <tinyformat.h> static std::atomic<int64_t> nMockTime(0); //!< For unit testing @@ -75,25 +76,23 @@ void MilliSleep(int64_t n) #endif } -std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) -{ - static std::locale classic(std::locale::classic()); - // std::locale takes ownership of the pointer - std::locale loc(classic, new boost::posix_time::time_facet(pszFormat)); - std::stringstream ss; - ss.imbue(loc); - ss << boost::posix_time::from_time_t(nTime); - return ss.str(); -} - std::string FormatISO8601DateTime(int64_t nTime) { - return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime); + struct tm ts; + time_t time_val = nTime; + gmtime_r(&time_val, &ts); + return strprintf("%04i-%02i-%02iT%02i:%02i:%02iZ", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec); } std::string FormatISO8601Date(int64_t nTime) { - return DateTimeStrFormat("%Y-%m-%d", nTime); + struct tm ts; + time_t time_val = nTime; + gmtime_r(&time_val, &ts); + return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday); } std::string FormatISO8601Time(int64_t nTime) { - return DateTimeStrFormat("%H:%M:%SZ", nTime); + struct tm ts; + time_t time_val = nTime; + gmtime_r(&time_val, &ts); + return strprintf("%02i:%02i:%02iZ", ts.tm_hour, ts.tm_min, ts.tm_sec); } diff --git a/src/utiltime.h b/src/utiltime.h index 807c52ffaf..3bcbb00c16 100644 --- a/src/utiltime.h +++ b/src/utiltime.h @@ -31,8 +31,6 @@ void MilliSleep(int64_t n); * ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date,Time} * helper functions if possible. */ -std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); - std::string FormatISO8601DateTime(int64_t nTime); std::string FormatISO8601Date(int64_t nTime); std::string FormatISO8601Time(int64_t nTime); diff --git a/src/validation.cpp b/src/validation.cpp index 8066e6c7a8..3726cb3b14 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -202,8 +202,9 @@ CCriticalSection cs_main; BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex; CChain& chainActive = g_chainstate.chainActive; CBlockIndex *pindexBestHeader = nullptr; -CWaitableCriticalSection csBestBlock; -CConditionVariable cvBlockChange; +CWaitableCriticalSection g_best_block_mutex; +CConditionVariable g_best_block_cv; +uint256 g_best_block; int nScriptCheckThreads = 0; std::atomic_bool fImporting(false); std::atomic_bool fReindex(false); @@ -263,7 +264,8 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc { AssertLockHeld(cs_main); - // Find the first block the caller has in the main chain + // Find the latest block common to locator and chain - we expect that + // locator.vHave is sorted descending by height. for (const uint256& hash : locator.vHave) { CBlockIndex* pindex = LookupBlockIndex(hash); if (pindex) { @@ -352,7 +354,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool CBlockIndex* tip = chainActive.Tip(); assert(tip != nullptr); - + CBlockIndex index; index.pprev = tip; // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate @@ -2204,7 +2206,11 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar // New best block mempool.AddTransactionsUpdated(1); - cvBlockChange.notify_all(); + { + WaitableLock lock(g_best_block_mutex); + g_best_block = pindexNew->GetBlockHash(); + g_best_block_cv.notify_all(); + } std::vector<std::string> warningMessages; if (!IsInitialBlockDownload()) @@ -2672,18 +2678,17 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& assert(trace.pblock && trace.pindex); GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs); } - } - // When we reach this point, we switched to a new tip (stored in pindexNewTip). - - // Notifications/callbacks that can run without cs_main - // Notify external listeners about the new tip. - GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); + // Notify external listeners about the new tip. + // Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected + GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); - // Always notify the UI if a new block tip was connected - if (pindexFork != pindexNewTip) { - uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); + // Always notify the UI if a new block tip was connected + if (pindexFork != pindexNewTip) { + uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); + } } + // When we reach this point, we switched to a new tip (stored in pindexNewTip). if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown(); diff --git a/src/validation.h b/src/validation.h index dad6858b1e..3668484696 100644 --- a/src/validation.h +++ b/src/validation.h @@ -165,8 +165,9 @@ extern BlockMap& mapBlockIndex; extern uint64_t nLastBlockTx; extern uint64_t nLastBlockWeight; extern const std::string strMessageMagic; -extern CWaitableCriticalSection csBestBlock; -extern CConditionVariable cvBlockChange; +extern CWaitableCriticalSection g_best_block_mutex; +extern CConditionVariable g_best_block_cv; +extern uint256 g_best_block; extern std::atomic_bool fImporting; extern std::atomic_bool fReindex; extern int nScriptCheckThreads; diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 928df4fa65..746263f113 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -139,6 +139,10 @@ void CMainSignals::MempoolEntryRemoved(CTransactionRef ptx, MemPoolRemovalReason } void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { + // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which + // the chain actually updates. One way to ensure this is for the caller to invoke this signal + // in the same critical section where the chain is updated + m_internals->m_schedulerClient.AddToProcessQueue([pindexNew, pindexFork, fInitialDownload, this] { m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); }); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index b6f4a0e1e1..2fd9aa1a6f 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -18,39 +18,38 @@ class WalletInit : public WalletInitInterface { public: //! Return the wallets help message. - std::string GetHelpString(bool showDebug) override; + std::string GetHelpString(bool showDebug) const override; //! Wallets parameter interaction - bool ParameterInteraction() override; + bool ParameterInteraction() const override; //! Register wallet RPCs. - void RegisterRPC(CRPCTable &tableRPC) override; + void RegisterRPC(CRPCTable &tableRPC) const override; //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. // This function will perform salvage on the wallet if requested, as long as only one wallet is // being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). - bool Verify() override; + bool Verify() const override; //! Load wallet databases. - bool Open() override; + bool Open() const override; //! Complete startup of wallets. - void Start(CScheduler& scheduler) override; + void Start(CScheduler& scheduler) const override; //! Flush all wallets in preparation for shutdown. - void Flush() override; + void Flush() const override; //! Stop all wallets. Wallets will be flushed first. - void Stop() override; + void Stop() const override; //! Close all wallets. - void Close() override; + void Close() const override; }; -static WalletInit g_wallet_init; -WalletInitInterface* const g_wallet_init_interface = &g_wallet_init; +const WalletInitInterface& g_wallet_init_interface = WalletInit(); -std::string WalletInit::GetHelpString(bool showDebug) +std::string WalletInit::GetHelpString(bool showDebug) const { std::string strUsage = HelpMessageGroup(_("Wallet options:")); strUsage += HelpMessageOpt("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE))); @@ -92,7 +91,7 @@ std::string WalletInit::GetHelpString(bool showDebug) return strUsage; } -bool WalletInit::ParameterInteraction() +bool WalletInit::ParameterInteraction() const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { for (const std::string& wallet : gArgs.GetArgs("-wallet")) { @@ -220,7 +219,7 @@ bool WalletInit::ParameterInteraction() return true; } -void WalletInit::RegisterRPC(CRPCTable &t) +void WalletInit::RegisterRPC(CRPCTable &t) const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return; @@ -229,7 +228,7 @@ void WalletInit::RegisterRPC(CRPCTable &t) RegisterWalletRPCCommands(t); } -bool WalletInit::Verify() +bool WalletInit::Verify() const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; @@ -304,7 +303,7 @@ bool WalletInit::Verify() return true; } -bool WalletInit::Open() +bool WalletInit::Open() const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { LogPrintf("Wallet disabled!\n"); @@ -322,28 +321,28 @@ bool WalletInit::Open() return true; } -void WalletInit::Start(CScheduler& scheduler) +void WalletInit::Start(CScheduler& scheduler) const { for (CWalletRef pwallet : vpwallets) { pwallet->postInitProcess(scheduler); } } -void WalletInit::Flush() +void WalletInit::Flush() const { for (CWalletRef pwallet : vpwallets) { pwallet->Flush(false); } } -void WalletInit::Stop() +void WalletInit::Stop() const { for (CWalletRef pwallet : vpwallets) { pwallet->Flush(true); } } -void WalletInit::Close() +void WalletInit::Close() const { for (CWalletRef pwallet : vpwallets) { delete pwallet; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index a3594aa692..b8533839a0 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -172,7 +172,13 @@ UniValue importprivkey(const JSONRPCRequest& request) } } if (fRescan) { - pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > TIMESTAMP_MIN) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } } return NullUniValue; @@ -310,7 +316,13 @@ UniValue importaddress(const JSONRPCRequest& request) } if (fRescan) { - pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > TIMESTAMP_MIN) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } pwallet->ReacceptWalletTransactions(); } @@ -479,7 +491,13 @@ UniValue importpubkey(const JSONRPCRequest& request) } if (fRescan) { - pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > TIMESTAMP_MIN) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } pwallet->ReacceptWalletTransactions(); } @@ -534,9 +552,11 @@ UniValue importwallet(const JSONRPCRequest& request) int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); file.seekg(0, file.beg); - pwallet->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI + // Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which + // we don't want for this progress bar shoing the import progress. uiInterface.ShowProgress does not have a cancel button. + uiInterface.ShowProgress(_("Importing..."), 0, false); // show progress dialog in GUI while (file.good()) { - pwallet->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); + uiInterface.ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false); std::string line; std::getline(file, line); if (line.empty() || line[0] == '#') @@ -599,10 +619,17 @@ UniValue importwallet(const JSONRPCRequest& request) } } file.close(); - pwallet->ShowProgress("", 100); // hide progress dialog in GUI + uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI pwallet->UpdateTimeFirstKey(nTimeBegin); } - pwallet->RescanFromTime(nTimeBegin, reserver, false /* update */); + uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI + int64_t scanned_time = pwallet->RescanFromTime(nTimeBegin, reserver, false /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > nTimeBegin) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } pwallet->MarkDirty(); if (!fGood) @@ -1212,6 +1239,9 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */); pwallet->ReacceptWalletTransactions(); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } if (scannedTime > nLowestTimestamp) { std::vector<UniValue> results = response.getValues(); response.clear(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 6212ea7512..56bdc0695c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1577,7 +1577,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" " \"label\" : \"label\", (string) The label of the receiving address. The default label is \"\".\n" " \"txids\": [\n" - " n, (numeric) The ids of transactions received with the address \n" + " \"txid\", (string) The ids of transactions received with the address \n" " ...\n" " ]\n" " }\n" diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7d46f02745..45c85a7912 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1535,7 +1535,7 @@ bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout) const const CScript& scriptPubKey = txout.scriptPubKey; SignatureData sigdata; - if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata)) + if (!ProduceSignature(*this, DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) { return false; } else { @@ -2582,7 +2582,7 @@ bool CWallet::SignTransaction(CMutableTransaction &tx) const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey; const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue; SignatureData sigdata; - if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { + if (!ProduceSignature(*this, TransactionSignatureCreator(&txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { return false; } UpdateTransaction(tx, nIn, sigdata); @@ -3011,7 +3011,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac const CScript& scriptPubKey = coin.txout.scriptPubKey; SignatureData sigdata; - if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + if (!ProduceSignature(*this, TransactionSignatureCreator(&txNewConst, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata)) { strFailReason = _("Signing transaction failed"); return false; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 803cc5f0a0..bcc7cf877d 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -232,13 +232,14 @@ public: unsigned int nCKeys; unsigned int nWatchKeys; unsigned int nKeyMeta; + unsigned int m_unknown_records; bool fIsEncrypted; bool fAnyUnordered; int nFileVersion; std::vector<uint256> vWalletUpgrade; CWalletScanState() { - nKeys = nCKeys = nWatchKeys = nKeyMeta = 0; + nKeys = nCKeys = nWatchKeys = nKeyMeta = m_unknown_records = 0; fIsEncrypted = false; fAnyUnordered = false; nFileVersion = 0; @@ -509,6 +510,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, strErr = "Error reading wallet database: SetHDChain failed"; return false; } + } else if (strType != "bestblock" && strType != "bestblock_nomerkle"){ + wss.m_unknown_records++; } } catch (...) { @@ -600,8 +603,8 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) LogPrintf("nFileVersion = %d\n", wss.nFileVersion); - LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", - wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); + LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n", + wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records); // nTimeFirstKey is only reliable if all keys have metadata if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h index c7eee37ce5..5bfde6faaf 100644 --- a/src/walletinitinterface.h +++ b/src/walletinitinterface.h @@ -13,23 +13,23 @@ class CRPCTable; class WalletInitInterface { public: /** Get wallet help string */ - virtual std::string GetHelpString(bool showDebug) = 0; + virtual std::string GetHelpString(bool showDebug) const = 0; /** Check wallet parameter interaction */ - virtual bool ParameterInteraction() = 0; + virtual bool ParameterInteraction() const = 0; /** Register wallet RPC*/ - virtual void RegisterRPC(CRPCTable &) = 0; + virtual void RegisterRPC(CRPCTable &) const = 0; /** Verify wallets */ - virtual bool Verify() = 0; + virtual bool Verify() const = 0; /** Open wallets*/ - virtual bool Open() = 0; + virtual bool Open() const = 0; /** Start wallets*/ - virtual void Start(CScheduler& scheduler) = 0; + virtual void Start(CScheduler& scheduler) const = 0; /** Flush Wallets*/ - virtual void Flush() = 0; + virtual void Flush() const = 0; /** Stop Wallets*/ - virtual void Stop() = 0; + virtual void Stop() const = 0; /** Close wallets */ - virtual void Close() = 0; + virtual void Close() const = 0; virtual ~WalletInitInterface() {} }; diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py index e5db9e18c7..eee38ce648 100755 --- a/test/functional/feature_bip68_sequence.py +++ b/test/functional/feature_bip68_sequence.py @@ -129,7 +129,7 @@ class BIP68Test(BitcoinTestFramework): # Track whether any sequence locks used should fail should_pass = True - + # Track whether this transaction was built with sequence locks using_sequence_locks = False @@ -343,7 +343,7 @@ class BIP68Test(BitcoinTestFramework): tx2.rehash() self.nodes[0].sendrawtransaction(ToHex(tx2)) - + # Now make an invalid spend of tx2 according to BIP68 sequence_value = 100 # 100 block relative locktime diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index a1d22191af..e9924451d1 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -29,8 +29,13 @@ class ConfArgsTest(BitcoinTestFramework): # Check that using non-existent datadir in conf file fails conf_file = os.path.join(default_data_dir, "bitcoin.conf") - with open(conf_file, 'a', encoding='utf8') as f: + + # datadir needs to be set before [regtest] section + conf_file_contents = open(conf_file, encoding='utf8').read() + with open(conf_file, 'w', encoding='utf8') as f: f.write("datadir=" + new_data_dir + "\n") + f.write(conf_file_contents) + self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.') # Create the directory and ensure the config file now works diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py index 3c7aecf10a..166f8f8694 100755 --- a/test/functional/feature_logging.py +++ b/test/functional/feature_logging.py @@ -15,13 +15,17 @@ class LoggingTest(BitcoinTestFramework): self.num_nodes = 1 self.setup_clean_chain = True + def relative_log_path(self, name): + return os.path.join(self.nodes[0].datadir, "regtest", name) + def run_test(self): # test default log file name - assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "debug.log")) + default_log_path = self.relative_log_path("debug.log") + assert os.path.isfile(default_log_path) # test alternative log file name in datadir self.restart_node(0, ["-debuglogfile=foo.log"]) - assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "foo.log")) + assert os.path.isfile(self.relative_log_path("foo.log")) # test alternative log file name outside datadir tempname = os.path.join(self.options.tmpdir, "foo.log") @@ -29,7 +33,7 @@ class LoggingTest(BitcoinTestFramework): assert os.path.isfile(tempname) # check that invalid log (relative) will cause error - invdir = os.path.join(self.nodes[0].datadir, "regtest", "foo") + invdir = self.relative_log_path("foo") invalidname = os.path.join("foo", "foo.log") self.stop_node(0) exp_stderr = "Error: Could not open debug log file \S+$" @@ -55,6 +59,17 @@ class LoggingTest(BitcoinTestFramework): self.start_node(0, ["-debuglogfile=%s" % (invalidname)]) assert os.path.isfile(os.path.join(invdir, "foo.log")) + # check that -nodebuglogfile disables logging + self.stop_node(0) + os.unlink(default_log_path) + assert not os.path.isfile(default_log_path) + self.start_node(0, ["-nodebuglogfile"]) + assert not os.path.isfile(default_log_path) + + # just sanity check no crash here + self.stop_node(0) + self.start_node(0, ["-debuglogfile=%s" % os.devnull]) + if __name__ == '__main__': LoggingTest().main() diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py index ce6ec76c61..072ba6c7c7 100755 --- a/test/functional/feature_maxuploadtarget.py +++ b/test/functional/feature_maxuploadtarget.py @@ -30,7 +30,7 @@ class TestP2PConn(P2PInterface): self.block_receive_map[message.block.sha256] += 1 class MaxUploadTest(BitcoinTestFramework): - + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py index 2eb1be47a5..60859de7a5 100755 --- a/test/functional/feature_proxy.py +++ b/test/functional/feature_proxy.py @@ -182,7 +182,7 @@ class ProxyTest(BitcoinTestFramework): assert_equal(n1['onion']['proxy'], '%s:%i' % (self.conf2.addr)) assert_equal(n1['onion']['proxy_randomize_credentials'], False) assert_equal(n1['onion']['reachable'], True) - + n2 = networks_dict(self.nodes[2].getnetworkinfo()) for net in ['ipv4','ipv6','onion']: assert_equal(n2[net]['proxy'], '%s:%i' % (self.conf2.addr)) diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index 2ee33aa869..a48939d2e0 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -186,10 +186,10 @@ class RESTTest (BitcoinTestFramework): self.test_rest_request("/getutxos/checkmempool", http_method='POST', req_type=ReqType.JSON, status=400, ret_type=RetType.OBJ) # Test limits - long_uri = '/'.join(["{}-{}".format(txid, n) for n in range(20)]) + long_uri = '/'.join(["{}-{}".format(txid, n_) for n_ in range(20)]) self.test_rest_request("/getutxos/checkmempool/{}".format(long_uri), http_method='POST', status=400, ret_type=RetType.OBJ) - long_uri = '/'.join(['{}-{}'.format(txid, n) for n in range(15)]) + long_uri = '/'.join(['{}-{}'.format(txid, n_) for n_ in range(15)]) self.test_rest_request("/getutxos/checkmempool/{}".format(long_uri), http_method='POST', status=200) self.nodes[0].generate(1) # generate block to not affect upcoming tests diff --git a/test/functional/p2p_mempool.py b/test/functional/p2p_mempool.py index 485a8af3d0..e54843b26f 100755 --- a/test/functional/p2p_mempool.py +++ b/test/functional/p2p_mempool.py @@ -30,6 +30,6 @@ class P2PMempoolTests(BitcoinTestFramework): #mininode must be disconnected at this point assert_equal(len(self.nodes[0].getpeerinfo()), 0) - + if __name__ == '__main__': P2PMempoolTests().main() diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index 5546bf6b29..10c8475d01 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -450,7 +450,7 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() assert(len(self.utxo) > 0) - + # Create a P2WSH transaction. # The witness program will be a bunch of OP_2DROP's, followed by OP_TRUE. # This should give us plenty of room to tweak the spending tx's @@ -562,7 +562,7 @@ class SegWitTest(BitcoinTestFramework): self.log.info("Testing extra witness data in tx") assert(len(self.utxo) > 0) - + block = self.build_next_block() witness_program = CScript([OP_DROP, OP_TRUE]) @@ -730,7 +730,7 @@ class SegWitTest(BitcoinTestFramework): witness_program = CScript([OP_DROP, OP_TRUE]) witness_hash = sha256(witness_program) scriptPubKey = CScript([OP_0, witness_hash]) - + # Create a transaction that splits our utxo into many outputs tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py index aa91fb5b0d..1b3e510dc4 100644 --- a/test/functional/test_framework/key.py +++ b/test/functional/test_framework/key.py @@ -10,7 +10,6 @@ This file is modified from python-bitcoinlib. import ctypes import ctypes.util import hashlib -import sys ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library ('ssl') or 'libeay32') @@ -223,10 +222,5 @@ class CPubKey(bytes): return repr(self) def __repr__(self): - # Always have represent as b'<secret>' so test cases don't have to - # change for py2/3 - if sys.version > '3': - return '%s(%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__()) - else: - return '%s(b%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__()) + return '%s(%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__()) diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 6fe0b445da..44650d7584 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -10,15 +10,6 @@ This file is modified from python-bitcoinlib. from .mininode import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string from binascii import hexlify import hashlib - -import sys -bchr = chr -bord = ord -if sys.version > '3': - long = int - bchr = lambda x: bytes([x]) - bord = lambda x: x - import struct from .bignum import bn2vch @@ -40,9 +31,9 @@ class CScriptOp(int): def encode_op_pushdata(d): """Encode a PUSHDATA op, returning bytes""" if len(d) < 0x4c: - return b'' + bchr(len(d)) + d # OP_PUSHDATA + return b'' + bytes([len(d)]) + d # OP_PUSHDATA elif len(d) <= 0xff: - return b'\x4c' + bchr(len(d)) + d # OP_PUSHDATA1 + return b'\x4c' + bytes([len(d)]) + d # OP_PUSHDATA1 elif len(d) <= 0xffff: return b'\x4d' + struct.pack(b'<H', len(d)) + d # OP_PUSHDATA2 elif len(d) <= 0xffffffff: @@ -388,7 +379,7 @@ class CScriptNum(): r.append(0x80 if neg else 0) elif neg: r[-1] |= 0x80 - return bytes(bchr(len(r)) + r) + return bytes([len(r)]) + r class CScript(bytes): @@ -405,17 +396,17 @@ class CScript(bytes): def __coerce_instance(cls, other): # Coerce other into bytes if isinstance(other, CScriptOp): - other = bchr(other) + other = bytes([other]) elif isinstance(other, CScriptNum): if (other.value == 0): - other = bchr(CScriptOp(OP_0)) + other = bytes([CScriptOp(OP_0)]) else: other = CScriptNum.encode(other) elif isinstance(other, int): if 0 <= other <= 16: - other = bytes(bchr(CScriptOp.encode_op_n(other))) + other = bytes([CScriptOp.encode_op_n(other)]) elif other == -1: - other = bytes(bchr(OP_1NEGATE)) + other = bytes([OP_1NEGATE]) else: other = CScriptOp.encode_op_pushdata(bn2vch(other)) elif isinstance(other, (bytes, bytearray)): @@ -458,7 +449,7 @@ class CScript(bytes): i = 0 while i < len(self): sop_idx = i - opcode = bord(self[i]) + opcode = self[i] i += 1 if opcode > OP_PUSHDATA4: @@ -474,21 +465,21 @@ class CScript(bytes): pushdata_type = 'PUSHDATA1' if i >= len(self): raise CScriptInvalidError('PUSHDATA1: missing data length') - datasize = bord(self[i]) + datasize = self[i] i += 1 elif opcode == OP_PUSHDATA2: pushdata_type = 'PUSHDATA2' if i + 1 >= len(self): raise CScriptInvalidError('PUSHDATA2: missing data length') - datasize = bord(self[i]) + (bord(self[i+1]) << 8) + datasize = self[i] + (self[i+1] << 8) i += 2 elif opcode == OP_PUSHDATA4: pushdata_type = 'PUSHDATA4' if i + 3 >= len(self): raise CScriptInvalidError('PUSHDATA4: missing data length') - datasize = bord(self[i]) + (bord(self[i+1]) << 8) + (bord(self[i+2]) << 16) + (bord(self[i+3]) << 24) + datasize = self[i] + (self[i+1] << 8) + (self[i+2] << 16) + (self[i+3] << 24) i += 4 else: diff --git a/test/functional/test_framework/socks5.py b/test/functional/test_framework/socks5.py index 4721809a3b..581de0ed5d 100644 --- a/test/functional/test_framework/socks5.py +++ b/test/functional/test_framework/socks5.py @@ -4,12 +4,14 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Dummy Socks5 server for testing.""" -import socket, threading, queue +import socket +import threading +import queue import logging logger = logging.getLogger("TestFramework.socks5") -### Protocol constants +# Protocol constants class Command: CONNECT = 0x01 @@ -18,7 +20,7 @@ class AddressType: DOMAINNAME = 0x03 IPV6 = 0x04 -### Utility functions +# Utility functions def recvall(s, n): """Receive n bytes from a socket, or fail.""" rv = bytearray() @@ -30,7 +32,7 @@ def recvall(s, n): n -= len(d) return rv -### Implementation classes +# Implementation classes class Socks5Configuration(): """Proxy configuration.""" def __init__(self): @@ -141,7 +143,7 @@ class Socks5Server(): thread = threading.Thread(None, conn.handle) thread.daemon = True thread.start() - + def start(self): assert(not self.running) self.running = True diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 0f0d031f35..04d1de8d91 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -78,7 +78,17 @@ class TestNode(): # For those callers that need more flexibility, they can just set the args property directly. # Note that common args are set in the config file (see initialize_datadir) self.extra_args = extra_args - self.args = [self.binary, "-datadir=" + self.datadir, "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i] + self.args = [ + self.binary, + "-datadir=" + self.datadir, + "-logtimemicros", + "-debug", + "-debugexclude=libevent", + "-debugexclude=leveldb", + "-mocktime=" + str(mocktime), + "-uacomment=testnode%d" % i, + "-noprinttoconsole" + ] self.cli = TestNodeCLI(os.getenv("BITCOINCLI", "bitcoin-cli"), self.datadir) self.use_cli = use_cli diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index f22322fbbc..4ec3175cd6 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -294,6 +294,7 @@ def initialize_datadir(dirname, n): os.makedirs(datadir) with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f: f.write("regtest=1\n") + f.write("[regtest]\n") f.write("port=" + str(p2p_port(n)) + "\n") f.write("rpcport=" + str(rpc_port(n)) + "\n") f.write("server=1\n") diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index eb6747c6f4..8c754807e6 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -48,8 +48,8 @@ class WalletHDTest(BitcoinTestFramework): # Also send funds to each add self.nodes[0].generate(101) hd_add = None - num_hd_adds = 300 - for i in range(num_hd_adds): + NUM_HD_ADDS = 10 + for i in range(NUM_HD_ADDS): hd_add = self.nodes[1].getnewaddress() hd_info = self.nodes[1].getaddressinfo(hd_add) assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'") @@ -65,7 +65,7 @@ class WalletHDTest(BitcoinTestFramework): assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") #second internal child key self.sync_all() - assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) + assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1) self.log.info("Restore backup ...") self.stop_node(1) @@ -78,10 +78,10 @@ class WalletHDTest(BitcoinTestFramework): # Assert that derivation is deterministic hd_add_2 = None - for _ in range(num_hd_adds): + for i in range(NUM_HD_ADDS): hd_add_2 = self.nodes[1].getnewaddress() hd_info_2 = self.nodes[1].getaddressinfo(hd_add_2) - assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(_)+"'") + assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(i)+"'") assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid) assert_equal(hd_add, hd_add_2) connect_nodes_bi(self.nodes, 0, 1) @@ -90,7 +90,7 @@ class WalletHDTest(BitcoinTestFramework): # Needs rescan self.stop_node(1) self.start_node(1, extra_args=self.extra_args[1] + ['-rescan']) - assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) + assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1) # Try a RPC based rescan self.stop_node(1) @@ -100,13 +100,15 @@ class WalletHDTest(BitcoinTestFramework): self.start_node(1, extra_args=self.extra_args[1]) connect_nodes_bi(self.nodes, 0, 1) self.sync_all() + # Wallet automatically scans blocks older than key on startup + assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1) out = self.nodes[1].rescanblockchain(0, 1) assert_equal(out['start_height'], 0) assert_equal(out['stop_height'], 1) out = self.nodes[1].rescanblockchain() assert_equal(out['start_height'], 0) assert_equal(out['stop_height'], self.nodes[1].getblockcount()) - assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) + assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1) # send a tx and make sure its using the internal chain for the changeoutput txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1) diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py index 4d349db23f..5f5bfcf683 100755 --- a/test/functional/wallet_importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -16,7 +16,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): self.nodes[0].generate(101) self.sync_all() - + # address address1 = self.nodes[0].getnewaddress() # pubkey diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index 9825e4d894..505014e48f 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -17,7 +17,7 @@ class KeyPoolTest(BitcoinTestFramework): addr_before_encrypting_data = nodes[0].getaddressinfo(addr_before_encrypting) wallet_info_old = nodes[0].getwalletinfo() assert(addr_before_encrypting_data['hdmasterkeyid'] == wallet_info_old['hdmasterkeyid']) - + # Encrypt wallet and wait to terminate nodes[0].node_encrypt_wallet('test') # Restart node 0 diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py index d742ec4618..7577c4a0d2 100755 --- a/test/functional/wallet_txn_clone.py +++ b/test/functional/wallet_txn_clone.py @@ -92,7 +92,8 @@ class TxnMallTest(BitcoinTestFramework): # Node0's balance should be starting balance, plus 50BTC for another # matured block, minus tx1 and tx2 amounts, and minus transaction fees: expected = starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"] - if self.options.mine_block: expected += 50 + if self.options.mine_block: + expected += 50 expected += tx1["amount"] + tx1["fee"] expected += tx2["amount"] + tx2["fee"] assert_equal(self.nodes[0].getbalance(), expected) @@ -131,7 +132,7 @@ class TxnMallTest(BitcoinTestFramework): tx1 = self.nodes[0].gettransaction(txid1) tx1_clone = self.nodes[0].gettransaction(txid1_clone) tx2 = self.nodes[0].gettransaction(txid2) - + # Verify expected confirmations assert_equal(tx1["confirmations"], -2) assert_equal(tx1_clone["confirmations"], 2) diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py index f16756eeaa..8419d6b545 100755 --- a/test/functional/wallet_txn_doublespend.py +++ b/test/functional/wallet_txn_doublespend.py @@ -27,7 +27,7 @@ class TxnMallTest(BitcoinTestFramework): for i in range(4): assert_equal(self.nodes[i].getbalance(), starting_balance) self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress! - + # Assign coins to foo and bar accounts: node0_address_foo = self.nodes[0].getnewaddress("foo") fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219) @@ -64,7 +64,7 @@ class TxnMallTest(BitcoinTestFramework): # Create two spends using 1 50 BTC coin each txid1 = self.nodes[0].sendfrom("foo", node1_address, 40, 0) txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0) - + # Have node0 mine a block: if (self.options.mine_block): self.nodes[0].generate(1) @@ -76,7 +76,8 @@ class TxnMallTest(BitcoinTestFramework): # Node0's balance should be starting balance, plus 50BTC for another # matured block, minus 40, minus 20, and minus transaction fees: expected = starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"] - if self.options.mine_block: expected += 50 + if self.options.mine_block: + expected += 50 expected += tx1["amount"] + tx1["fee"] expected += tx2["amount"] + tx2["fee"] assert_equal(self.nodes[0].getbalance(), expected) @@ -93,7 +94,7 @@ class TxnMallTest(BitcoinTestFramework): else: assert_equal(tx1["confirmations"], 0) assert_equal(tx2["confirmations"], 0) - + # Now give doublespend and its parents to miner: self.nodes[2].sendrawtransaction(fund_foo_tx["hex"]) self.nodes[2].sendrawtransaction(fund_bar_tx["hex"]) |