diff options
71 files changed, 1184 insertions, 873 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 5bfdcbc9b1..c4aae3a50a 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -96,7 +96,7 @@ task: env: PATH: 'C:\jom;C:\Python39;C:\Python39\Scripts;C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin;%PATH%' PYTHONUTF8: 1 - CI_VCPKG_TAG: '2022.02.23' + CI_VCPKG_TAG: '2022.04.12' VCPKG_DOWNLOADS: 'C:\Users\ContainerAdministrator\AppData\Local\vcpkg\downloads' VCPKG_DEFAULT_BINARY_CACHE: 'C:\Users\ContainerAdministrator\AppData\Local\vcpkg\archives' CCACHE_DIR: 'C:\Users\ContainerAdministrator\AppData\Local\ccache' @@ -106,6 +106,7 @@ task: QT_SOURCE_DIR: 'C:\qt-everywhere-src-5.15.3' QTBASEDIR: 'C:\Qt_static' x64_NATIVE_TOOLS: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"' + QT_CONFIGURE_COMMAND: '..\configure -release -silent -opensource -confirm-license -opengl desktop -static -static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -nomake examples -nomake tests -nomake tools -no-angle -no-dbus -no-gif -no-gtk -no-ico -no-icu -no-libjpeg -no-libudev -no-sql-sqlite -no-sql-odbc -no-sqlite -no-vulkan -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip doc -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtsvg -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -no-openssl -no-feature-bearermanagement -no-feature-printdialog -no-feature-printer -no-feature-printpreviewdialog -no-feature-printpreviewwidget -no-feature-sql -no-feature-sqlmodel -no-feature-textbrowser -no-feature-textmarkdownwriter -no-feature-textodfwriter -no-feature-xml' IgnoreWarnIntDirInTempDetected: 'true' merge_script: - git config --global user.email "ci@ci.ci" @@ -120,7 +121,7 @@ task: folder: "%QTBASEDIR%" reupload_on_changes: false fingerprint_script: - - echo %QT_DOWNLOAD_URL% + - echo %QT_DOWNLOAD_URL% %QT_CONFIGURE_COMMAND% - msbuild -version populate_script: - curl -L -o C:\jom.zip http://download.qt.io/official_releases/jom/jom.zip @@ -132,7 +133,7 @@ task: - cd %QT_SOURCE_DIR% - mkdir build - cd build - - ..\configure -release -silent -opensource -confirm-license -opengl desktop -static -static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -nomake examples -nomake tests -nomake tools -no-angle -no-dbus -no-gif -no-gtk -no-ico -no-icu -no-libjpeg -no-libudev -no-sql-sqlite -no-sql-odbc -no-sqlite -no-vulkan -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip doc -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtsvg -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -no-openssl -no-feature-bearermanagement -no-feature-printdialog -no-feature-printer -no-feature-printpreviewdialog -no-feature-printpreviewwidget -no-feature-sql -no-feature-sqlmodel -no-feature-textbrowser -no-feature-textmarkdownwriter -no-feature-textodfwriter -no-feature-xml -prefix %QTBASEDIR% + - '%QT_CONFIGURE_COMMAND% -prefix %QTBASEDIR%' - jom - jom install vcpkg_tools_cache: diff --git a/Makefile.am b/Makefile.am index 54e9a6865f..60167e3b7e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,7 +47,8 @@ DIST_CONTRIB = \ $(top_srcdir)/test/sanitizer_suppressions/tsan \ $(top_srcdir)/test/sanitizer_suppressions/ubsan \ $(top_srcdir)/contrib/linearize/linearize-data.py \ - $(top_srcdir)/contrib/linearize/linearize-hashes.py + $(top_srcdir)/contrib/linearize/linearize-hashes.py \ + $(top_srcdir)/contrib/signet/miner DIST_SHARE = \ $(top_srcdir)/share/genbuild.sh \ diff --git a/build_msvc/vcpkg.json b/build_msvc/vcpkg.json index 48b4d1fd20..86773d1fd3 100644 --- a/build_msvc/vcpkg.json +++ b/build_msvc/vcpkg.json @@ -13,13 +13,5 @@ "features": ["thread"] }, "zeromq" - ], - "builtin-baseline": "b86c0c35b88e2bf3557ff49dc831689c2f085090", - "overrides": [ - { - "name": "zeromq", - "version": "4.3.4", - "port-version": 3 - } ] } diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh index d8dcb708aa..fc2f76797c 100755 --- a/ci/test/05_before_script.sh +++ b/ci/test/05_before_script.sh @@ -45,7 +45,7 @@ if [ -z "$NO_DEPENDS" ]; then else SHELL_OPTS="CONFIG_SHELL=" fi - CI_EXEC "$SHELL_OPTS" make "$MAKEJOBS" -C depends HOST="$HOST" "$DEP_OPTS" + CI_EXEC "$SHELL_OPTS" make "$MAKEJOBS" -C depends HOST="$HOST" "$DEP_OPTS" LOG=1 fi if [ -n "$PREVIOUS_RELEASES_TO_DOWNLOAD" ]; then CI_EXEC test/get_previous_releases.py -b -t "$PREVIOUS_RELEASES_DIR" "${PREVIOUS_RELEASES_TO_DOWNLOAD}" diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm index fcec592c2c..708d2e698d 100644 --- a/contrib/guix/manifest.scm +++ b/contrib/guix/manifest.scm @@ -162,9 +162,10 @@ desirable for building Bitcoin Core release binaries." (define (make-gcc-with-pthreads gcc) (package-with-extra-configure-variable gcc "--enable-threads" "posix")) -(define (make-mingw-w64-cross-gcc-vmov-alignment cross-gcc) +(define (make-mingw-w64-cross-gcc cross-gcc) (package-with-extra-patches cross-gcc - (search-our-patches "vmov-alignment.patch"))) + (search-our-patches "vmov-alignment.patch" + "gcc-broken-longjmp.patch"))) (define (make-mingw-pthreads-cross-toolchain target) "Create a cross-compilation toolchain package for TARGET" @@ -172,7 +173,7 @@ desirable for building Bitcoin Core release binaries." (pthreads-xlibc mingw-w64-x86_64-winpthreads) (pthreads-xgcc (make-gcc-with-pthreads (cross-gcc target - #:xgcc (make-ssp-fixed-gcc (make-mingw-w64-cross-gcc-vmov-alignment base-gcc)) + #:xgcc (make-ssp-fixed-gcc (make-mingw-w64-cross-gcc base-gcc)) #:xbinutils xbinutils #:libc pthreads-xlibc)))) ;; Define a meta-package that propagates the resulting XBINUTILS, XLIBC, and diff --git a/contrib/guix/patches/gcc-broken-longjmp.patch b/contrib/guix/patches/gcc-broken-longjmp.patch new file mode 100644 index 0000000000..1cfc0918b0 --- /dev/null +++ b/contrib/guix/patches/gcc-broken-longjmp.patch @@ -0,0 +1,68 @@ +commit eb5698897c52702498938592d7f76e67d126451f +Author: Eric Botcazou <ebotcazou@adacore.com> +Date: Wed May 5 22:48:51 2021 +0200 + + Fix PR target/100402 + + This is a regression for 64-bit Windows present from mainline down to the 9 + branch and introduced by the fix for PR target/99234. Again SEH, but with + a twist related to the way MinGW implements setjmp/longjmp, which turns out + to be piggybacked on SEH with recent versions of MinGW, i.e. the longjmp + performs a bona-fide unwinding of the stack, because it calls RtlUnwindEx + with the second argument initially passed to setjmp, which is the result of + __builtin_frame_address (0) in the MinGW header file: + + define setjmp(BUF) _setjmp((BUF), __builtin_frame_address (0)) + + This means that we directly expose the frame pointer to the SEH machinery + here (unlike with regular exception handling where we use an intermediate + CFA) and thus that we cannot do whatever we want with it. The old code + would leave it unaligned, i.e. not multiple of 16, whereas the new code + aligns it, but this breaks for some reason; at least it appears that a + .seh_setframe directive with 0 as second argument always works, so the + fix aligns it this way. + + gcc/ + PR target/100402 + * config/i386/i386.c (ix86_compute_frame_layout): For a SEH target, + always return the establisher frame for __builtin_frame_address (0). + gcc/testsuite/ + * gcc.c-torture/execute/20210505-1.c: New test. + +diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c +index 2f838840e96..06ad1b2274e 100644 +--- a/gcc/config/i386/i386.c ++++ b/gcc/config/i386/i386.c +@@ -6356,12 +6356,29 @@ ix86_compute_frame_layout (void) + area, see the SEH code in config/i386/winnt.c for the rationale. */ + frame->hard_frame_pointer_offset = frame->sse_reg_save_offset; + +- /* If we can leave the frame pointer where it is, do so. Also, return ++ /* If we can leave the frame pointer where it is, do so; however return + the establisher frame for __builtin_frame_address (0) or else if the +- frame overflows the SEH maximum frame size. */ ++ frame overflows the SEH maximum frame size. ++ ++ Note that the value returned by __builtin_frame_address (0) is quite ++ constrained, because setjmp is piggybacked on the SEH machinery with ++ recent versions of MinGW: ++ ++ # elif defined(__SEH__) ++ # if defined(__aarch64__) || defined(_ARM64_) ++ # define setjmp(BUF) _setjmp((BUF), __builtin_sponentry()) ++ # elif (__MINGW_GCC_VERSION < 40702) ++ # define setjmp(BUF) _setjmp((BUF), mingw_getsp()) ++ # else ++ # define setjmp(BUF) _setjmp((BUF), __builtin_frame_address (0)) ++ # endif ++ ++ and the second argument passed to _setjmp, if not null, is forwarded ++ to the TargetFrame parameter of RtlUnwindEx by longjmp (after it has ++ built an ExceptionRecord on the fly describing the setjmp buffer). */ + const HOST_WIDE_INT diff + = frame->stack_pointer_offset - frame->hard_frame_pointer_offset; +- if (diff <= 255) ++ if (diff <= 255 && !crtl->accesses_prior_frames) + { + /* The resulting diff will be a multiple of 16 lower than 255, + i.e. at most 240 as required by the unwind data structure. */ diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py index 2b377f6c01..78eb04a836 100755 --- a/contrib/seeds/makeseeds.py +++ b/contrib/seeds/makeseeds.py @@ -10,18 +10,16 @@ import re import sys import dns.resolver import collections +from typing import List, Dict, Union NSEEDS=512 -MAX_SEEDS_PER_ASN=2 - -MIN_BLOCKS = 337600 - -# These are hosts that have been observed to be behaving strangely (e.g. -# aggressively connecting to every node). -with open("suspicious_hosts.txt", mode="r", encoding="utf-8") as f: - SUSPICIOUS_HOSTS = {s.strip() for s in f if s.strip()} +MAX_SEEDS_PER_ASN = { + 'ipv4': 2, + 'ipv6': 10, +} +MIN_BLOCKS = 730000 PATTERN_IPV4 = re.compile(r"^((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})):(\d+)$") PATTERN_IPV6 = re.compile(r"^\[([0-9a-z:]+)\]:(\d+)$") @@ -40,9 +38,13 @@ PATTERN_AGENT = re.compile( r"23.99" r")") -def parseline(line): +def parseline(line: str) -> Union[dict, None]: + """ Parses a line from `seeds_main.txt` into a dictionary of details for that line. + or `None`, if the line could not be parsed. + """ sline = line.split() if len(sline) < 11: + # line too short to be valid, skip it. return None m = PATTERN_IPV4.match(sline[0]) sortkey = None @@ -107,25 +109,26 @@ def parseline(line): 'sortkey': sortkey, } -def dedup(ips): - '''deduplicate by address,port''' +def dedup(ips: List[Dict]) -> List[Dict]: + """ Remove duplicates from `ips` where multiple ips share address and port. """ d = {} for ip in ips: d[ip['ip'],ip['port']] = ip return list(d.values()) -def filtermultiport(ips): - '''Filter out hosts with more nodes per IP''' +def filtermultiport(ips: List[Dict]) -> List[Dict]: + """ Filter out hosts with more nodes per IP""" hist = collections.defaultdict(list) for ip in ips: hist[ip['sortkey']].append(ip) return [value[0] for (key,value) in list(hist.items()) if len(value)==1] -def lookup_asn(net, ip): - ''' - Look up the asn for an IP (4 or 6) address by querying cymru.com, or None - if it could not be found. - ''' +def lookup_asn(net: str, ip: str) -> Union[int, None]: + """ Look up the asn for an `ip` address by querying cymru.com + on network `net` (e.g. ipv4 or ipv6). + + Returns in integer ASN or None if it could not be found. + """ try: if net == 'ipv4': ipaddr = ip @@ -147,20 +150,33 @@ def lookup_asn(net, ip): return None # Based on Greg Maxwell's seed_filter.py -def filterbyasn(ips, max_per_asn, max_per_net): +def filterbyasn(ips: List[Dict], max_per_asn: Dict, max_per_net: int) -> List[Dict]: + """ Prunes `ips` by + (a) trimming ips to have at most `max_per_net` ips from each net (e.g. ipv4, ipv6); and + (b) trimming ips to have at most `max_per_asn` ips from each asn in each net. + """ # Sift out ips by type ips_ipv46 = [ip for ip in ips if ip['net'] in ['ipv4', 'ipv6']] ips_onion = [ip for ip in ips if ip['net'] == 'onion'] # Filter IPv46 by ASN, and limit to max_per_net per network result = [] - net_count = collections.defaultdict(int) - asn_count = collections.defaultdict(int) - for ip in ips_ipv46: + net_count: Dict[str, int] = collections.defaultdict(int) + asn_count: Dict[int, int] = collections.defaultdict(int) + + for i, ip in enumerate(ips_ipv46): + if i % 10 == 0: + # give progress update + print(f"{i:6d}/{len(ips_ipv46)} [{100*i/len(ips_ipv46):04.1f}%]\r", file=sys.stderr, end='', flush=True) + if net_count[ip['net']] == max_per_net: + # do not add this ip as we already too many + # ips from this network continue asn = lookup_asn(ip['net'], ip['ip']) - if asn is None or asn_count[asn] == max_per_asn: + if asn is None or asn_count[asn] == max_per_asn[ip['net']]: + # do not add this ip as we already have too many + # ips from this ASN on this network continue asn_count[asn] += 1 net_count[ip['net']] += 1 @@ -170,35 +186,33 @@ def filterbyasn(ips, max_per_asn, max_per_net): result.extend(ips_onion[0:max_per_net]) return result -def ip_stats(ips): - hist = collections.defaultdict(int) +def ip_stats(ips: List[Dict]) -> str: + """ Format and return pretty string from `ips`. """ + hist: Dict[str, int] = collections.defaultdict(int) for ip in ips: if ip is not None: hist[ip['net']] += 1 - return '%6d %6d %6d' % (hist['ipv4'], hist['ipv6'], hist['onion']) + return f"{hist['ipv4']:6d} {hist['ipv6']:6d} {hist['onion']:6d}" def main(): lines = sys.stdin.readlines() ips = [parseline(line) for line in lines] print('\x1b[7m IPv4 IPv6 Onion Pass \x1b[0m', file=sys.stderr) - print('%s Initial' % (ip_stats(ips)), file=sys.stderr) + print(f'{ip_stats(ips):s} Initial', file=sys.stderr) # Skip entries with invalid address. ips = [ip for ip in ips if ip is not None] - print('%s Skip entries with invalid address' % (ip_stats(ips)), file=sys.stderr) + print(f'{ip_stats(ips):s} Skip entries with invalid address', file=sys.stderr) # Skip duplicates (in case multiple seeds files were concatenated) ips = dedup(ips) - print('%s After removing duplicates' % (ip_stats(ips)), file=sys.stderr) - # Skip entries from suspicious hosts. - ips = [ip for ip in ips if ip['ip'] not in SUSPICIOUS_HOSTS] - print('%s Skip entries from suspicious hosts' % (ip_stats(ips)), file=sys.stderr) + print(f'{ip_stats(ips):s} After removing duplicates', file=sys.stderr) # Enforce minimal number of blocks. ips = [ip for ip in ips if ip['blocks'] >= MIN_BLOCKS] - print('%s Enforce minimal number of blocks' % (ip_stats(ips)), file=sys.stderr) + print(f'{ip_stats(ips):s} Enforce minimal number of blocks', file=sys.stderr) # Require service bit 1. ips = [ip for ip in ips if (ip['service'] & 1) == 1] - print('%s Require service bit 1' % (ip_stats(ips)), file=sys.stderr) + print(f'{ip_stats(ips):s} Require service bit 1', file=sys.stderr) # Require at least 50% 30-day uptime for clearnet, 10% for onion. req_uptime = { 'ipv4': 50, @@ -206,18 +220,18 @@ def main(): 'onion': 10, } ips = [ip for ip in ips if ip['uptime'] > req_uptime[ip['net']]] - print('%s Require minimum uptime' % (ip_stats(ips)), file=sys.stderr) + print(f'{ip_stats(ips):s} Require minimum uptime', file=sys.stderr) # Require a known and recent user agent. ips = [ip for ip in ips if PATTERN_AGENT.match(ip['agent'])] - print('%s Require a known and recent user agent' % (ip_stats(ips)), file=sys.stderr) + print(f'{ip_stats(ips):s} Require a known and recent user agent', file=sys.stderr) # Sort by availability (and use last success as tie breaker) ips.sort(key=lambda x: (x['uptime'], x['lastsuccess'], x['ip']), reverse=True) # Filter out hosts with multiple bitcoin ports, these are likely abusive ips = filtermultiport(ips) - print('%s Filter out hosts with multiple bitcoin ports' % (ip_stats(ips)), file=sys.stderr) + print(f'{ip_stats(ips):s} Filter out hosts with multiple bitcoin ports', file=sys.stderr) # Look up ASNs and limit results, both per ASN and globally. ips = filterbyasn(ips, MAX_SEEDS_PER_ASN, NSEEDS) - print('%s Look up ASNs and limit results per ASN and per net' % (ip_stats(ips)), file=sys.stderr) + print(f'{ip_stats(ips):s} Look up ASNs and limit results per ASN and per net', file=sys.stderr) # Sort the results by IP address (for deterministic output). ips.sort(key=lambda x: (x['net'], x['sortkey'])) for ip in ips: diff --git a/contrib/seeds/suspicious_hosts.txt b/contrib/seeds/suspicious_hosts.txt deleted file mode 100644 index 13385cc816..0000000000 --- a/contrib/seeds/suspicious_hosts.txt +++ /dev/null @@ -1,16 +0,0 @@ -130.211.129.106 -148.251.238.178 -176.9.46.6 -178.63.107.226 -54.173.72.127 -54.174.10.182 -54.183.64.54 -54.194.231.211 -54.66.214.167 -54.66.220.137 -54.67.33.14 -54.77.251.214 -54.94.195.96 -54.94.200.247 -83.81.130.26 -88.198.17.7
\ No newline at end of file diff --git a/contrib/testgen/README.md b/contrib/testgen/README.md index 66276ec9dd..2f0288df16 100644 --- a/contrib/testgen/README.md +++ b/contrib/testgen/README.md @@ -2,7 +2,7 @@ Utilities to generate test vectors for the data-driven Bitcoin tests. -Usage: +To use inside a scripted-diff (or just execute directly): ./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json ./gen_key_io_test_vectors.py invalid 70 > ../../src/test/data/key_io_invalid.json diff --git a/contrib/testgen/gen_key_io_test_vectors.py b/contrib/testgen/gen_key_io_test_vectors.py index 4aa7dc200b..7bfb1d76a8 100755 --- a/contrib/testgen/gen_key_io_test_vectors.py +++ b/contrib/testgen/gen_key_io_test_vectors.py @@ -4,10 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' Generate valid and invalid base58/bech32(m) address and private key test vectors. - -Usage: - ./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json - ./gen_key_io_test_vectors.py invalid 70 > ../../src/test/data/key_io_invalid.json ''' from itertools import islice @@ -131,7 +127,7 @@ def is_valid_bech32(v): def gen_valid_base58_vector(template): '''Generate valid base58 vector''' prefix = bytearray(template[0]) - payload = bytearray(os.urandom(template[1])) + payload = rand_bytes(size=template[1]) suffix = bytearray(template[2]) dst_prefix = bytearray(template[4]) dst_suffix = bytearray(template[5]) @@ -143,7 +139,7 @@ def gen_valid_bech32_vector(template): '''Generate valid bech32 vector''' hrp = template[0] witver = template[1] - witprog = bytearray(os.urandom(template[2])) + witprog = rand_bytes(size=template[2]) encoding = template[4] dst_prefix = bytearray(template[5]) rv = bech32_encode(encoding, hrp, [witver] + convertbits(witprog, 8, 5)) @@ -173,17 +169,17 @@ def gen_invalid_base58_vector(template): corrupt_suffix = randbool(0.2) if corrupt_prefix: - prefix = os.urandom(1) + prefix = rand_bytes(size=1) else: prefix = bytearray(template[0]) if randomize_payload_size: - payload = os.urandom(max(int(random.expovariate(0.5)), 50)) + payload = rand_bytes(size=max(int(random.expovariate(0.5)), 50)) else: - payload = os.urandom(template[1]) + payload = rand_bytes(size=template[1]) if corrupt_suffix: - suffix = os.urandom(len(template[2])) + suffix = rand_bytes(size=len(template[2])) else: suffix = bytearray(template[2]) @@ -204,7 +200,7 @@ def gen_invalid_bech32_vector(template): to_upper = randbool(0.1) hrp = template[0] witver = template[1] - witprog = bytearray(os.urandom(template[2])) + witprog = rand_bytes(size=template[2]) encoding = template[3] if no_data: @@ -234,6 +230,9 @@ def randbool(p = 0.5): '''Return True with P(p)''' return random.random() < p +def rand_bytes(*, size): + return bytearray(random.getrandbits(8) for _ in range(size)) + def gen_invalid_vectors(): '''Generate invalid test vectors''' # start with some manual edge-cases @@ -250,6 +249,7 @@ def gen_invalid_vectors(): if __name__ == '__main__': import json iters = {'valid':gen_valid_vectors, 'invalid':gen_invalid_vectors} + random.seed(42) try: uiter = iters[sys.argv[1]] except IndexError: diff --git a/depends/Makefile b/depends/Makefile index 723509c81d..b901533786 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -271,7 +271,7 @@ clean-all: clean @rm -rf $(SOURCES_PATH) x86_64* i686* mips* arm* aarch64* powerpc* riscv32* riscv64* s390x* clean: - @rm -rf $(WORK_PATH) $(BASE_CACHE) $(BUILD) + @rm -rf $(WORK_PATH) $(BASE_CACHE) $(BUILD) *.log install: check-packages $(host_prefix)/share/config.site diff --git a/depends/README.md b/depends/README.md index f7647198c6..da2a74e0e7 100644 --- a/depends/README.md +++ b/depends/README.md @@ -113,7 +113,10 @@ The following can be set when running make: `make FOO=bar` - `BUILD_ID_SALT`: Optional salt to use when generating build package ids - `FORCE_USE_SYSTEM_CLANG`: (EXPERTS ONLY) When cross-compiling for macOS, use Clang found in the system's `$PATH` rather than the default prebuilt release of Clang - from llvm.org. Clang 8 or later is required. + from llvm.org. Clang 8 or later is required +- `LOG`: Use file-based logging for individual packages. During a package build its log file + resides in the `depends` directory, and the log file is printed out automatically in case + of build error. After successful build log files are moved along with package archives If some packages are not built, for example `make NO_WALLET=1`, the appropriate options will be passed to bitcoin's configure. In this case, `--disable-wallet`. diff --git a/depends/funcs.mk b/depends/funcs.mk index c4ad109c69..75fa1ed43f 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -67,6 +67,7 @@ $(1)_cached_checksum:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_bui $(1)_patch_dir:=$(base_build_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id)/.patches-$($(1)_build_id) $(1)_prefixbin:=$($($(1)_type)_prefix)/bin/ $(1)_cached:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_build_id).tar.gz +$(1)_build_log:=$(BASEDIR)/$(1)-$($(1)_version)-$($(1)_build_id).log $(1)_all_sources=$($(1)_file_name) $($(1)_extra_sources) #stamps @@ -85,7 +86,7 @@ $(1)_download_path_fixed=$(subst :,\:,$$($(1)_download_path)) # The default behavior for tar will try to set ownership when running as uid 0 and may not succeed, --no-same-owner disables this behavior $(1)_fetch_cmds ?= $(call fetch_file,$(1),$(subst \:,:,$$($(1)_download_path_fixed)),$$($(1)_download_file),$($(1)_file_name),$($(1)_sha256_hash)) $(1)_extract_cmds ?= mkdir -p $$($(1)_extract_dir) && echo "$$($(1)_sha256_hash) $$($(1)_source)" > $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_SHA256SUM) -c $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_TAR) --no-same-owner --strip-components=1 -xf $$($(1)_source) -$(1)_preprocess_cmds ?= +$(1)_preprocess_cmds ?= true $(1)_build_cmds ?= $(1)_config_cmds ?= $(1)_stage_cmds ?= @@ -187,44 +188,48 @@ endif endef define int_add_cmds +ifneq ($(LOG),) +$(1)_logging = >>$$($(1)_build_log) 2>&1 || { if test -f $$($(1)_build_log); then cat $$($(1)_build_log); fi; exit 1; } +endif + $($(1)_fetched): mkdir -p $$(@D) $(SOURCES_PATH) rm -f $$@ touch $$@ - cd $$(@D); $(call $(1)_fetch_cmds,$(1)) + cd $$(@D); $($(1)_fetch_cmds) cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);) touch $$@ $($(1)_extracted): | $($(1)_fetched) echo Extracting $(1)... mkdir -p $$(@D) - cd $$(@D); $(call $(1)_extract_cmds,$(1)) + cd $$(@D); $($(1)_extract_cmds) touch $$@ $($(1)_preprocessed): | $($(1)_extracted) echo Preprocessing $(1)... mkdir -p $$(@D) $($(1)_patch_dir) $(foreach patch,$($(1)_patches),cd $(PATCHES_PATH)/$(1); cp $(patch) $($(1)_patch_dir) ;) - cd $$(@D); $(call $(1)_preprocess_cmds, $(1)) + { cd $$(@D); $($(1)_preprocess_cmds); } $$($(1)_logging) touch $$@ $($(1)_configured): | $($(1)_dependencies) $($(1)_preprocessed) echo Configuring $(1)... rm -rf $(host_prefix); mkdir -p $(host_prefix)/lib; cd $(host_prefix); $(foreach package,$($(1)_all_dependencies), $(build_TAR) --no-same-owner -xf $($(package)_cached); ) mkdir -p $$(@D) - +cd $$(@D); $($(1)_config_env) $(call $(1)_config_cmds, $(1)) + +{ cd $$(@D); $($(1)_config_env) $($(1)_config_cmds); } $$($(1)_logging) touch $$@ $($(1)_built): | $($(1)_configured) echo Building $(1)... mkdir -p $$(@D) - +cd $$(@D); $($(1)_build_env) $(call $(1)_build_cmds, $(1)) + +{ cd $$(@D); $($(1)_build_env) $($(1)_build_cmds); } $$($(1)_logging) touch $$@ $($(1)_staged): | $($(1)_built) echo Staging $(1)... mkdir -p $($(1)_staging_dir)/$(host_prefix) - cd $($(1)_build_dir); $($(1)_stage_env) $(call $(1)_stage_cmds, $(1)) + +{ cd $($(1)_build_dir); $($(1)_stage_env) $($(1)_stage_cmds); } $$($(1)_logging) rm -rf $($(1)_extract_dir) touch $$@ $($(1)_postprocessed): | $($(1)_staged) echo Postprocessing $(1)... - cd $($(1)_staging_prefix_dir); $(call $(1)_postprocess_cmds) + cd $($(1)_staging_prefix_dir); $($(1)_postprocess_cmds) touch $$@ $($(1)_cached): | $($(1)_dependencies) $($(1)_postprocessed) echo Caching $(1)... @@ -233,6 +238,7 @@ $($(1)_cached): | $($(1)_dependencies) $($(1)_postprocessed) rm -rf $$(@D) && mkdir -p $$(@D) mv $$($(1)_staging_dir)/$$(@F) $$(@) rm -rf $($(1)_staging_dir) + if test -f $($(1)_build_log); then mv $($(1)_build_log) $$(@D); fi $($(1)_cached_checksum): $($(1)_cached) cd $$(@D); $(build_SHA256SUM) $$(<F) > $$(@) diff --git a/doc/REST-interface.md b/doc/REST-interface.md index c359725faf..f11624fd28 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -82,17 +82,7 @@ Given a height: returns hash of block in best-block-chain at height provided. Returns various state info regarding block chain processing. Only supports JSON as output format. -* chain : (string) current network name (main, test, signet, regtest) -* blocks : (numeric) the current number of blocks processed in the server -* headers : (numeric) the current number of headers we have validated -* bestblockhash : (string) the hash of the currently best block -* difficulty : (numeric) the current difficulty -* mediantime : (numeric) the median time of the 11 blocks before the most recent block on the blockchain -* verificationprogress : (numeric) estimate of verification progress [0..1] -* chainwork : (string) total amount of work in active chain, in hexadecimal -* pruned : (boolean) if the blocks are subject to pruning -* pruneheight : (numeric) highest block available -* softforks : (array) status of softforks in progress +Refer to the `getblockchaininfo` RPC help for details. #### Query UTXO set `GET /rest/getutxos/<checkmempool>/<txid>-<n>/<txid>-<n>/.../<txid>-<n>.<bin|hex|json>` @@ -127,13 +117,13 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76 #### Memory pool `GET /rest/mempool/info.json` -Returns various information about the TX mempool. +Returns various information about the transaction mempool. Only supports JSON as output format. -Refer to the `getmempoolinfo` RPC for documentation of the fields. +Refer to the `getmempoolinfo` RPC help for details. `GET /rest/mempool/contents.json` -Returns transactions in the TX mempool. +Returns the transactions in the mempool. Only supports JSON as output format. Risks diff --git a/doc/release-process.md b/doc/release-process.md index bc80e3d072..2f3a163a8e 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -37,7 +37,8 @@ Release Process - This update should be reviewed with a reindex-chainstate with assumevalid=0 to catch any defect that causes rejection of blocks in the past history. - Clear the release notes and move them to the wiki (see "Write the release notes" below). -- Translations on Transifex +- Translations on Transifex: + - Pull translations from Transifex into the master branch. - Create [a new resource](https://www.transifex.com/bitcoin/bitcoin/content/) named after the major version with the slug `[bitcoin.qt-translation-<RRR>x]`, where `RRR` is the major branch number padded with zeros. Use `src/qt/locale/bitcoin_en.xlf` to create it. - In the project workflow settings, ensure that [Translation Memory Fill-up](https://docs.transifex.com/translation-memory/enabling-autofill) is enabled and that [Translation Memory Context Matching](https://docs.transifex.com/translation-memory/translation-memory-with-context) is disabled. - Update the Transifex slug in [`.tx/config`](/.tx/config) to the slug of the resource created in the first step. This identifies which resource the translations will be synchronized from. diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 5dae4374e3..58a09cd4a4 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -74,6 +74,7 @@ endif if ENABLE_WALLET bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp +bench_bench_bitcoin_SOURCES += bench/wallet_loading.cpp endif bench_bench_bitcoin_LDADD += $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp index f7f62dfc68..e614102de3 100644 --- a/src/arith_uint256.cpp +++ b/src/arith_uint256.cpp @@ -146,13 +146,21 @@ double base_uint<BITS>::getdouble() const template <unsigned int BITS> std::string base_uint<BITS>::GetHex() const { - return ArithToUint256(*this).GetHex(); + base_blob<BITS> b; + for (int x = 0; x < this->WIDTH; ++x) { + WriteLE32(b.begin() + x*4, this->pn[x]); + } + return b.GetHex(); } template <unsigned int BITS> void base_uint<BITS>::SetHex(const char* psz) { - *this = UintToArith256(uint256S(psz)); + base_blob<BITS> b; + b.SetHex(psz); + for (int x = 0; x < this->WIDTH; ++x) { + this->pn[x] = ReadLE32(b.begin() + x*4); + } } template <unsigned int BITS> @@ -164,7 +172,7 @@ void base_uint<BITS>::SetHex(const std::string& str) template <unsigned int BITS> std::string base_uint<BITS>::ToString() const { - return (GetHex()); + return GetHex(); } template <unsigned int BITS> @@ -183,20 +191,7 @@ unsigned int base_uint<BITS>::bits() const } // Explicit instantiations for base_uint<256> -template base_uint<256>::base_uint(const std::string&); -template base_uint<256>& base_uint<256>::operator<<=(unsigned int); -template base_uint<256>& base_uint<256>::operator>>=(unsigned int); -template base_uint<256>& base_uint<256>::operator*=(uint32_t b32); -template base_uint<256>& base_uint<256>::operator*=(const base_uint<256>& b); -template base_uint<256>& base_uint<256>::operator/=(const base_uint<256>& b); -template int base_uint<256>::CompareTo(const base_uint<256>&) const; -template bool base_uint<256>::EqualTo(uint64_t) const; -template double base_uint<256>::getdouble() const; -template std::string base_uint<256>::GetHex() const; -template std::string base_uint<256>::ToString() const; -template void base_uint<256>::SetHex(const char*); -template void base_uint<256>::SetHex(const std::string&); -template unsigned int base_uint<256>::bits() const; +template class base_uint<256>; // This implementation directly uses shifts instead of going // through an intermediate MPI representation. diff --git a/src/arith_uint256.h b/src/arith_uint256.h index a0a0429c2a..19193972a4 100644 --- a/src/arith_uint256.h +++ b/src/arith_uint256.h @@ -284,4 +284,6 @@ public: uint256 ArithToUint256(const arith_uint256 &); arith_uint256 UintToArith256(const uint256 &); +extern template class base_uint<256>; + #endif // BITCOIN_ARITH_UINT256_H diff --git a/src/bench/wallet_loading.cpp b/src/bench/wallet_loading.cpp new file mode 100644 index 0000000000..38d3460001 --- /dev/null +++ b/src/bench/wallet_loading.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <bench/bench.h> +#include <interfaces/chain.h> +#include <node/context.h> +#include <test/util/mining.h> +#include <test/util/setup_common.h> +#include <test/util/wallet.h> +#include <util/translation.h> +#include <validationinterface.h> +#include <wallet/context.h> +#include <wallet/receive.h> +#include <wallet/wallet.h> + +#include <optional> + +using wallet::CWallet; +using wallet::DatabaseOptions; +using wallet::DatabaseStatus; +using wallet::ISMINE_SPENDABLE; +using wallet::MakeWalletDatabase; +using wallet::WALLET_FLAG_DESCRIPTORS; +using wallet::WalletContext; + +static const std::shared_ptr<CWallet> BenchLoadWallet(WalletContext& context, DatabaseOptions& options) +{ + DatabaseStatus status; + bilingual_str error; + std::vector<bilingual_str> warnings; + auto database = MakeWalletDatabase("", options, status, error); + assert(database); + auto wallet = CWallet::Create(context, "", std::move(database), options.create_flags, error, warnings); + NotifyWalletLoaded(context, wallet); + if (context.chain) { + wallet->postInitProcess(); + } + return wallet; +} + +static void BenchUnloadWallet(std::shared_ptr<CWallet>&& wallet) +{ + SyncWithValidationInterfaceQueue(); + wallet->m_chain_notifications_handler.reset(); + UnloadWallet(std::move(wallet)); +} + +static void WalletLoading(benchmark::Bench& bench, bool legacy_wallet) +{ + const auto test_setup = MakeNoLogFileContext<TestingSetup>(); + + WalletContext context; + context.args = &test_setup->m_args; + context.chain = test_setup->m_node.chain.get(); + + // Setup the wallet + // Loading the wallet will also create it + DatabaseOptions options; + if (!legacy_wallet) options.create_flags = WALLET_FLAG_DESCRIPTORS; + auto wallet = BenchLoadWallet(context, options); + + // Generate a bunch of transactions and addresses to put into the wallet + for (int i = 0; i < 5000; ++i) { + generatetoaddress(test_setup->m_node, getnewaddress(*wallet)); + } + + // reload the wallet for the actual benchmark + BenchUnloadWallet(std::move(wallet)); + + bench.minEpochIterations(10).run([&] { + wallet = BenchLoadWallet(context, options); + + // Cleanup + BenchUnloadWallet(std::move(wallet)); + }); +} + +static void WalletLoadingLegacy(benchmark::Bench& bench) { WalletLoading(bench, /*legacy_wallet=*/true); } +static void WalletLoadingDescriptors(benchmark::Bench& bench) { WalletLoading(bench, /*legacy_wallet=*/false); } + +BENCHMARK(WalletLoadingLegacy); +BENCHMARK(WalletLoadingDescriptors); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 6925a4c8c6..dea46693bc 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -1055,7 +1055,9 @@ static void ParseGetInfoResult(UniValue& result) result_string += "\n"; } - result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, result["warnings"].getValStr()); + const std::string warnings{result["warnings"].getValStr()}; + result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, warnings.empty() ? "(none)" : warnings); + result.setStr(result_string); } diff --git a/src/init.cpp b/src/init.cpp index 86e6ec4451..fdab296593 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -660,7 +660,8 @@ void InitParameterInteraction(ArgsManager& args) LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__); } - if (args.IsArgSet("-proxy")) { + std::string proxy_arg = args.GetArg("-proxy", ""); + if (proxy_arg != "" && proxy_arg != "0") { // to protect privacy, do not listen by default if a default proxy server is specified if (args.SoftSetBoolArg("-listen", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); diff --git a/src/logging/timer.h b/src/logging/timer.h index fc5307bc62..d954e46301 100644 --- a/src/logging/timer.h +++ b/src/logging/timer.h @@ -97,13 +97,13 @@ private: #define LOG_TIME_MICROS_WITH_CATEGORY(end_msg, log_category) \ - BCLog::Timer<std::chrono::microseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category) + BCLog::Timer<std::chrono::microseconds> UNIQUE_NAME(logging_timer)(__func__, end_msg, log_category) #define LOG_TIME_MILLIS_WITH_CATEGORY(end_msg, log_category) \ - BCLog::Timer<std::chrono::milliseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category) + BCLog::Timer<std::chrono::milliseconds> UNIQUE_NAME(logging_timer)(__func__, end_msg, log_category) #define LOG_TIME_MILLIS_WITH_CATEGORY_MSG_ONCE(end_msg, log_category) \ - BCLog::Timer<std::chrono::milliseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category, /* msg_on_completion=*/false) + BCLog::Timer<std::chrono::milliseconds> UNIQUE_NAME(logging_timer)(__func__, end_msg, log_category, /* msg_on_completion=*/false) #define LOG_TIME_SECONDS(end_msg) \ - BCLog::Timer<std::chrono::seconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg) + BCLog::Timer<std::chrono::seconds> UNIQUE_NAME(logging_timer)(__func__, end_msg) #endif // BITCOIN_LOGGING_TIMER_H diff --git a/src/netbase.cpp b/src/netbase.cpp index 9a0b800565..e6d4f16ba0 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -305,7 +305,7 @@ enum class IntrRecvError { * * @see This function can be interrupted by calling InterruptSocks5(bool). * Sockets can be made non-blocking with SetSocketNonBlocking(const - * SOCKET&, bool). + * SOCKET&). */ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock) { @@ -518,7 +518,7 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family) SetSocketNoDelay(hSocket); // Set the non-blocking option on the socket. - if (!SetSocketNonBlocking(hSocket, true)) { + if (!SetSocketNonBlocking(hSocket)) { CloseSocket(hSocket); LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError())); return nullptr; @@ -711,28 +711,16 @@ bool LookupSubNet(const std::string& subnet_str, CSubNet& subnet_out) return false; } -bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking) +bool SetSocketNonBlocking(const SOCKET& hSocket) { - if (fNonBlocking) { #ifdef WIN32 - u_long nOne = 1; - if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) { + u_long nOne = 1; + if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) { #else - int fFlags = fcntl(hSocket, F_GETFL, 0); - if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == SOCKET_ERROR) { + int fFlags = fcntl(hSocket, F_GETFL, 0); + if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == SOCKET_ERROR) { #endif - return false; - } - } else { -#ifdef WIN32 - u_long nZero = 0; - if (ioctlsocket(hSocket, FIONBIO, &nZero) == SOCKET_ERROR) { -#else - int fFlags = fcntl(hSocket, F_GETFL, 0); - if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR) { -#endif - return false; - } + return false; } return true; diff --git a/src/netbase.h b/src/netbase.h index f9e3872c16..c226e6b73d 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -221,8 +221,8 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT */ bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed); -/** Disable or enable blocking-mode for a socket */ -bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking); +/** Enable non-blocking mode for a socket */ +bool SetSocketNonBlocking(const SOCKET& hSocket); /** Set the TCP_NODELAY flag on a socket */ bool SetSocketNoDelay(const SOCKET& hSocket); void InterruptSocks5(bool interrupt); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c6b884e40a..8cac28400f 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -95,6 +95,8 @@ static void RegisterMetaTypes() qRegisterMetaType<std::function<void()>>("std::function<void()>"); qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon"); qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo"); + + qRegisterMetaTypeStreamOperators<BitcoinUnit>("BitcoinUnit"); } static QString GetLangTerritory() diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index a257e250e0..c92aecd095 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -14,6 +14,9 @@ #include <QHBoxLayout> #include <QKeyEvent> #include <QLineEdit> +#include <QVariant> + +#include <cassert> /** QSpinBox that uses fixed-point numbers internally and uses our own * formatting/parsing functions. @@ -96,7 +99,7 @@ public: setValue(val); } - void setDisplayUnit(int unit) + void setDisplayUnit(BitcoinUnit unit) { bool valid = false; CAmount val = value(&valid); @@ -122,7 +125,7 @@ public: const QFontMetrics fm(fontMetrics()); int h = lineEdit()->minimumSizeHint().height(); - int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS)); + int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnit::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS)); w += 2; // cursor blinking space QStyleOptionSpinBox opt; @@ -141,14 +144,13 @@ public: opt.rect = rect(); - cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this) - .expandedTo(QApplication::globalStrut()); + cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this); } return cachedMinimumSizeHint; } private: - int currentUnit{BitcoinUnits::BTC}; + BitcoinUnit currentUnit{BitcoinUnit::BTC}; CAmount singleStep{CAmount(100000)}; // satoshis mutable QSize cachedMinimumSizeHint; bool m_allow_empty{true}; @@ -326,14 +328,14 @@ void BitcoinAmountField::unitChanged(int idx) unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString()); // Determine new unit ID - int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt(); - - amount->setDisplayUnit(newUnit); + QVariant new_unit = unit->currentData(BitcoinUnits::UnitRole); + assert(new_unit.isValid()); + amount->setDisplayUnit(new_unit.value<BitcoinUnit>()); } -void BitcoinAmountField::setDisplayUnit(int newUnit) +void BitcoinAmountField::setDisplayUnit(BitcoinUnit new_unit) { - unit->setValue(newUnit); + unit->setValue(QVariant::fromValue(new_unit)); } void BitcoinAmountField::setSingleStep(const CAmount& step) diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 366a6fc4b5..a40cd38332 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_BITCOINAMOUNTFIELD_H #include <consensus/amount.h> +#include <qt/bitcoinunits.h> #include <QWidget> @@ -52,7 +53,7 @@ public: bool validate(); /** Change unit used to display amount. */ - void setDisplayUnit(int unit); + void setDisplayUnit(BitcoinUnit new_unit); /** Make field empty and ready for new input. */ void clear(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index be0d33d667..81b0e711b2 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1245,7 +1245,7 @@ void BitcoinGUI::showEvent(QShowEvent *event) } #ifdef ENABLE_WALLET -void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName) +void BitcoinGUI::incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName) { // On new transaction, make an info balloon QString msg = tr("Date: %1\n").arg(date) + @@ -1496,11 +1496,10 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *pl { createContextMenu(); setToolTip(tr("Unit to show amounts in. Click to select another unit.")); - QList<BitcoinUnits::Unit> units = BitcoinUnits::availableUnits(); + QList<BitcoinUnit> units = BitcoinUnits::availableUnits(); int max_width = 0; const QFontMetrics fm(font()); - for (const BitcoinUnits::Unit unit : units) - { + for (const BitcoinUnit unit : units) { max_width = qMax(max_width, GUIUtil::TextWidth(fm, BitcoinUnits::longName(unit))); } setMinimumSize(max_width, 0); @@ -1530,8 +1529,8 @@ void UnitDisplayStatusBarControl::changeEvent(QEvent* e) void UnitDisplayStatusBarControl::createContextMenu() { menu = new QMenu(this); - for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) { - menu->addAction(BitcoinUnits::longName(u))->setData(QVariant(u)); + for (const BitcoinUnit u : BitcoinUnits::availableUnits()) { + menu->addAction(BitcoinUnits::longName(u))->setData(QVariant::fromValue(u)); } connect(menu, &QMenu::triggered, this, &UnitDisplayStatusBarControl::onMenuSelection); } @@ -1552,7 +1551,7 @@ void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel) } /** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */ -void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits) +void UnitDisplayStatusBarControl::updateDisplayUnit(BitcoinUnit newUnits) { setText(BitcoinUnits::longName(newUnits)); } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index d2b29ba27b..5d9a978695 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -9,6 +9,7 @@ #include <config/bitcoin-config.h> #endif +#include <qt/bitcoinunits.h> #include <qt/guiutil.h> #include <qt/optionsdialog.h> @@ -260,7 +261,7 @@ public Q_SLOTS: bool handlePaymentRequest(const SendCoinsRecipient& recipient); /** Show incoming transaction notification for new transactions. */ - void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); + void incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); #endif // ENABLE_WALLET private: @@ -341,7 +342,7 @@ private: private Q_SLOTS: /** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */ - void updateDisplayUnit(int newUnits); + void updateDisplayUnit(BitcoinUnit newUnits); /** Tells underlying optionsModel to update its current display unit. */ void onMenuSelection(QAction* action); }; diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 69caf64d5c..fe3eb3240b 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -18,94 +18,75 @@ BitcoinUnits::BitcoinUnits(QObject *parent): { } -QList<BitcoinUnits::Unit> BitcoinUnits::availableUnits() +QList<BitcoinUnit> BitcoinUnits::availableUnits() { - QList<BitcoinUnits::Unit> unitlist; - unitlist.append(BTC); - unitlist.append(mBTC); - unitlist.append(uBTC); - unitlist.append(SAT); + QList<BitcoinUnit> unitlist; + unitlist.append(Unit::BTC); + unitlist.append(Unit::mBTC); + unitlist.append(Unit::uBTC); + unitlist.append(Unit::SAT); return unitlist; } -bool BitcoinUnits::valid(int unit) +QString BitcoinUnits::longName(Unit unit) { - switch(unit) - { - case BTC: - case mBTC: - case uBTC: - case SAT: - return true; - default: - return false; - } -} - -QString BitcoinUnits::longName(int unit) -{ - switch(unit) - { - case BTC: return QString("BTC"); - case mBTC: return QString("mBTC"); - case uBTC: return QString::fromUtf8("µBTC (bits)"); - case SAT: return QString("Satoshi (sat)"); - default: return QString("???"); - } + switch (unit) { + case Unit::BTC: return QString("BTC"); + case Unit::mBTC: return QString("mBTC"); + case Unit::uBTC: return QString::fromUtf8("µBTC (bits)"); + case Unit::SAT: return QString("Satoshi (sat)"); + } // no default case, so the compiler can warn about missing cases + assert(false); } -QString BitcoinUnits::shortName(int unit) +QString BitcoinUnits::shortName(Unit unit) { - switch(unit) - { - case uBTC: return QString::fromUtf8("bits"); - case SAT: return QString("sat"); - default: return longName(unit); - } + switch (unit) { + case Unit::BTC: return longName(unit); + case Unit::mBTC: return longName(unit); + case Unit::uBTC: return QString("bits"); + case Unit::SAT: return QString("sat"); + } // no default case, so the compiler can warn about missing cases + assert(false); } -QString BitcoinUnits::description(int unit) +QString BitcoinUnits::description(Unit unit) { - switch(unit) - { - case BTC: return QString("Bitcoins"); - case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)"); - case uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); - case SAT: return QString("Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); - default: return QString("???"); - } + switch (unit) { + case Unit::BTC: return QString("Bitcoins"); + case Unit::mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)"); + case Unit::uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); + case Unit::SAT: return QString("Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); + } // no default case, so the compiler can warn about missing cases + assert(false); } -qint64 BitcoinUnits::factor(int unit) +qint64 BitcoinUnits::factor(Unit unit) { - switch(unit) - { - case BTC: return 100000000; - case mBTC: return 100000; - case uBTC: return 100; - case SAT: return 1; - default: return 100000000; - } + switch (unit) { + case Unit::BTC: return 100'000'000; + case Unit::mBTC: return 100'000; + case Unit::uBTC: return 100; + case Unit::SAT: return 1; + } // no default case, so the compiler can warn about missing cases + assert(false); } -int BitcoinUnits::decimals(int unit) +int BitcoinUnits::decimals(Unit unit) { - switch(unit) - { - case BTC: return 8; - case mBTC: return 5; - case uBTC: return 2; - case SAT: return 0; - default: return 0; - } + switch (unit) { + case Unit::BTC: return 8; + case Unit::mBTC: return 5; + case Unit::uBTC: return 2; + case Unit::SAT: return 0; + } // no default case, so the compiler can warn about missing cases + assert(false); } -QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify) +QString BitcoinUnits::format(Unit unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. - if(!valid(unit)) - return QString(); // Refuse to format invalid unit qint64 n = (qint64)nIn; qint64 coin = factor(unit); int num_decimals = decimals(unit); @@ -147,19 +128,19 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator // Please take care to use formatHtmlWithUnit instead, when // appropriate. -QString BitcoinUnits::formatWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators) +QString BitcoinUnits::formatWithUnit(Unit unit, const CAmount& amount, bool plussign, SeparatorStyle separators) { return format(unit, amount, plussign, separators) + QString(" ") + shortName(unit); } -QString BitcoinUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators) +QString BitcoinUnits::formatHtmlWithUnit(Unit unit, const CAmount& amount, bool plussign, SeparatorStyle separators) { QString str(formatWithUnit(unit, amount, plussign, separators)); str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML)); return QString("<span style='white-space: nowrap;'>%1</span>").arg(str); } -QString BitcoinUnits::formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy) +QString BitcoinUnits::formatWithPrivacy(Unit unit, const CAmount& amount, SeparatorStyle separators, bool privacy) { assert(amount >= 0); QString value; @@ -171,10 +152,11 @@ QString BitcoinUnits::formatWithPrivacy(int unit, const CAmount& amount, Separat return value + QString(" ") + shortName(unit); } -bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out) +bool BitcoinUnits::parse(Unit unit, const QString& value, CAmount* val_out) { - if(!valid(unit) || value.isEmpty()) + if (value.isEmpty()) { return false; // Refuse to parse invalid unit or empty string + } int num_decimals = decimals(unit); // Ignore spaces and thin spaces when parsing @@ -210,14 +192,9 @@ bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out) return ok; } -QString BitcoinUnits::getAmountColumnTitle(int unit) +QString BitcoinUnits::getAmountColumnTitle(Unit unit) { - QString amountTitle = QObject::tr("Amount"); - if (BitcoinUnits::valid(unit)) - { - amountTitle += " ("+BitcoinUnits::shortName(unit) + ")"; - } - return amountTitle; + return QObject::tr("Amount") + " (" + shortName(unit) + ")"; } int BitcoinUnits::rowCount(const QModelIndex &parent) const @@ -240,7 +217,7 @@ QVariant BitcoinUnits::data(const QModelIndex &index, int role) const case Qt::ToolTipRole: return QVariant(description(unit)); case UnitRole: - return QVariant(static_cast<int>(unit)); + return QVariant::fromValue(unit); } } return QVariant(); @@ -250,3 +227,40 @@ CAmount BitcoinUnits::maxMoney() { return MAX_MONEY; } + +namespace { +qint8 ToQint8(BitcoinUnit unit) +{ + switch (unit) { + case BitcoinUnit::BTC: return 0; + case BitcoinUnit::mBTC: return 1; + case BitcoinUnit::uBTC: return 2; + case BitcoinUnit::SAT: return 3; + } // no default case, so the compiler can warn about missing cases + assert(false); +} + +BitcoinUnit FromQint8(qint8 num) +{ + switch (num) { + case 0: return BitcoinUnit::BTC; + case 1: return BitcoinUnit::mBTC; + case 2: return BitcoinUnit::uBTC; + case 3: return BitcoinUnit::SAT; + } + assert(false); +} +} // namespace + +QDataStream& operator<<(QDataStream& out, const BitcoinUnit& unit) +{ + return out << ToQint8(unit); +} + +QDataStream& operator>>(QDataStream& in, BitcoinUnit& unit) +{ + qint8 input; + in >> input; + unit = FromQint8(input); + return in; +} diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 9fedec0d4f..b3b5a8fc18 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -8,6 +8,7 @@ #include <consensus/amount.h> #include <QAbstractListModel> +#include <QDataStream> #include <QString> // U+2009 THIN SPACE = UTF-8 E2 80 89 @@ -38,13 +39,13 @@ public: /** Bitcoin units. @note Source: https://en.bitcoin.it/wiki/Units . Please add only sensible ones */ - enum Unit - { + enum class Unit { BTC, mBTC, uBTC, SAT }; + Q_ENUM(Unit) enum class SeparatorStyle { @@ -59,30 +60,28 @@ public: //! Get list of units, for drop-down box static QList<Unit> availableUnits(); - //! Is unit ID valid? - static bool valid(int unit); //! Long name - static QString longName(int unit); + static QString longName(Unit unit); //! Short name - static QString shortName(int unit); + static QString shortName(Unit unit); //! Longer description - static QString description(int unit); + static QString description(Unit unit); //! Number of Satoshis (1e-8) per unit - static qint64 factor(int unit); + static qint64 factor(Unit unit); //! Number of decimals left - static int decimals(int unit); + static int decimals(Unit unit); //! Format as string - static QString format(int unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false); + static QString format(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false); //! Format as string (with unit) - static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); + static QString formatWithUnit(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD); //! Format as HTML string (with unit) - static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); + static QString formatHtmlWithUnit(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD); //! Format as string (with unit) of fixed length to preserve privacy, if it is set. - static QString formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy); + static QString formatWithPrivacy(Unit unit, const CAmount& amount, SeparatorStyle separators, bool privacy); //! Parse string to coin amount - static bool parse(int unit, const QString &value, CAmount *val_out); + static bool parse(Unit unit, const QString& value, CAmount* val_out); //! Gets title for amount column including current display unit if optionsModel reference available */ - static QString getAmountColumnTitle(int unit); + static QString getAmountColumnTitle(Unit unit); ///@} //! @name AbstractListModel implementation @@ -107,8 +106,11 @@ public: static CAmount maxMoney(); private: - QList<BitcoinUnits::Unit> unitlist; + QList<Unit> unitlist; }; typedef BitcoinUnits::Unit BitcoinUnit; +QDataStream& operator<<(QDataStream& out, const BitcoinUnit& unit); +QDataStream& operator>>(QDataStream& in, BitcoinUnit& unit); + #endif // BITCOIN_QT_BITCOINUNITS_H diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 07c3e62a03..bd9a90a890 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -507,7 +507,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * } // actually update labels - int nDisplayUnit = BitcoinUnits::BTC; + BitcoinUnit nDisplayUnit = BitcoinUnit::BTC; if (model && model->getOptionsModel()) nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); @@ -590,7 +590,7 @@ void CoinControlDialog::updateView() QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsAutoTristate; - int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + BitcoinUnit nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); for (const auto& coins : model->wallet().listCoins()) { CCoinControlWidgetItem* itemWalletAddress{nullptr}; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index ca0b8c20f0..6fb5fce5b3 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -178,8 +178,7 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { if(!i->second.isEmpty()) { - if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount)) - { + if (!BitcoinUnits::parse(BitcoinUnit::BTC, i->second, &rv.amount)) { return false; } } @@ -211,7 +210,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info) if (info.amount) { - ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER)); + ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnit::BTC, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER)); paramCount++; } diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 52bda59748..40b9ed5483 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -24,6 +24,7 @@ #include <QLatin1Char> #include <QSettings> #include <QStringList> +#include <QVariant> const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1"; @@ -71,9 +72,16 @@ void OptionsModel::Init(bool resetSettings) fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool(); // Display - if (!settings.contains("nDisplayUnit")) - settings.setValue("nDisplayUnit", BitcoinUnits::BTC); - nDisplayUnit = settings.value("nDisplayUnit").toInt(); + if (!settings.contains("DisplayBitcoinUnit")) { + settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(BitcoinUnit::BTC)); + } + QVariant unit = settings.value("DisplayBitcoinUnit"); + if (unit.canConvert<BitcoinUnit>()) { + m_display_bitcoin_unit = unit.value<BitcoinUnit>(); + } else { + m_display_bitcoin_unit = BitcoinUnit::BTC; + settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit)); + } if (!settings.contains("strThirdPartyTxUrls")) settings.setValue("strThirdPartyTxUrls", ""); @@ -376,7 +384,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return m_sub_fee_from_amount; #endif case DisplayUnit: - return nDisplayUnit; + return QVariant::fromValue(m_display_bitcoin_unit); case ThirdPartyTxUrls: return strThirdPartyTxUrls; case Language: @@ -584,16 +592,13 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in return successful; } -/** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */ -void OptionsModel::setDisplayUnit(const QVariant &value) +void OptionsModel::setDisplayUnit(const QVariant& new_unit) { - if (!value.isNull()) - { - QSettings settings; - nDisplayUnit = value.toInt(); - settings.setValue("nDisplayUnit", nDisplayUnit); - Q_EMIT displayUnitChanged(nDisplayUnit); - } + if (new_unit.isNull() || new_unit.value<BitcoinUnit>() == m_display_bitcoin_unit) return; + m_display_bitcoin_unit = new_unit.value<BitcoinUnit>(); + QSettings settings; + settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit)); + Q_EMIT displayUnitChanged(m_display_bitcoin_unit); } void OptionsModel::setRestartRequired(bool fRequired) diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index bb9a8c1f8c..510ebb5cfd 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_OPTIONSMODEL_H #include <cstdint> +#include <qt/bitcoinunits.h> #include <qt/guiconstants.h> #include <QAbstractListModel> @@ -55,7 +56,7 @@ public: ProxyUseTor, // bool ProxyIPTor, // QString ProxyPortTor, // int - DisplayUnit, // BitcoinUnits::Unit + DisplayUnit, // BitcoinUnit ThirdPartyTxUrls, // QString Language, // QString UseEmbeddedMonospacedFont, // bool @@ -79,14 +80,14 @@ public: int rowCount(const QModelIndex & parent = QModelIndex()) const override; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override; - /** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */ - void setDisplayUnit(const QVariant &value); + /** Updates current unit in memory, settings and emits displayUnitChanged(new_unit) signal */ + void setDisplayUnit(const QVariant& new_unit); /* Explicit getters */ bool getShowTrayIcon() const { return m_show_tray_icon; } bool getMinimizeToTray() const { return fMinimizeToTray; } bool getMinimizeOnClose() const { return fMinimizeOnClose; } - int getDisplayUnit() const { return nDisplayUnit; } + BitcoinUnit getDisplayUnit() const { return m_display_bitcoin_unit; } QString getThirdPartyTxUrls() const { return strThirdPartyTxUrls; } bool getUseEmbeddedMonospacedFont() const { return m_use_embedded_monospaced_font; } bool getCoinControlFeatures() const { return fCoinControlFeatures; } @@ -112,7 +113,7 @@ private: bool fMinimizeToTray; bool fMinimizeOnClose; QString language; - int nDisplayUnit; + BitcoinUnit m_display_bitcoin_unit; QString strThirdPartyTxUrls; bool m_use_embedded_monospaced_font; bool fCoinControlFeatures; @@ -127,7 +128,7 @@ private: // Check settings version and upgrade default values if required void checkAndMigrate(); Q_SIGNALS: - void displayUnitChanged(int unit); + void displayUnitChanged(BitcoinUnit unit); void coinControlFeaturesChanged(bool); void showTrayIconChanged(bool); void useEmbeddedMonospacedFontChanged(bool); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 7127706463..820bcbf3cd 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -34,9 +34,9 @@ class TxViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: - explicit TxViewDelegate(const PlatformStyle *_platformStyle, QObject *parent=nullptr): - QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC), - platformStyle(_platformStyle) + explicit TxViewDelegate(const PlatformStyle* _platformStyle, QObject* parent = nullptr) + : QAbstractItemDelegate(parent), unit(BitcoinUnit::BTC), + platformStyle(_platformStyle) { connect(this, &TxViewDelegate::width_changed, this, &TxViewDelegate::sizeHintChanged); } @@ -125,7 +125,7 @@ public: return {DECORATION_SIZE + 8 + minimum_text_width, DECORATION_SIZE}; } - int unit; + BitcoinUnit unit; Q_SIGNALS: //! An intermediate signal for emitting from the `paint() const` member function. @@ -197,7 +197,7 @@ OverviewPage::~OverviewPage() void OverviewPage::setBalance(const interfaces::WalletBalances& balances) { - int unit = walletModel->getOptionsModel()->getDisplayUnit(); + BitcoinUnit unit = walletModel->getOptionsModel()->getDisplayUnit(); m_balances = balances; if (walletModel->wallet().isLegacy()) { if (walletModel->wallet().privateKeysDisabled()) { diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp index 6880c157c0..333766ce21 100644 --- a/src/qt/psbtoperationsdialog.cpp +++ b/src/qt/psbtoperationsdialog.cpp @@ -181,7 +181,7 @@ std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransac ExtractDestination(out.scriptPubKey, address); totalAmount += out.nValue; tx_description.append(tr(" * Sends %1 to %2") - .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, out.nValue)) + .arg(BitcoinUnits::formatWithUnit(BitcoinUnit::BTC, out.nValue)) .arg(QString::fromStdString(EncodeDestination(address)))); tx_description.append("<br>"); } @@ -193,7 +193,7 @@ std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransac tx_description.append(tr("Unable to calculate transaction fee or total transaction amount.")); } else { tx_description.append(tr("Pays transaction fee: ")); - tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, *analysis.fee)); + tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnit::BTC, *analysis.fee)); // add total amount in all subdivision units tx_description.append("<hr />"); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index c924789796..fd8eccb86d 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -380,8 +380,7 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa question_string.append("<hr />"); CAmount totalAmount = m_current_transaction->getTotalTransactionAmount() + txFee; QStringList alternativeUnits; - for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) - { + for (const BitcoinUnit u : BitcoinUnits::availableUnits()) { if(u != model->getOptionsModel()->getDisplayUnit()) alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount)); } diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 6ab534764b..c4cd0f4cd1 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -7,24 +7,25 @@ #include <interfaces/chain.h> #include <interfaces/node.h> +#include <key_io.h> #include <qt/bitcoinamountfield.h> +#include <qt/bitcoinunits.h> #include <qt/clientmodel.h> #include <qt/optionsmodel.h> +#include <qt/overviewpage.h> #include <qt/platformstyle.h> #include <qt/qvalidatedlineedit.h> +#include <qt/receivecoinsdialog.h> +#include <qt/receiverequestdialog.h> +#include <qt/recentrequeststablemodel.h> #include <qt/sendcoinsdialog.h> #include <qt/sendcoinsentry.h> #include <qt/transactiontablemodel.h> #include <qt/transactionview.h> #include <qt/walletmodel.h> -#include <key_io.h> #include <test/util/setup_common.h> #include <validation.h> #include <wallet/wallet.h> -#include <qt/overviewpage.h> -#include <qt/receivecoinsdialog.h> -#include <qt/recentrequeststablemodel.h> -#include <qt/receiverequestdialog.h> #include <chrono> #include <memory> @@ -196,7 +197,7 @@ void TestGUI(interfaces::Node& node) // Check balance in send dialog QLabel* balanceLabel = sendCoinsDialog.findChild<QLabel*>("labelBalance"); QString balanceText = balanceLabel->text(); - int unit = walletModel.getOptionsModel()->getDisplayUnit(); + BitcoinUnit unit = walletModel.getOptionsModel()->getDisplayUnit(); CAmount balance = walletModel.wallet().getBalance(); QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS); QCOMPARE(balanceText, balanceComparison); @@ -222,7 +223,7 @@ void TestGUI(interfaces::Node& node) overviewPage.setWalletModel(&walletModel); QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance"); QString balanceText = balanceLabel->text().trimmed(); - int unit = walletModel.getOptionsModel()->getDisplayUnit(); + BitcoinUnit unit = walletModel.getOptionsModel()->getDisplayUnit(); CAmount balance = walletModel.wallet().getBalance(); QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS); QCOMPARE(balanceText, balanceComparison); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index be5851d627..9e92f89543 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -32,20 +32,18 @@ using wallet::ISMINE_SPENDABLE; using wallet::ISMINE_WATCH_ONLY; using wallet::isminetype; -QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks) +QString TransactionDesc::FormatTxStatus(const interfaces::WalletTxStatus& status, bool inMempool) { - { - int nDepth = status.depth_in_main_chain; - if (nDepth < 0) { - return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth); - } else if (nDepth == 0) { - const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()}; - return tr("0/unconfirmed, %1").arg(inMempool ? tr("in memory pool") : tr("not in memory pool")) + abandoned; - } else if (nDepth < 6) { - return tr("%1/unconfirmed").arg(nDepth); - } else { - return tr("%1 confirmations").arg(nDepth); - } + int depth = status.depth_in_main_chain; + if (depth < 0) { + return tr("conflicted with a transaction with %1 confirmations").arg(-depth); + } else if (depth == 0) { + const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()}; + return tr("0/unconfirmed, %1").arg(inMempool ? tr("in memory pool") : tr("not in memory pool")) + abandoned; + } else if (depth < 6) { + return tr("%1/unconfirmed").arg(depth); + } else { + return tr("%1 confirmations").arg(depth); } } @@ -77,7 +75,7 @@ bool GetPaymentRequestMerchant(const std::string& pr, QString& merchant) return false; } -QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit) +QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord* rec, BitcoinUnit unit) { int numBlocks; interfaces::WalletTxStatus status; @@ -95,7 +93,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall CAmount nDebit = wtx.debit; CAmount nNet = nCredit - nDebit; - strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx, status, inMempool, numBlocks); + strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(status, inMempool); strHTML += "<br>"; strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>"; diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h index cf955a433c..803e41b699 100644 --- a/src/qt/transactiondesc.h +++ b/src/qt/transactiondesc.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_QT_TRANSACTIONDESC_H #define BITCOIN_QT_TRANSACTIONDESC_H +#include <qt/bitcoinunits.h> + #include <QObject> #include <QString> @@ -24,12 +26,12 @@ class TransactionDesc: public QObject Q_OBJECT public: - static QString toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit); + static QString toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord* rec, BitcoinUnit unit); private: TransactionDesc() {} - static QString FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks); + static QString FormatTxStatus(const interfaces::WalletTxStatus& status, bool inMempool); }; #endif // BITCOIN_QT_TRANSACTIONDESC_H diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index dd34656d5f..d8748d7dc9 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -20,13 +20,7 @@ struct WalletTxStatus; /** UI model for transaction status. The transaction status is the part of a transaction that will change over time. */ -class TransactionStatus -{ -public: - TransactionStatus() : countsForBalance(false), sortKey(""), - matures_in(0), status(Unconfirmed), depth(0), open_for(0) - { } - +struct TransactionStatus { enum Status { Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/ /// Normal (sent/received) transactions @@ -40,28 +34,25 @@ public: }; /// Transaction counts towards available balance - bool countsForBalance; + bool countsForBalance{false}; /// Sorting key based on status std::string sortKey; /** @name Generated (mined) transactions @{*/ - int matures_in; + int matures_in{0}; /**@}*/ /** @name Reported status @{*/ - Status status; - qint64 depth; - qint64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number - of additional blocks that need to be mined before - finalization */ + Status status{Unconfirmed}; + qint64 depth{0}; /**@}*/ /** Current block hash (to know whether cached status is still valid) */ uint256 m_cur_block_hash{}; - bool needsUpdate; + bool needsUpdate{false}; }; /** UI model for a transaction. A core transaction can be represented by multiple UI transactions if it has diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 6b0495f5a8..7b932890cf 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -5,6 +5,7 @@ #include <qt/transactiontablemodel.h> #include <qt/addresstablemodel.h> +#include <qt/bitcoinunits.h> #include <qt/clientmodel.h> #include <qt/guiconstants.h> #include <qt/guiutil.h> @@ -232,7 +233,7 @@ public: return nullptr; } - QString describe(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit) + QString describe(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord* rec, BitcoinUnit unit) { return TransactionDesc::toHTML(node, wallet, rec, unit); } diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 2f9d344bc8..301084ffa9 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_WALLETVIEW_H #include <consensus/amount.h> +#include <qt/bitcoinunits.h> #include <QStackedWidget> @@ -115,7 +116,7 @@ Q_SIGNALS: /** Encryption status of wallet changed */ void encryptionStatusChanged(); /** Notify that a new transaction appeared */ - void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); + void incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); /** Notify that the out of sync warning icon has been pressed */ void outOfSyncWarningClicked(); }; diff --git a/src/random.cpp b/src/random.cpp index b862510524..77d2ae4d43 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -13,7 +13,7 @@ #include <compat.h> // for Windows API #include <wincrypt.h> #endif -#include <logging.h> // for LogPrintf() +#include <logging.h> #include <randomenv.h> #include <support/allocators/secure.h> #include <sync.h> // for Mutex diff --git a/src/sync.h b/src/sync.h index af7595e6fa..c69b58741b 100644 --- a/src/sync.h +++ b/src/sync.h @@ -223,12 +223,12 @@ public: friend class reverse_lock; }; -#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__) +#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, #g, __FILE__, __LINE__) template<typename MutexArg> using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>; -#define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) +#define LOCK(cs) DebugLock<decltype(cs)> UNIQUE_NAME(criticalblock)(cs, #cs, __FILE__, __LINE__) #define LOCK2(cs1, cs2) \ DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \ DebugLock<decltype(cs2)> criticalblock2(cs2, #cs2, __FILE__, __LINE__); diff --git a/src/test/data/key_io_invalid.json b/src/test/data/key_io_invalid.json index abe07dad24..8f55abfec7 100644 --- a/src/test/data/key_io_invalid.json +++ b/src/test/data/key_io_invalid.json @@ -6,207 +6,207 @@ "x" ], [ - "2v7k5Bb8Lr1MMgTgW6HAf5YHXi6BzpPjHpQ4srD4RSwHYpzXKiXmLAgiLhkXvp3JF5v7nq45EWr" + "1GAdfviErV2Ew95FPtZyikz2qGP3gyCB6Hyu94sedAkPpA523m3fQwps9YKUZkKgQckGPKhRsFR" ], [ - "RAZzCGtMbiUgMiiyrZySrSpdfnQReFXA3r" + "37G2kMDLpmWVhimxRdzwNfE8JFvWXnJYnVcXeeGrek2qumdJuK7XArcVVpRtLLjRra3t64BEPF2" ], [ - "NYamy7tcPQTzoU5iyQojD3sqhiz7zxkvn8" + "giymtio7u7oqWtmC9YnvAEKkLF3JQpAdkEFkVJKYrVDfaLbhaDpX1ihfF2vZmya1i61fwLPC3YQ" ], [ - "geaFG555Ex5nyRf7JjW6Pj2GwZA8KYxtJJLbr1eZhVW75STbYBZeRszy3wg4pkKdF4ez9J4wQiz" + "8iVk9nLM3nYwRuwypjy9NK5rsuZH7BbrQRZ1pgcQmvMnjAgRXD" ], [ - "2Cxmid3c2XQ2zvQ8SA1ha2TKqvqbJS9XFmXRsCneBS3Po7Qqb65z5zNdsoF9AfieXFcpoVPmkmfa" + "cPTVQ1hbo4qdoysf6Jx5GthqucNmdfqt6J2pZRFeXv8Ep7Kmjqud" ], [ - "gaJ7UVge2njVg9tFTetJrtHgruMm7aQDiSAxfHrVEgzK8N2ooagDVmDkdph434xzc4K96Gjyxcs" + "cQbR2Ny85XFBzUMx3Ed6HsTLw2pVruSgPvt5AofnBUnhiv86gYeW" ], [ - "5JN5BEVQPZ3tAiatz1RGXkrJuE3EC6bervMaPb38wTNgEuZCeqp" + "2UB3iG3VJbX2TRrMwm6ssWskgvU9VjFBYSqCzwqkrihCwo7mg4mtS4WuGZgxTKuxf5A3EcotYEymz" ], [ - "3TnFbyUtBRS5rE1KTW81qLVspjJNaB3uu6uuvLjxhZo2DB6PCGh" + "cQe12pqwPR6ExtZKfrKf1q4b3CTh1Qi7MwuvMvzs79nWXDvESfBJ" ], [ - "7UgSZGaMaTc4d2mdEgcGBFiMeS6eMsithGUqvBsKTQdGzD7XQDbMEYo3gojdbXEPbUdFF3CQoK72f" + "tc1qeul5g2xfkvdkrhcfmdursv73ad64jnkjl9c40f" ], [ - "9261wfqQqruNDnBDhbbb4tN9oKA1KpRFHeoYeufyJApVGixyAG4V" + "bt1pq65rzej5glw3ra79gav6fqnx4haa0z257qr3mc8cggkefahmgvyseufhc0" ], [ - "cS824CTUh18scFmYuqt6BgxuRhdR4dEEnCHs3fzBbcyQgbfasHbw" + "tb13hty4qmumlwpp6chxjvcyzza4duqgtmxw3xhm3u9ahj4nyhtwz8eq7ynrj4" ], [ - "tc1q0ywf7wkz6t580n3yemd3ucfw8jxn93tpc6wskt" + "bcrt1r2qxpwuge" ], [ - "bt1pxeeuh96wpm5c6u3kavts2qgwlv6y8um7u7ga6ltlwrhrv7w9vers8lgt3k" + "bc10uexgzna2dpfk0vjt35srz6a27ps6m0l89jweznt83n2sqn2fx4hvn9ym5af8wut34sfrqhk3" ], [ - "tb130lvl2lyugsk2tf3zhwcjjv39dmwt2tt7ytqaexy8edwcuwks6p5scll5kz" + "tb1qum6uh0pt4q253qaf520929737v63w5gf" ], [ - "bcrt1rhsveeudk" + "bcrt1q888ryfgxpvl0k7vum8zpyar2u2sexvdhkf38ue37yknmqq0ycrwpl3w48y" ], [ - "bc10rmfwl8nxdweeyc4sf89t0tn9fv9w6qpyzsnl2r4k48vjqh03qas9asdje0rlr0phru0wqw0p" + "bc1qdsuzmn04k2z8vryw8l4dj8m5ygqgnne5n" ], [ - "tb1qjqnfsuatr54e957xzg9sqk7yqcry9lns" + "tb1qlj8es50nc8j8r8xshrjgzmw5azx89efghmw8ju6zcqla0g6xcnrstsjz7k" ], [ - "bcrt1q8p08mv8echkf3es027u4cdswxlylm3th76ls8v6y4zy4vwsavngpr4e4td" + "bcrt1qzwmyj0z924g7fzs5yvnrkc43y76RVyr2lh5t4r" ], [ - "BC1QNC2H66VLWTWTW52DP0FYUSNU3QQG5VT4V" + "bc1qpu6d26mrulzetu4jqhd7rsunv9aqru26f5c4j8" ], [ - "tb1qgk665m2auw09rc7pqyf7aulcuhmatz9xqtr5mxew7zuysacaascqs9v0vn" + "tb1qun6d26ufh77ghny6u5u8cwz9da7qwc6k4wkuceae9tth06eqlw0syupl4w" ], [ - "bcrt17CAPP7" + "bcrt1qj7g2jps453kj9htk9cxyyc2nxe69x4kzzmth7v" ], [ - "bc1qxmf2d6aerjzam3rur0zufqxqnyqfts5u302s7x" + "bc1p702xksx4z3uqf0u2phllxkfe5cgu0adxptqs0uelx0tqt8e885sqryes2l" ], [ - "tb1qn8x5dnzpexq7nnvrvnhwr9c3wkakpcyu9wwsjzq9pstkwg0t6qhs4l3rv6" + "tb1z7gmh0v6pc30z4xum76lmw8w86yswrlmw" ], [ - "BCRT1Q397G2RNVYRL5LK07CE8NCKHVKP8Z4SC9U0MVH9" + "bcrt1sjsrw6nun4h502cr97xmnyyuhkr22q0s6efrgtu" ], [ - "bc1pgxwyajq0gdn389f69uwn2fw9q0z5c9s063j5dgkdd23ajaud4hpsercr9h" + "2UVPFpGYnLHJezFzjUo42our6PMEoozzRdM" ], [ - "tb1z6mnmp5k542l6yk4ul0mp4rq3yvz44lfm" + "2MygHQjE1U33q3LSC53p69YqFjP8PihumJAF" ], [ - "bcrt17capp7" + "KzNbAQ4mexfAxa6RKBzHQqfoTycaeWpv2p" ], [ - "2D2bqvKseKHdoKjCNvjVULUgmxHu9hjKGwDbPRjTRH59tsHNLeyKwq3vyVBbo9LByY9wiapqjwFY" + "2jDPrDfAKihCGPbPD9ztY8TswAia4V8Bc6vx" ], [ - "2SSjAim4wZpeQRe5zTj1qqS6Li9ttJDaZ3ze" + "4VQUNG1hG64QFtaNyQZQWDdwpxB275Pwb3tvyPt2HDxB8Mi2MgH8Tz3AC83YYiz9LydsLNXEZJLHY" ], [ - "mi9H6MjLwXxy9kxe1x4ToxyLRBsmcZxgVi" + "39TKsUQ5QpEL1wowc6GMUqak94ijirPuP69ooV3xsFmiKQX2dau" ], [ - "VciXoxEitcn88jy197J9n9cpJ1pZahzU3SyWUiHqLgcfjttLEEJz" + "2UEJjT3dSdwc8dAo7oedPzznXceXCEsBbDfAvSymqpqDrkZMv7JBEUpLyhkghioYAWC9W4sKysry" ], [ - "KppmwADGoExPT9Eq5hjRWpWFDbzJyfzHFgsfxBiDHNpVBgWPRNuy" + "7VmMEkphxCFSV1y659Th4dkk6x6bJS5eQvbt8rzUYKQyd6ACgwQ4vXHtXKFUwP2kW3XULipnHJdZ7" ], [ - "TN7EQXMxKffzvHo54yHHu9R4ks9f5gWBW3MMVf5k72zAqrgVK9ys" + "tc1qdlapns4zkn03juf2k9xwwpct209suj6mgcd9gh" ], [ - "92dbrMEYzP5dD5UhQ6maNkCQ4GLG42BM4Gc6XKZzSSMSfosfkkcB" + "bt1psa5eptk29c4jc9yumeseat3a0l5e2fpmw635za2p4gpwdnthueysxga9je" ], [ - "J7VQxPxyzuWEkRstQWpCz2AgysEz1APgnWCEQrFvkN3umAnCrhQF" + "tb13w8c43lykfj3lvm9sgp6dsnfjla3d57cm83seykunf0ltxjc9lt2q4efm4d" ], [ - "tc1qymllj6c96v5qj2504y27ldtner6eh8ldx38t83" + "bcrt1rjqr2tdkm" ], [ - "bt1flep4g" + "bc10lyxwnxa70l270e6fcmxr4x7dtgu2yvy7gzkurwxy4zhdvgaqrrn6pfg2flyhqzy5t5se8yu3" ], [ - "tb13c553hwygcgj48qwmr9f8q0hgdcfklyaye5sxzcpcjnmxv4z506xs90tchn" + "TB1QFDFM763VXVSUNZHQLPWC0Q8FG5LJX6ZN" ], [ - "bcrt1tyddyu" + "bcrt1q60chha7wfwlau4kdr4mlvyeyc8mnnh9dhxk05e0hmrxcuhghefj36uwyha" ], [ - "bc10qssq2mknjqf0glwe2f3587wc4jysvs3f8s6chysae6hcl6fxzdm4wxyyscrl5k9f5qmnf05a" + "bc1gmk9yu" ], [ - "tb1q425lmgvxdgtyl2m6xuu2pc354y4fvgg8" + "tb1ly0q7p" ], [ - "bcrt1q9wp8e5d2u3u4g0pll0cy7smeeuqezdun9xl439n3p2gg4fvgfvk3hu52hj" + "bcrt1qdwttaw38uf42wxw40kwk3u8nguyTQH3hx6jmqp" ], [ - "bc1qrz5acazpue8vl4zsaxn8fxtmeuqmyjkq3" + "bc1qtsvlht6730n04f2mpaj5vv8hrledn5n5ug8c79" ], [ - "tb1qkeuglpgmnex9tv3fr7htzfrh3rwrk23r52rx9halxzmv9fr85lwq0fwhmp" + "tb1dclvmr" ], [ - "bcrt1qd0t2wrhl7s57z99rsyaekpq0dyjcQRSSmz80r4" + "bcrt1q3fqvctqu48wsvggrt09vj0yk2gzzcscdp4h98u" ], [ - "BC1QXLFDUCGX90T3E53PQCNKJ2PK25MSF3VLPMVY6T" + "bc1prklpq7tjcawg89cmwwqr3u5apwav36xa4zz56ady7crsllm6mpnqts7p86" ], [ - "tb1qmycg4zszgnk34vaurx3cu8wpvteg9h40yq6cp52gt26gjel03t3su3x3xu" + "tb1zkm58zyhxz3ffkfgsyprflg543slsl4c4" ], [ - "bcrt1q9hy58r4fnuxqzdqndpmq9pptc9nt2dw3rczf5e" + "bcrt1snzr5kaypnfhpnjanrhd20fhqcjxm3hfh7dw9fu" ], [ - "BC1PA7682NAY6JQSLUWAJYTC0ERWTMW7A4RPWLNTUS32LCXWLHVKKKTQ2UL8CG" + "2GgnYKqBGuA2Mm5GnrPsMTZR81xPhNtgMYoFUZngZGiobhCuUpCaTriUHRcgFreEekNdPAR17q8d" ], [ - "tb1z850dpxnwz2fzae5h2myatj4yvu6rq5xq" + "AZEah8d1EK362okRBS66e8SvdtYkrE8tsX" ], [ - "bcrt1sp525pzjsmpqvcrawjreww36e9keg876skjvpwt" + "gep8xr77FyPW6zYP15RiV9W8nL6w2HyHB16cUDakfyDceMA6ZzUdhJjk2LPuLYHnLkBqkRTTi6z" ], [ - "xcAvW5jurCpzSpLxBKEhCewCgwwuGhqJnC" + "2NDNP7GY59tTJPZTpbkprhM9SR99Nn5rUs7" ], [ - "2Cvv8yp9kXbQt8EKh6Yma95yJ1uwYF9YKXuVhGJyu3dHGVsb2AVpTC62TFACZZ3KDNrALxR2CVNs" + "2Csgzy2T287YAjeU5tFtt1nPshBZAUFQi4WtgaWyZGKSBNnKXHy2Tmxo8QK4Mfdds977ShcDWC5o" ], [ - "niUuL46hCuEVvkAzZKHvD746qbmLmzip9Pv3F6UZV14JxzEXBnTkVxCT4URapChJG6qAEgsZs6G" + "Kwjk3Vy6sdXMQDGWJzaWmqFxUNtWZCX1q4F4Kpt8jNNUoWJUUaTY" ], [ - "2UHHgGfiipzvB8Eumnmvq6SowvrMJimjT3NwwG1839XEiUfwtpSdkUrseNsQuagXv21ce7aZu6yo" + "Svj8kk98bAS9V4L2crmxakbhmnPm3cJ1tJ4Je4yVzDreU8eSTFURS1SPYv5oWEQD8Q9VBDvx5uF" ], [ - "8u9djKu4u6o3bsgeR4BKNnLK3akpo64FYzDAmA9239wKeshgF97" + "KNYsv6v9GtkGeD4WdQnBEJCrPKQm91PTxAbCfXr66LEd4JDmhPWC" ], [ - "TC1QPAARXSLVMXHVRR0474LZXQYZWLGFZYPSFVL9E4" + "2UJ2H2xvAeXmFKfQwMyDoSdQTTPFMNCT3SsoUafBWKzoGP3NsUK1buEgQZG38viyD53jgMdpqfT7" ], [ - "bt1pakek0n2267t9yaksxaczgr2syhv9y3xkx0wnsdwchfa6xkmjtvuqg3kgyr" + "6aLMfayKF4TW4ecn5SEc8FExpyJA2peKxYRGZhes6tQ4NTTzuGy" ], [ - "tb13h83rtwq62udrhwpn87uely7cyxcjrj0azz6a4r3n9s87x5uj98ys6ufp83" + "7VP4FmcebU2thJns9MnXde7LWfuqR5vMizrAuUoq2GcJjzTyA4RHFcPVdZL8PLg1SbpSFdJrvLXoY5" ], [ - "bcrt1rk5vw5qf2" + "tc1q5qdvt99uc92jyz663dtdpfpv6nr67ahmgwcpq2" ], [ - "bc10d3rmtg62h747en5j6fju5g5qyvsransrkty6ghh96pu647wumctejlsngh9pf26cysrys2x2" + "bt1peu3ppd7x796sjjenp09r8cs22rhylqm9lhggk72qp8q22vzft0wq2a0x6j" ], [ - "tb1qajuy2cdwqgmrzc7la85al5cwcq374tsp" + "tb1323z3lnz7dl3kd0nsuh6xy4he9almzl67anxgg3xdzkaxc9rwntlqdhdzd7" ], [ - "bcrt1q3udxvj6x20chqh723mn064mzz65yr56ef00xk8czvu3jnx04ydapzk02s5" + "bcrt1r2gc42sky" ], [ - "bc1qule2szwzyaq4qy0s3aa4mauucyqt6fewe" + "bc10fd889x4hd54tqu2ewg9t4hhft2wl7m6x50av4uswzw46xe6as0xmltfg7vrjfkvm459vld7w" ], [ - "tb1ql0qny5vg9gh5tyzke6dw36px5ulkrp24x53x0pl2t5lpwrtejw3s2seej2" + "TB1QZY7V0F2AT3308YGGNGN66ULJTCN3RY6F" ], [ - "bcrt17CAPP7" + "bcrt1qjg3cwht92znyw0l4r5rtctmls337nrc7g0ry9drjxmlecjd3atl3fake7c" ], [ - "bc1qtvm6davyf725wfedc2d5mrgfewqgcrce8gjrpl" + "bc1qmgf8xt8xkecl79k04mma3lz34gqep7hg4" ], [ - "tb1q5acjgtqrrw3an0dzavxxxzlex8k7aukjzjk9v2u4rmfdqxjphcyq7ge97e" + "TB1Q3F9WGNXE9ZMTTMDN5VKVKHYZ8Y0LCV72YV7V5LSXTJXEYHNHEHASLYL0TZ" ] ] diff --git a/src/test/data/key_io_valid.json b/src/test/data/key_io_valid.json index 5dee44c04b..c051f8b76b 100644 --- a/src/test/data/key_io_valid.json +++ b/src/test/data/key_io_valid.json @@ -1,71 +1,71 @@ [ [ - "1BShJZ8A5q53oJJfMJoEF1gfZCWdZqZwwD", - "76a914728d4cc27d19707b0197cfcd7c412d43287864b588ac", + "1FsSia9rv4NeEwvJ2GvXrX7LyxYspbN2mo", + "76a914a31c06bd463e3923bc1aadbde48b16976c08071788ac", { "chain": "main", "isPrivkey": false } ], [ - "3L1YkZjdeNSqaZcNKZFXQfyokx3zVYm7r6", - "a914c8f37c3cc21561296ad81f4bec6b5de10ebc185187", + "36j4NfKv6Akva9amjWrLG6MuSQym1GuEmm", + "a914373b819a068f32b7a6b38b6b38729647cfde01c287", { "chain": "main", "isPrivkey": false } ], [ - "mhJuoGLgnJC8gdBgBzEigsoyG4omQXejPT", - "76a91413a92d1998e081354d36c13ce0c9dc04b865d40a88ac", + "mzK2FFDEhxqHcmrJw1ysqFkVyhUULo45hZ", + "76a914ce28b26c57472737f5c3561a1761185bd8589a4388ac", { "chain": "test", "isPrivkey": false } ], [ - "2N5VpzKEuYvZJbmg6eUNGnfrrD1ir92FWGu", - "a91486648cc2faaf05660e72c04c7a837bcc3e986f1787", + "2NC2hEhe28ULKAJkW5MjZ3jtTMJdvXmByvK", + "a914ce0bba75891ff9ec60148d4bd4a09ee2dc5c933187", { "chain": "test", "isPrivkey": false } ], [ - "mtQueCtmAnP3E4aBHXCiFNEQAuPaLMuQNy", - "76a9148d74ecd86c845baf9c6d4484d2d00e731b79e34788ac", + "mww4LvqtTMKvmeQvizPz2EQv26xTneWrbg", + "76a914b4110ba93ac54afc14da3bdd19614774a2d55d2988ac", { "chain": "signet", "isPrivkey": false } ], [ - "2NEvWRTHjh89gV52fkperFtwzoFWQiQmiCh", - "a914edc895152c67ccff0ba620bcc373b789ec68266f87", + "2N1r7aC69VHeE7yQJPDLi9T1PYq4wnwvjuT", + "a9145e5a35ab44b3efaea5129ba22b88ba3e2976614587", { "chain": "signet", "isPrivkey": false } ], [ - "mngdx94qJFhSf7A7SAEgQSC9fQJuapujJp", - "76a9144e9dba545455a80ce94c343d1cac9dec62cbf22288ac", + "n4fajahJrAuKbN7uNsKjLjQkz9Qn5ewJXQ", + "76a914fdeca3b08e38af53d7c4c60e3ad208ce5066441088ac", { "chain": "regtest", "isPrivkey": false } ], [ - "2NBzRN3pV56k3JUvSHifaHyzjGHv7ZS9FZZ", - "a914cd9da5642451273e5b6d088854cc1fad4a8d442187", + "2MxFajLApXpYk4VodBSZSt7rw8y4ryABkfA", + "a91436e9f191e0b75036a77f65e2eaa4752443233fbe87", { "chain": "regtest", "isPrivkey": false } ], [ - "5KcrFZvJ2p4dM6QVUPu53cKXcCfozA1PJLHm1mNAxkDYhgThLu4", - "ed6c796e2f62377410766214f55aa81ac9a6590ad7ed57c509c983bf648409ac", + "5JuW2AMDYu4xVwRG9DZW18VbzQrGcd5RCgb99sS6ehJsNQXu5b9", + "8f8943bf956de595665c38ffff23827e17c10cdc1c27a028caae6c9810626198", { "chain": "main", "isCompressed": false, @@ -73,8 +73,8 @@ } ], [ - "L195WBrf2G3nCnun4CLxrb8XKk9LbCqH43THh4n4QrL5SzRzpq9j", - "74f76c106e38d20514a99a86e4fe3bb28319e7dd2ad21dbc170cbb516a5358fa", + "L5nJeqKmpHp4P7F8ZYyjwc5a7P4d8EabuGAzfGJk7yC1BJyzNaEd", + "ff778740f88ddcf102aeb81daee289c044c4a4571c4b6f287400f4b8e0b843f8", { "chain": "main", "isCompressed": true, @@ -82,8 +82,8 @@ } ], [ - "92z6HnMQR4tWqjfVA3UaUN5EuUMgoVMdCa5rZFYZfmgyD7wxYCw", - "b8511e1d74549e305517d48a1d394d1be2cfa5d0f3c0d83f9f450316ffa01276", + "92ZdE5HoLafywnTBbzPxbvRmp75pSfzvdU3XaZGh1cToipgdHVh", + "80c32d81e91bdea04cd7a3819b32275fc3298af4c7ec87eb0099527d041ced5c", { "chain": "test", "isCompressed": false, @@ -91,8 +91,8 @@ } ], [ - "cTPnaF52x4w4Tq6afPxRHux3wbYb86thS7S45A7r3oZc1AHTQ6Qm", - "ad68c48d337181da125de9061933ececcdf7d917631af7d34f7e38082bff9a11", + "cV83kKisF3RQSvXbUCm9ox3kaz5JjEUBWcx8tNydfGJcyeUxuH47", + "e0fcd4ce4e3d0e3de091f21415bb7cd011fac288c42020a879f28c2a4387df9b", { "chain": "test", "isCompressed": true, @@ -100,8 +100,8 @@ } ], [ - "924U35yFcYkxe2JXGmuhSRVaShGyhRDZx1ysPmw1sAHuszGMoxq", - "3e8dfaf78d4f02b11d0b645648a4f3080d71d0d068979c47f7255c9a29eee01d", + "92QuSnywrhsV7WPZChTgSQA23uSmj9MCEEno1eRBDG9sg8M29cX", + "6cf636ed8ac1bab033b64f66feaba65f70e684731e3f39105605968d3a963801", { "chain": "signet", "isCompressed": false, @@ -109,8 +109,8 @@ } ], [ - "cRy1qCf2LUesGPQagTkYwk2V3PyN2KCPKgxeg6k6KoJPzH7nrVjw", - "82d4187690d6b59bcffda27dae52f2ecb87313cfc0904e0b674a27d906a65fde", + "cND53Dhp8eCZqG2ghe8YhSCGesXZ8fE5PGD1khrqNvEi4RBoXhEK", + "12b5a10f3a11e708dc5412833c47ab7c368a21b9efe19293793ec879ce683018", { "chain": "signet", "isCompressed": true, @@ -118,8 +118,8 @@ } ], [ - "932NTcHK35Apf2C3K9Zv1ZdeZEmB1x7ZT2Ju3SjoEY6pUgUpT7H", - "bd7dba24df9e003e145ae9b4862776413a0bb6fa5b4e42753397f2d9536e58a9", + "91mn1wYKEB1zyof1VFm8tMtocZx1oBrKKRCu9GCpgZvPmBLEJjp", + "18a86e5a6c6977ddba0daca7fba5190f67ba56ccdc1b3f31308972236c2e4776", { "chain": "regtest", "isCompressed": false, @@ -127,8 +127,8 @@ } ], [ - "cNa75orYQ2oos52zCnMaS5PG6XbNZKc5LpGxTHacrxwWeX4WAK3E", - "1d87e3c58b08766fea03598380ec8d59f8c88d5392bf683ab1088bd4caf073ee", + "cPisAUdLvqqAr6MYtXnrWvgvyUAwuNyuTvZkDGw6miPhZdaiSDNH", + "3fdfec1371cedcdb8c190ca6ff8ad603f817edc0d93c2a687c7b36dd66e70f2a", { "chain": "regtest", "isCompressed": true, @@ -136,8 +136,8 @@ } ], [ - "bc1q5cuatynjmk4szh40mmunszfzh7zrc5xm9w8ccy", - "0014a639d59272ddab015eafdef9380922bf843c50db", + "bc1qvyq0cc6rahyvsazfdje0twl7ez82ndmuac2lhv", + "00146100fc6343edc8c874496cb2f5bbfec88ea9b77c", { "chain": "main", "isPrivkey": false, @@ -145,8 +145,8 @@ } ], [ - "bc1qkw7lz3ahms6e0ajv27mzh7g62tchjpmve4afc29u7w49tddydy2syv0087", - "0020b3bdf147b7dc3597f64c57b62bf91a52f179076ccd7a9c28bcf3aa55b5a46915", + "bc1qyucykdlhp62tezs0hagqury402qwhk589q80tqs5myh3rxq34nwqhkdhv7", + "002027304b37f70e94bc8a0fbf500e0c957a80ebda87280ef58214d92f119811acdc", { "chain": "main", "isPrivkey": false, @@ -154,8 +154,8 @@ } ], [ - "bc1p5rgvqejqh9dh37t9g94dd9cm8vtqns7dndgj423egwggsggcdzmsspvr7j", - "5120a0d0c06640b95b78f965416ad6971b3b1609c3cd9b512aaa39439088211868b7", + "bc1p83n3au0rjylefxq2nc2xh2y4jzz4pm6zxj4mw5pagdjjr2a9f36s6jjnnu", + "51203c671ef1e3913f94980a9e146ba895908550ef4234abb7503d436521aba54c75", { "chain": "main", "isPrivkey": false, @@ -163,8 +163,8 @@ } ], [ - "bc1zr4pq63udck", - "52021d42", + "bc1z2rksukkjr8", + "520250ed", { "chain": "main", "isPrivkey": false, @@ -172,8 +172,8 @@ } ], [ - "tb1q74fxwnvhsue0l8wremgq66xzvn48jlc5zthsvz", - "0014f552674d978732ff9dc3ced00d68c264ea797f14", + "tb1qcrh3yqn4nlleplcez2yndq2ry8h9ncg3qh7n54", + "0014c0ef1202759fff90ff19128936814321ee59e111", { "chain": "test", "isPrivkey": false, @@ -181,8 +181,8 @@ } ], [ - "tb1qpt7cqgq8ukv92dcraun9c3n0s3aswrt62vtv8nqmkfpa2tjfghesv9ln74", - "00200afd802007e598553703ef265c466f847b070d7a5316c3cc1bb243d52e4945f3", + "tb1quyl9ujpgwr2chdzdnnalen48sup245vdfnh2jxhsuq3yx80rrwlq5hqfe4", + "0020e13e5e482870d58bb44d9cfbfccea78702aad18d4ceea91af0e022431de31bbe", { "chain": "test", "isPrivkey": false, @@ -190,8 +190,8 @@ } ], [ - "tb1ph9v3e8nxct57hknlkhkz75p5pnxnkn05cw8ewpxu6tek56g29xgqydzfu7", - "5120b9591c9e66c2e9ebda7fb5ec2f50340ccd3b4df4c38f9704dcd2f36a690a2990", + "tb1p35n52jy6xkm4wd905tdy8qtagrn73kqdz73xe4zxpvq9t3fp50aqk3s6gz", + "51208d2745489a35b75734afa2da43817d40e7e8d80d17a26cd4460b0055c521a3fa", { "chain": "test", "isPrivkey": false, @@ -199,8 +199,8 @@ } ], [ - "tb1ray6e8gxfx49ers6c4c70l3c8lsxtcmlx", - "5310e93593a0c9354b91c358ae3cffc707fc", + "tb1rgv5m6uvdk3kc7qsuz0c79v88ycr5w4wa", + "53104329bd718db46d8f021c13f1e2b0e726", { "chain": "test", "isPrivkey": false, @@ -208,8 +208,8 @@ } ], [ - "tb1q0sqzfp3zj42u0perxr6jahhu4y03uw4dypk6sc", - "00147c002486229555c7872330f52edefca91f1e3aad", + "tb1q3vya2h5435jkugq2few7dmktlrwq4ejmfaw7kr", + "00148b09d55e958d256e200a4e5de6eecbf8dc0ae65b", { "chain": "signet", "isPrivkey": false, @@ -217,8 +217,8 @@ } ], [ - "tb1q9jv4qnawnuevqaeadn47gkq05ev78m4qg3zqejykdr9u0cm7yutq6gu5dj", - "00202c99504fae9f32c0773d6cebe4580fa659e3eea044440cc89668cbc7e37e2716", + "tb1qxkhrl2s6ttrclckldruea0e8anhrehffl8xv7t0pdyrzm08v2hyqy408nf", + "002035ae3faa1a5ac78fe2df68f99ebf27ecee3cdd29f9cccf2de169062dbcec55c8", { "chain": "signet", "isPrivkey": false, @@ -226,8 +226,8 @@ } ], [ - "tb1pxqf7d825wjtcftj7uep8w24jq3tz8vudfaqj20rns8ahqya56gcs92eqtu", - "51203013e69d54749784ae5ee642772ab2045623b38d4f41253c7381fb7013b4d231", + "tb1pae5um27ahn8n73pgexe3kcwlp8dhswpn684h2k2w6t9a7w3eq65qephd5y", + "5120ee69cdabddbccf3f4428c9b31b61df09db783833d1eb75594ed2cbdf3a3906a8", { "chain": "signet", "isPrivkey": false, @@ -235,8 +235,8 @@ } ], [ - "tb1rsrzkyvu2rt0dcgexajtazlw5nft4j7494ay396q6auw9375wxsrsgag884", - "532080c562338a1adedc2326ec97d17dd49a57597aa5af4912e81aef1c58fa8e3407", + "tb1rx9n9g37az8mu236e5jpxdt0m67y4fuq8rhs0ss3djnm0kscfrwvq0ntlyg", + "532031665447dd11f7c54759a48266adfbd78954f0071de0f8422d94f6fb43091b98", { "chain": "signet", "isPrivkey": false, @@ -244,8 +244,8 @@ } ], [ - "bcrt1qwf52dt9y2sv0f7fwkcpmtfjf74d4np2saeljt6", - "00147268a6aca45418f4f92eb603b5a649f55b598550", + "bcrt1qdavt4j2sd7dlhqsavtnfxvzppw6k7qy97tmnu9", + "00146f58bac9506f9bfb821d62e69330410bb56f0085", { "chain": "regtest", "isPrivkey": false, @@ -253,8 +253,8 @@ } ], [ - "bcrt1q0lma84unycxl4n96etffthqlf7y5axyp4fxf64kmhymvw8l6pwfs39futd", - "00207ff7d3d793260dfaccbacad295dc1f4f894e9881aa4c9d56dbb936c71ffa0b93", + "bcrt1qan8gntac7z7me2ejt4hpru42ad2f759fmy0m3ejvs98656znv7eqga4uhv", + "0020ecce89afb8f0bdbcab325d6e11f2aaeb549f50a9d91fb8e64c814faa685367b2", { "chain": "regtest", "isPrivkey": false, @@ -262,8 +262,8 @@ } ], [ - "bcrt1p3xat2ryucc2v0adrktqnavfzttvezrr27ngltsa2726p2ehvxz4se722v2", - "512089bab50c9cc614c7f5a3b2c13eb1225ad9910c6af4d1f5c3aaf2b41566ec30ab", + "bcrt1pfwxjqvtt4tcxrtdluukfmy2dv7xd2qzdfy6kajv5nwn4yam3wxkq3553uh", + "51204b8d20316baaf061adbfe72c9d914d678cd5004d49356ec9949ba752777171ac", { "chain": "regtest", "isPrivkey": false, @@ -271,8 +271,8 @@ } ], [ - "bcrt1saflydw6e26xhp29euhy5jke5jjqyywk3wvtc9ulgw9dvxyuqy9hdnxthyw755c7ldavy7u", - "6028ea7e46bb59568d70a8b9e5c9495b349480423ad1731782f3e8715ac31380216ed9997723bd4a63df", + "bcrt1sx6p8njlx7h9mc2agz4yg82dzne23050ncq72cneeecez2pst8mahn8xecsf8g6hzx94420", + "6028368279cbe6f5cbbc2ba8154883a9a29e5517d1f3c03cac4f39ce3225060b3efb799cd9c412746ae2", { "chain": "regtest", "isPrivkey": false, @@ -280,72 +280,72 @@ } ], [ - "16y3Q1XVRZqMR9T1XL1FkvNtD2E1bXBuYa", - "76a9144171ec673aeb9fcf42af6094a3c82207e3b9a78188ac", + "1FjL87pn8ky6Vbavd1ZHeChRXtoxwRGCRd", + "76a914a19331b7b2627e663e25a7b001e4c0dcc5e21bc788ac", { "chain": "main", "isPrivkey": false } ], [ - "3CmZZnAiHVQgiAKSakf864oJMxN2BP1eLC", - "a914798575fc1041b9440c4e63c28e57e597d00b7e4387", + "3BZECeAH8gSKkjrTx8PwMrNQBLG18yHpvf", + "a9146c382dcdf5b284760c8e3fead91f7422cd76aa8787", { "chain": "main", "isPrivkey": false } ], [ - "mtCB3SoBo7EYUv8j54kUubGY4x3aJPY8nk", - "76a9148b0c5f9ee714e0d1d24642ad63d9d5f398d9b56588ac", + "n4YNbYuFdPwFrxSP8sjHFbAhUbLMUiY9jE", + "76a914fc8f9851f3c1e4719cd0b8e4816dd4e88c72e52888ac", { "chain": "test", "isPrivkey": false } ], [ - "2N5ymzzKpx6EdUR4UdMZ7t9hcuwqtpHwgw5", - "a9148badb3c3b5c0d39f906f7618e0018b7eae4baf7387", + "2NAeQVZayzVFAtgeC3iYJsjpjWDmsDph71A", + "a914bedc797342c03fd7a346c4c7857ca03d467013b687", { "chain": "test", "isPrivkey": false } ], [ - "myXnpYbub28zgiJupDdZSWZtDbjcyfJVby", - "76a914c59ac57661b57daadd7c0caf7318c14f54c6c0fa88ac", + "mnCBpkNMJEJLehgdEkzSo2eioniyJMxLpZ", + "76a914493c455551e48a1423263b62b127b436106a685488ac", { "chain": "signet", "isPrivkey": false } ], [ - "2MtLg8jS5jSXm9evMzTtvpLjy26dBmjFEoT", - "a9140c0007e89cea625d3bf9543baa5a470bb7e5b67287", + "2N5sNHomeNJDZv67AcFx9ES7FBZY4jx9KDA", + "a9148a776a0f34d56b63e7c595f2b205dbe1c393617a87", { "chain": "signet", "isPrivkey": false } ], [ - "mzCyqdf2UNGdpgkD9NBgLcxdwXRg1i9buY", - "76a914cd04311bdd1ef9c5c24e41930e032aade82a863a88ac", + "mfhE6jAUwjUDNZhaX1PAsDTKfneQF2Nshc", + "76a91401f15a4cc063dae4f4d56b89bfbc8bcc9ae5387c88ac", { "chain": "regtest", "isPrivkey": false } ], [ - "2N3zGiwFku2vQjYnAqXv5Qu2ztfYRhh7tbF", - "a91475d56d75c88e704d6c72fbe84ac1505abf736b4087", + "2MxNm1VHyVU4RuP3u1c1v5aQLk2dQjwy1Qk", + "a91438456f7c076356abadcc67b92ad777eb20fb9f8887", { "chain": "regtest", "isPrivkey": false } ], [ - "5JUHCgyxNSHg64wwju72eNsG6ajqo4Z2fHHw9iLDLfh69rSiL7w", - "5644d06d88855dacf3192a31df8f4acd8e4c155c52a86d2c1fa48303f5cff053", + "5HsL2nZuEebU5nM3RxNVQD9GcAnvNMahqQskf4fkqHe54zwd14e", + "06e8649790a90615a46d22dd762e0c42615336745356c2e16147c0f3d46b40d5", { "chain": "main", "isCompressed": false, @@ -353,8 +353,8 @@ } ], [ - "L2kZaexG69VSriMe9T2m1jkS86iPe3xNbjcdfakRC1PHe7ay78Ji", - "a50ee94aefcabf5a5d7c85be5b3844dee03c5604861dbfc77fe388c91e5a30f8", + "KwuVvu6hsuEMHrfFWJQV64tRrWX3QzqHH18JuAHYqYV6dqBvNKxd", + "147804bf8a0dfff35939a611c7f5a60ac107f33f33d6059f273d2079ab1d90f2", { "chain": "main", "isCompressed": true, @@ -362,8 +362,8 @@ } ], [ - "927JwT1ViCr5TD2ZX8CsMNhg17dXmou5xu4y2KiH54zD7i34UJq", - "4502a54c0026b0150281d41f40860d1e23870c63cdc32645bbed688f2ee41f64", + "921M1RNxghFcsVGqAJksQVbSgx36Yz4u6vebfz1wDujNvgNt93B", + "3777b341c45e2a9b9bf6bfb71dc7d129f64f1b9406ed4f93ade8f56065f1b732", { "chain": "test", "isCompressed": false, @@ -371,8 +371,8 @@ } ], [ - "cTpGGNPVy2Eagawohbr4aGtRJzpLnjxGsGYh9DUcBM45f3KdKGF6", - "ba005a0cb39587aab00bd54c848b59e8adaed47403228567ddc739c2a344ff59", + "cNEnbfF2fcxmmCLWqMAaq6fxJvVkwMbyU3kCbpQznz4Z1j6TZDGb", + "1397b0d4a03e1ab2c54dd9af99ce1ecbfb90c80a58886da95e1181a55703d96b", { "chain": "test", "isCompressed": true, @@ -380,8 +380,8 @@ } ], [ - "932PLCLA19yPNqV67qwHBSGjxi82LVzWBF7josL9ab4Q1kxgPGF", - "bd8677e076eb39770bf7e9f9e8d3f2cf257effab9b4c220fd3439ccfc208c984", + "93BcpCMKPmFCuY8bqS4k3HFrhJ1Afxi4uSsEeJFvX86GYW7PC7W", + "d27d1b6ef55ca2e4d475b5276f2dbb85f7a6459dceeb89c67b776fd3bb974452", { "chain": "signet", "isCompressed": false, @@ -389,8 +389,8 @@ } ], [ - "cViUpEy8URSsLjUvxwL7cEuNgCVqM7oKBzd1ZPbA4khcQsQJuj1j", - "f2b36ade8393e29dc71e52cb75ba1109ba210203cd7d0a5ae881ad6846516203", + "cUtwbyxoL1owPxUafgH2meEpydeywjhnTYv2mJaFHHchz39AaEgy", + "da3ed4ef1647e1733ec076919cab6156077ed9532e7c365acc425747e198b3e1", { "chain": "signet", "isCompressed": true, @@ -398,8 +398,8 @@ } ], [ - "92jddDjJCVDmJtgvBHQ9i58PMash8kwsYhRdNo22ea2MYPXdCBE", - "977bf8686f1bcad28f86c4c14afbd33215746bd19203647bf7ff9c6fddc9cc87", + "927zPWny2SiNaUmHF5NnGQXQWDwbByfFzXGgu88j91ZoutSosvE", + "468e0284f230153db8687d8ec23db079a5b67d72ca04174b3867b13e4ea9945e", { "chain": "regtest", "isCompressed": false, @@ -407,8 +407,8 @@ } ], [ - "cVwAuMoUqRo399X7vXzuzQyPEvZJMXM8c82zHzRkFCxPCSGx8A6y", - "f93acbbce02b8cb9ddca3fad495441e324cc01eb640b0a7b4c9f0e31644c822a", + "cRez45VGSp5EXNqm89K3NJJPSKKapJg5Kbw3atxr2337x2gtgYed", + "798d87586cffbe8c545ab374454e403b1eb831501ebe89f3c3b02f3137bd7b46", { "chain": "regtest", "isCompressed": true, @@ -416,8 +416,8 @@ } ], [ - "bc1qz377zwe5awr68dnggengqx9vrjt05k98q3sw2n", - "0014147de13b34eb87a3b66846668018ac1c96fa58a7", + "bc1qhxt04s5xnpy0kxw4x99n5hpdf5pmtzpqs52es2", + "0014b996fac2869848fb19d5314b3a5c2d4d03b58820", { "chain": "main", "isPrivkey": false, @@ -425,8 +425,8 @@ } ], [ - "bc1qkmhskpdzg8kdkfywhu09kswwn9qan9vnkrf6mk40jvnr06s6sz5ssf82ya", - "0020b6ef0b05a241ecdb248ebf1e5b41ce9941d99593b0d3addaaf932637ea1a80a9", + "bc1qgc9ljrvdf2e0zg9rmmq86xklqwfys7r6wptjlacdgrcdc7sa6ggqu4rrxf", + "0020460bf90d8d4ab2f120a3dec07d1adf039248787a70572ff70d40f0dc7a1dd210", { "chain": "main", "isPrivkey": false, @@ -434,8 +434,8 @@ } ], [ - "bc1ps8cndas60cntk8x79sg9f5e5jz7x050z8agyugln2ukkks23rryqpejzkc", - "512081f136f61a7e26bb1cde2c1054d33490bc67d1e23f504e23f3572d6b415118c8", + "bc1pve739yap4uxjvfk0jrey69078u0gasm2nwvv483ec6zkzulgw9xqu4w9fd", + "5120667d1293a1af0d2626cf90f24d15fe3f1e8ec36a9b98ca9e39c6856173e8714c", { "chain": "main", "isPrivkey": false, @@ -443,8 +443,8 @@ } ], [ - "bc1zn4tsczge9l", - "52029d57", + "bc1zmjtqxkzs89", + "5202dc96", { "chain": "main", "isPrivkey": false, @@ -452,8 +452,8 @@ } ], [ - "tb1q6xw0wwd9n9d7ge87dryz4vm5vtahzhvz6yett3", - "0014d19cf739a5995be464fe68c82ab37462fb715d82", + "tb1ql4k5ayv7p7w0t0ge7tpntgpkgw53g2payxkszr", + "0014fd6d4e919e0f9cf5bd19f2c335a03643a914283d", { "chain": "test", "isPrivkey": false, @@ -461,8 +461,8 @@ } ], [ - "tb1qwn9zq9fu5uk35ykpgsc7rz4uawy4yh0r5m5er26768h5ur50su3qj6evun", - "002074ca20153ca72d1a12c14431e18abceb89525de3a6e991ab5ed1ef4e0e8f8722", + "tb1q9jx3x2qqdpempxrcfgyrkjd5fzeacaqj4ua7cs7fe2sfd2wdaueq5wn26y", + "00202c8d1328006873b098784a083b49b448b3dc7412af3bec43c9caa096a9cdef32", { "chain": "test", "isPrivkey": false, @@ -470,8 +470,8 @@ } ], [ - "tb1pmcdc5d8gr92rtemfsnhpeqanvs0nr82upn5dktxluz9n0qcv34lqxke0wq", - "5120de1b8a34e8195435e76984ee1c83b3641f319d5c0ce8db2cdfe08b37830c8d7e", + "tb1pdswckwd9ym5yf5eyzg8j4jjwnzla8y0tf9cp7aasfkek0u29sz9qfr00yf", + "51206c1d8b39a526e844d324120f2aca4e98bfd391eb49701f77b04db367f145808a", { "chain": "test", "isPrivkey": false, @@ -479,8 +479,8 @@ } ], [ - "tb1rgxjvtfzp0xczz6dlzqv8d5cmuykk4qkk", - "531041a4c5a44179b02169bf101876d31be1", + "tb1r0ecpfxg2udhtc556gqrpwwhk4sw3f0kc", + "53107e7014990ae36ebc529a4006173af6ac", { "chain": "test", "isPrivkey": false, @@ -488,8 +488,8 @@ } ], [ - "tb1qa9dlyt6fydestul4y4wh72yshh044w32np8etk", - "0014e95bf22f49237305f3f5255d7f2890bddf5aba2a", + "tb1q6mwf89hnqhlu8txjgjfs4s7p93ugffn3k062ll", + "0014d6dc9396f305ffc3acd244930ac3c12c7884a671", { "chain": "signet", "isPrivkey": false, @@ -497,8 +497,8 @@ } ], [ - "tb1qu4p26n0033720xm0rjgkds5ehdwf039k2fgv75um5krrvfhrrj7qckl9r2", - "0020e542ad4def8c7ca79b6f1c9166c299bb5c97c4b65250cf539ba5863626e31cbc", + "tb1qafrjalu4d73dql0czau9j6z422434kef235mzljf48ckd5xz3sys09jm97", + "0020ea472eff956fa2d07df8177859685552ab1adb295469b17e49a9f166d0c28c09", { "chain": "signet", "isPrivkey": false, @@ -506,8 +506,8 @@ } ], [ - "tb1pjyukm4n4flwd0ey3lrl06c9kalr60ggmlkcxq2rhhxmy4lvkmkpqexdzqy", - "512091396dd6754fdcd7e491f8fefd60b6efc7a7a11bfdb0602877b9b64afd96dd82", + "tb1pwst9qszjrhuv2e7as0flcq9gm698v6gdxzz9e87p07s8rssdx3zqklm3vf", + "512074165040521df8c567dd83d3fc00a8de8a76690d30845c9fc17fa071c20d3444", { "chain": "signet", "isPrivkey": false, @@ -515,8 +515,8 @@ } ], [ - "tb1r4k75s5syvewsvxufdc3xfhf4tw4u30alw39xny3dnxrl6hau7systymfdv", - "5320adbd485204665d061b896e2264dd355babc8bfbf744a69922d9987fd5fbcf409", + "tb1r3ss76jtsuxe8c8c8lxsehnpak55ylrgr345pww076l536ahjr6jsydamx3", + "53208c21ed4970e1b27c1f07f9a19bcc3db5284f8d038d681739fed7e91d76f21ea5", { "chain": "signet", "isPrivkey": false, @@ -524,8 +524,8 @@ } ], [ - "bcrt1qnk3tdwwj47ppc4pqmxkjdusegedn9ru5gvccwa", - "00149da2b6b9d2af821c5420d9ad26f219465b328f94", + "bcrt1q65nhlm4hf2ptg3t264al57p7wjxj2c3s6kyt83", + "0014d5277feeb74a82b4456ad57bfa783e748d256230", { "chain": "regtest", "isPrivkey": false, @@ -533,8 +533,8 @@ } ], [ - "bcrt1qz7prfshfkwsxuk72pt6mzr6uumq4qllxe4vmwqt89tat48d362yqlykk6a", - "0020178234c2e9b3a06e5bca0af5b10f5ce6c1507fe6cd59b701672afaba9db1d288", + "bcrt1qawvc90lpytw3z3k9etdx54l0exq5f5sqfzu5e45kjnl6slwayeeqx2dyac", + "0020eb9982bfe122dd1146c5cada6a57efc98144d20048b94cd69694ffa87ddd2672", { "chain": "regtest", "isPrivkey": false, @@ -542,8 +542,8 @@ } ], [ - "bcrt1pumee3wj80xvyr7wjmj7zsk26x5pn095aegy862yhx6f2j9sgc9hq6cj4cm", - "5120e6f398ba47799841f9d2dcbc28595a350337969dca087d28973692a91608c16e", + "bcrt1p39a4s4vdcw9kqa8w2t0rp7aj8kfxyw7mce5sk5d70x6wnnmpvt7skf2kxy", + "5120897b58558dc38b6074ee52de30fbb23d92623bdbc6690b51be79b4e9cf6162fd", { "chain": "regtest", "isPrivkey": false, @@ -551,8 +551,8 @@ } ], [ - "bcrt1szqz8hj64d2hhc6nt65v09jxal66pgff2xpcp9kj648qkk8kjzxelsts4dktd799g47uase", - "602810047bcb556aaf7c6a6bd518f2c8ddfeb414252a307012da5aa9c16b1ed211b3f82e156d96df14a8", + "bcrt1s489d9fhmyel0vzfqsrmew4x7r80asuqesm5hgqacy35daflcyufh3j8cgdtflvt99ph05m", + "6028a9cad2a6fb267ef6092080f79754de19dfd8701986e97403b82468dea7f8271378c8f843569fb165", { "chain": "regtest", "isPrivkey": false, @@ -560,48 +560,48 @@ } ], [ - "12agZTajtRE3STSchwWNWnrm467zzTQ916", - "76a9141156e00f70061e5faba8b71593a8c7554b47090c88ac", + "1G9A9j6W8TLuh6dEeVwWeyibK1Uc5MfVFV", + "76a914a614da54daacdb8861f451a0b7e3c27cdf8a099e88ac", { "chain": "main", "isPrivkey": false } ], [ - "3NXqB6iZiPYbKruNT3d9xNBTmtb73xMvvf", - "a914e49decc9e5d97e0547d3642f3a4795b13ae62bca87", + "33GA3ZXbw5o5HeUrBEaqkWXFYYZmdxGRRP", + "a914113ca1afeb49ff3abf176ffa19c2a2b4df19712a87", { "chain": "main", "isPrivkey": false } ], [ - "mjgt4BoCYxjzWvJFoh68x7cj5GeaKDYhyx", - "76a9142dc11fc7b8072f733f690ffb0591c00f4062295c88ac", + "mwgS2HRbjyfYxFnR1nF9VKLvmdgMfFBmGq", + "76a914b14ce7070b53cb0e4b5b5f6e253e876990aeca2e88ac", { "chain": "test", "isPrivkey": false } ], [ - "2NCT6FdQ5MxorHgnFxLeHyGwTGRdkHcrJDH", - "a914d2a8ec992b0894a0d9391ca5d9c45c388c41be7e87", + "2MwBVrJQ76BdaGD76CTmou8cZzQYLpe4NqU", + "a9142b2c149cde619eae3d7fe995243b76a3417541aa87", { "chain": "test", "isPrivkey": false } ], [ - "mpomiA7wqDnMcxaNLC23eBuXAb4U6H4ZqW", - "76a91465e75e340415ed297c58d6a14d3c17ceeaa17bbd88ac", + "mfnJ8tEkqKNFE5YaHTXFxyHk2mnDK2fvDh", + "76a91402e6cd77e649ad8b281271f158fc964ca3f66cb088ac", { "chain": "signet", "isPrivkey": false } ], [ - "2N1pGAA5uatbU2PKvMA9BnJmHcK6yHfMiZa", - "a9145e008b6cc232164570befc23d216060bf4ea793b87", + "2My83D67ir7K8PPzeT6mE2oth3ZwNTVRS9F", + "a9144074d84d32ff62da7b1b3c61925b934bfeb34b0587", { "chain": "signet", "isPrivkey": false diff --git a/src/test/util/logging.h b/src/test/util/logging.h index ebe0ecf623..f477088392 100644 --- a/src/test/util/logging.h +++ b/src/test/util/logging.h @@ -36,6 +36,6 @@ public: ~DebugLogHelper() { check_found(); } }; -#define ASSERT_DEBUG_LOG(message) DebugLogHelper PASTE2(debugloghelper, __COUNTER__)(message) +#define ASSERT_DEBUG_LOG(message) DebugLogHelper UNIQUE_NAME(debugloghelper)(message) #endif // BITCOIN_TEST_UTIL_LOGGING_H diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 211153f06c..13f17ca277 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -15,10 +15,10 @@ #include <interfaces/chain.h> #include <net.h> #include <net_processing.h> -#include <node/miner.h> -#include <noui.h> #include <node/blockstorage.h> #include <node/chainstate.h> +#include <node/miner.h> +#include <noui.h> #include <policy/fees.h> #include <pow.h> #include <rpc/blockchain.h> @@ -28,6 +28,7 @@ #include <script/sigcache.h> #include <shutdown.h> #include <streams.h> +#include <test/util/net.h> #include <txdb.h> #include <util/strencodings.h> #include <util/string.h> @@ -226,7 +227,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const /*deterministic=*/false, m_node.args->GetIntArg("-checkaddrman", 0)); m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME); - m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests. + m_node.connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests. m_node.peerman = PeerManager::make(chainparams, *m_node.connman, *m_node.addrman, m_node.banman.get(), *m_node.chainman, *m_node.mempool, false); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f73cc5da5f..65c8b4ea60 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -481,8 +481,6 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces indexed_transaction_set::iterator newit = mapTx.insert(entry).first; // Update transaction for any feeDelta created by PrioritiseTransaction - // TODO: refactor so that the fee delta is calculated before inserting - // into mapTx. CAmount delta{0}; ApplyDelta(entry.GetTx().GetHash(), delta); if (delta) { diff --git a/src/util/epochguard.h b/src/util/epochguard.h index 0fec7d2624..7f6477fb3b 100644 --- a/src/util/epochguard.h +++ b/src/util/epochguard.h @@ -7,6 +7,7 @@ #define BITCOIN_UTIL_EPOCHGUARD_H #include <threadsafety.h> +#include <util/macros.h> #include <cassert> @@ -96,6 +97,6 @@ public: } }; -#define WITH_FRESH_EPOCH(epoch) const Epoch::Guard PASTE2(epoch_guard_, __COUNTER__)(epoch) +#define WITH_FRESH_EPOCH(epoch) const Epoch::Guard UNIQUE_NAME(epoch_guard_)(epoch) #endif // BITCOIN_UTIL_EPOCHGUARD_H diff --git a/src/util/macros.h b/src/util/macros.h index c9740c8e82..bf6ba665dc 100644 --- a/src/util/macros.h +++ b/src/util/macros.h @@ -8,6 +8,8 @@ #define PASTE(x, y) x ## y #define PASTE2(x, y) PASTE(x, y) +#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) + /** * Converts the parameter X to a string after macro replacement on X has been performed. * Don't merge these into one macro! diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index 4baf16fdcb..1291663847 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -257,7 +257,7 @@ static RPCHelpMan setwalletflag() { {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"}, {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"}, - {RPCResult::Type::STR, "warnings", "Any warnings associated with the change"}, + {RPCResult::Type::STR, "warnings", /*optional=*/true, "Any warnings associated with the change"}, } }, RPCExamples{ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2a0653c719..c3ae098aee 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -299,6 +299,13 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string& return nullptr; } + // Do not allow a passphrase when private keys are disabled + if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { + error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled."); + status = DatabaseStatus::FAILED_CREATE; + return nullptr; + } + // Wallet::Verify will check if we're trying to create a wallet with a duplicate name. std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error); if (!database) { @@ -307,13 +314,6 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string& return nullptr; } - // Do not allow a passphrase when private keys are disabled - if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { - error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled."); - status = DatabaseStatus::FAILED_CREATE; - return nullptr; - } - // Make the wallet context.chain->initMessage(_("Loading wallet…").translated); const std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), wallet_creation_flags, error, warnings); diff --git a/test/README.md b/test/README.md index 7ff2d6d9f2..e5a184d23c 100644 --- a/test/README.md +++ b/test/README.md @@ -305,9 +305,9 @@ Use the `-v` option for verbose output. | Lint test | Dependency | |-----------|:----------:| -| [`lint-python.sh`](lint/lint-python.sh) | [flake8](https://gitlab.com/pycqa/flake8) -| [`lint-python.sh`](lint/lint-python.sh) | [mypy](https://github.com/python/mypy) -| [`lint-python.sh`](lint/lint-python.sh) | [pyzmq](https://github.com/zeromq/pyzmq) +| [`lint-python.py`](lint/lint-python.py) | [flake8](https://gitlab.com/pycqa/flake8) +| [`lint-python.py`](lint/lint-python.py) | [mypy](https://github.com/python/mypy) +| [`lint-python.py`](lint/lint-python.py) | [pyzmq](https://github.com/zeromq/pyzmq) | [`lint-python-dead-code.py`](lint/lint-python-dead-code.py) | [vulture](https://github.com/jendrikseipp/vulture) | [`lint-shell.sh`](lint/lint-shell.sh) | [ShellCheck](https://github.com/koalaman/shellcheck) | [`lint-spelling.py`](lint/lint-spelling.py) | [codespell](https://github.com/codespell-project/codespell) diff --git a/test/config.ini.in b/test/config.ini.in index d7105c419b..5888ef443b 100644 --- a/test/config.ini.in +++ b/test/config.ini.in @@ -19,6 +19,7 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py @USE_SQLITE_TRUE@USE_SQLITE=true @USE_BDB_TRUE@USE_BDB=true @BUILD_BITCOIN_CLI_TRUE@ENABLE_CLI=true +@BUILD_BITCOIN_UTIL_TRUE@ENABLE_BITCOIN_UTIL=true @BUILD_BITCOIN_WALLET_TRUE@ENABLE_WALLET_TOOL=true @BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true @ENABLE_FUZZ_TRUE@ENABLE_FUZZ=true diff --git a/test/functional/README.md b/test/functional/README.md index 926810cf03..914dbfd977 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -24,7 +24,7 @@ don't have test cases for. Consider using [pyenv](https://github.com/pyenv/pyenv), which checks [.python-version](/.python-version), to prevent accidentally introducing modern syntax from an unsupported Python version. The CI linter job also checks this, but [possibly not in all cases](https://github.com/bitcoin/bitcoin/pull/14884#discussion_r239585126). -- See [the python lint script](/test/lint/lint-python.sh) that checks for violations that +- See [the python lint script](/test/lint/lint-python.py) that checks for violations that could lead to bugs and issues in the test code. - Use [type hints](https://docs.python.org/3/library/typing.html) in your code to improve code readability and to detect possible bugs earlier. diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 2fb9ec0942..a39ee003ef 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -244,8 +244,14 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): "src", "bitcoin-cli" + config["environment"]["EXEEXT"], ) + fname_bitcoinutil = os.path.join( + config["environment"]["BUILDDIR"], + "src", + "bitcoin-util" + config["environment"]["EXEEXT"], + ) self.options.bitcoind = os.getenv("BITCOIND", default=fname_bitcoind) self.options.bitcoincli = os.getenv("BITCOINCLI", default=fname_bitcoincli) + self.options.bitcoinutil = os.getenv("BITCOINUTIL", default=fname_bitcoinutil) os.environ['PATH'] = os.pathsep.join([ os.path.join(config['environment']['BUILDDIR'], 'src'), @@ -880,6 +886,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): if not self.is_wallet_tool_compiled(): raise SkipTest("bitcoin-wallet has not been compiled") + def skip_if_no_bitcoin_util(self): + """Skip the running test if bitcoin-util has not been compiled.""" + if not self.is_bitcoin_util_compiled(): + raise SkipTest("bitcoin-util has not been compiled") + def skip_if_no_cli(self): """Skip the running test if bitcoin-cli has not been compiled.""" if not self.is_cli_compiled(): @@ -927,6 +938,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): """Checks whether bitcoin-wallet was compiled.""" return self.config["components"].getboolean("ENABLE_WALLET_TOOL") + def is_bitcoin_util_compiled(self): + """Checks whether bitcoin-util was compiled.""" + return self.config["components"].getboolean("ENABLE_BITCOIN_UTIL") + def is_zmq_compiled(self): """Checks whether the zmq module was compiled.""" return self.config["components"].getboolean("ENABLE_ZMQ") diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 1f0f806d91..a3c938ae26 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -145,6 +145,8 @@ BASE_SCRIPTS = [ 'wallet_txn_doublespend.py --mineblock', 'tool_wallet.py --legacy-wallet', 'tool_wallet.py --descriptors', + 'tool_signet_miner.py --legacy-wallet', + 'tool_signet_miner.py --descriptors', 'wallet_txn_clone.py', 'wallet_txn_clone.py --segwit', 'rpc_getchaintips.py', diff --git a/test/functional/tool_signet_miner.py b/test/functional/tool_signet_miner.py new file mode 100755 index 0000000000..e6fc9072ab --- /dev/null +++ b/test/functional/tool_signet_miner.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# Copyright (c) 2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test signet miner tool""" + +import os.path +import subprocess +import sys +import time + +from test_framework.key import ECKey +from test_framework.script_util import key_to_p2wpkh_script +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal +from test_framework.wallet_util import bytes_to_wif + + +CHALLENGE_PRIVATE_KEY = (42).to_bytes(32, 'big') + + +class SignetMinerTest(BitcoinTestFramework): + def set_test_params(self): + self.chain = "signet" + self.setup_clean_chain = True + self.num_nodes = 1 + + # generate and specify signet challenge (simple p2wpkh script) + privkey = ECKey() + privkey.set(CHALLENGE_PRIVATE_KEY, True) + pubkey = privkey.get_pubkey().get_bytes() + challenge = key_to_p2wpkh_script(pubkey) + self.extra_args = [[f'-signetchallenge={challenge.hex()}']] + + def skip_test_if_missing_module(self): + self.skip_if_no_cli() + self.skip_if_no_wallet() + self.skip_if_no_bitcoin_util() + + def run_test(self): + node = self.nodes[0] + # import private key needed for signing block + node.importprivkey(bytes_to_wif(CHALLENGE_PRIVATE_KEY)) + + # generate block with signet miner tool + base_dir = self.config["environment"]["SRCDIR"] + signet_miner_path = os.path.join(base_dir, "contrib", "signet", "miner") + subprocess.run([ + sys.executable, + signet_miner_path, + f'--cli={node.cli.binary} -datadir={node.cli.datadir}', + 'generate', + f'--address={node.getnewaddress()}', + f'--grind-cmd={self.options.bitcoinutil} grind', + '--nbits=1d00ffff', + f'--set-block-time={int(time.time())}', + ], check=True, stderr=subprocess.STDOUT) + assert_equal(node.getblockcount(), 1) + + +if __name__ == "__main__": + SignetMinerTest().main() diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py index dc823c2c60..f663666f57 100755 --- a/test/functional/wallet_avoidreuse.py +++ b/test/functional/wallet_avoidreuse.py @@ -118,6 +118,17 @@ class AvoidReuseTest(BitcoinTestFramework): assert_raises_rpc_error(-8, "Wallet flag is already set to false", self.nodes[0].setwalletflag, 'avoid_reuse', False) assert_raises_rpc_error(-8, "Wallet flag is already set to true", self.nodes[1].setwalletflag, 'avoid_reuse', True) + # Create a wallet with avoid reuse, and test that disabling it afterwards persists + self.nodes[1].createwallet(wallet_name="avoid_reuse_persist", avoid_reuse=True) + w = self.nodes[1].get_wallet_rpc("avoid_reuse_persist") + assert_equal(w.getwalletinfo()["avoid_reuse"], True) + w.setwalletflag("avoid_reuse", False) + assert_equal(w.getwalletinfo()["avoid_reuse"], False) + w.unloadwallet() + self.nodes[1].loadwallet("avoid_reuse_persist") + assert_equal(w.getwalletinfo()["avoid_reuse"], False) + w.unloadwallet() + def test_immutable(self): '''Test immutable wallet flags''' self.log.info("Test immutable wallet flags") diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py index e8234de032..dcf2e98638 100755 --- a/test/functional/wallet_createwallet.py +++ b/test/functional/wallet_createwallet.py @@ -26,6 +26,11 @@ class CreateWalletTest(BitcoinTestFramework): node = self.nodes[0] self.generate(node, 1) # Leave IBD for sethdseed + self.log.info("Run createwallet with invalid parameters.") + # Run createwallet with invalid parameters. This must not prevent a new wallet with the same name from being created with the correct parameters. + assert_raises_rpc_error(-4, "Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.", + self.nodes[0].createwallet, wallet_name='w0', descriptors=True, disable_private_keys=True, passphrase="passphrase") + self.nodes[0].createwallet(wallet_name='w0') w0 = node.get_wallet_rpc('w0') address1 = w0.getnewaddress() diff --git a/test/lint/lint-git-commit-check.py b/test/lint/lint-git-commit-check.py new file mode 100755 index 0000000000..a1d03370e8 --- /dev/null +++ b/test/lint/lint-git-commit-check.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2020-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Linter to check that commit messages have a new line before the body +# or no body at all + +import argparse +import os +import sys + +from subprocess import check_output + + +def parse_args(): + """Parse command line arguments.""" + parser = argparse.ArgumentParser( + description=""" + Linter to check that commit messages have a new line before + the body or no body at all. + """, + epilog=f""" + You can manually set the commit-range with the COMMIT_RANGE + environment variable (e.g. "COMMIT_RANGE='47ba2c3...ee50c9e' + {sys.argv[0]}"). Defaults to current merge base when neither + prev-commits nor the environment variable is set. + """) + + parser.add_argument("--prev-commits", "-p", required=False, help="The previous n commits to check") + + return parser.parse_args() + + +def main(): + args = parse_args() + exit_code = 0 + + if not os.getenv("COMMIT_RANGE"): + if args.prev_commits: + commit_range = "HEAD~" + args.prev_commits + "...HEAD" + else: + # This assumes that the target branch of the pull request will be master. + merge_base = check_output(["git", "merge-base", "HEAD", "master"], universal_newlines=True, encoding="utf8").rstrip("\n") + commit_range = merge_base + "..HEAD" + else: + commit_range = os.getenv("COMMIT_RANGE") + + commit_hashes = check_output(["git", "log", commit_range, "--format=%H"], universal_newlines=True, encoding="utf8").splitlines() + + for hash in commit_hashes: + commit_info = check_output(["git", "log", "--format=%B", "-n", "1", hash], universal_newlines=True, encoding="utf8").splitlines() + if len(commit_info) >= 2: + if commit_info[1]: + print(f"The subject line of commit hash {hash} is followed by a non-empty line. Subject lines should always be followed by a blank line.") + exit_code = 1 + + sys.exit(exit_code) + + +if __name__ == "__main__": + main() diff --git a/test/lint/lint-git-commit-check.sh b/test/lint/lint-git-commit-check.sh deleted file mode 100755 index f77373ed00..0000000000 --- a/test/lint/lint-git-commit-check.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) 2020-2021 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# -# Linter to check that commit messages have a new line before the body -# or no body at all - -export LC_ALL=C - -EXIT_CODE=0 - -while getopts "?" opt; do - case $opt in - ?) - echo "Usage: $0 [N]" - echo " COMMIT_RANGE='<commit range>' $0" - echo " $0 -?" - echo "Checks unmerged commits, the previous N commits, or a commit range." - echo "COMMIT_RANGE='47ba2c3...ee50c9e' $0" - exit ${EXIT_CODE} - ;; - esac -done - -if [ -z "${COMMIT_RANGE}" ]; then - if [ -n "$1" ]; then - COMMIT_RANGE="HEAD~$1...HEAD" - else - # This assumes that the target branch of the pull request will be master. - MERGE_BASE=$(git merge-base HEAD master) - COMMIT_RANGE="$MERGE_BASE..HEAD" - fi -fi - -while IFS= read -r commit_hash || [[ -n "$commit_hash" ]]; do - n_line=0 - while IFS= read -r line || [[ -n "$line" ]]; do - n_line=$((n_line+1)) - length=${#line} - if [ $n_line -eq 2 ] && [ "$length" -ne 0 ]; then - echo "The subject line of commit hash ${commit_hash} is followed by a non-empty line. Subject lines should always be followed by a blank line." - EXIT_CODE=1 - fi - done < <(git log --format=%B -n 1 "$commit_hash") -done < <(git log "${COMMIT_RANGE}" --format=%H) - -exit ${EXIT_CODE} diff --git a/test/lint/lint-logs.py b/test/lint/lint-logs.py new file mode 100755 index 0000000000..e6c4c068fb --- /dev/null +++ b/test/lint/lint-logs.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2018-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Check that all logs are terminated with '\n' +# +# Some logs are continued over multiple lines. They should be explicitly +# commented with /* Continued */ + +import re +import sys + +from subprocess import check_output + + +def main(): + logs_list = check_output(["git", "grep", "--extended-regexp", r"LogPrintf?\(", "--", "*.cpp"], universal_newlines=True, encoding="utf8").splitlines() + + unterminated_logs = [line for line in logs_list if not re.search(r'(\\n"|/\* Continued \*/)', line)] + + if unterminated_logs != []: + print("All calls to LogPrintf() and LogPrint() should be terminated with \\n") + print("") + + for line in unterminated_logs: + print(line) + + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/test/lint/lint-logs.sh b/test/lint/lint-logs.sh deleted file mode 100755 index 6d5165f649..0000000000 --- a/test/lint/lint-logs.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2018-2021 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# -# Check that all logs are terminated with '\n' -# -# Some logs are continued over multiple lines. They should be explicitly -# commented with /* Continued */ -# -# There are some instances of LogPrintf() in comments. Those can be -# ignored - -export LC_ALL=C -UNTERMINATED_LOGS=$(git grep --extended-regexp "LogPrintf?\(" -- "*.cpp" | \ - grep -v '\\n"' | \ - grep -v '\.\.\.' | \ - grep -v "/\* Continued \*/" | \ - grep -v "LogPrint()" | \ - grep -v "LogPrintf()") -if [[ ${UNTERMINATED_LOGS} != "" ]]; then - # shellcheck disable=SC2028 - echo "All calls to LogPrintf() and LogPrint() should be terminated with \\n" - echo - echo "${UNTERMINATED_LOGS}" - exit 1 -fi diff --git a/test/lint/lint-python.py b/test/lint/lint-python.py new file mode 100755 index 0000000000..4d16facfea --- /dev/null +++ b/test/lint/lint-python.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +""" +Check for specified flake8 and mypy warnings in python files. +""" + +import os +import pkg_resources +import subprocess +import sys + +DEPS = ['flake8', 'mypy', 'pyzmq'] +MYPY_CACHE_DIR = f"{os.getenv('BASE_ROOT_DIR', '')}/test/.mypy_cache" +FILES_ARGS = ['git', 'ls-files', 'test/functional/*.py', 'contrib/devtools/*.py'] + +ENABLED = ( + 'E101,' # indentation contains mixed spaces and tabs + 'E112,' # expected an indented block + 'E113,' # unexpected indentation + 'E115,' # expected an indented block (comment) + 'E116,' # unexpected indentation (comment) + 'E125,' # continuation line with same indent as next logical line + 'E129,' # visually indented line with same indent as next logical line + 'E131,' # continuation line unaligned for hanging indent + 'E133,' # closing bracket is missing indentation + 'E223,' # tab before operator + 'E224,' # tab after operator + 'E242,' # tab after ',' + 'E266,' # too many leading '#' for block comment + 'E271,' # multiple spaces after keyword + 'E272,' # multiple spaces before keyword + 'E273,' # tab after keyword + 'E274,' # tab before keyword + 'E275,' # missing whitespace after keyword + 'E304,' # blank lines found after function decorator + 'E306,' # expected 1 blank line before a nested definition + 'E401,' # multiple imports on one line + 'E402,' # module level import not at top of file + 'E502,' # the backslash is redundant between brackets + 'E701,' # multiple statements on one line (colon) + 'E702,' # multiple statements on one line (semicolon) + 'E703,' # statement ends with a semicolon + 'E711,' # comparison to None should be 'if cond is None:' + 'E714,' # test for object identity should be "is not" + 'E721,' # do not compare types, use "isinstance()" + 'E742,' # do not define classes named "l", "O", or "I" + 'E743,' # do not define functions named "l", "O", or "I" + 'E901,' # SyntaxError: invalid syntax + 'E902,' # TokenError: EOF in multi-line string + 'F401,' # module imported but unused + 'F402,' # import module from line N shadowed by loop variable + 'F403,' # 'from foo_module import *' used; unable to detect undefined names + 'F404,' # future import(s) name after other statements + 'F405,' # foo_function may be undefined, or defined from star imports: bar_module + 'F406,' # "from module import *" only allowed at module level + 'F407,' # an undefined __future__ feature name was imported + 'F601,' # dictionary key name repeated with different values + 'F602,' # dictionary key variable name repeated with different values + 'F621,' # too many expressions in an assignment with star-unpacking + 'F622,' # two or more starred expressions in an assignment (a, *b, *c = d) + 'F631,' # assertion test is a tuple, which are always True + 'F632,' # use ==/!= to compare str, bytes, and int literals + 'F701,' # a break statement outside of a while or for loop + 'F702,' # a continue statement outside of a while or for loop + 'F703,' # a continue statement in a finally block in a loop + 'F704,' # a yield or yield from statement outside of a function + 'F705,' # a return statement with arguments inside a generator + 'F706,' # a return statement outside of a function/method + 'F707,' # an except: block as not the last exception handler + 'F811,' # redefinition of unused name from line N + 'F812,' # list comprehension redefines 'foo' from line N + 'F821,' # undefined name 'Foo' + 'F822,' # undefined name name in __all__ + 'F823,' # local variable name … referenced before assignment + 'F831,' # duplicate argument name in function definition + 'F841,' # local variable 'foo' is assigned to but never used + 'W191,' # indentation contains tabs + 'W291,' # trailing whitespace + 'W292,' # no newline at end of file + 'W293,' # blank line contains whitespace + 'W601,' # .has_key() is deprecated, use "in" + 'W602,' # deprecated form of raising exception + 'W603,' # "<>" is deprecated, use "!=" + 'W604,' # backticks are deprecated, use "repr()" + 'W605,' # invalid escape sequence "x" + 'W606,' # 'async' and 'await' are reserved keywords starting with Python 3.7 +) + + +def check_dependencies(): + working_set = {pkg.key for pkg in pkg_resources.working_set} + + for dep in DEPS: + if dep not in working_set: + print(f"Skipping Python linting since {dep} is not installed.") + exit(0) + + +def main(): + check_dependencies() + + if len(sys.argv) > 1: + flake8_files = sys.argv[1:] + else: + files_args = ['git', 'ls-files', '*.py'] + flake8_files = subprocess.check_output(files_args).decode("utf-8").splitlines() + + flake8_args = ['flake8', '--ignore=B,C,E,F,I,N,W', f'--select={ENABLED}'] + flake8_files + flake8_env = os.environ.copy() + flake8_env["PYTHONWARNINGS"] = "ignore" + + try: + subprocess.check_call(flake8_args, env=flake8_env) + except subprocess.CalledProcessError: + exit(1) + + mypy_files = subprocess.check_output(FILES_ARGS).decode("utf-8").splitlines() + mypy_args = ['mypy', '--show-error-codes'] + mypy_files + + try: + subprocess.check_call(mypy_args) + except subprocess.CalledProcessError: + exit(1) + + +if __name__ == "__main__": + main() diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh deleted file mode 100755 index 7d7857d325..0000000000 --- a/test/lint/lint-python.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2017-2021 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# -# Check for specified flake8 warnings in python files. - -export LC_ALL=C -export MYPY_CACHE_DIR="${BASE_ROOT_DIR}/test/.mypy_cache" - -enabled=( - E101 # indentation contains mixed spaces and tabs - E112 # expected an indented block - E113 # unexpected indentation - E115 # expected an indented block (comment) - E116 # unexpected indentation (comment) - E125 # continuation line with same indent as next logical line - E129 # visually indented line with same indent as next logical line - E131 # continuation line unaligned for hanging indent - E133 # closing bracket is missing indentation - E223 # tab before operator - E224 # tab after operator - E242 # tab after ',' - E266 # too many leading '#' for block comment - E271 # multiple spaces after keyword - E272 # multiple spaces before keyword - E273 # tab after keyword - E274 # tab before keyword - E275 # missing whitespace after keyword - E304 # blank lines found after function decorator - E306 # expected 1 blank line before a nested definition - E401 # multiple imports on one line - E402 # module level import not at top of file - E502 # the backslash is redundant between brackets - E701 # multiple statements on one line (colon) - E702 # multiple statements on one line (semicolon) - E703 # statement ends with a semicolon - E711 # comparison to None should be 'if cond is None:' - E714 # test for object identity should be "is not" - E721 # do not compare types, use "isinstance()" - E742 # do not define classes named "l", "O", or "I" - E743 # do not define functions named "l", "O", or "I" - E901 # SyntaxError: invalid syntax - E902 # TokenError: EOF in multi-line string - F401 # module imported but unused - F402 # import module from line N shadowed by loop variable - F403 # 'from foo_module import *' used; unable to detect undefined names - F404 # future import(s) name after other statements - F405 # foo_function may be undefined, or defined from star imports: bar_module - F406 # "from module import *" only allowed at module level - F407 # an undefined __future__ feature name was imported - F601 # dictionary key name repeated with different values - F602 # dictionary key variable name repeated with different values - F621 # too many expressions in an assignment with star-unpacking - F622 # two or more starred expressions in an assignment (a, *b, *c = d) - F631 # assertion test is a tuple, which are always True - F632 # use ==/!= to compare str, bytes, and int literals - F701 # a break statement outside of a while or for loop - F702 # a continue statement outside of a while or for loop - F703 # a continue statement in a finally block in a loop - F704 # a yield or yield from statement outside of a function - F705 # a return statement with arguments inside a generator - F706 # a return statement outside of a function/method - F707 # an except: block as not the last exception handler - F811 # redefinition of unused name from line N - F812 # list comprehension redefines 'foo' from line N - F821 # undefined name 'Foo' - F822 # undefined name name in __all__ - F823 # local variable name … referenced before assignment - F831 # duplicate argument name in function definition - F841 # local variable 'foo' is assigned to but never used - W191 # indentation contains tabs - W291 # trailing whitespace - W292 # no newline at end of file - W293 # blank line contains whitespace - W601 # .has_key() is deprecated, use "in" - W602 # deprecated form of raising exception - W603 # "<>" is deprecated, use "!=" - W604 # backticks are deprecated, use "repr()" - W605 # invalid escape sequence "x" - W606 # 'async' and 'await' are reserved keywords starting with Python 3.7 -) - -if ! command -v flake8 > /dev/null; then - echo "Skipping Python linting since flake8 is not installed." - exit 0 -elif PYTHONWARNINGS="ignore" flake8 --version | grep -q "Python 2"; then - echo "Skipping Python linting since flake8 is running under Python 2. Install the Python 3 version of flake8." - exit 0 -fi - -EXIT_CODE=0 - -# shellcheck disable=SC2046 -if ! PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=$(IFS=","; echo "${enabled[*]}") $( - if [[ $# == 0 ]]; then - git ls-files "*.py" - else - echo "$@" - fi -); then - EXIT_CODE=1 -fi - -mapfile -t FILES < <(git ls-files "test/functional/*.py" "contrib/devtools/*.py") -if ! mypy --show-error-codes "${FILES[@]}"; then - EXIT_CODE=1 -fi - -exit $EXIT_CODE diff --git a/test/lint/lint-whitespace.py b/test/lint/lint-whitespace.py new file mode 100755 index 0000000000..d98fc8d9a2 --- /dev/null +++ b/test/lint/lint-whitespace.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2017-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Check for new lines in diff that introduce trailing whitespace or +# tab characters instead of spaces. + +# We can't run this check unless we know the commit range for the PR. + +import argparse +import os +import re +import sys + +from subprocess import check_output + +EXCLUDED_DIRS = ["depends/patches/", + "contrib/guix/patches/", + "src/leveldb/", + "src/crc32c/", + "src/secp256k1/", + "src/minisketch/", + "src/univalue/", + "doc/release-notes/", + "src/qt/locale"] + +def parse_args(): + """Parse command line arguments.""" + parser = argparse.ArgumentParser( + description=""" + Check for new lines in diff that introduce trailing whitespace + or tab characters instead of spaces in unstaged changes, the + previous n commits, or a commit-range. + """, + epilog=f""" + You can manually set the commit-range with the COMMIT_RANGE + environment variable (e.g. "COMMIT_RANGE='47ba2c3...ee50c9e' + {sys.argv[0]}"). Defaults to current merge base when neither + prev-commits nor the environment variable is set. + """) + + parser.add_argument("--prev-commits", "-p", required=False, help="The previous n commits to check") + + return parser.parse_args() + + +def report_diff(selection): + filename = "" + seen = False + seenln = False + + print("The following changes were suspected:") + + for line in selection: + if re.match(r"^diff", line): + filename = line + seen = False + elif re.match(r"^@@", line): + linenumber = line + seenln = False + else: + if not seen: + # The first time a file is seen with trailing whitespace or a tab character, we print the + # filename (preceded by a newline). + print("") + print(filename) + seen = True + if not seenln: + print(linenumber) + seenln = True + print(line) + + +def get_diff(commit_range, check_only_code): + exclude_args = [":(exclude)" + dir for dir in EXCLUDED_DIRS] + + if check_only_code: + what_files = ["*.cpp", "*.h", "*.md", "*.py", "*.sh"] + else: + what_files = ["."] + + diff = check_output(["git", "diff", "-U0", commit_range, "--"] + what_files + exclude_args, universal_newlines=True, encoding="utf8") + + return diff + + +def main(): + args = parse_args() + + if not os.getenv("COMMIT_RANGE"): + if args.prev_commits: + commit_range = "HEAD~" + args.prev_commits + "...HEAD" + else: + # This assumes that the target branch of the pull request will be master. + merge_base = check_output(["git", "merge-base", "HEAD", "master"], universal_newlines=True, encoding="utf8").rstrip("\n") + commit_range = merge_base + "..HEAD" + else: + commit_range = os.getenv("COMMIT_RANGE") + + whitespace_selection = [] + tab_selection = [] + + # Check if trailing whitespace was found in the diff. + for line in get_diff(commit_range, check_only_code=False).splitlines(): + if re.match(r"^(diff --git|\@@|^\+.*\s+$)", line): + whitespace_selection.append(line) + + whitespace_additions = [i for i in whitespace_selection if i.startswith("+")] + + # Check if tab characters were found in the diff. + for line in get_diff(commit_range, check_only_code=True).splitlines(): + if re.match(r"^(diff --git|\@@|^\+.*\t)", line): + tab_selection.append(line) + + tab_additions = [i for i in tab_selection if i.startswith("+")] + + ret = 0 + + if len(whitespace_additions) > 0: + print("This diff appears to have added new lines with trailing whitespace.") + report_diff(whitespace_selection) + ret = 1 + + if len(tab_additions) > 0: + print("This diff appears to have added new lines with tab characters instead of spaces.") + report_diff(tab_selection) + ret = 1 + + sys.exit(ret) + + +if __name__ == "__main__": + main() diff --git a/test/lint/lint-whitespace.sh b/test/lint/lint-whitespace.sh deleted file mode 100755 index 9d55c71eb5..0000000000 --- a/test/lint/lint-whitespace.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2017-2021 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# -# Check for new lines in diff that introduce trailing whitespace. - -# We can't run this check unless we know the commit range for the PR. - -export LC_ALL=C -while getopts "?" opt; do - case $opt in - ?) - echo "Usage: $0 [N]" - echo " COMMIT_RANGE='<commit range>' $0" - echo " $0 -?" - echo "Checks unstaged changes, the previous N commits, or a commit range." - echo "COMMIT_RANGE='47ba2c3...ee50c9e' $0" - exit 0 - ;; - esac -done - -if [ -z "${COMMIT_RANGE}" ]; then - if [ -n "$1" ]; then - COMMIT_RANGE="HEAD~$1...HEAD" - else - # This assumes that the target branch of the pull request will be master. - MERGE_BASE=$(git merge-base HEAD master) - COMMIT_RANGE="$MERGE_BASE..HEAD" - fi -fi - -showdiff() { - if ! git diff -U0 "${COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)contrib/guix/patches/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/minisketch/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then - echo "Failed to get a diff" - exit 1 - fi -} - -showcodediff() { - if ! git diff -U0 "${COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/minisketch/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then - echo "Failed to get a diff" - exit 1 - fi -} - -RET=0 - -# Check if trailing whitespace was found in the diff. -if showdiff | grep -E -q '^\+.*\s+$'; then - echo "This diff appears to have added new lines with trailing whitespace." - echo "The following changes were suspected:" - FILENAME="" - SEEN=0 - SEENLN=0 - while read -r line; do - if [[ "$line" =~ ^diff ]]; then - FILENAME="$line" - SEEN=0 - elif [[ "$line" =~ ^@@ ]]; then - LINENUMBER="$line" - SEENLN=0 - else - if [ "$SEEN" -eq 0 ]; then - # The first time a file is seen with trailing whitespace, we print the - # filename (preceded by a newline). - echo - echo "$FILENAME" - SEEN=1 - fi - if [ "$SEENLN" -eq 0 ]; then - echo "$LINENUMBER" - SEENLN=1 - fi - echo "$line" - fi - done < <(showdiff | grep -E '^(diff --git |@@|\+.*\s+$)') - RET=1 -fi - -# Check if tab characters were found in the diff. -if showcodediff | perl -nle '$MATCH++ if m{^\+.*\t}; END{exit 1 unless $MATCH>0}' > /dev/null; then - echo "This diff appears to have added new lines with tab characters instead of spaces." - echo "The following changes were suspected:" - FILENAME="" - SEEN=0 - SEENLN=0 - while read -r line; do - if [[ "$line" =~ ^diff ]]; then - FILENAME="$line" - SEEN=0 - elif [[ "$line" =~ ^@@ ]]; then - LINENUMBER="$line" - SEENLN=0 - else - if [ "$SEEN" -eq 0 ]; then - # The first time a file is seen with a tab character, we print the - # filename (preceded by a newline). - echo - echo "$FILENAME" - SEEN=1 - fi - if [ "$SEENLN" -eq 0 ]; then - echo "$LINENUMBER" - SEENLN=1 - fi - echo "$line" - fi - done < <(showcodediff | perl -nle 'print if m{^(diff --git |@@|\+.*\t)}') - RET=1 -fi - -exit $RET |