diff options
109 files changed, 1008 insertions, 938 deletions
diff --git a/.travis.yml b/.travis.yml index f515ab2b87..04308a5fa6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ cache: directories: - $TRAVIS_BUILD_DIR/depends/built - $TRAVIS_BUILD_DIR/depends/sdk-sources - - $HOME/.ccache + - $TRAVIS_BUILD_DIR/ci/scratch/.ccache stages: - lint - test diff --git a/.tx/config b/.tx/config index 743510a7f2..0e18a0df98 100644 --- a/.tx/config +++ b/.tx/config @@ -1,7 +1,7 @@ [main] host = https://www.transifex.com -[bitcoin.qt-translation-018x] +[bitcoin.qt-translation-019x] file_filter = src/qt/locale/bitcoin_<lang>.ts source_file = src/qt/locale/bitcoin_en.ts source_lang = en diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 268259c82d..1f4a2c081e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -79,25 +79,26 @@ about Git. The title of the pull request should be prefixed by the component or area that the pull request affects. Valid areas as: - - *Consensus* for changes to consensus critical code - - *Doc* for changes to the documentation - - *Qt* for changes to bitcoin-qt - - *Log* Changes to log messages - - *Mining* for changes to the mining code - - *Net* or *P2P* for changes to the peer-to-peer network code - - *Refactor* for structural changes that do not change behavior - - *RPC/REST/ZMQ* for changes to the RPC, REST or ZMQ APIs - - *Scripts and tools* for changes to the scripts and tools - - *Test* for changes to the bitcoin unit tests or QA tests - - *Utils and libraries* for changes to the utils and libraries - - *Wallet* for changes to the wallet code + - `consensus` for changes to consensus critical code + - `doc` for changes to the documentation + - `qt` or `gui` for changes to bitcoin-qt + - `log` for changes to log messages + - `mining` for changes to the mining code + - `net` or `p2p` for changes to the peer-to-peer network code + - `refactor` for structural changes that do not change behavior + - `rpc`, `rest` or `zmq` for changes to the RPC, REST or ZMQ APIs + - `script` for changes to the scripts and tools + - `test` for changes to the bitcoin unit tests or QA tests + - `util` or `lib` for changes to the utils or libraries + - `wallet` for changes to the wallet code + - `build` for changes to the GNU Autotools, reproducible builds or CI code Examples: - Consensus: Add new opcode for BIP-XXXX OP_CHECKAWESOMESIG - Net: Automatically create hidden service, listen on Tor - Qt: Add feed bump button - Log: Fix typo in log message + consensus: Add new opcode for BIP-XXXX OP_CHECKAWESOMESIG + net: Automatically create hidden service, listen on Tor + qt: Add feed bump button + log: Fix typo in log message Note that translations should not be submitted as pull requests, please see [Translation Process](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md) diff --git a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj.in index e64614c09d..56401d7618 100644 --- a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj +++ b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj.in @@ -9,27 +9,7 @@ <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir> </PropertyGroup> <ItemGroup> - <ClCompile Include="..\..\src\test\util.cpp" /> - <ClCompile Include="..\..\src\test\setup_common.cpp" /> - <ClCompile Include="..\..\src\bench\base58.cpp" /> - <ClCompile Include="..\..\src\bench\bech32.cpp" /> - <ClCompile Include="..\..\src\bench\bench.cpp" /> - <ClCompile Include="..\..\src\bench\bench_bitcoin.cpp" /> - <ClCompile Include="..\..\src\bench\ccoins_caching.cpp" /> - <ClCompile Include="..\..\src\bench\checkblock.cpp" /> - <ClCompile Include="..\..\src\bench\checkqueue.cpp" /> - <ClCompile Include="..\..\src\bench\coin_selection.cpp" /> - <ClCompile Include="..\..\src\bench\crypto_hash.cpp" /> - <ClCompile Include="..\..\src\bench\data.cpp" /> - <ClCompile Include="..\..\src\bench\examples.cpp" /> - <ClCompile Include="..\..\src\bench\lockedpool.cpp" /> - <ClCompile Include="..\..\src\bench\mempool_eviction.cpp" /> - <ClCompile Include="..\..\src\bench\rpc_blockchain.cpp" /> - <ClCompile Include="..\..\src\bench\rpc_mempool.cpp" /> - <ClCompile Include="..\..\src\bench\merkle_root.cpp" /> - <ClCompile Include="..\..\src\bench\rollingbloom.cpp" /> - <ClCompile Include="..\..\src\bench\wallet_balance.cpp" /> - <ClCompile Include="..\..\src\bench\verify_script.cpp" /> +@SOURCE_FILES@ </ItemGroup> <ItemGroup> <ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj"> diff --git a/build_msvc/common.init.vcxproj b/build_msvc/common.init.vcxproj index 0d186b5af2..e9f4e23862 100644 --- a/build_msvc/common.init.vcxproj +++ b/build_msvc/common.init.vcxproj @@ -113,6 +113,9 @@ <GenerateDebugInformation>true</GenerateDebugInformation> <AdditionalDependencies>crypt32.lib;Iphlpapi.lib;ws2_32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> + <Lib> + <AdditionalOptions>/ignore:4221</AdditionalOptions> + </Lib> </ItemDefinitionGroup> <Import Project="common.init.vcxproj.user" Condition="Exists('common.init.vcxproj.user')" /> </Project> diff --git a/build_msvc/msvc-autogen.py b/build_msvc/msvc-autogen.py index b612467bf3..5ddda3c03e 100644 --- a/build_msvc/msvc-autogen.py +++ b/build_msvc/msvc-autogen.py @@ -17,6 +17,7 @@ libs = [ 'libbitcoin_wallet_tool', 'libbitcoin_wallet', 'libbitcoin_zmq', + 'bench_bitcoin', ] ignore_list = [ diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh index 20bff368a5..01322f61e6 100755 --- a/ci/lint/04_install.sh +++ b/ci/lint/04_install.sh @@ -7,7 +7,7 @@ export LC_ALL=C travis_retry pip3 install codespell==1.15.0 -travis_retry pip3 install flake8==3.5.0 +travis_retry pip3 install flake8==3.7.8 travis_retry pip3 install vulture==0.29 SHELLCHECK_VERSION=v0.6.0 diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh index 09b37f8240..51b5cfdd3f 100755 --- a/ci/test/00_setup_env.sh +++ b/ci/test/00_setup_env.sh @@ -11,7 +11,10 @@ echo "Setting default values in env" BASE_ROOT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../ >/dev/null 2>&1 && pwd ) export BASE_ROOT_DIR -export MAKEJOBS=${MAKEJOBS:--j3} +# The number of parallel jobs to pass down to make and test_runner.py +export MAKEJOBS=${MAKEJOBS:--j4} +# A folder for the ci system to put temporary files (ccache, datadirs for tests, ...) +export BASE_SCRATCH_DIR=${BASE_SCRATCH_DIR:-$BASE_ROOT_DIR/ci/scratch/} export HOST=${HOST:-x86_64-unknown-linux-gnu} export RUN_UNIT_TESTS=${RUN_UNIT_TESTS:-true} export RUN_FUNCTIONAL_TESTS=${RUN_FUNCTIONAL_TESTS:-true} @@ -21,7 +24,7 @@ export BOOST_TEST_RANDOM=${BOOST_TEST_RANDOM:-1$TRAVIS_BUILD_ID} export CCACHE_SIZE=${CCACHE_SIZE:-100M} export CCACHE_TEMPDIR=${CCACHE_TEMPDIR:-/tmp/.ccache-temp} export CCACHE_COMPRESS=${CCACHE_COMPRESS:-1} -export CCACHE_DIR=${CCACHE_DIR:-$HOME/.ccache} +export CCACHE_DIR=${CCACHE_DIR:-$BASE_SCRATCH_DIR/.ccache} export BASE_BUILD_DIR=${BASE_BUILD_DIR:-${TRAVIS_BUILD_DIR:-$BASE_ROOT_DIR}} export BASE_OUTDIR=${BASE_OUTDIR:-$BASE_BUILD_DIR/out/$HOST} export SDK_URL=${SDK_URL:-https://bitcoincore.org/depends-sources/sdks} diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh index 3535746e83..54d7a9b814 100755 --- a/ci/test/04_install.sh +++ b/ci/test/04_install.sh @@ -6,6 +6,7 @@ export LC_ALL=C.UTF-8 +mkdir -p "${BASE_SCRATCH_DIR}" ccache echo "Creating ccache dir if it didn't already exist" if [ ! -d ${DIR_QA_ASSETS} ]; then diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh index d179ce81c0..ea7beae85f 100755 --- a/ci/test/06_script_b.sh +++ b/ci/test/06_script_b.sh @@ -16,7 +16,7 @@ fi if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then BEGIN_FOLD functional-tests - DOCKER_EXEC test/functional/test_runner.py --ci --ansi --combinedlogslen=4000 ${TEST_RUNNER_EXTRA} --quiet --failfast + DOCKER_EXEC test/functional/test_runner.py --ci $MAKEJOBS --tmpdirprefix "${BASE_SCRATCH_DIR}/test_runner/" --ansi --combinedlogslen=4000 ${TEST_RUNNER_EXTRA} --quiet --failfast END_FOLD fi diff --git a/configure.ac b/configure.ac index 35bb0c0231..97974d0f14 100644 --- a/configure.ac +++ b/configure.ac @@ -585,7 +585,7 @@ case $host in fi AX_CHECK_LINK_FLAG([[-Wl,-headerpad_max_install_names]], [LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names"]) - CPPFLAGS="$CPPFLAGS -DMAC_OSX" + CPPFLAGS="$CPPFLAGS -DMAC_OSX -DOBJC_OLD_DISPATCH_PROTOTYPES=0" OBJCXXFLAGS="$CXXFLAGS" ;; *android*) diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md index 3d1024c7a5..04fa02484f 100644 --- a/contrib/devtools/README.md +++ b/contrib/devtools/README.md @@ -120,18 +120,6 @@ If there are 'unsupported' symbols, the return value will be 1 a list like this .../64/test_bitcoin: symbol std::out_of_range::~out_of_range() from unsupported version GLIBCXX_3.4.15 .../64/test_bitcoin: symbol _ZNSt8__detail15_List_nod from unsupported version GLIBCXX_3.4.15 -update-translations.py -====================== - -Run this script from the root of the repository to update all translations from transifex. -It will do the following automatically: - -- fetch all translations -- post-process them into valid and committable format -- add missing translations to the build system (TODO) - -See doc/translation-process.md for more information. - circular-dependencies.py ======================== diff --git a/contrib/devtools/clang-format-diff.py b/contrib/devtools/clang-format-diff.py index f322b3a880..98eee67f43 100755 --- a/contrib/devtools/clang-format-diff.py +++ b/contrib/devtools/clang-format-diff.py @@ -106,7 +106,7 @@ def main(): filename = None lines_by_file = {} for line in sys.stdin: - match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line) + match = re.search(r'^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line) if match: filename = match.group(2) if filename is None: @@ -119,7 +119,7 @@ def main(): if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE): continue - match = re.search('^@@.*\+(\d+)(,(\d+))?', line) + match = re.search(r'^@@.*\+(\d+)(,(\d+))?', line) if match: start_line = int(match.group(1)) line_count = 1 diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py index fc01e570aa..67e77bc63d 100755 --- a/contrib/devtools/copyright_header.py +++ b/contrib/devtools/copyright_header.py @@ -71,7 +71,7 @@ def get_filenames_to_examine(base_directory): ################################################################################ -COPYRIGHT_WITH_C = 'Copyright \(c\)' +COPYRIGHT_WITH_C = r'Copyright \(c\)' COPYRIGHT_WITHOUT_C = 'Copyright' ANY_COPYRIGHT_STYLE = '(%s|%s)' % (COPYRIGHT_WITH_C, COPYRIGHT_WITHOUT_C) @@ -85,21 +85,21 @@ ANY_COPYRIGHT_STYLE_OR_YEAR_STYLE = ("%s %s" % (ANY_COPYRIGHT_STYLE, ANY_COPYRIGHT_COMPILED = re.compile(ANY_COPYRIGHT_STYLE_OR_YEAR_STYLE) def compile_copyright_regex(copyright_style, year_style, name): - return re.compile('%s %s,? %s' % (copyright_style, year_style, name)) + return re.compile(r'%s %s,? %s( +\*)?\n' % (copyright_style, year_style, name)) EXPECTED_HOLDER_NAMES = [ - "Satoshi Nakamoto\n", - "The Bitcoin Core developers\n", - "BitPay Inc\.\n", - "University of Illinois at Urbana-Champaign\.\n", - "Pieter Wuille\n", - "Wladimir J. van der Laan\n", - "Jeff Garzik\n", - "Jan-Klaas Kollhof\n", - "ArtForz -- public domain half-a-node\n", - "Intel Corporation", - "The Zcash developers", - "Jeremy Rubin", + r"Satoshi Nakamoto", + r"The Bitcoin Core developers", + r"BitPay Inc\.", + r"University of Illinois at Urbana-Champaign\.", + r"Pieter Wuille", + r"Wladimir J\. van der Laan", + r"Jeff Garzik", + r"Jan-Klaas Kollhof", + r"ArtForz -- public domain half-a-node", + r"Intel Corporation ?", + r"The Zcash developers", + r"Jeremy Rubin", ] DOMINANT_STYLE_COMPILED = {} @@ -329,7 +329,7 @@ def write_file_lines(filename, file_lines): # update header years execution ################################################################################ -COPYRIGHT = 'Copyright \(c\)' +COPYRIGHT = r'Copyright \(c\)' YEAR = "20[0-9][0-9]" YEAR_RANGE = '(%s)(-%s)?' % (YEAR, YEAR) HOLDER = 'The Bitcoin Core developers' diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index dd35d862c9..d8b684026c 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -141,7 +141,7 @@ def read_libraries(filename): for line in stdout.splitlines(): tokens = line.split() if len(tokens)>2 and tokens[1] == '(NEEDED)': - match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) + match = re.match(r'^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) if match: libraries.append(match.group(1)) else: @@ -171,5 +171,3 @@ if __name__ == '__main__': retval = 1 sys.exit(retval) - - diff --git a/contrib/devtools/update-translations.py b/contrib/devtools/update-translations.py deleted file mode 100755 index 1b9d3a4c27..0000000000 --- a/contrib/devtools/update-translations.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2014 Wladimir J. van der Laan -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -''' -Run this script from the root of the repository to update all translations from -transifex. -It will do the following automatically: - -- fetch all translations using the tx tool -- post-process them into valid and committable format - - remove invalid control characters - - remove location tags (makes diffs less noisy) - -TODO: -- auto-add new translations to the build system according to the translation process -''' -import subprocess -import re -import sys -import os -import io -import xml.etree.ElementTree as ET - -# Name of transifex tool -TX = 'tx' -# Name of source language file -SOURCE_LANG = 'bitcoin_en.ts' -# Directory with locale files -LOCALE_DIR = 'src/qt/locale' -# Minimum number of messages for translation to be considered at all -MIN_NUM_MESSAGES = 10 -# Regexp to check for Bitcoin addresses -ADDRESS_REGEXP = re.compile('([13]|bc1)[a-zA-Z0-9]{30,}') - -def check_at_repository_root(): - if not os.path.exists('.git'): - print('No .git directory found') - print('Execute this script at the root of the repository', file=sys.stderr) - sys.exit(1) - -def fetch_all_translations(): - if subprocess.call([TX, 'pull', '-f', '-a']): - print('Error while fetching translations', file=sys.stderr) - sys.exit(1) - -def find_format_specifiers(s): - '''Find all format specifiers in a string.''' - pos = 0 - specifiers = [] - while True: - percent = s.find('%', pos) - if percent < 0: - break - specifiers.append(s[percent+1]) - pos = percent+2 - return specifiers - -def split_format_specifiers(specifiers): - '''Split format specifiers between numeric (Qt) and others (strprintf)''' - numeric = [] - other = [] - for s in specifiers: - if s in {'1','2','3','4','5','6','7','8','9'}: - numeric.append(s) - else: - other.append(s) - - # If both numeric format specifiers and "others" are used, assume we're dealing - # with a Qt-formatted message. In the case of Qt formatting (see https://doc.qt.io/qt-5/qstring.html#arg) - # only numeric formats are replaced at all. This means "(percentage: %1%)" is valid, without needing - # any kind of escaping that would be necessary for strprintf. Without this, this function - # would wrongly detect '%)' as a printf format specifier. - if numeric: - other = [] - - # numeric (Qt) can be present in any order, others (strprintf) must be in specified order - return set(numeric),other - -def sanitize_string(s): - '''Sanitize string for printing''' - return s.replace('\n',' ') - -def check_format_specifiers(source, translation, errors, numerus): - source_f = split_format_specifiers(find_format_specifiers(source)) - # assert that no source messages contain both Qt and strprintf format specifiers - # if this fails, go change the source as this is hacky and confusing! - assert(not(source_f[0] and source_f[1])) - try: - translation_f = split_format_specifiers(find_format_specifiers(translation)) - except IndexError: - errors.append("Parse error in translation for '%s': '%s'" % (sanitize_string(source), sanitize_string(translation))) - return False - else: - if source_f != translation_f: - if numerus and source_f == (set(), ['n']) and translation_f == (set(), []) and translation.find('%') == -1: - # Allow numerus translations to omit %n specifier (usually when it only has one possible value) - return True - errors.append("Mismatch between '%s' and '%s'" % (sanitize_string(source), sanitize_string(translation))) - return False - return True - -def all_ts_files(suffix=''): - for filename in os.listdir(LOCALE_DIR): - # process only language files, and do not process source language - if not filename.endswith('.ts'+suffix) or filename == SOURCE_LANG+suffix: - continue - if suffix: # remove provided suffix - filename = filename[0:-len(suffix)] - filepath = os.path.join(LOCALE_DIR, filename) - yield(filename, filepath) - -FIX_RE = re.compile(b'[\x00-\x09\x0b\x0c\x0e-\x1f]') -def remove_invalid_characters(s): - '''Remove invalid characters from translation string''' - return FIX_RE.sub(b'', s) - -# Override cdata escape function to make our output match Qt's (optional, just for cleaner diffs for -# comparison, disable by default) -_orig_escape_cdata = None -def escape_cdata(text): - text = _orig_escape_cdata(text) - text = text.replace("'", ''') - text = text.replace('"', '"') - return text - -def contains_bitcoin_addr(text, errors): - if text is not None and ADDRESS_REGEXP.search(text) is not None: - errors.append('Translation "%s" contains a bitcoin address. This will be removed.' % (text)) - return True - return False - -def postprocess_translations(reduce_diff_hacks=False): - print('Checking and postprocessing...') - - if reduce_diff_hacks: - global _orig_escape_cdata - _orig_escape_cdata = ET._escape_cdata - ET._escape_cdata = escape_cdata - - for (filename,filepath) in all_ts_files(): - os.rename(filepath, filepath+'.orig') - - have_errors = False - for (filename,filepath) in all_ts_files('.orig'): - # pre-fixups to cope with transifex output - parser = ET.XMLParser(encoding='utf-8') # need to override encoding because 'utf8' is not understood only 'utf-8' - with open(filepath + '.orig', 'rb') as f: - data = f.read() - # remove control characters; this must be done over the entire file otherwise the XML parser will fail - data = remove_invalid_characters(data) - tree = ET.parse(io.BytesIO(data), parser=parser) - - # iterate over all messages in file - root = tree.getroot() - for context in root.findall('context'): - for message in context.findall('message'): - numerus = message.get('numerus') == 'yes' - source = message.find('source').text - translation_node = message.find('translation') - # pick all numerusforms - if numerus: - translations = [i.text for i in translation_node.findall('numerusform')] - else: - translations = [translation_node.text] - - for translation in translations: - if translation is None: - continue - errors = [] - valid = check_format_specifiers(source, translation, errors, numerus) and not contains_bitcoin_addr(translation, errors) - - for error in errors: - print('%s: %s' % (filename, error)) - - if not valid: # set type to unfinished and clear string if invalid - translation_node.clear() - translation_node.set('type', 'unfinished') - have_errors = True - - # Remove location tags - for location in message.findall('location'): - message.remove(location) - - # Remove entire message if it is an unfinished translation - if translation_node.get('type') == 'unfinished': - context.remove(message) - - # check if document is (virtually) empty, and remove it if so - num_messages = 0 - for context in root.findall('context'): - for message in context.findall('message'): - num_messages += 1 - if num_messages < MIN_NUM_MESSAGES: - print('Removing %s, as it contains only %i messages' % (filepath, num_messages)) - continue - - # write fixed-up tree - # if diff reduction requested, replace some XML to 'sanitize' to qt formatting - if reduce_diff_hacks: - out = io.BytesIO() - tree.write(out, encoding='utf-8') - out = out.getvalue() - out = out.replace(b' />', b'/>') - with open(filepath, 'wb') as f: - f.write(out) - else: - tree.write(filepath, encoding='utf-8') - return have_errors - -if __name__ == '__main__': - check_at_repository_root() - fetch_all_translations() - postprocess_translations() - diff --git a/contrib/init/bitcoind.service b/contrib/init/bitcoind.service index cfc5f77580..34c3e7b3ab 100644 --- a/contrib/init/bitcoind.service +++ b/contrib/init/bitcoind.service @@ -5,8 +5,9 @@ # See "man systemd.service" for details. # Note that almost all daemon options could be specified in -# /etc/bitcoin/bitcoin.conf, except for those explicitly specified as arguments -# in ExecStart= +# /etc/bitcoin/bitcoin.conf, but keep in mind those explicitly +# specified as arguments in ExecStart= will override those in the +# config file. [Unit] Description=Bitcoin daemon @@ -18,6 +19,10 @@ ExecStart=/usr/bin/bitcoind -daemon \ -conf=/etc/bitcoin/bitcoin.conf \ -datadir=/var/lib/bitcoind +# Make sure the config directory is readable by the service user +PermissionsStartOnly=true +ExecStartPre=/bin/chgrp bitcoin /etc/bitcoin + # Process management #################### @@ -53,6 +58,9 @@ PrivateTmp=true # Mount /usr, /boot/ and /etc read-only for the process. ProtectSystem=full +# Deny access to /home, /root and /run/user +ProtectHome=true + # Disallow the process and all of its children to gain # new privileges through execve(). NoNewPrivileges=true diff --git a/contrib/linearize/linearize-data.py b/contrib/linearize/linearize-data.py index 468aec04b5..95754ab937 100755 --- a/contrib/linearize/linearize-data.py +++ b/contrib/linearize/linearize-data.py @@ -263,12 +263,12 @@ if __name__ == '__main__': f = open(sys.argv[1], encoding="utf8") for line in f: # skip comment lines - m = re.search('^\s*#', line) + m = re.search(r'^\s*#', line) if m: continue # parse key=value lines - m = re.search('^(\w+)\s*=\s*(\S.*)$', line) + m = re.search(r'^(\w+)\s*=\s*(\S.*)$', line) if m is None: continue settings[m.group(1)] = m.group(2) diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py index 8529470e09..02c96d2a75 100755 --- a/contrib/linearize/linearize-hashes.py +++ b/contrib/linearize/linearize-hashes.py @@ -106,12 +106,12 @@ if __name__ == '__main__': f = open(sys.argv[1], encoding="utf8") for line in f: # skip comment lines - m = re.search('^\s*#', line) + m = re.search(r'^\s*#', line) if m: continue # parse key=value lines - m = re.search('^(\w+)\s*=\s*(\S.*)$', line) + m = re.search(r'^(\w+)\s*=\s*(\S.*)$', line) if m is None: continue settings[m.group(1)] = m.group(2) diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index 9da03e5b02..5374a6a382 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -765,8 +765,8 @@ if config.dmg is not None: output = runHDIUtil("attach", dmg_name + ".temp", readwrite=True, noverify=True, noautoopen=True, capture_stdout=True) except subprocess.CalledProcessError as e: sys.exit(e.returncode) - - m = re.search("/Volumes/(.+$)", output) + + m = re.search(r"/Volumes/(.+$)", output) disk_root = m.group(0) disk_name = m.group(1) diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index fe7cd1d597..7630a7a4fa 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -74,7 +74,7 @@ def name_to_ipv6(addr): raise ValueError('Could not parse address %s' % addr) def parse_spec(s, defaultport): - match = re.match('\[([0-9a-fA-F:]+)\](?::([0-9]+))?$', s) + match = re.match(r'\[([0-9a-fA-F:]+)\](?::([0-9]+))?$', s) if match: # ipv6 host = match.group(1) port = match.group(2) @@ -136,4 +136,3 @@ def main(): if __name__ == '__main__': main() - diff --git a/depends/packages/fontconfig.mk b/depends/packages/fontconfig.mk index 293631739d..ed21fbba33 100644 --- a/depends/packages/fontconfig.mk +++ b/depends/packages/fontconfig.mk @@ -6,7 +6,7 @@ $(package)_sha256_hash=b449a3e10c47e1d1c7a6ec6e2016cca73d3bd68fbbd4f0ae5cc6b573f $(package)_dependencies=freetype expat define $(package)_set_vars - $(package)_config_opts=--disable-docs --disable-static + $(package)_config_opts=--disable-docs --disable-static --disable-libxml2 --disable-iconv endef define $(package)_config_cmds diff --git a/depends/packages/libXau.mk b/depends/packages/libXau.mk index 0e4a60ad25..058416f793 100644 --- a/depends/packages/libXau.mk +++ b/depends/packages/libXau.mk @@ -5,8 +5,10 @@ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=fdd477320aeb5cdd67272838722d6b7d544887dfe7de46e1e7cc0c27c2bea4f2 $(package)_dependencies=xproto +# When updating this package, check the default value of +# --disable-xthreads. It is currently enabled. define $(package)_set_vars - $(package)_config_opts=--disable-shared + $(package)_config_opts=--disable-shared --disable-lint-library --without-lint $(package)_config_opts_linux=--with-pic endef diff --git a/depends/packages/libxcb.mk b/depends/packages/libxcb.mk index 90bc15f9f4..49d3e3d15b 100644 --- a/depends/packages/libxcb.mk +++ b/depends/packages/libxcb.mk @@ -6,7 +6,7 @@ $(package)_sha256_hash=98d9ab05b636dd088603b64229dd1ab2d2cc02ab807892e107d674f9c $(package)_dependencies=xcb_proto libXau define $(package)_set_vars -$(package)_config_opts=--disable-static +$(package)_config_opts=--disable-static --disable-build-docs --without-doxygen --without-launchd # Because we pass -qt-xcb to Qt, it will compile in a set of xcb helper libraries and extensions, # so we skip building all of the extensions here. # More info is available from: https://doc.qt.io/qt-5.9/linux-requirements.html diff --git a/depends/packages/qrencode.mk b/depends/packages/qrencode.mk index 56681b52a1..3bc2cb768c 100644 --- a/depends/packages/qrencode.mk +++ b/depends/packages/qrencode.mk @@ -5,7 +5,8 @@ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=efe5188b1ddbcbf98763b819b146be6a90481aac30cfc8d858ab78a19cde1fa5 define $(package)_set_vars -$(package)_config_opts=--disable-shared -without-tools --disable-sdltest +$(package)_config_opts=--disable-shared --without-tools --without-tests --disable-sdltest +$(package)_config_opts += --disable-gprof --disable-gcov --disable-mudflap $(package)_config_opts_linux=--with-pic endef diff --git a/depends/packages/xproto.mk b/depends/packages/xproto.mk index 23ad5ffa10..2462f3c647 100644 --- a/depends/packages/xproto.mk +++ b/depends/packages/xproto.mk @@ -5,7 +5,7 @@ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=636162c1759805a5a0114a369dffdeccb8af8c859ef6e1445f26a4e6e046514f define $(package)_set_vars -$(package)_config_opts=--disable-shared +$(package)_config_opts=--without-fop --without-xmlto --without-xsltproc --disable-specs endef define $(package)_preprocess_cmds diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index e69a37e093..9ac037ebb5 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -6,7 +6,9 @@ $(package)_sha256_hash=bcbabe1e2c7d0eec4ed612e10b94b112dd5f06fcefa994a0c79a45d83 $(package)_patches=0001-fix-build-with-older-mingw64.patch 0002-disable-pthread_set_name_np.patch define $(package)_set_vars - $(package)_config_opts=--without-docs --disable-shared --without-libsodium --disable-curve --disable-curve-keygen --disable-perf --disable-Werror + $(package)_config_opts=--without-docs --disable-shared --disable-curve --disable-curve-keygen --disable-perf --disable-Werror --disable-drafts + $(package)_config_opts += --without-libsodium --without-libgssapi_krb5 --without-pgm --without-norm --without-vmci + $(package)_config_opts += --disable-libunwind --disable-radix-tree --without-gcov $(package)_config_opts_linux=--with-pic $(package)_cxxflags=-std=c++11 endef diff --git a/doc/bitcoin-conf.md b/doc/bitcoin-conf.md index f8146b5d75..f4a8edec75 100644 --- a/doc/bitcoin-conf.md +++ b/doc/bitcoin-conf.md @@ -50,3 +50,13 @@ rpcport=4000 The configuration file is not automatically created; you can create it using your favorite text editor. By default, the configuration file name is `bitcoin.conf` and it is located in the Bitcoin data directory, but both the Bitcoin data directory and the configuration file path may be changed using the `-datadir` and `-conf` command-line options. The `includeconf=<file>` option in the `bitcoin.conf` file can be used to include additional configuration files. + +### Default configuration file locations + +Operating System | Data Directory | Example Path +-- | -- | -- +Windows | `%APPDATA%\Bitcoin\` | `C:\Users\username\AppData\Roaming\Bitcoin\bitcoin.conf` +Linux | `$HOME/.bitcoin/` | `/home/username/.bitcoin/bitcoin.conf` +macOS | `$HOME/Library/Application Support/Bitcoin/` | `/Users/username/Library/Application Support/Bitcoin/bitcoin.conf` + +You can find an example bitcoin.conf file in [share/examples/bitcoin.conf](../share/examples/bitcoin.conf). diff --git a/doc/developer-notes.md b/doc/developer-notes.md index d1114b0c73..561f623cd5 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -27,7 +27,7 @@ Developer Notes - [General C++](#general-c) - [C++ data structures](#c-data-structures) - [Strings and formatting](#strings-and-formatting) - - [Variable names](#variable-names) + - [Shadowing](#shadowing) - [Threads and synchronization](#threads-and-synchronization) - [Scripts](#scripts) - [Shebang](#shebang) @@ -613,27 +613,13 @@ Strings and formatting - *Rationale*: Bitcoin Core uses tinyformat, which is type safe. Leave them out to avoid confusion. -Variable names +Shadowing -------------- Although the shadowing warning (`-Wshadow`) is not enabled by default (it prevents issues arising from using a different variable with the same name), please name variables so that their names do not shadow variables defined in the source code. -E.g. in member initializers, prepend `_` to the argument name shadowing the -member name: - -```c++ -class AddressBookPage -{ - Mode m_mode; -} - -AddressBookPage::AddressBookPage(Mode _mode) - : m_mode(_mode) -... -``` - When using nested cycles, do not name the inner cycle variable the same as in the upper cycle, etc. diff --git a/doc/init.md b/doc/init.md index a6c9bb94d8..87e939c636 100644 --- a/doc/init.md +++ b/doc/init.md @@ -59,11 +59,11 @@ Data directory: `/var/lib/bitcoind` PID file: `/var/run/bitcoind/bitcoind.pid` (OpenRC and Upstart) or `/run/bitcoind/bitcoind.pid` (systemd) Lock file: `/var/lock/subsys/bitcoind` (CentOS) -The configuration file, PID directory (if applicable) and data directory -should all be owned by the bitcoin user and group. It is advised for security -reasons to make the configuration file and data directory only readable by the -bitcoin user and group. Access to bitcoin-cli and other bitcoind rpc clients -can then be controlled by group membership. +The PID directory (if applicable) and data directory should both be owned by the +bitcoin user and group. It is advised for security reasons to make the +configuration file and data directory only readable by the bitcoin user and +group. Access to bitcoin-cli and other bitcoind rpc clients can then be +controlled by group membership. NOTE: When using the systemd .service file, the creation of the aforementioned directories and the setting of their permissions is automatically handled by diff --git a/doc/release-notes-0.18.1-16257.md b/doc/release-notes-0.18.1-16257.md deleted file mode 100644 index 21867b7fb2..0000000000 --- a/doc/release-notes-0.18.1-16257.md +++ /dev/null @@ -1,6 +0,0 @@ -Wallet changes --------------- -When creating a transaction with a fee above `-maxtxfee` (default 0.1 BTC), -the RPC commands `walletcreatefundedpsbt` and `fundrawtransaction` will now fail -instead of rounding down the fee. Beware that the `feeRate` argument is specified -in BTC per kilobyte, not satoshi per byte. diff --git a/doc/release-notes-13756.md b/doc/release-notes-13756.md deleted file mode 100644 index a500aceb0f..0000000000 --- a/doc/release-notes-13756.md +++ /dev/null @@ -1,41 +0,0 @@ -Coin selection --------------- - -### Reuse Avoidance - -A new wallet flag `avoid_reuse` has been added (default off). When enabled, -a wallet will distinguish between used and unused addresses, and default to not -use the former in coin selection. - -Rescanning the blockchain is required, to correctly mark previously -used destinations. - -Together with "avoid partial spends" (present as of Bitcoin v0.17), this -addresses a serious privacy issue where a malicious user can track spends by -peppering a previously paid to address with near-dust outputs, which would then -be inadvertently included in future payments. - -New RPCs --------- - -- A new `setwalletflag` RPC sets/unsets flags for an existing wallet. - - -Updated RPCs ------------- - -Several RPCs have been updated to include an "avoid_reuse" flag, used to control -whether already used addresses should be left out or included in the operation. -These include: - -- createwallet -- getbalance -- getbalances -- sendtoaddress - -In addition, `sendtoaddress` has been changed to avoid partial spends when `avoid_reuse` -is enabled (if not already enabled via the `-avoidpartialspends` command line flag), -as it would otherwise risk using up the "wrong" UTXO for an address reuse case. - -The listunspent RPC has also been updated to now include a "reused" bool, for nodes -with "avoid_reuse" enabled. diff --git a/doc/release-notes-14054.md b/doc/release-notes-14054.md deleted file mode 100644 index d8cad369c5..0000000000 --- a/doc/release-notes-14054.md +++ /dev/null @@ -1,7 +0,0 @@ -P2P changes ------------ - -BIP 61 reject messages were deprecated in v0.18. They are now disabled by -default, but can be enabled by setting the `-enablebip61` command line option. -BIP 61 reject messages will be removed entirely in a future version of -Bitcoin Core. diff --git a/doc/release-notes-14802.md b/doc/release-notes-14802.md deleted file mode 100644 index 1fcc38866a..0000000000 --- a/doc/release-notes-14802.md +++ /dev/null @@ -1,3 +0,0 @@ -RPC changes ------------ -The `getblockstats` RPC is faster for fee calculation by using BlockUndo data. Also, `-txindex` is no longer required and `getblockstats` works for all non-pruned blocks. diff --git a/doc/release-notes-14954.md b/doc/release-notes-14954.md deleted file mode 100644 index 4bb0fcca74..0000000000 --- a/doc/release-notes-14954.md +++ /dev/null @@ -1,3 +0,0 @@ -Build system changes --------------------- -Python >=3.5 is now required by all aspects of the project. This includes the build systems, test framework and linters. The previously supported minimum (3.4), was EOL in March 2019. See #14954 for more details.
\ No newline at end of file diff --git a/doc/release-notes-15006.md b/doc/release-notes-15006.md deleted file mode 100644 index 76ed3247a6..0000000000 --- a/doc/release-notes-15006.md +++ /dev/null @@ -1,4 +0,0 @@ -Miscellaneous RPC changes ------------- - -- `createwallet` can now create encrypted wallets if a non-empty passphrase is specified. diff --git a/doc/release-notes-15427.md b/doc/release-notes-15427.md deleted file mode 100644 index 25edfd4402..0000000000 --- a/doc/release-notes-15427.md +++ /dev/null @@ -1,9 +0,0 @@ -Updated RPCs ------------- - -The `utxoupdatepsbt` RPC method has been updated to take a `descriptors` -argument. When provided, input and output scripts and keys will be filled in -when known, and P2SH-witness inputs will be filled in from the UTXO set when a -descriptor is provided that shows they're spending segwit outputs. - -See the RPC help text for full details. diff --git a/doc/release-notes-15566.md b/doc/release-notes-15566.md deleted file mode 100644 index 49964d7550..0000000000 --- a/doc/release-notes-15566.md +++ /dev/null @@ -1,3 +0,0 @@ -Miscellaneous CLI Changes -------------------------- -- The `testnet` field in `bitcoin-cli -getinfo` has been renamed to `chain` and now returns the current network name as defined in BIP70 (main, test, regtest).
\ No newline at end of file diff --git a/doc/release-notes-15620.md b/doc/release-notes-15620.md deleted file mode 100644 index bf89a70a4e..0000000000 --- a/doc/release-notes-15620.md +++ /dev/null @@ -1,13 +0,0 @@ -Updated RPCs ------------- - -* The -maxtxfee setting no longer has any effect on non-wallet RPCs. - - The `sendrawtransaction` and `testmempoolaccept` RPC methods previously - accepted an `allowhighfees` parameter to fail the mempool acceptance in case - the transaction's fee would exceed the value of the command line argument - `-maxtxfee`. To uncouple the RPCs from the global option, they now have a - hardcoded default for the maximum transaction fee, that can be changed for - both RPCs on a per-call basis with the `maxfeerate` parameter. The - `allowhighfees` boolean option has been removed and replaced by the - `maxfeerate` numeric option. diff --git a/doc/release-notes-15637.md b/doc/release-notes-15637.md deleted file mode 100644 index 048d5e7218..0000000000 --- a/doc/release-notes-15637.md +++ /dev/null @@ -1,3 +0,0 @@ -RPC changes ------------ -In getmempoolancestors, getmempooldescendants, getmempoolentry and getrawmempool RPCs, to be consistent with the returned value and other RPCs such as getrawtransaction, vsize has been added and size is now deprecated. size will only be returned if bitcoind is started with `-deprecatedrpc=size`.
\ No newline at end of file diff --git a/doc/release-notes-15730.md b/doc/release-notes-15730.md deleted file mode 100644 index 7a4a60b1ee..0000000000 --- a/doc/release-notes-15730.md +++ /dev/null @@ -1,5 +0,0 @@ -RPC changes ------------ -The RPC `getwalletinfo` response now includes the `scanning` key with an object -if there is a scanning in progress or `false` otherwise. Currently the object -has the scanning duration and progress. diff --git a/doc/release-notes-15849.md b/doc/release-notes-15849.md deleted file mode 100644 index a1df31f250..0000000000 --- a/doc/release-notes-15849.md +++ /dev/null @@ -1,6 +0,0 @@ -Thread names in logs --------------------- - -On platforms supporting `thread_local`, log lines can be prefixed with the name -of the thread that caused the log. To enable this behavior, use -`-logthreadnames=1`. diff --git a/doc/release-notes-15993.md b/doc/release-notes-15993.md deleted file mode 100644 index 493c7126ee..0000000000 --- a/doc/release-notes-15993.md +++ /dev/null @@ -1,3 +0,0 @@ -Build system changes --------------------- -The minimum supported miniUPnPc API version is set to 10. This keeps compatibility with Ubuntu 16.04 LTS and Debian 8 `libminiupnpc-dev` packages. Please note, on Debian this package is still vulnerable to [CVE-2017-8798](https://security-tracker.debian.org/tracker/CVE-2017-8798) (in jessie only) and [CVE-2017-1000494](https://security-tracker.debian.org/tracker/CVE-2017-1000494) (both in jessie and in stretch). diff --git a/doc/release-notes-16060.md b/doc/release-notes-16060.md deleted file mode 100644 index 7e150d10e7..0000000000 --- a/doc/release-notes-16060.md +++ /dev/null @@ -1,15 +0,0 @@ -Low-level RPC changes ----------------------- - -- Soft fork reporting in the `getblockchaininfo` return object has been - updated. For full details, see the RPC help text. In summary: - - The `bip9_softforks` sub-object is no longer returned - - The `softforks` sub-object now returns an object keyed by soft fork name, - rather than an array - - Each softfork object in the `softforks` object contains a `type` value which - is either `buried` (for soft fork deployments where the activation height is - hard-coded into the client implementation), or `bip9` (for soft fork deployments - where activation is controlled by BIP 9 signaling). - -- `getblocktemplate` no longer returns a `rules` array containing `CSV` - and `segwit` (the BIP 9 deployments that are currently in active state). diff --git a/doc/release-notes-16185.md b/doc/release-notes-16185.md new file mode 100644 index 0000000000..eeeb951e5b --- /dev/null +++ b/doc/release-notes-16185.md @@ -0,0 +1,3 @@ +RPC changes +----------- +The `gettransaction` RPC now accepts a third (boolean) argument `decode`. If set to `true`, a new `decoded` field will be added to the response containing the decoded transaction. diff --git a/doc/release-notes-16383.md b/doc/release-notes-16383.md deleted file mode 100644 index 8015715167..0000000000 --- a/doc/release-notes-16383.md +++ /dev/null @@ -1,8 +0,0 @@ -RPC changes ------------ - -RPCs which have an `include_watchonly` argument or `includeWatching` -option now default to `true` for watch-only wallets. Affected RPCs -are: `getbalance`, `listreceivedbyaddress`, `listreceivedbylabel`, -`listtransactions`, `listsinceblock`, `gettransaction`, -`walletcreatefundedpsbt`, and `fundrawtransaction`. diff --git a/doc/release-notes-16394.md b/doc/release-notes-16394.md deleted file mode 100644 index f09cba4b6d..0000000000 --- a/doc/release-notes-16394.md +++ /dev/null @@ -1,4 +0,0 @@ -RPC changes ------------ -`createwallet` now returns a warning if an empty string is used as an encryption password, and does not encrypt the wallet, instead of raising an error. -This makes it easier to disable encryption but also specify other options when using the `bitcoin-cli` tool. diff --git a/doc/release-notes-16647.md b/doc/release-notes-16647.md deleted file mode 100644 index 7e5539cb0d..0000000000 --- a/doc/release-notes-16647.md +++ /dev/null @@ -1,3 +0,0 @@ -RPC changes ------------ -`getmempoolentry` now provides a `weight` field containing the transaction weight as defined in BIP 141. diff --git a/doc/release-notes-16695.md b/doc/release-notes-16695.md new file mode 100644 index 0000000000..7acf1dcf97 --- /dev/null +++ b/doc/release-notes-16695.md @@ -0,0 +1,5 @@ +Updated RPCs +------------ + +- The `getchaintxstats` RPC now returns the additional key of + `window_final_block_height`. diff --git a/doc/release-notes.md b/doc/release-notes.md index e0673ae7ee..04aab56a72 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -64,14 +64,51 @@ platform. Notable changes =============== +New user documentation +---------------------- + +- [Reduce memory](https://github.com/bitcoin/bitcoin/blob/master/doc/reduce-memory.md) + suggests configuration tweaks for running Bitcoin Core on systems with + limited memory. (#16339) + New RPCs -------- - `getbalances` returns an object with all balances (`mine`, `untrusted_pending` and `immature`). Please refer to the RPC help of `getbalances` for details. The new RPC is intended to replace - `getunconfirmedbalance` and the balance fields in `getwalletinfo`, as well as - `getbalance`. The old calls may be removed in a future version. + `getbalance`, `getunconfirmedbalance`, and the balance fields in + `getwalletinfo`. These old calls and fields may be removed in a + future version. (#15930, #16239) + +- `setwalletflag` sets and unsets wallet flags that enable or disable + features specific to that existing wallet, such as the new + `avoid_reuse` feature documented elsewhere in these release notes. + (#13756) + +- `getblockfilter` gets the BIP158 filter for the specified block. This + RPC is only enabled if block filters have been created using the + `-blockfilterindex` configuration option. (#14121) + +New settings +------------ + +- `-blockfilterindex` enables the creation of BIP158 block filters for + the entire blockchain. Filters will be created in the background and + currently use about 4 GiB of space. Note: this version of Bitcoin + Core does not serve block filters over the P2P network, although the + local user may obtain block filters using the `getblockfilter` RPC. + (#14121) + +Updated settings +---------------- + +- `whitebind` and `whitelist` now accept a list of permissions to + provide peers connecting using the indicated interfaces or IP + addresses. If no permissions are specified with an address or CIDR + network, the implicit default permissions are the same as previous + releases. See the `bitcoind -help` output for these two options for + details about the available permissions. (#16248) Updated RPCs ------------ @@ -79,21 +116,128 @@ Updated RPCs Note: some low-level RPC changes mainly useful for testing are described in the Low-level Changes section below. -- The `sendmany` RPC had an argument `minconf` that was not well specified and - would lead to RPC errors even when the wallet's coin selection would succeed. - The `sendtoaddress` RPC never had this check, so to normalize the behavior, - `minconf` is now ignored in `sendmany`. If the coin selection does not - succeed due to missing coins, it will still throw an RPC error. Be reminded - that coin selection is influenced by the `-spendzeroconfchange`, - `-limitancestorcount`, `-limitdescendantcount` and `-walletrejectlongchains` - command line arguments. +- `sendmany` no longer has a `minconf` argument. This argument was not + well specified and would lead to RPC errors even when the wallet's + coin selection succeeded. Users who want to influence coin selection + can use the existing `-spendzeroconfchange`, `-limitancestorcount`, + `-limitdescendantcount` and `-walletrejectlongchains` configuration + arguments. (#15596) + +- `getbalance` and `sendtoaddress`, plus the new RPCs `getbalances` and + `createwallet`, now accept an "avoid_reuse" parameter that controls + whether already used addresses should be included in the operation. + Additionally, `sendtoaddress` will avoid partial spends when + `avoid_reuse` is enabled even if this feature is not already enabled + via the `-avoidpartialspends` command line flag because not doing so + would risk using up the "wrong" UTXO for an address reuse case. + (#13756) + +- `listunspent` now returns a "reused" bool for each output if the + wallet flag "avoid_reuse" is enabled. (#13756) + +- `getblockstats` now uses BlockUndo data instead of the transaction + index, making it much faster, no longer dependent on the `-txindex` + configuration option, and functional for all non-pruned blocks. + (#14802) + +- `utxoupdatepsbt` now accepts a `descriptors` parameter that will fill + out input and output scripts and keys when known. P2SH-witness inputs + will be filled in from the UTXO set when a descriptor is provided that + shows they're spending segwit outputs. See the RPC help text for full + details. (#15427) + +- `sendrawtransaction` and `testmempoolaccept` no longer accept a + `allowhighfees` parameter to fail mempool acceptance if the + transaction fee exceedes the value of the configuration option + `-maxtxfee`. Now there is a hardcoded default maximum feerate that + can be changed when calling either RPC using a `maxfeerate` parameter. + (#15620) + +- `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry`, and + `getrawmempool` no longer return a `size` field unless the + configuration option `-deprecatedrpc=size` is used. Instead a new + `vsize` field is returned with the transaction's virtual size + (consistent with other RPCs such as `getrawtransaction`). (#15637) + +- `getwalletinfo` now includes a `scanning` field that is either `false` + (no scanning) or an object with information about the duration and + progress of the wallet's scanning historical blocks for transactions + affecting its balances. (#15730) + +- `createwallet` accepts a new `passphrase` parameter. If set, this + will create the new wallet encrypted with the given passphrase. If + unset (the default) or set to an empty string, no encryption will be + used. (#16394) + +- `getmempoolentry` now provides a `weight` field containing the + transaction weight as defined in BIP141. (#16647) + +- `getdescriptorinfo` now returns an additional `checksum` field + containing the checksum for the unmodified descriptor provided by the + user (that is, before the descriptor is normalized for the + `descriptor` field). (#15986) + +- `walletcreatefundedpsbt` now signals BIP125 Replace-by-Fee if the + `-walletrbf` configuration option is set to true. (#15911) + +GUI changes +----------- + +- Provides bech32 addresses by default. The user may change the address + during invoice generation using a GUI toggle, or the default address + type may be changed by the `-addresstype` configuration option. + (#15711, #16497) + +Deprecated or removed configuration options +------------------------------------------- + +- `-mempoolreplacement` is removed, although default node behavior + remains the same. This option previously allowed the user to prevent + the node from accepting or relaying BIP125 transaction replacements. + This is different from the remaining configuration option + `-walletrbf`. (#16171) Deprecated or removed RPCs -------------------------- -- The `totalFee` option of the `bumpfee` RPC has been deprecated and will be - removed in 0.20. To continue using this option start with - `-deprecatedrpc=totalFee`. See the `bumpfee` RPC help text for more details. +- `bumpfee` no longer accepts a `totalFee` option unless the + configuration parameter `deprecatedrpc=totalFee` is specified. This + parameter will be fully removed in a subsequent release. (#15996) + +- `generate` is now removed after being deprecated in Bitcoin Core 0.18. + Use the `generatetoaddress` RPC instead. (#15492) + +P2P changes +----------- + +- BIP 61 reject messages were deprecated in v0.18. They are now disabled + by default, but can be enabled by setting the `-enablebip61` command + line option. BIP 61 reject messages will be removed entirely in a + future version of Bitcoin Core. (#14054) + +- To eliminate well-known denial-of-service vectors in Bitcoin Core, + especially for nodes with spinning disks, the default value for the + `-peerbloomfilters` configuration option has been changed to false. + This prevents Bitcoin Core from sending the BIP111 NODE_BLOOM service + flag, accepting BIP37 bloom filters, or serving merkle blocks or + transactions matching a bloom filter. Users who still want to provide + bloom filter support may either set the configuration option to true + to re-enable both BIP111 and BIP37 support or enable just BIP37 + support for specific peers using the updated `-whitelist` and + `-whitebind` configuration options described elsewhere in these + release notes. For the near future, lightweight clients using public + BIP111/BIP37 nodes should still be able to connect to older versions + of Bitcoin Core and nodes that have manually enabled BIP37 support, + but developers of such software should consider migrating to either + using specific BIP37 nodes or an alternative transaction filtering + system. (#16152) + +Miscellaneous CLI Changes +------------------------- + +- The `testnet` field in `bitcoin-cli -getinfo` has been renamed to + `chain` and now returns the current network name as defined in BIP70 + (main, test, regtest). (#15566) Low-level changes ================= @@ -101,24 +245,42 @@ Low-level changes RPC --- +- `getblockchaininfo` no longer returns a `bip9_softforks` object. + Instead, information has been moved into the `softforks` object and + an additional `type` field describes how Bitcoin Core determines + whether that soft fork is active (e.g. BIP9 or BIP90). See the RPC + help for details. (#16060) + +- `getblocktemplate` no longer returns a `rules` array containing `CSV` + and `segwit` (the BIP9 deployments that are currently in active + state). (#16060) + +- `getrpcinfo` now returns a `logpath` field with the path to + `debug.log`. (#15483) Tests ----- -- The regression test chain, that can be enabled by the `-regtest` command line - flag, now requires transactions to not violate standard policy by default. - Making the default the same as for mainnet, makes it easier to test mainnet - behavior on regtest. Be reminded that the testnet still allows non-standard - txs by default and that the policy can be locally adjusted with the - `-acceptnonstdtxn` command line flag for both test chains. +- The regression test chain enabled by the `-regtest` command line flag + now requires transactions to not violate standard policy by default. + This is the same default used for mainnet and makes it easier to test + mainnet behavior on regtest. Note that the testnet still allows + non-standard txs by default and that the policy can be locally + adjusted with the `-acceptnonstdtxn` command line flag for both test + chains. (#15891) Configuration ------------ -- An error is issued where previously a warning was issued when a setting in - the config file was specified in the default section, but not overridden for - the selected network. This change takes only effect if the selected network - is not mainnet. +- A setting specified in the default section but not also specified in a + network-specific section (e.g. testnet) will now produce a error + preventing startup instead of just a warning unless the network is + mainnet. This prevents settings intended for mainnet from being + applied to testnet or regtest. (#15629) + +- On platforms supporting `thread_local`, log lines can be prefixed with + the name of the thread that caused the log. To enable this behavior, + use `-logthreadnames=1`. (#15849) Network ------- @@ -129,17 +291,81 @@ Network peers' announcements were received. In this release, the download logic has changed to randomize the fetch order across peers and to prefer sending download requests to outbound peers over inbound peers. This fixes an issue - where inbound peers can prevent a node from getting a transaction. + where inbound peers could prevent a node from getting a transaction. + (#14897, #15834) + +- If a Tor hidden service is being used, Bitcoin Core will be bound to + the standard port 8333 even if a different port is configured for + clearnet connections. This prevents leaking node identity through use + of identical non-default port numbers. (#15651) + +Mempool and transaction relay +----------------------------- + +- Allows one extra single-ancestor transaction per package. Previously, + if a transaction in the mempool had 25 descendants, or it and all of + its descendants were over 101,000 vbytes, any newly-received + transaction that was also a descendant would be ignored. Now, one + extra descendant will be allowed provided it is an immediate + descendant (child) and the child's size is 10,000 vbytes or less. + This makes it possible for two-party contract protocols such as + Lightning Network to give each participant an output they can spend + immediately for Child-Pays-For-Parent (CPFP) fee bumping without + allowing one malicious participant to fill the entire package and thus + prevent the other participant from spending their output. (#15681) + +- Transactions with outputs paying v1 to v16 witness versions (future + segwit versions) are now accepted into the mempool, relayed, and + mined. Attempting to spend those outputs remains forbidden by policy + ("non-standard"). When this change has been widely deployed, wallets + and services can accept any valid bech32 Bitcoin address without + concern that transactions paying future segwit versions will become + stuck in an unconfirmed state. (#15846) + +- Legacy transactions (transactions with no segwit inputs) must now be + sent using the legacy encoding format, enforcing the rule specified in + BIP144. (#14039) Wallet ------ - When in pruned mode, a rescan that was triggered by an `importwallet`, - `importpubkey`, `importaddress`, or `importprivkey` RPC will only fail when - blocks have been pruned. Previously it would fail when `-prune` has been set. - This change allows to set `-prune` to a high value (e.g. the disk size) and - the calls to any of the import RPCs would fail when the first block is - pruned. + `importpubkey`, `importaddress`, or `importprivkey` RPC will only fail + when blocks have been pruned. Previously it would fail when `-prune` + has been set. This change allows setting `-prune` to a high value + (e.g. the disk size) without the calls to any of the import RPCs + failing until the first block is pruned. (#15870) + +- When creating a transaction with a fee above `-maxtxfee` (default 0.1 + BTC), the RPC commands `walletcreatefundedpsbt` and + `fundrawtransaction` will now fail instead of rounding down the fee. + Be aware that the `feeRate` argument is specified in BTC per 1,000 + vbytes, not satoshi per vbyte. (#16257) + +- A new wallet flag `avoid_reuse` has been added (default off). When + enabled, a wallet will distinguish between used and unused addresses, + and default to not use the former in coin selection. When setting + this flag on an existing wallet, rescanning the blockchain is required + to correctly mark previously used destinations. Together with "avoid + partial spends" (added in Bitcoin Core v0.17.0), this can eliminate a + serious privacy issue where a malicious user can track spends by + sending small payments to a previously-paid address that would then + be included with unrelated inputs in future payments. (#13756) + +Build system changes +-------------------- + +- Python >=3.5 is now required by all aspects of the project. This + includes the build systems, test framework and linters. The previously + supported minimum (3.4), was EOL in March 2019. (#14954) + +- The minimum supported miniUPnPc API version is set to 10. This keeps + compatibility with Ubuntu 16.04 LTS and Debian 8 `libminiupnpc-dev` + packages. Please note, on Debian this package is still vulnerable to + [CVE-2017-8798](https://security-tracker.debian.org/tracker/CVE-2017-8798) + (in jessie only) and + [CVE-2017-1000494](https://security-tracker.debian.org/tracker/CVE-2017-1000494) + (both in jessie and in stretch). (#15993) Credits ======= diff --git a/doc/release-notes/release-notes-16152.md b/doc/release-notes/release-notes-16152.md deleted file mode 100644 index 9c77cb9ae5..0000000000 --- a/doc/release-notes/release-notes-16152.md +++ /dev/null @@ -1,7 +0,0 @@ -P2P Changes ------------ -- The default value for the -peerbloomfilters configuration option (and, thus, NODE_BLOOM support) has been changed to false. - This resolves well-known DoS vectors in Bitcoin Core, especially for nodes with spinning disks. It is not anticipated that - this will result in a significant lack of availability of NODE_BLOOM-enabled nodes in the coming years, however, clients - which rely on the availability of NODE_BLOOM-supporting nodes on the P2P network should consider the process of migrating - to a more modern (and less trustful and privacy-violating) alternative over the coming years. diff --git a/doc/translation_process.md b/doc/translation_process.md index b9a10b6527..0e9245250f 100644 --- a/doc/translation_process.md +++ b/doc/translation_process.md @@ -65,13 +65,13 @@ username = USERNAME The Transifex Bitcoin project config file is included as part of the repo. It can be found at `.tx/config`, however you shouldn’t need to change anything. ### Synchronising translations -To assist in updating translations, we have created a script to help. +To assist in updating translations, a helper script is available in the [maintainer-tools repo](https://github.com/bitcoin-core/bitcoin-maintainer-tools). -1. `python contrib/devtools/update-translations.py` +1. `python3 ../bitcoin-maintainer-tools/update-translations.py` 2. `git add` new translations from `src/qt/locale/` 3. Update `src/qt/bitcoin_locale.qrc` manually or via ```bash -git ls-files src/qt/locale/*ts|xargs -n1 basename|sed 's/\(bitcoin_\(.*\)\).ts/<file alias="\2">locale\/\1.qm<\/file>/' +git ls-files src/qt/locale/*ts|xargs -n1 basename|sed 's/\(bitcoin_\(.*\)\).ts/ <file alias="\2">locale\/\1.qm<\/file>/' ``` 4. Update `src/Makefile.qt.include` manually or via ```bash diff --git a/doc/zmq.md b/doc/zmq.md index 7ffc5623b6..a309abd0cc 100644 --- a/doc/zmq.md +++ b/doc/zmq.md @@ -111,7 +111,9 @@ using other means such as firewalling. Note that when the block chain tip changes, a reorganisation may occur and just the tip will be notified. It is up to the subscriber to -retrieve the chain from the last known block to the new tip. +retrieve the chain from the last known block to the new tip. Also note +that no notification occurs if the tip was in the active chain - this +is the case after calling invalidateblock RPC. There are several possibilities that ZMQ notification can get lost during transmission depending on the communication type you are diff --git a/share/setup.nsi.in b/share/setup.nsi.in index acf9e86667..e9aa1f2b73 100644 --- a/share/setup.nsi.in +++ b/share/setup.nsi.in @@ -101,7 +101,7 @@ Section -post SEC0001 WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "@PACKAGE_VERSION@" WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}" WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}" - WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\uninstall.exe + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\bitcoin-qt.exe WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\uninstall.exe WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1 WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1 diff --git a/src/Makefile.am b/src/Makefile.am index 477b1300bc..8fc7f61d4b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -156,6 +156,7 @@ BITCOIN_CORE_H = \ netbase.h \ netmessagemaker.h \ node/coin.h \ + node/coinstats.h \ node/psbt.h \ node/transaction.h \ noui.h \ @@ -278,6 +279,7 @@ libbitcoin_server_a_SOURCES = \ net.cpp \ net_processing.cpp \ node/coin.cpp \ + node/coinstats.cpp \ node/psbt.cpp \ node/transaction.cpp \ noui.cpp \ diff --git a/src/bech32.cpp b/src/bech32.cpp index d6b29391a9..4c966350b4 100644 --- a/src/bech32.cpp +++ b/src/bech32.cpp @@ -4,6 +4,8 @@ #include <bech32.h> +#include <assert.h> + namespace { @@ -58,7 +60,7 @@ uint32_t PolyMod(const data& v) // During the course of the loop below, `c` contains the bitpacked coefficients of the // polynomial constructed from just the values of v that were processed so far, mod g(x). In - // the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of + // the above example, `c` initially corresponds to 1 mod g(x), and after processing 2 inputs of // v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value // for `c`. uint32_t c = 1; @@ -145,6 +147,10 @@ namespace bech32 /** Encode a Bech32 string. */ std::string Encode(const std::string& hrp, const data& values) { + // First ensure that the HRP is all lowercase. BIP-173 requires an encoder + // to return a lowercase Bech32 string, but if given an uppercase HRP, the + // result will always be invalid. + for (const char& c : hrp) assert(c < 'A' || c > 'Z'); data checksum = CreateChecksum(hrp, values); data combined = Cat(values, checksum); std::string ret = hrp + '1'; diff --git a/src/bech32.h b/src/bech32.h index 2e2823e974..fb39cd352b 100644 --- a/src/bech32.h +++ b/src/bech32.h @@ -19,7 +19,7 @@ namespace bech32 { -/** Encode a Bech32 string. Returns the empty string in case of failure. */ +/** Encode a Bech32 string. If hrp contains uppercase characters, this will cause an assertion error. */ std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values); /** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */ diff --git a/src/chain.h b/src/chain.h index dd9cc2a598..1b67ebbe41 100644 --- a/src/chain.h +++ b/src/chain.h @@ -95,8 +95,8 @@ enum BlockStatus: uint32_t { //! Unused. BLOCK_VALID_UNKNOWN = 0, - //! Parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future - BLOCK_VALID_HEADER = 1, + //! Reserved (was BLOCK_VALID_HEADER). + BLOCK_VALID_RESERVED = 1, //! All parent headers found, difficulty matches, timestamp >= median previous, checkpoint. Implies all parents //! are also at least TREE. @@ -117,7 +117,7 @@ enum BlockStatus: uint32_t { BLOCK_VALID_SCRIPTS = 5, //! All validity bits. - BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS | + BLOCK_VALID_MASK = BLOCK_VALID_RESERVED | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS | BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS, BLOCK_HAVE_DATA = 8, //!< full block available in blk*.dat diff --git a/src/init.cpp b/src/init.cpp index dd43ef71c9..ca419c05fa 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -438,9 +438,17 @@ void SetupServerArgs() #else hidden_args.emplace_back("-upnp"); #endif - gArgs.AddArg("-whitebind=<addr>", "Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - gArgs.AddArg("-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", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + gArgs.AddArg("-whitebind=<[permissions@]addr>", "Bind to given address and whitelist peers connecting to it. " + "Use [host]:port notation for IPv6. Allowed permissions are bloomfilter (allow requesting BIP37 filtered blocks and transactions), " + "noban (do not ban for misbehavior), " + "forcerelay (relay even non-standard transactions), " + "relay (relay even in -blocksonly mode), " + "and mempool (allow requesting BIP35 mempool contents). " + "Specify multiple permissions separated by commas (default: noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + + gArgs.AddArg("-whitelist=<[permissions@]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). Uses same permissions as " + "-whitebind. Can be specified multiple times." , ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); g_wallet_init_interface.AddWalletOptions(); @@ -510,8 +518,8 @@ void SetupServerArgs() gArgs.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); gArgs.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); - gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted inbound peers even if the transactions were already in the mempool or violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); - gArgs.AddArg("-whitelistrelay", strprintf("Accept relayed transactions received from whitelisted inbound peers even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool or violate local relay policy. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted inbound peers with default permissions. The will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); gArgs.AddArg("-blockmaxweight=<n>", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION); @@ -1052,7 +1060,7 @@ bool AppInitParameterInteraction() { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n)) - return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", ""))); + return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")).translated); incrementalRelayFee = CFeeRate(n); } @@ -1096,7 +1104,7 @@ bool AppInitParameterInteraction() if (gArgs.IsArgSet("-minrelaytxfee")) { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) { - return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", ""))); + return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")).translated); } // High fee check is done afterward in WalletParameterInteraction() ::minRelayTxFee = CFeeRate(n); @@ -1112,7 +1120,7 @@ bool AppInitParameterInteraction() { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) - return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", ""))); + return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")).translated); } // Feerate used to define dust. Shouldn't be changed lightly as old @@ -1121,7 +1129,7 @@ bool AppInitParameterInteraction() { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n)) - return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", ""))); + return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")).translated); dustRelayFee = CFeeRate(n); } diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp new file mode 100644 index 0000000000..e1891b9898 --- /dev/null +++ b/src/node/coinstats.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <node/coinstats.h> + +#include <amount.h> +#include <coins.h> +#include <chain.h> +#include <hash.h> +#include <serialize.h> +#include <validation.h> +#include <uint256.h> +#include <util/system.h> + +#include <map> + +#include <boost/thread.hpp> + + +static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs) +{ + assert(!outputs.empty()); + ss << hash; + ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.fCoinBase ? 1u : 0u); + stats.nTransactions++; + for (const auto& output : outputs) { + ss << VARINT(output.first + 1); + ss << output.second.out.scriptPubKey; + ss << VARINT(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED); + stats.nTransactionOutputs++; + stats.nTotalAmount += output.second.out.nValue; + stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ + + 2 /* scriptPubKey len */ + output.second.out.scriptPubKey.size() /* scriptPubKey */; + } + ss << VARINT(0u); +} + +//! Calculate statistics about the unspent transaction output set +bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) +{ + std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor()); + assert(pcursor); + + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + stats.hashBlock = pcursor->GetBestBlock(); + { + LOCK(cs_main); + stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight; + } + ss << stats.hashBlock; + uint256 prevkey; + std::map<uint32_t, Coin> outputs; + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + COutPoint key; + Coin coin; + if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { + if (!outputs.empty() && key.hash != prevkey) { + ApplyStats(stats, ss, prevkey, outputs); + outputs.clear(); + } + prevkey = key.hash; + outputs[key.n] = std::move(coin); + } else { + return error("%s: unable to read value", __func__); + } + pcursor->Next(); + } + if (!outputs.empty()) { + ApplyStats(stats, ss, prevkey, outputs); + } + stats.hashSerialized = ss.GetHash(); + stats.nDiskSize = view->EstimateSize(); + return true; +} diff --git a/src/node/coinstats.h b/src/node/coinstats.h new file mode 100644 index 0000000000..7c11aab8bd --- /dev/null +++ b/src/node/coinstats.h @@ -0,0 +1,33 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NODE_COINSTATS_H +#define BITCOIN_NODE_COINSTATS_H + +#include <amount.h> +#include <uint256.h> + +#include <cstdint> + +class CCoinsView; + +struct CCoinsStats +{ + int nHeight; + uint256 hashBlock; + uint64_t nTransactions; + uint64_t nTransactionOutputs; + uint64_t nBogoSize; + uint256 hashSerialized; + uint64_t nDiskSize; + CAmount nTotalAmount; + + CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), nDiskSize(0), nTotalAmount(0) {} +}; + +//! Calculate statistics about the unspent transaction output set +bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats); + +#endif // BITCOIN_NODE_COINSTATS_H diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 5854ade655..9fa49b87fa 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -6,6 +6,7 @@ #include <qt/bitcoinunits.h> #include <qt/guiconstants.h> +#include <qt/guiutil.h> #include <qt/qvaluecombobox.h> #include <QApplication> @@ -121,7 +122,7 @@ public: const QFontMetrics fm(fontMetrics()); int h = lineEdit()->minimumSizeHint().height(); - int w = fm.width(BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways)); + int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways)); w += 2; // cursor blinking space QStyleOptionSpinBox opt; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bc9af9793e..323797a4b6 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -40,7 +40,6 @@ #include <QApplication> #include <QComboBox> #include <QDateTime> -#include <QDesktopWidget> #include <QDragEnterEvent> #include <QListWidget> #include <QMenu> @@ -48,6 +47,7 @@ #include <QMessageBox> #include <QMimeData> #include <QProgressDialog> +#include <QScreen> #include <QSettings> #include <QShortcut> #include <QStackedWidget> @@ -81,7 +81,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty QSettings settings; if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { // Restore failed (perhaps missing setting), center the window - move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center()); } #ifdef ENABLE_WALLET @@ -1407,7 +1407,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *pl const QFontMetrics fm(font()); for (const BitcoinUnits::Unit unit : units) { - max_width = qMax(max_width, fm.width(BitcoinUnits::longName(unit))); + max_width = qMax(max_width, GUIUtil::TextWidth(fm, BitcoinUnits::longName(unit))); } setMinimumSize(max_width, 0); setAlignment(Qt::AlignRight | Qt::AlignVCenter); diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 87736cd185..5cde21eec6 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -131,12 +131,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. %s is shu QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address or hostname: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address or hostname: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid P2P permission: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -%s=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -discardfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -fallbackfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid netmask specified in -whitelist: '%s'"), -QT_TRANSLATE_NOOP("bitcoin-core", "Keypool ran out, please call keypoolrefill first"), QT_TRANSLATE_NOOP("bitcoin-core", "Loading P2P addresses..."), QT_TRANSLATE_NOOP("bitcoin-core", "Loading banlist..."), QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."), @@ -170,7 +170,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must not be negative"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction fee and change calculation failed"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction has too long of a mempool chain"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction must have at least one recipient"), -QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large for fee policy"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"), QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer. %s is probably already running."), diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 6e52c5e477..be807b20c0 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -15,6 +15,25 @@ </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> + <widget class="QLabel" name="label_alerts"> + <property name="visible"> + <bool>false</bool> + </property> + <property name="styleSheet"> + <string notr="true">QLabel { background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000; }</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="margin"> + <number>3</number> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> <widget class="QTabWidget" name="tabWidget"> <property name="currentIndex"> <number>0</number> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index dc1da7f8a9..070df31aa6 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -39,7 +39,6 @@ #include <QClipboard> #include <QDateTime> #include <QDesktopServices> -#include <QDesktopWidget> #include <QDoubleValidator> #include <QFileDialog> #include <QFont> @@ -58,9 +57,10 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#include <objc/objc-runtime.h> #include <CoreServices/CoreServices.h> #include <QProcess> + +void ForceActivation(); #endif namespace GUIUtil { @@ -360,10 +360,7 @@ bool isObscured(QWidget *w) void bringToFront(QWidget* w) { #ifdef Q_OS_MAC - // Force application activation on macOS. With Qt 5.4 this is required when - // an action in the dock menu is triggered. - id app = objc_msgSend((id) objc_getClass("NSApplication"), sel_registerName("sharedApplication")); - objc_msgSend(app, sel_registerName("activateIgnoringOtherApps:"), YES); + ForceActivation(); #endif if (w) { @@ -916,7 +913,7 @@ qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal m while(font_size >= minPointSize) { font.setPointSizeF(font_size); QFontMetrics fm(font); - if (fm.width(text) < width) { + if (TextWidth(fm, text) < width) { break; } font_size -= 0.5; @@ -948,7 +945,7 @@ void PolishProgressDialog(QProgressDialog* dialog) { #ifdef Q_OS_MAC // Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357. - const int margin = dialog->fontMetrics().width("X"); + const int margin = TextWidth(dialog->fontMetrics(), ("X")); dialog->resize(dialog->width() + 2 * margin, dialog->height()); dialog->show(); #else @@ -956,4 +953,13 @@ void PolishProgressDialog(QProgressDialog* dialog) #endif } +int TextWidth(const QFontMetrics& fm, const QString& text) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + return fm.horizontalAdvance(text); +#else + return fm.width(text); +#endif +} + } // namespace GUIUtil diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index bea4a83494..9db92f94d7 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -257,6 +257,14 @@ namespace GUIUtil // Fix known bugs in QProgressDialog class. void PolishProgressDialog(QProgressDialog* dialog); + + /** + * Returns the distance in pixels appropriate for drawing a subsequent character after text. + * + * In Qt 5.12 and before the QFontMetrics::width() is used and it is deprecated since Qt 13.0. + * In Qt 5.11 the QFontMetrics::horizontalAdvance() was introduced. + */ + int TextWidth(const QFontMetrics& fm, const QString& text); } // namespace GUIUtil #endif // BITCOIN_QT_GUIUTIL_H diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index bff7469071..7864f97f31 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -132,7 +132,7 @@ <context> <name>AddressTableModel</name> <message> - <location filename="../addresstablemodel.cpp" line="+163"/> + <location filename="../addresstablemodel.cpp" line="+165"/> <source>Label</source> <translation type="unfinished"></translation> </message> @@ -297,7 +297,7 @@ <context> <name>BanTableModel</name> <message> - <location filename="../bantablemodel.cpp" line="+86"/> + <location filename="../bantablemodel.cpp" line="+88"/> <source>IP/Netmask</source> <translation type="unfinished"></translation> </message> @@ -310,17 +310,17 @@ <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+318"/> + <location filename="../bitcoingui.cpp" line="+315"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> <message> - <location line="+638"/> + <location line="+637"/> <source>Synchronizing with network...</source> <translation>Synchronizing with network...</translation> </message> <message> - <location line="-716"/> + <location line="-715"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -400,7 +400,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+217"/> + <location line="+216"/> <source>Wallet:</source> <translation type="unfinished"></translation> </message> @@ -435,7 +435,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-1036"/> + <location line="-1035"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> @@ -500,7 +500,7 @@ <translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation> </message> <message> - <location line="+118"/> + <location line="+117"/> <source>&File</source> <translation>&File</translation> </message> @@ -520,7 +520,7 @@ <translation>Tabs toolbar</translation> </message> <message> - <location line="-271"/> + <location line="-270"/> <source>Request payments (generates QR codes and bitcoin: URIs)</source> <translation type="unfinished"></translation> </message> @@ -545,7 +545,7 @@ <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="+540"/> + <location line="+539"/> <source>%n active connection(s) to Bitcoin network</source> <translation> <numerusform>%n active connection to Bitcoin network</numerusform> @@ -606,7 +606,7 @@ <translation>Up to date</translation> </message> <message> - <location line="-657"/> + <location line="-656"/> <source>&Sending addresses</source> <translation type="unfinished"></translation> </message> @@ -641,7 +641,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+30"/> + <location line="+29"/> <source>default wallet</source> <translation type="unfinished"></translation> </message> @@ -782,7 +782,7 @@ <translation>Wallet is <b>encrypted</b> and currently <b>locked</b></translation> </message> <message> - <location filename="../bitcoin.cpp" line="+390"/> + <location filename="../bitcoin.cpp" line="+382"/> <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source> <translation type="unfinished"></translation> </message> @@ -941,7 +941,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+155"/> + <location line="+157"/> <source>yes</source> <translation type="unfinished"></translation> </message> @@ -1736,14 +1736,14 @@ <location filename="../paymentserver.cpp" line="+226"/> <location line="+346"/> <location line="+42"/> - <location line="+110"/> + <location line="+108"/> <location line="+14"/> <location line="+18"/> <source>Payment request error</source> <translation type="unfinished"></translation> </message> <message> - <location line="-529"/> + <location line="-527"/> <source>Cannot start bitcoin: click-to-pay handler</source> <translation type="unfinished"></translation> </message> @@ -1805,12 +1805,12 @@ <location line="+31"/> <location line="+10"/> <location line="+17"/> - <location line="+85"/> + <location line="+83"/> <source>Payment request rejected</source> <translation type="unfinished"></translation> </message> <message> - <location line="-152"/> + <location line="-150"/> <source>Payment request network doesn't match client network.</source> <translation type="unfinished"></translation> </message> @@ -1841,7 +1841,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+65"/> + <location line="+63"/> <source>Refund from %1</source> <translation type="unfinished"></translation> </message> @@ -1879,7 +1879,7 @@ <context> <name>PeerTableModel</name> <message> - <location filename="../peertablemodel.cpp" line="+108"/> + <location filename="../peertablemodel.cpp" line="+110"/> <source>User Agent</source> <translation type="unfinished"></translation> </message> @@ -1922,7 +1922,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+702"/> + <location line="+699"/> <source>%1 d</source> <translation type="unfinished"></translation> </message> @@ -1938,7 +1938,7 @@ </message> <message> <location line="+2"/> - <location line="+50"/> + <location line="+47"/> <source>%1 s</source> <translation type="unfinished"></translation> </message> @@ -2032,27 +2032,22 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../bitcoin.cpp" line="+74"/> - <source>Error parsing command line arguments: %1.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+37"/> + <location filename="../bitcoin.cpp" line="+116"/> <source>Error: Specified data directory "%1" does not exist.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> + <location line="+6"/> <source>Error: Cannot parse configuration file: %1.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+14"/> + <location line="+15"/> <source>Error: %1</source> <translation type="unfinished"></translation> </message> <message> - <location line="+57"/> + <location line="+59"/> <source>%1 didn't yet exit safely...</source> <translation type="unfinished"></translation> </message> @@ -2103,7 +2098,7 @@ <context> <name>RPCConsole</name> <message> - <location filename="../forms/debugwindow.ui" line="+56"/> + <location filename="../forms/debugwindow.ui" line="+75"/> <location line="+26"/> <location line="+26"/> <location line="+26"/> @@ -2147,12 +2142,12 @@ <translation>&Information</translation> </message> <message> - <location line="-10"/> + <location line="-29"/> <source>Debug window</source> <translation type="unfinished"></translation> </message> <message> - <location line="+25"/> + <location line="+44"/> <source>General</source> <translation type="unfinished"></translation> </message> @@ -2265,8 +2260,8 @@ </message> <message> <location line="+65"/> - <location filename="../rpcconsole.cpp" line="+498"/> - <location line="+757"/> + <location filename="../rpcconsole.cpp" line="+497"/> + <location line="+759"/> <source>Select a peer to view detailed information.</source> <translation type="unfinished"></translation> </message> @@ -2417,7 +2412,7 @@ <translation>Clear console</translation> </message> <message> - <location filename="../rpcconsole.cpp" line="-252"/> + <location filename="../rpcconsole.cpp" line="-243"/> <source>1 &hour</source> <translation type="unfinished"></translation> </message> @@ -2450,7 +2445,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+47"/> + <location line="+38"/> <source>&Unban</source> <translation type="unfinished"></translation> </message> @@ -2628,7 +2623,7 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../receivecoinsdialog.cpp" line="+45"/> + <location filename="../receivecoinsdialog.cpp" line="+46"/> <source>Copy URI</source> <translation type="unfinished"></translation> </message> @@ -2714,7 +2709,7 @@ <context> <name>RecentRequestsTableModel</name> <message> - <location filename="../recentrequeststablemodel.cpp" line="+25"/> + <location filename="../recentrequeststablemodel.cpp" line="+27"/> <source>Date</source> <translation type="unfinished">Date</translation> </message> @@ -2753,7 +2748,7 @@ <name>SendCoinsDialog</name> <message> <location filename="../forms/sendcoinsdialog.ui" line="+14"/> - <location filename="../sendcoinsdialog.cpp" line="+600"/> + <location filename="../sendcoinsdialog.cpp" line="+601"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> @@ -2940,7 +2935,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation>S&end</translation> </message> <message> - <location filename="../sendcoinsdialog.cpp" line="-512"/> + <location filename="../sendcoinsdialog.cpp" line="-513"/> <source>Copy quantity</source> <translation type="unfinished"></translation> </message> @@ -2980,7 +2975,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+117"/> + <location line="+118"/> <source> from wallet '%1'</source> <translation type="unfinished"></translation> </message> @@ -3698,7 +3693,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <context> <name>TransactionTableModel</name> <message> - <location filename="../transactiontablemodel.cpp" line="+223"/> + <location filename="../transactiontablemodel.cpp" line="+225"/> <source>Date</source> <translation type="unfinished">Date</translation> </message> @@ -3834,7 +3829,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <context> <name>TransactionView</name> <message> - <location filename="../transactionview.cpp" line="+70"/> + <location filename="../transactionview.cpp" line="+69"/> <location line="+16"/> <source>All</source> <translation type="unfinished"></translation> @@ -3955,7 +3950,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+199"/> + <location line="+194"/> <source>Export Transaction History</source> <translation type="unfinished"></translation> </message> @@ -4033,7 +4028,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <context> <name>UnitDisplayStatusBarControl</name> <message> - <location filename="../bitcoingui.cpp" line="+155"/> + <location filename="../bitcoingui.cpp" line="+156"/> <source>Unit to show amounts in. Click to select another unit.</source> <translation type="unfinished"></translation> </message> @@ -4041,7 +4036,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <context> <name>WalletController</name> <message> - <location filename="../walletcontroller.cpp" line="+70"/> + <location filename="../walletcontroller.cpp" line="+73"/> <source>Close wallet</source> <translation type="unfinished"></translation> </message> @@ -4072,7 +4067,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished">Send Coins</translation> </message> <message> - <location line="+301"/> + <location line="+309"/> <location line="+39"/> <location line="+5"/> <source>Fee bump error</source> @@ -4205,12 +4200,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+31"/> + <location line="+30"/> <source>Unable to start HTTP server. See debug log for details.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-168"/> + <location line="-167"/> <source>The %s developers</source> <translation type="unfinished"></translation> </message> @@ -4391,6 +4386,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos </message> <message> <location line="+4"/> + <source>Invalid P2P permission: '%s'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Invalid amount for -%s=<amount>: '%s'</source> <translation type="unfinished"></translation> </message> @@ -4405,17 +4405,17 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+23"/> + <location line="+22"/> <source>Specified blocks directory "%s" does not exist.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+26"/> + <location line="+25"/> <source>Upgrading txindex database</source> <translation type="unfinished"></translation> </message> <message> - <location line="-45"/> + <location line="-44"/> <source>Loading P2P addresses...</source> <translation type="unfinished"></translation> </message> @@ -4465,7 +4465,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <source>Unable to bind to %s on this computer. %s is probably already running.</source> <translation type="unfinished"></translation> </message> @@ -4500,7 +4500,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="-155"/> + <location line="-154"/> <source>Error: Listening for incoming connections failed (listen returned error %s)</source> <translation type="unfinished"></translation> </message> @@ -4545,7 +4545,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+5"/> <source>Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)</source> <translation type="unfinished"></translation> </message> @@ -4555,7 +4555,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <source>Need to specify a port with -whitebind: '%s'</source> <translation type="unfinished"></translation> </message> @@ -4617,11 +4617,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos </message> <message> <location line="+5"/> - <source>Transaction too large for fee policy</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> <source>Transaction too large</source> <translation>Transaction too large</translation> </message> @@ -4661,7 +4656,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="-178"/> + <location line="-177"/> <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source> <translation type="unfinished"></translation> </message> @@ -4696,12 +4691,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+20"/> - <source>Keypool ran out, please call keypoolrefill first</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+21"/> + <location line="+41"/> <source>Starting network threads...</source> <translation type="unfinished"></translation> </message> @@ -4736,12 +4726,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Unknown network specified in -onlynet: '%s'</translation> </message> <message> - <location line="-51"/> + <location line="-50"/> <source>Insufficient funds</source> <translation>Insufficient funds</translation> </message> diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index 102adce6c5..5eb23c76e6 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -1,12 +1,11 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "macdockiconhandler.h" -#undef slots -#include <objc/objc.h> -#include <objc/message.h> +#include <AppKit/AppKit.h> +#include <objc/runtime.h> static MacDockIconHandler *s_instance = nullptr; @@ -21,9 +20,7 @@ bool dockClickHandler(id self, SEL _cmd, ...) { } void setupDockClickHandler() { - id app = objc_msgSend((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication")); - id delegate = objc_msgSend(app, sel_registerName("delegate")); - Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class")); + Class delClass = (Class)[[[NSApplication sharedApplication] delegate] class]; SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"); class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:"); } @@ -44,3 +41,13 @@ void MacDockIconHandler::cleanup() { delete s_instance; } + +/** + * Force application activation on macOS. With Qt 5.5.1 this is required when + * an action in the Dock menu is triggered. + * TODO: Define a Qt version where it's no-longer necessary. + */ +void ForceActivation() +{ + [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; +} diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index d8e48f350a..07ffff0126 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -204,9 +204,8 @@ void OverviewPage::updateWatchOnlyLabels(bool showWatchOnly) void OverviewPage::setClientModel(ClientModel *model) { this->clientModel = model; - if(model) - { - // Show warning if this is a prerelease version + if (model) { + // Show warning, for example if this is a prerelease version connect(model, &ClientModel::alertsChanged, this, &OverviewPage::updateAlerts); updateAlerts(model->getStatusBarWarnings()); } diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index f3f5d28af9..0bb87742e9 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -41,8 +41,8 @@ #include <QNetworkReply> #include <QNetworkRequest> #include <QSslCertificate> +#include <QSslConfiguration> #include <QSslError> -#include <QSslSocket> #include <QStringList> #include <QTextDocument> #include <QUrlQuery> @@ -448,9 +448,9 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) certList = QSslCertificate::fromPath(certFile); // Use those certificates when fetching payment requests, too: - QSslSocket::setDefaultCaCertificates(certList); + QSslConfiguration::defaultConfiguration().setCaCertificates(certList); } else - certList = QSslSocket::systemCaCertificates(); + certList = QSslConfiguration::systemCaCertificates(); int nRootCerts = 0; const QDateTime currentTime = QDateTime::currentDateTime(); diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 05157c2a4a..e8cf432131 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -261,7 +261,7 @@ void ReceiveCoinsDialog::copyColumnToClipboard(int column) if (!firstIndex.isValid()) { return; } - GUIUtil::setClipboard(model->getRecentRequestsTableModel()->data(firstIndex.child(firstIndex.row(), column), Qt::EditRole).toString()); + GUIUtil::setClipboard(model->getRecentRequestsTableModel()->index(firstIndex.row(), column).data(Qt::EditRole).toString()); } // context menu diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index 8a1140e952..130b709d46 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -76,7 +76,7 @@ public: QVariant data(const QModelIndex &index, int role) const; bool setData(const QModelIndex &index, const QVariant &value, int role); QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); Qt::ItemFlags flags(const QModelIndex &index) const; /*@}*/ diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 19b11ba1cd..eccc34e12f 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -28,13 +28,12 @@ #include <wallet/wallet.h> #endif -#include <QDesktopWidget> #include <QKeyEvent> #include <QMenu> #include <QMessageBox> #include <QScrollBar> +#include <QScreen> #include <QSettings> -#include <QSignalMapper> #include <QTime> #include <QTimer> #include <QStringList> @@ -451,7 +450,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty QSettings settings; if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) { // Restore failed (perhaps missing setting), center the window - move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center()); } QChar nonbreaking_hyphen(8209); @@ -558,6 +557,17 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event) void RPCConsole::setClientModel(ClientModel *model) { clientModel = model; + + bool wallet_enabled{false}; +#ifdef ENABLE_WALLET + wallet_enabled = WalletModel::isWalletEnabled(); +#endif // ENABLE_WALLET + if (model && !wallet_enabled) { + // Show warning, for example if this is a prerelease version + connect(model, &ClientModel::alertsChanged, this, &RPCConsole::updateAlerts); + updateAlerts(model->getStatusBarWarnings()); + } + ui->trafficGraph->setClientModel(model); if (model && clientModel->getPeerTableModel() && clientModel->getBanTableModel()) { // Keep up to date with client @@ -603,19 +613,10 @@ void RPCConsole::setClientModel(ClientModel *model) peersTableContextMenu->addAction(banAction7d); peersTableContextMenu->addAction(banAction365d); - // Add a signal mapping to allow dynamic context menu arguments. - // We need to use int (instead of int64_t), because signal mapper only supports - // int or objects, which is okay because max bantime (1 year) is < int_max. - QSignalMapper* signalMapper = new QSignalMapper(this); - signalMapper->setMapping(banAction1h, 60*60); - signalMapper->setMapping(banAction24h, 60*60*24); - signalMapper->setMapping(banAction7d, 60*60*24*7); - signalMapper->setMapping(banAction365d, 60*60*24*365); - connect(banAction1h, &QAction::triggered, signalMapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map)); - connect(banAction24h, &QAction::triggered, signalMapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map)); - connect(banAction7d, &QAction::triggered, signalMapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map)); - connect(banAction365d, &QAction::triggered, signalMapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map)); - connect(signalMapper, static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped), this, &RPCConsole::banSelectedNode); + connect(banAction1h, &QAction::triggered, [this] { banSelectedNode(60 * 60); }); + connect(banAction24h, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24); }); + connect(banAction7d, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24 * 7); }); + connect(banAction365d, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24 * 365); }); // peer table context menu signals connect(ui->peerWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showPeersTableContextMenu); @@ -1274,3 +1275,9 @@ QString RPCConsole::tabTitle(TabTypes tab_type) const { return ui->tabWidget->tabText(tab_type); } + +void RPCConsole::updateAlerts(const QString& warnings) +{ + this->ui->label_alerts->setVisible(!warnings.isEmpty()); + this->ui->label_alerts->setText(warnings); +} diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 38015e38fd..3f7a74ba03 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -167,6 +167,9 @@ private: /** Update UI with latest network info from model. */ void updateNetworkState(); + +private Q_SLOTS: + void updateAlerts(const QString& warnings); }; #endif // BITCOIN_QT_RPCCONSOLE_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 193fba78b1..f23c47736f 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -230,8 +230,9 @@ void SendCoinsDialog::on_sendButton_clicked() { recipients.append(entry->getValue()); } - else + else if (valid) { + ui->scrollArea->ensureWidgetVisible(entry); valid = false; } } @@ -703,7 +704,7 @@ void SendCoinsDialog::updateSmartFeeLabel() int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness(); QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14)); ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }"); - ui->fallbackFeeWarningLabel->setIndent(QFontMetrics(ui->fallbackFeeWarningLabel->font()).width("x")); + ui->fallbackFeeWarningLabel->setIndent(GUIUtil::TextWidth(QFontMetrics(ui->fallbackFeeWarningLabel->font()), "x")); } else { diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 5bceb1f945..0e5abb89f3 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -8,12 +8,12 @@ #include <qt/splashscreen.h> -#include <qt/networkstyle.h> - #include <clientversion.h> #include <interfaces/handler.h> #include <interfaces/node.h> #include <interfaces/wallet.h> +#include <qt/guiutil.h> +#include <qt/networkstyle.h> #include <ui_interface.h> #include <util/system.h> #include <util/translation.h> @@ -21,9 +21,9 @@ #include <QApplication> #include <QCloseEvent> -#include <QDesktopWidget> #include <QPainter> #include <QRadialGradient> +#include <QScreen> SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) : @@ -75,21 +75,21 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw // check font size and drawing with pixPaint.setFont(QFont(font, 33*fontFactor)); QFontMetrics fm = pixPaint.fontMetrics(); - int titleTextWidth = fm.width(titleText); + int titleTextWidth = GUIUtil::TextWidth(fm, titleText); if (titleTextWidth > 176) { fontFactor = fontFactor * 176 / titleTextWidth; } pixPaint.setFont(QFont(font, 33*fontFactor)); fm = pixPaint.fontMetrics(); - titleTextWidth = fm.width(titleText); + titleTextWidth = GUIUtil::TextWidth(fm, titleText); pixPaint.drawText(pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight,paddingTop,titleText); pixPaint.setFont(QFont(font, 15*fontFactor)); // if the version string is too long, reduce size fm = pixPaint.fontMetrics(); - int versionTextWidth = fm.width(versionText); + int versionTextWidth = GUIUtil::TextWidth(fm, versionText); if(versionTextWidth > titleTextWidth+paddingRight-10) { pixPaint.setFont(QFont(font, 10*fontFactor)); titleVersionVSpace -= 5; @@ -111,7 +111,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw boldFont.setWeight(QFont::Bold); pixPaint.setFont(boldFont); fm = pixPaint.fontMetrics(); - int titleAddTextWidth = fm.width(titleAddText); + int titleAddTextWidth = GUIUtil::TextWidth(fm, titleAddText); pixPaint.drawText(pixmap.width()/devicePixelRatio-titleAddTextWidth-10,15,titleAddText); } @@ -124,7 +124,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw QRect r(QPoint(), QSize(pixmap.size().width()/devicePixelRatio,pixmap.size().height()/devicePixelRatio)); resize(r.size()); setFixedSize(r.size()); - move(QApplication::desktop()->screenGeometry().center() - r.center()); + move(QGuiApplication::primaryScreen()->geometry().center() - r.center()); subscribeToCoreSignals(); installEventFilter(this); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 17e174e57a..cbc4ab49f5 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -30,7 +30,6 @@ #include <QMenu> #include <QPoint> #include <QScrollBar> -#include <QSignalMapper> #include <QTableView> #include <QTimer> #include <QUrl> @@ -176,11 +175,6 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa contextMenu->addAction(abandonAction); contextMenu->addAction(editLabelAction); - mapperThirdPartyTxUrls = new QSignalMapper(this); - - // Connect actions - connect(mapperThirdPartyTxUrls, static_cast<void (QSignalMapper::*)(const QString&)>(&QSignalMapper::mapped), this, &TransactionView::openThirdPartyTxUrl); - connect(dateWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseDate); connect(typeWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseType); connect(watchOnlyWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseWatchonly); @@ -246,15 +240,15 @@ void TransactionView::setModel(WalletModel *_model) QStringList listUrls = _model->getOptionsModel()->getThirdPartyTxUrls().split("|", QString::SkipEmptyParts); for (int i = 0; i < listUrls.size(); ++i) { - QString host = QUrl(listUrls[i].trimmed(), QUrl::StrictMode).host(); + QString url = listUrls[i].trimmed(); + QString host = QUrl(url, QUrl::StrictMode).host(); if (!host.isEmpty()) { QAction *thirdPartyTxUrlAction = new QAction(host, this); // use host as menu item label if (i == 0) contextMenu->addSeparator(); contextMenu->addAction(thirdPartyTxUrlAction); - connect(thirdPartyTxUrlAction, &QAction::triggered, mapperThirdPartyTxUrls, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map)); - mapperThirdPartyTxUrls->setMapping(thirdPartyTxUrlAction, listUrls[i].trimmed()); + connect(thirdPartyTxUrlAction, &QAction::triggered, [this, url] { openThirdPartyTxUrl(url); }); } } } diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index e07181d1c8..79347c371f 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -23,7 +23,6 @@ class QFrame; class QLineEdit; class QMenu; class QModelIndex; -class QSignalMapper; class QTableView; QT_END_NAMESPACE @@ -72,7 +71,6 @@ private: QLineEdit *amountWidget; QMenu *contextMenu; - QSignalMapper *mapperThirdPartyTxUrls; QFrame *dateRangeWidget; QDateTimeEdit *dateFrom; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index a74003149d..5419e33396 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -10,6 +10,7 @@ #include <chain.h> #include <chainparams.h> #include <coins.h> +#include <node/coinstats.h> #include <consensus/validation.h> #include <core_io.h> #include <hash.h> @@ -909,77 +910,6 @@ static UniValue getblock(const JSONRPCRequest& request) return blockToJSON(block, tip, pblockindex, verbosity >= 2); } -struct CCoinsStats -{ - int nHeight; - uint256 hashBlock; - uint64_t nTransactions; - uint64_t nTransactionOutputs; - uint64_t nBogoSize; - uint256 hashSerialized; - uint64_t nDiskSize; - CAmount nTotalAmount; - - CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), nDiskSize(0), nTotalAmount(0) {} -}; - -static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs) -{ - assert(!outputs.empty()); - ss << hash; - ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.fCoinBase ? 1u : 0u); - stats.nTransactions++; - for (const auto& output : outputs) { - ss << VARINT(output.first + 1); - ss << output.second.out.scriptPubKey; - ss << VARINT(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED); - stats.nTransactionOutputs++; - stats.nTotalAmount += output.second.out.nValue; - stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ + - 2 /* scriptPubKey len */ + output.second.out.scriptPubKey.size() /* scriptPubKey */; - } - ss << VARINT(0u); -} - -//! Calculate statistics about the unspent transaction output set -static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) -{ - std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor()); - assert(pcursor); - - CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); - stats.hashBlock = pcursor->GetBestBlock(); - { - LOCK(cs_main); - stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight; - } - ss << stats.hashBlock; - uint256 prevkey; - std::map<uint32_t, Coin> outputs; - while (pcursor->Valid()) { - boost::this_thread::interruption_point(); - COutPoint key; - Coin coin; - if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { - if (!outputs.empty() && key.hash != prevkey) { - ApplyStats(stats, ss, prevkey, outputs); - outputs.clear(); - } - prevkey = key.hash; - outputs[key.n] = std::move(coin); - } else { - return error("%s: unable to read value", __func__); - } - pcursor->Next(); - } - if (!outputs.empty()) { - ApplyStats(stats, ss, prevkey, outputs); - } - stats.hashSerialized = ss.GetHash(); - stats.nDiskSize = view->EstimateSize(); - return true; -} - static UniValue pruneblockchain(const JSONRPCRequest& request) { RPCHelpMan{"pruneblockchain", "", @@ -1639,6 +1569,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" " \"window_final_block_hash\": \"...\", (string) The hash of the final block in the window.\n" + " \"window_final_block_height\": xxxxx, (numeric) The height of the final block in the window.\n" " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n" " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" @@ -1689,6 +1620,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) ret.pushKV("time", (int64_t)pindex->nTime); ret.pushKV("txcount", (int64_t)pindex->nChainTx); ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex()); + ret.pushKV("window_final_block_height", pindex->nHeight); ret.pushKV("window_block_count", blockcount); if (blockcount > 0) { ret.pushKV("window_tx_count", nTxDiff); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 3cd661e067..93fca5a6de 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -85,6 +85,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getblockheader", 1, "verbose" }, { "getchaintxstats", 0, "nblocks" }, { "gettransaction", 1, "include_watchonly" }, + { "gettransaction", 2, "decode" }, { "getrawtransaction", 1, "verbose" }, { "createrawtransaction", 0, "inputs" }, { "createrawtransaction", 1, "outputs" }, diff --git a/src/serialize.h b/src/serialize.h index 1dc27d84eb..a38d76fc18 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -555,6 +555,7 @@ template<typename Stream, unsigned int N, typename T> inline void Unserialize(St * vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob. */ template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&); +template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const bool&); template<typename Stream, typename T, typename A, typename V> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&); template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v); template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&); @@ -713,6 +714,18 @@ void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char& os.write((char*)v.data(), v.size() * sizeof(T)); } +template<typename Stream, typename T, typename A> +void Serialize_impl(Stream& os, const std::vector<T, A>& v, const bool&) +{ + // A special case for std::vector<bool>, as dereferencing + // std::vector<bool>::const_iterator does not result in a const bool& + // due to std::vector's special casing for bool arguments. + WriteCompactSize(os, v.size()); + for (bool elem : v) { + ::Serialize(os, elem); + } +} + template<typename Stream, typename T, typename A, typename V> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&) { diff --git a/src/test/README.md b/src/test/README.md index 0017e3de26..8901fae7bd 100644 --- a/src/test/README.md +++ b/src/test/README.md @@ -49,7 +49,3 @@ unit tests. The file naming convention is `<source_filename>_tests.cpp` and such files should wrap their tests in a test suite called `<source_filename>_tests`. For an example of this pattern, examine `uint256_tests.cpp`. - -For further reading, I found the following website to be helpful in -explaining how the boost unit test framework works: -[http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/](http://archive.is/dRBGf). diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 8a8620938e..b90be15fba 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -258,6 +258,14 @@ static bool isCanonicalException(const std::ios_base::failure& ex) return strcmp(expectedException.what(), ex.what()) == 0; } +BOOST_AUTO_TEST_CASE(vector_bool) +{ + std::vector<uint8_t> vec1{1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1}; + std::vector<bool> vec2{1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1}; + + BOOST_CHECK(vec1 == std::vector<uint8_t>(vec2.begin(), vec2.end())); + BOOST_CHECK(SerializeHash(vec1) == SerializeHash(vec2)); +} BOOST_AUTO_TEST_CASE(noncanonical) { diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index e69ebcc2c3..193858cca9 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -13,7 +13,7 @@ #include <boost/test/unit_test.hpp> -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks); +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks); BOOST_AUTO_TEST_SUITE(tx_validationcache_tests) @@ -125,7 +125,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail // WITNESS requires P2SH test_flags |= SCRIPT_VERIFY_P2SH; } - bool ret = CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, nullptr); + bool ret = CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, nullptr); // CheckInputs should succeed iff test_flags doesn't intersect with // failing_flags bool expected_return_value = !(test_flags & failing_flags); @@ -135,13 +135,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail if (ret && add_to_cache) { // Check that we get a cache hit if the tx was valid std::vector<CScriptCheck> scriptchecks; - BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, &scriptchecks)); + BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks)); BOOST_CHECK(scriptchecks.empty()); } else { // Check that we get script executions to check, if the transaction // was invalid, or we didn't add to cache. std::vector<CScriptCheck> scriptchecks; - BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, &scriptchecks)); + BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks)); BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size()); } } @@ -204,13 +204,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) CValidationState state; PrecomputedTransactionData ptd_spend_tx(spend_tx); - BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); + BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); // If we call again asking for scriptchecks (as happens in // ConnectBlock), we should add a script check object for this -- we're // not caching invalidity (if that changes, delete this test case). std::vector<CScriptCheck> scriptchecks; - BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); + BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); BOOST_CHECK_EQUAL(scriptchecks.size(), 1U); // Test that CheckInputs returns true iff DERSIG-enforcing flags are @@ -272,7 +272,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; CValidationState state; PrecomputedTransactionData txdata(invalid_with_cltv_tx); - BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, ::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); + BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, ::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); } // TEST CHECKSEQUENCEVERIFY @@ -300,7 +300,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; CValidationState state; PrecomputedTransactionData txdata(invalid_with_csv_tx); - BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); + BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); } // TODO: add tests for remaining script flags @@ -362,12 +362,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) CValidationState state; PrecomputedTransactionData txdata(tx); // This transaction is now invalid under segwit, because of the second input. - BOOST_CHECK(!CheckInputs(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr)); + BOOST_CHECK(!CheckInputs(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr)); std::vector<CScriptCheck> scriptchecks; // Make sure this transaction was not cached (ie because the first // input was valid) - BOOST_CHECK(CheckInputs(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks)); + BOOST_CHECK(CheckInputs(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks)); // Should get 2 script checks back -- caching is on a whole-transaction basis. BOOST_CHECK_EQUAL(scriptchecks.size(), 2U); } diff --git a/src/util/error.cpp b/src/util/error.cpp index 287476c0d3..aa44ed3e3a 100644 --- a/src/util/error.cpp +++ b/src/util/error.cpp @@ -41,12 +41,12 @@ std::string ResolveErrMsg(const std::string& optname, const std::string& strBind return strprintf(_("Cannot resolve -%s address: '%s'").translated, optname, strBind); } -std::string AmountHighWarn(const std::string& optname) +bilingual_str AmountHighWarn(const std::string& optname) { - return strprintf(_("%s is set very high!").translated, optname); + return strprintf(_("%s is set very high!"), optname); } -std::string AmountErrMsg(const std::string& optname, const std::string& strValue) +bilingual_str AmountErrMsg(const std::string& optname, const std::string& strValue) { - return strprintf(_("Invalid amount for -%s=<amount>: '%s'").translated, optname, strValue); + return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue); } diff --git a/src/util/error.h b/src/util/error.h index 7777cc0c5d..f540b0020d 100644 --- a/src/util/error.h +++ b/src/util/error.h @@ -17,6 +17,8 @@ #include <string> +struct bilingual_str; + enum class TransactionError { OK, //!< No error MISSING_INPUTS, @@ -34,8 +36,8 @@ std::string TransactionErrorString(const TransactionError error); std::string ResolveErrMsg(const std::string& optname, const std::string& strBind); -std::string AmountHighWarn(const std::string& optname); +bilingual_str AmountHighWarn(const std::string& optname); -std::string AmountErrMsg(const std::string& optname, const std::string& strValue); +bilingual_str AmountErrMsg(const std::string& optname, const std::string& strValue); #endif // BITCOIN_UTIL_ERROR_H diff --git a/src/validation.cpp b/src/validation.cpp index 74f68a3047..cbf8c90392 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -184,7 +184,7 @@ std::unique_ptr<CBlockTreeDB> pblocktree; // See definition for documentation static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight); static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight); -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr); +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr); static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false); static FlatFileSeq BlockFileSeq(); static FlatFileSeq UndoFileSeq(); @@ -425,7 +425,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt } } - return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata); + return CheckInputs(tx, state, view, flags, cacheSigStore, true, txdata); } /** @@ -773,15 +773,17 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; // Check against previous transactions - // This is done last to help prevent CPU exhaustion denial-of-service attacks. + // The first loop above does all the inexpensive checks. + // Only if ALL inputs pass do we perform expensive ECDSA signature checks. + // Helps prevent CPU exhaustion denial-of-service attacks. PrecomputedTransactionData txdata(tx); - if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, txdata)) { + if (!CheckInputs(tx, state, view, scriptVerifyFlags, true, false, txdata)) { // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we // need to turn both off, and compare against just turning off CLEANSTACK // to see if the failure is specifically due to witness validation. CValidationState stateDummy; // Want reported failures to be from first CheckInputs - if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) && - !CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) { + if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) && + !CheckInputs(tx, stateDummy, view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) { // Only the witness is missing, so the transaction itself may be fine. state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false, state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage()); @@ -1298,90 +1300,79 @@ void InitScriptExecutionCache() { * * Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp */ -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { - if (!tx.IsCoinBase()) - { - if (pvChecks) - pvChecks->reserve(tx.vin.size()); - - // The first loop above does all the inexpensive checks. - // Only if ALL inputs pass do we perform expensive ECDSA signature checks. - // Helps prevent CPU exhaustion attacks. - - // Skip script verification when connecting blocks under the - // assumevalid block. Assuming the assumevalid block is valid this - // is safe because block merkle hashes are still computed and checked, - // Of course, if an assumed valid block is invalid due to false scriptSigs - // this optimization would allow an invalid chain to be accepted. - if (fScriptChecks) { - // First check if script executions have been cached with the same - // flags. Note that this assumes that the inputs provided are - // correct (ie that the transaction hash which is in tx's prevouts - // properly commits to the scriptPubKey in the inputs view of that - // transaction). - uint256 hashCacheEntry; - // We only use the first 19 bytes of nonce to avoid a second SHA - // round - giving us 19 + 32 + 4 = 55 bytes (+ 8 + 1 = 64) - static_assert(55 - sizeof(flags) - 32 >= 128/8, "Want at least 128 bits of nonce for script execution cache"); - CSHA256().Write(scriptExecutionCacheNonce.begin(), 55 - sizeof(flags) - 32).Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin()); - AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks - if (scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) { - return true; - } - - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint &prevout = tx.vin[i].prevout; - const Coin& coin = inputs.AccessCoin(prevout); - assert(!coin.IsSpent()); - - // We very carefully only pass in things to CScriptCheck which - // are clearly committed to by tx' witness hash. This provides - // a sanity check that our caching is not introducing consensus - // failures through additional data in, eg, the coins being - // spent being checked as a part of CScriptCheck. - - // Verify signature - CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata); - if (pvChecks) { - pvChecks->push_back(CScriptCheck()); - check.swap(pvChecks->back()); - } else if (!check()) { - if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { - // Check whether the failure was caused by a - // non-mandatory script verification check, such as - // non-standard DER encodings or non-null dummy - // arguments; if so, ensure we return NOT_STANDARD - // instead of CONSENSUS to avoid downstream users - // splitting the network between upgraded and - // non-upgraded nodes by banning CONSENSUS-failing - // data providers. - CScriptCheck check2(coin.out, tx, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata); - if (check2()) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); - } - // MANDATORY flag failures correspond to - // ValidationInvalidReason::CONSENSUS. Because CONSENSUS - // failures are the most serious case of validation - // failures, we may need to consider using - // RECENT_CONSENSUS_CHANGE for any script failure that - // could be due to non-upgraded nodes which we may want to - // support, to avoid splitting the network (but this - // depends on the details of how net_processing handles - // such errors). - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); - } - } + if (tx.IsCoinBase()) return true; + + if (pvChecks) { + pvChecks->reserve(tx.vin.size()); + } + + // First check if script executions have been cached with the same + // flags. Note that this assumes that the inputs provided are + // correct (ie that the transaction hash which is in tx's prevouts + // properly commits to the scriptPubKey in the inputs view of that + // transaction). + uint256 hashCacheEntry; + // We only use the first 19 bytes of nonce to avoid a second SHA + // round - giving us 19 + 32 + 4 = 55 bytes (+ 8 + 1 = 64) + static_assert(55 - sizeof(flags) - 32 >= 128/8, "Want at least 128 bits of nonce for script execution cache"); + CSHA256().Write(scriptExecutionCacheNonce.begin(), 55 - sizeof(flags) - 32).Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin()); + AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks + if (scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) { + return true; + } - if (cacheFullScriptStore && !pvChecks) { - // We executed all of the provided scripts, and were told to - // cache the result. Do so now. - scriptExecutionCache.insert(hashCacheEntry); + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + const Coin& coin = inputs.AccessCoin(prevout); + assert(!coin.IsSpent()); + + // We very carefully only pass in things to CScriptCheck which + // are clearly committed to by tx' witness hash. This provides + // a sanity check that our caching is not introducing consensus + // failures through additional data in, eg, the coins being + // spent being checked as a part of CScriptCheck. + + // Verify signature + CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata); + if (pvChecks) { + pvChecks->push_back(CScriptCheck()); + check.swap(pvChecks->back()); + } else if (!check()) { + if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { + // Check whether the failure was caused by a + // non-mandatory script verification check, such as + // non-standard DER encodings or non-null dummy + // arguments; if so, ensure we return NOT_STANDARD + // instead of CONSENSUS to avoid downstream users + // splitting the network between upgraded and + // non-upgraded nodes by banning CONSENSUS-failing + // data providers. + CScriptCheck check2(coin.out, tx, i, + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata); + if (check2()) + return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); } + // MANDATORY flag failures correspond to + // ValidationInvalidReason::CONSENSUS. Because CONSENSUS + // failures are the most serious case of validation + // failures, we may need to consider using + // RECENT_CONSENSUS_CHANGE for any script failure that + // could be due to non-upgraded nodes which we may want to + // support, to avoid splitting the network (but this + // depends on the details of how net_processing handles + // such errors). + return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); } } + if (cacheFullScriptStore && !pvChecks) { + // We executed all of the provided scripts, and were told to + // cache the result. Do so now. + scriptExecutionCache.insert(hashCacheEntry); + } + return true; } @@ -1769,6 +1760,11 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && pindexBestHeader->nChainWork >= nMinimumChainWork) { // This block is a member of the assumed verified chain and an ancestor of the best header. + // Script verification is skipped when connecting blocks under the + // assumevalid block. Assuming the assumevalid block is valid this + // is safe because block merkle hashes are still computed and checked, + // Of course, if an assumed valid block is invalid due to false scriptSigs + // this optimization would allow an invalid chain to be accepted. // The equivalent time check discourages hash power from extorting the network via DOS attack // into accepting an invalid block through telling users they must manually set assumevalid. // Requiring a software change or burying the invalid block, regardless of the setting, makes @@ -1952,7 +1948,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl { std::vector<CScriptCheck> vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ - if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) { + if (fScriptChecks && !CheckInputs(tx, state, view, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) { if (state.GetReason() == ValidationInvalidReason::TX_NOT_STANDARD) { // CheckInputs may return NOT_STANDARD for extra flags we passed, // but we can't return that, as it's not defined for a block, so @@ -2561,7 +2557,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar return true; } -static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) { +static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) { bool fNotify = false; bool fInitialBlockDownload = false; static CBlockIndex* pindexHeaderOld = nullptr; @@ -2580,6 +2576,7 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) { if (fNotify) { uiInterface.NotifyHeaderTip(fInitialBlockDownload, pindexHeader); } + return fNotify; } static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) { @@ -3400,7 +3397,12 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio } } } - NotifyHeaderTip(); + if (NotifyHeaderTip()) { + LOCK(cs_main); + if (::ChainstateActive().IsInitialBlockDownload() && ppindex && *ppindex) { + LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", (*ppindex)->nHeight, 100.0/((*ppindex)->nHeight+(GetAdjustedTime() - (*ppindex)->GetBlockTime()) / Params().GetConsensus().nPowTargetSpacing) * (*ppindex)->nHeight); + } + } return true; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8a11f76609..22a5f7e249 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1649,6 +1649,7 @@ static UniValue gettransaction(const JSONRPCRequest& request) { {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"}, {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses in balance calculation and details[]"}, + {"decode", RPCArg::Type::BOOL, /* default */ "false", "Whether to add a field with the decoded transaction"}, }, RPCResult{ "{\n" @@ -1684,11 +1685,13 @@ static UniValue gettransaction(const JSONRPCRequest& request) " ,...\n" " ],\n" " \"hex\" : \"data\" (string) Raw data for transaction\n" + " \"decoded\" : transaction (json object) Optional, the decoded transaction\n" "}\n" }, RPCExamples{ HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") + + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" false true") + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") }, }.Check(request); @@ -1708,6 +1711,8 @@ static UniValue gettransaction(const JSONRPCRequest& request) filter |= ISMINE_WATCH_ONLY; } + bool decode_tx = request.params[2].isNull() ? false : request.params[2].get_bool(); + UniValue entry(UniValue::VOBJ); auto it = pwallet->mapWallet.find(hash); if (it == pwallet->mapWallet.end()) { @@ -1733,6 +1738,12 @@ static UniValue gettransaction(const JSONRPCRequest& request) std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags()); entry.pushKV("hex", strHex); + if (decode_tx) { + UniValue decoded(UniValue::VOBJ); + TxToUniv(*wtx.tx, uint256(), decoded, false); + entry.pushKV("decoded", decoded); + } + return entry; } @@ -4175,7 +4186,7 @@ static const CRPCCommand commands[] = { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} }, { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} }, { "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} }, - { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} }, + { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly","decode"} }, { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, { "wallet", "getbalances", &getbalances, {} }, { "wallet", "getwalletinfo", &getwalletinfo, {} }, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1c07f29c98..7629a40c5e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -93,13 +93,14 @@ std::shared_ptr<CWallet> GetWallet(const std::string& name) static Mutex g_wallet_release_mutex; static std::condition_variable g_wallet_release_cv; -static std::set<CWallet*> g_unloading_wallet_set; +static std::set<std::string> g_unloading_wallet_set; // Custom deleter for shared_ptr<CWallet>. static void ReleaseWallet(CWallet* wallet) { // Unregister and delete the wallet right after BlockUntilSyncedToCurrentChain // so that it's in sync with the current chainstate. + const std::string name = wallet->GetName(); wallet->WalletLogPrintf("Releasing wallet\n"); wallet->BlockUntilSyncedToCurrentChain(); wallet->Flush(); @@ -108,7 +109,7 @@ static void ReleaseWallet(CWallet* wallet) // Wallet is now released, notify UnloadWallet, if any. { LOCK(g_wallet_release_mutex); - if (g_unloading_wallet_set.erase(wallet) == 0) { + if (g_unloading_wallet_set.erase(name) == 0) { // UnloadWallet was not called for this wallet, all done. return; } @@ -119,21 +120,21 @@ static void ReleaseWallet(CWallet* wallet) void UnloadWallet(std::shared_ptr<CWallet>&& wallet) { // Mark wallet for unloading. - CWallet* pwallet = wallet.get(); + const std::string name = wallet->GetName(); { LOCK(g_wallet_release_mutex); - auto it = g_unloading_wallet_set.insert(pwallet); + auto it = g_unloading_wallet_set.insert(name); assert(it.second); } // The wallet can be in use so it's not possible to explicitly unload here. // Notify the unload intent so that all remaining shared pointers are // released. - pwallet->NotifyUnload(); + wallet->NotifyUnload(); // Time to ditch our shared_ptr and wait for ReleaseWallet call. wallet.reset(); { WAIT_LOCK(g_wallet_release_mutex, lock); - while (g_unloading_wallet_set.count(pwallet) == 1) { + while (g_unloading_wallet_set.count(name) == 1) { g_wallet_release_cv.wait(lock); } } @@ -523,18 +524,9 @@ bool CWallet::LoadCScript(const CScript& redeemScript) static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut) { - //TODO: Use Solver to extract this? - CScript::const_iterator pc = dest.begin(); - opcodetype opcode; - std::vector<unsigned char> vch; - if (!dest.GetOp(pc, opcode, vch) || !CPubKey::ValidSize(vch)) - return false; - pubKeyOut = CPubKey(vch); - if (!pubKeyOut.IsFullyValid()) - return false; - if (!dest.GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG || dest.GetOp(pc, opcode, vch)) - return false; - return true; + std::vector<std::vector<unsigned char>> solutions; + return Solver(dest, solutions) == TX_PUBKEY && + (pubKeyOut = CPubKey(solutions[0])).IsFullyValid(); } bool CWallet::AddWatchOnlyInMem(const CScript &dest) @@ -4406,23 +4398,23 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, } if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) { - chain.initError(strprintf("Unknown address type '%s'", gArgs.GetArg("-addresstype", ""))); + chain.initError(strprintf(_("Unknown address type '%s'").translated, gArgs.GetArg("-addresstype", ""))); return nullptr; } if (!gArgs.GetArg("-changetype", "").empty() && !ParseOutputType(gArgs.GetArg("-changetype", ""), walletInstance->m_default_change_type)) { - chain.initError(strprintf("Unknown change type '%s'", gArgs.GetArg("-changetype", ""))); + chain.initError(strprintf(_("Unknown change type '%s'").translated, gArgs.GetArg("-changetype", ""))); return nullptr; } if (gArgs.IsArgSet("-mintxfee")) { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) { - chain.initError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); + chain.initError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")).translated); return nullptr; } if (n > HIGH_TX_FEE_PER_KB) { - chain.initWarning(AmountHighWarn("-mintxfee") + " " + + chain.initWarning(AmountHighWarn("-mintxfee").translated + " " + _("This is the minimum transaction fee you pay on every transaction.").translated); } walletInstance->m_min_fee = CFeeRate(n); @@ -4436,7 +4428,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, return nullptr; } if (nFeePerK > HIGH_TX_FEE_PER_KB) { - chain.initWarning(AmountHighWarn("-fallbackfee") + " " + + chain.initWarning(AmountHighWarn("-fallbackfee").translated + " " + _("This is the transaction fee you may pay when fee estimates are not available.").translated); } walletInstance->m_fallback_fee = CFeeRate(nFeePerK); @@ -4449,7 +4441,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, return nullptr; } if (nFeePerK > HIGH_TX_FEE_PER_KB) { - chain.initWarning(AmountHighWarn("-discardfee") + " " + + chain.initWarning(AmountHighWarn("-discardfee").translated + " " + _("This is the transaction fee you may discard if change is smaller than dust at this level").translated); } walletInstance->m_discard_rate = CFeeRate(nFeePerK); @@ -4457,11 +4449,11 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, if (gArgs.IsArgSet("-paytxfee")) { CAmount nFeePerK = 0; if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) { - chain.initError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); + chain.initError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")).translated); return nullptr; } if (nFeePerK > HIGH_TX_FEE_PER_KB) { - chain.initWarning(AmountHighWarn("-paytxfee") + " " + + chain.initWarning(AmountHighWarn("-paytxfee").translated + " " + _("This is the transaction fee you will pay if you send a transaction.").translated); } walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000); @@ -4476,7 +4468,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, { CAmount nMaxFee = 0; if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) { - chain.initError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""))); + chain.initError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")).translated); return nullptr; } if (nMaxFee > HIGH_MAX_TX_FEE) { @@ -4490,9 +4482,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, walletInstance->m_default_max_tx_fee = nMaxFee; } - if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) - chain.initWarning(AmountHighWarn("-minrelaytxfee") + " " + + if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) { + chain.initWarning(AmountHighWarn("-minrelaytxfee").translated + " " + _("The wallet will avoid paying less than the minimum relay fee.").translated); + } walletInstance->m_confirm_target = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); walletInstance->m_spend_zero_conf_change = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py index 5364ac4b8c..367d0f6916 100755 --- a/test/functional/combine_logs.py +++ b/test/functional/combine_logs.py @@ -81,7 +81,7 @@ def read_logs(tmp_dir): chain = glob.glob("{}/node0/*/debug.log".format(tmp_dir)) if chain: chain = chain[0] # pick the first one if more than one chain was found (should never happen) - chain = re.search('node0/(.+?)/debug\.log$', chain).group(1) # extract the chain name + chain = re.search(r'node0/(.+?)/debug\.log$', chain).group(1) # extract the chain name else: chain = 'regtest' # fallback to regtest (should only happen when none exists) diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py index 8bb7e02695..e6ff21ee9c 100755 --- a/test/functional/feature_logging.py +++ b/test/functional/feature_logging.py @@ -36,7 +36,7 @@ class LoggingTest(BitcoinTestFramework): 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+$" + exp_stderr = r"Error: Could not open debug log file \S+$" self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % (invalidname)], exp_stderr, match=ErrorMatch.FULL_REGEX) assert not os.path.isfile(os.path.join(invdir, "foo.log")) diff --git a/test/functional/feature_uacomment.py b/test/functional/feature_uacomment.py index fb4ad21359..85c250173f 100755 --- a/test/functional/feature_uacomment.py +++ b/test/functional/feature_uacomment.py @@ -27,12 +27,12 @@ class UacommentTest(BitcoinTestFramework): self.log.info("test -uacomment max length") self.stop_node(0) - expected = "Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of uacomments." + expected = r"Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of uacomments." self.nodes[0].assert_start_raises_init_error(["-uacomment=" + 'a' * 256], expected, match=ErrorMatch.FULL_REGEX) self.log.info("test -uacomment unsafe characters") for unsafe_char in ['/', ':', '(', ')', '₿', '🏃']: - expected = "Error: User Agent comment \(" + re.escape(unsafe_char) + "\) contains unsafe characters." + expected = r"Error: User Agent comment \(" + re.escape(unsafe_char) + r"\) contains unsafe characters." self.nodes[0].assert_start_raises_init_error(["-uacomment=" + unsafe_char], expected, match=ErrorMatch.FULL_REGEX) diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py index 1ba781c539..5aea10fbce 100755 --- a/test/functional/interface_zmq.py +++ b/test/functional/interface_zmq.py @@ -8,10 +8,9 @@ import struct from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE from test_framework.test_framework import BitcoinTestFramework from test_framework.messages import CTransaction, hash256 -from test_framework.util import assert_equal +from test_framework.util import assert_equal, connect_nodes from io import BytesIO - -ADDRESS = "tcp://127.0.0.1:28332" +from time import sleep def hash256_reversed(byte_str): return hash256(byte_str)[::-1] @@ -43,66 +42,64 @@ class ZMQTest (BitcoinTestFramework): self.skip_if_no_py3_zmq() self.skip_if_no_bitcoind_zmq() - def setup_nodes(self): + def run_test(self): import zmq + self.ctx = zmq.Context() + try: + self.test_basic() + self.test_reorg() + finally: + # Destroy the ZMQ context. + self.log.debug("Destroying ZMQ context") + self.ctx.destroy(linger=None) - # Initialize ZMQ context and socket. + def test_basic(self): # All messages are received in the same socket which means # that this test fails if the publishing order changes. # Note that the publishing order is not defined in the documentation and # is subject to change. - self.zmq_context = zmq.Context() - socket = self.zmq_context.socket(zmq.SUB) + import zmq + address = 'tcp://127.0.0.1:28332' + socket = self.ctx.socket(zmq.SUB) socket.set(zmq.RCVTIMEO, 60000) - socket.connect(ADDRESS) # Subscribe to all available topics. - self.hashblock = ZMQSubscriber(socket, b"hashblock") - self.hashtx = ZMQSubscriber(socket, b"hashtx") - self.rawblock = ZMQSubscriber(socket, b"rawblock") - self.rawtx = ZMQSubscriber(socket, b"rawtx") - - self.extra_args = [ - ["-zmqpub%s=%s" % (sub.topic.decode(), ADDRESS) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]], - [], - ] - self.add_nodes(self.num_nodes, self.extra_args) - self.start_nodes() - self.import_deterministic_coinbase_privkeys() + hashblock = ZMQSubscriber(socket, b"hashblock") + hashtx = ZMQSubscriber(socket, b"hashtx") + rawblock = ZMQSubscriber(socket, b"rawblock") + rawtx = ZMQSubscriber(socket, b"rawtx") - def run_test(self): - try: - self._zmq_test() - finally: - # Destroy the ZMQ context. - self.log.debug("Destroying ZMQ context") - self.zmq_context.destroy(linger=None) + self.restart_node(0, ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx, rawblock, rawtx]]) + connect_nodes(self.nodes[0], 1) + socket.connect(address) + # Relax so that the subscriber is ready before publishing zmq messages + sleep(0.2) - def _zmq_test(self): num_blocks = 5 self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks}) genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE) + self.sync_all() for x in range(num_blocks): # Should receive the coinbase txid. - txid = self.hashtx.receive() + txid = hashtx.receive() # Should receive the coinbase raw transaction. - hex = self.rawtx.receive() + hex = rawtx.receive() tx = CTransaction() tx.deserialize(BytesIO(hex)) tx.calc_sha256() assert_equal(tx.hash, txid.hex()) # Should receive the generated block hash. - hash = self.hashblock.receive().hex() + hash = hashblock.receive().hex() assert_equal(genhashes[x], hash) # The block should only have the coinbase txid. assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"]) # Should receive the generated raw block. - block = self.rawblock.receive() + block = rawblock.receive() assert_equal(genhashes[x], hash256_reversed(block[:80]).hex()) if self.is_wallet_compiled(): @@ -111,23 +108,49 @@ class ZMQTest (BitcoinTestFramework): self.sync_all() # Should receive the broadcasted txid. - txid = self.hashtx.receive() + txid = hashtx.receive() assert_equal(payment_txid, txid.hex()) # Should receive the broadcasted raw transaction. - hex = self.rawtx.receive() + hex = rawtx.receive() assert_equal(payment_txid, hash256_reversed(hex).hex()) self.log.info("Test the getzmqnotifications RPC") assert_equal(self.nodes[0].getzmqnotifications(), [ - {"type": "pubhashblock", "address": ADDRESS, "hwm": 1000}, - {"type": "pubhashtx", "address": ADDRESS, "hwm": 1000}, - {"type": "pubrawblock", "address": ADDRESS, "hwm": 1000}, - {"type": "pubrawtx", "address": ADDRESS, "hwm": 1000}, + {"type": "pubhashblock", "address": address, "hwm": 1000}, + {"type": "pubhashtx", "address": address, "hwm": 1000}, + {"type": "pubrawblock", "address": address, "hwm": 1000}, + {"type": "pubrawtx", "address": address, "hwm": 1000}, ]) assert_equal(self.nodes[1].getzmqnotifications(), []) + def test_reorg(self): + import zmq + address = 'tcp://127.0.0.1:28333' + socket = self.ctx.socket(zmq.SUB) + socket.set(zmq.RCVTIMEO, 60000) + hashblock = ZMQSubscriber(socket, b'hashblock') + + # Should only notify the tip if a reorg occurs + self.restart_node(0, ['-zmqpub%s=%s' % (hashblock.topic.decode(), address)]) + socket.connect(address) + # Relax so that the subscriber is ready before publishing zmq messages + sleep(0.2) + + # Generate 1 block in nodes[0] and receive all notifications + self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE) + assert_equal(self.nodes[0].getbestblockhash(), hashblock.receive().hex()) + + # Generate 2 blocks in nodes[1] + self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE) + + # nodes[0] will reorg chain after connecting back nodes[1] + connect_nodes(self.nodes[0], 1) + + # Should receive nodes[1] tip + assert_equal(self.nodes[1].getbestblockhash(), hashblock.receive().hex()) + if __name__ == '__main__': ZMQTest().main() diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 6c30e05084..266a0d6cd2 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -186,6 +186,7 @@ class BlockchainTest(BitcoinTestFramework): assert_equal(chaintxstats['time'], b200['time']) assert_equal(chaintxstats['txcount'], 201) assert_equal(chaintxstats['window_final_block_hash'], b200_hash) + assert_equal(chaintxstats['window_final_block_height'], 200) assert_equal(chaintxstats['window_block_count'], 199) assert_equal(chaintxstats['window_tx_count'], 199) assert_equal(chaintxstats['window_interval'], time_diff) @@ -195,6 +196,7 @@ class BlockchainTest(BitcoinTestFramework): assert_equal(chaintxstats['time'], b1['time']) assert_equal(chaintxstats['txcount'], 2) assert_equal(chaintxstats['window_final_block_hash'], b1_hash) + assert_equal(chaintxstats['window_final_block_height'], 1) assert_equal(chaintxstats['window_block_count'], 0) assert 'window_tx_count' not in chaintxstats assert 'window_interval' not in chaintxstats diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py index 423741fd27..b1d2b6f431 100755 --- a/test/functional/rpc_setban.py +++ b/test/functional/rpc_setban.py @@ -26,7 +26,7 @@ class SetBanTests(BitcoinTestFramework): self.nodes[1].setban("127.0.0.1", "add") # Node 0 should not be able to reconnect - with self.nodes[1].assert_debug_log(expected_msgs=['dropped (banned)\n'],timeout=5): + with self.nodes[1].assert_debug_log(expected_msgs=['dropped (banned)\n'], timeout=5): self.restart_node(1, []) self.nodes[0].addnode("127.0.0.1:" + str(p2p_port(1)), "onetry") diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 89a5a65e64..917efaa833 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -803,7 +803,9 @@ class HeaderAndShortIDs: return [ key0, key1 ] # Version 2 compact blocks use wtxid in shortids (rather than txid) - def initialize_from_block(self, block, nonce=0, prefill_list = [0], use_witness = False): + def initialize_from_block(self, block, nonce=0, prefill_list=None, use_witness=False): + if prefill_list is None: + prefill_list = [0] self.header = CBlockHeader(block) self.nonce = nonce self.prefilled_txn = [ PrefilledTransaction(i, block.vtx[i]) for i in prefill_list ] diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 9667cf4ea4..55e6d4caa6 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -313,24 +313,24 @@ class TestNode(): with open(debug_log, encoding='utf-8') as dl: dl.seek(0, 2) prev_size = dl.tell() - try: - yield - finally: - while True: - found = True - with open(debug_log, encoding='utf-8') as dl: - dl.seek(prev_size) - log = dl.read() - print_log = " - " + "\n - ".join(log.splitlines()) - for expected_msg in expected_msgs: - if re.search(re.escape(expected_msg), log, flags=re.MULTILINE) is None: - found = False - if found: - return - if time.time() >= time_end: - break - time.sleep(0.05) - self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log)) + + yield + + while True: + found = True + with open(debug_log, encoding='utf-8') as dl: + dl.seek(prev_size) + log = dl.read() + print_log = " - " + "\n - ".join(log.splitlines()) + for expected_msg in expected_msgs: + if re.search(re.escape(expected_msg), log, flags=re.MULTILINE) is None: + found = False + if found: + return + if time.time() >= time_end: + break + time.sleep(0.05) + self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log)) @contextlib.contextmanager def assert_memory_usage_stable(self, *, increase_allowed=0.03): diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index 34e84fcf55..74350649c7 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -499,6 +499,11 @@ class WalletTest(BitcoinTestFramework): self.nodes[0].setlabel(change, 'foobar') assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False) + # Test "decoded" field value in gettransaction response + self.log.info("Testing gettransaction decoding...") + tx = self.nodes[0].gettransaction(txid=txid, decode=True) + assert_equal(tx["decoded"], self.nodes[0].decoderawtransaction(tx["hex"])) + if __name__ == '__main__': WalletTest().main() diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py index e4a4ab1f35..23748e5dd7 100755 --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -44,8 +44,10 @@ class ImportMultiTest(BitcoinTestFramework): def setup_network(self): self.setup_nodes() - def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=[]): + def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=None): """Run importmulti and assert success""" + if warnings is None: + warnings = [] result = self.nodes[1].importmulti([req]) observed_warnings = [] if 'warnings' in result[0]: diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 984ffab5a4..99d2c77587 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -93,12 +93,12 @@ class MultiWalletTest(BitcoinTestFramework): # should not initialize if one wallet is a copy of another shutil.copyfile(wallet_dir('w8'), wallet_dir('w8_copy')) - exp_stderr = "BerkeleyBatch: Can't open database w8_copy \(duplicates fileid \w+ from w8\)" + exp_stderr = r"BerkeleyBatch: Can't open database w8_copy \(duplicates fileid \w+ from w8\)" self.nodes[0].assert_start_raises_init_error(['-wallet=w8', '-wallet=w8_copy'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX) # should not initialize if wallet file is a symlink os.symlink('w8', wallet_dir('w8_symlink')) - self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX) + self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], r'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX) # should not initialize if the specified walletdir does not exist self.nodes[0].assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist') @@ -139,7 +139,7 @@ class MultiWalletTest(BitcoinTestFramework): competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir') os.mkdir(competing_wallet_dir) self.restart_node(0, ['-walletdir=' + competing_wallet_dir]) - exp_stderr = "Error: Error initializing wallet database environment \"\S+competing_walletdir\"!" + exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\"!" self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX) self.restart_node(0, extra_args) diff --git a/test/functional/wallet_watchonly.py b/test/functional/wallet_watchonly.py index be8d7714fb..be8d7714fb 100644..100755 --- a/test/functional/wallet_watchonly.py +++ b/test/functional/wallet_watchonly.py diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py index 1d6122a13d..bd947d194c 100755 --- a/test/lint/check-doc.py +++ b/test/lint/check-doc.py @@ -15,8 +15,8 @@ import re FOLDER_GREP = 'src' FOLDER_TEST = 'src/test/' -REGEX_ARG = '(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"' -REGEX_DOC = 'AddArg\("(-[^"=]+?)(?:=|")' +REGEX_ARG = r'(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"' +REGEX_DOC = r'AddArg\("(-[^"=]+?)(?:=|")' CMD_ROOT_DIR = '$(git rev-parse --show-toplevel)/{}'.format(FOLDER_GREP) CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST) CMD_GREP_WALLET_ARGS = r"git grep --function-context 'void WalletInit::AddWalletOptions' -- {} | grep AddArg".format(CMD_ROOT_DIR) diff --git a/test/lint/check-rpc-mappings.py b/test/lint/check-rpc-mappings.py index 137cc82b5d..a33ab17f3f 100755 --- a/test/lint/check-rpc-mappings.py +++ b/test/lint/check-rpc-mappings.py @@ -48,13 +48,13 @@ def process_commands(fname): for line in f: line = line.rstrip() if not in_rpcs: - if re.match("static const CRPCCommand .*\[\] =", line): + if re.match(r"static const CRPCCommand .*\[\] =", line): in_rpcs = True else: if line.startswith('};'): in_rpcs = False elif '{' in line and '"' in line: - m = re.search('{ *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line) + m = re.search(r'{ *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line) assert m, 'No match to table expression: %s' % line name = parse_string(m.group(2)) args_str = m.group(4).strip() @@ -80,7 +80,7 @@ def process_mapping(fname): if line.startswith('};'): in_rpcs = False elif '{' in line and '"' in line: - m = re.search('{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line) + m = re.search(r'{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line) assert m, 'No match to table expression: %s' % line name = parse_string(m.group(1)) idx = int(m.group(2)) diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py index 47ad896589..9f34d0f4dd 100755 --- a/test/lint/lint-format-strings.py +++ b/test/lint/lint-format-strings.py @@ -55,7 +55,7 @@ def normalize(s): assert type(s) is str s = s.replace("\n", " ") s = s.replace("\t", " ") - s = re.sub("/\*.*?\*/", " ", s) + s = re.sub(r"/\*.*?\*/", " ", s) s = re.sub(" {2,}", " ", s) return s.strip() diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index 4b9e2615b6..d27e45a23f 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -11,6 +11,9 @@ export LC_ALL=C IGNORE_REGEXP="/(leveldb|secp256k1|univalue)/" +# cd to root folder of git repo for git ls-files to work properly +cd "$(dirname $0)/../.." || exit 1 + filter_suffix() { git ls-files | grep -E "^src/.*\.${1}"'$' | grep -Ev "${IGNORE_REGEXP}" } diff --git a/test/lint/lint-python-mutable-default-parameters.sh b/test/lint/lint-python-mutable-default-parameters.sh new file mode 100755 index 0000000000..1f9f035d30 --- /dev/null +++ b/test/lint/lint-python-mutable-default-parameters.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2019 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Detect when a mutable list or dict is used as a default parameter value in a Python function. + +export LC_ALL=C +EXIT_CODE=0 +OUTPUT=$(git grep -E '^\s*def [a-zA-Z0-9_]+\(.*=\s*(\[|\{)' -- "*.py") +if [[ ${OUTPUT} != "" ]]; then + echo "A mutable list or dict seems to be used as default parameter value:" + echo + echo "${OUTPUT}" + echo + cat << EXAMPLE +This is how mutable list and dict default parameter values behave: + +>>> def f(i, j=[], k={}): +... j.append(i) +... k[i] = True +... return j, k +... +>>> f(1) +([1], {1: True}) +>>> f(1) +([1, 1], {1: True}) +>>> f(2) +([1, 1, 2], {1: True, 2: True}) + +The intended behaviour was likely: + +>>> def f(i, j=None, k=None): +... if j is None: +... j = [] +... if k is None: +... k = {} +... j.append(i) +... k[i] = True +... return j, k +... +>>> f(1) +([1], {1: True}) +>>> f(1) +([1], {1: True}) +>>> f(2) +([2], {2: True}) +EXAMPLE + EXIT_CODE=1 +fi +exit ${EXIT_CODE} diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh index a76806003f..3c82ec19e3 100755 --- a/test/lint/lint-python.sh +++ b/test/lint/lint-python.sh @@ -73,7 +73,6 @@ enabled=( W291 # trailing whitespace 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 "!=" diff --git a/test/lint/lint-spelling.sh b/test/lint/lint-spelling.sh index 5d672698a7..e70b73e1cc 100755 --- a/test/lint/lint-spelling.sh +++ b/test/lint/lint-spelling.sh @@ -9,6 +9,11 @@ export LC_ALL=C +if ! command -v codespell > /dev/null; then + echo "Skipping spell check linting since codespell is not installed." + exit 0 +fi + IGNORE_WORDS_FILE=test/lint/lint-spelling.ignore-words.txt if ! codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=${IGNORE_WORDS_FILE} $(git ls-files -- ":(exclude)build-aux/m4/" ":(exclude)contrib/seeds/*.txt" ":(exclude)depends/" ":(exclude)doc/release-notes/" ":(exclude)src/leveldb/" ":(exclude)src/qt/locale/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"); then echo "^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in ${IGNORE_WORDS_FILE}" |