aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml7
-rw-r--r--Makefile.am3
-rw-r--r--build_msvc/vcpkg.json8
-rwxr-xr-xci/test/05_before_script.sh2
-rw-r--r--contrib/guix/manifest.scm7
-rw-r--r--contrib/guix/patches/gcc-broken-longjmp.patch68
-rwxr-xr-xcontrib/seeds/makeseeds.py83
-rw-r--r--depends/Makefile2
-rw-r--r--depends/README.md5
-rw-r--r--depends/funcs.mk22
-rw-r--r--doc/release-process.md3
-rw-r--r--src/arith_uint256.cpp29
-rw-r--r--src/arith_uint256.h2
-rw-r--r--src/bitcoin-cli.cpp4
-rw-r--r--src/logging/timer.h8
-rw-r--r--src/netbase.cpp28
-rw-r--r--src/netbase.h4
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/bitcoinamountfield.cpp22
-rw-r--r--src/qt/bitcoinamountfield.h3
-rw-r--r--src/qt/bitcoingui.cpp13
-rw-r--r--src/qt/bitcoingui.h5
-rw-r--r--src/qt/bitcoinunits.cpp172
-rw-r--r--src/qt/bitcoinunits.h34
-rw-r--r--src/qt/coincontroldialog.cpp4
-rw-r--r--src/qt/guiutil.cpp5
-rw-r--r--src/qt/optionsmodel.cpp31
-rw-r--r--src/qt/optionsmodel.h13
-rw-r--r--src/qt/overviewpage.cpp10
-rw-r--r--src/qt/psbtoperationsdialog.cpp4
-rw-r--r--src/qt/sendcoinsdialog.cpp3
-rw-r--r--src/qt/test/wallettests.cpp15
-rw-r--r--src/qt/transactiondesc.cpp28
-rw-r--r--src/qt/transactiondesc.h6
-rw-r--r--src/qt/transactionrecord.h21
-rw-r--r--src/qt/transactiontablemodel.cpp3
-rw-r--r--src/qt/walletview.h3
-rw-r--r--src/random.cpp2
-rw-r--r--src/sync.h4
-rw-r--r--src/test/util/logging.h2
-rw-r--r--src/txmempool.cpp2
-rw-r--r--src/util/epochguard.h3
-rw-r--r--src/util/macros.h2
-rw-r--r--test/config.ini.in1
-rwxr-xr-xtest/functional/test_framework/test_framework.py15
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/tool_signet_miner.py62
-rwxr-xr-xtest/lint/lint-logs.py34
-rwxr-xr-xtest/lint/lint-logs.sh28
49 files changed, 514 insertions, 325 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..4382b7d258 100755
--- a/contrib/seeds/makeseeds.py
+++ b/contrib/seeds/makeseeds.py
@@ -10,12 +10,16 @@ import re
import sys
import dns.resolver
import collections
+from typing import List, Dict, Union
NSEEDS=512
-MAX_SEEDS_PER_ASN=2
+MAX_SEEDS_PER_ASN = {
+ 'ipv4': 2,
+ 'ipv6': 10,
+}
-MIN_BLOCKS = 337600
+MIN_BLOCKS = 730000
# These are hosts that have been observed to be behaving strangely (e.g.
# aggressively connecting to every node).
@@ -40,9 +44,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 +115,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 +156,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 +192,36 @@ 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)
+ print(f'{ip_stats(ips):s} After removing duplicates', 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} Skip entries from suspicious hosts', 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 +229,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/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/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/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/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/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/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/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/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/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/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