diff --git a/.github/workflows/guix-build.yml b/.github/workflows/guix-build.yml index ec3a5ff992a42..9b91fb2beacd5 100644 --- a/.github/workflows/guix-build.yml +++ b/.github/workflows/guix-build.yml @@ -9,11 +9,13 @@ jobs: build: runs-on: [ "self-hosted", "linux", "x64", "ubuntu-core" ] if: contains(github.event.pull_request.labels.*.name, 'guix-build') + timeout-minutes: 480 steps: - name: Checkout uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} + path: dash - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -21,31 +23,48 @@ jobs: - name: Commit variables id: dockerfile run: | - echo "hash=$(sha256sum ./contrib/containers/guix/Dockerfile | cut -d ' ' -f1)" >> $GITHUB_OUTPUT + echo "hash=$(sha256sum ./dash/contrib/containers/guix/Dockerfile | cut -d ' ' -f1)" >> $GITHUB_OUTPUT echo "host_user_id=$(id -u)" >> $GITHUB_OUTPUT echo "host_group_id=$(id -g)" >> $GITHUB_OUTPUT - name: Build Docker image uses: docker/build-push-action@v5 with: - context: ${{ github.workspace }} + context: ${{ github.workspace }}/dash build-args: | USER_ID=${{ steps.dockerfile.outputs.host_user_id }} GROUP_ID=${{ steps.dockerfile.outputs.host_group_id }} build-contexts: | - docker_root=${{ github.workspace }}/contrib/containers/guix - file: ./contrib/containers/guix/Dockerfile + docker_root=${{ github.workspace }}/dash/contrib/containers/guix + file: ./dash/contrib/containers/guix/Dockerfile load: true tags: guix_ubuntu:latest cache-from: type=gha cache-to: type=gha,mode=max + - name: Restore Guix cache and depends + id: guix-cache-restore + uses: actions/cache/restore@v3 + with: + path: | + ${{ github.workspace }}/.cache + ${{ github.workspace }}/dash/depends/built + ${{ github.workspace }}/dash/depends/sources + ${{ github.workspace }}/dash/depends/work + key: ${{ runner.os }}-guix + + - name: Create .cache folder if missing + if: steps.guix-cache-restore.outputs.cache-hit != 'true' + run: mkdir -p .cache + - name: Run Guix build + timeout-minutes: 480 run: | docker run --privileged -d --rm -t \ --name guix-daemon \ -e ADDITIONAL_GUIX_COMMON_FLAGS="--max-jobs=$(nproc --all)" \ - -v ${{ github.workspace }}:/src/dash \ + -v ${{ github.workspace }}/dash:/src/dash \ + -v ${{ github.workspace }}/.cache:/home/ubuntu/.cache \ -w /src/dash \ guix_ubuntu:latest && \ docker exec guix-daemon bash -c '/usr/local/bin/guix-start' @@ -57,6 +76,17 @@ jobs: exit 1 fi + - name: Save Guix cache and depends + id: guix-cache-save + uses: actions/cache/save@v3 + with: + path: | + ${{ github.workspace }}/.cache + ${{ github.workspace }}/dash/depends/built + ${{ github.workspace }}/dash/depends/sources + ${{ github.workspace }}/dash/depends/work + key: ${{ steps.guix-cache-restore.outputs.cache-primary-key }} + - name: Compute SHA256 checksums run: | - ./contrib/containers/guix/scripts/guix-check ${{ github.workspace }} + ./dash/contrib/containers/guix/scripts/guix-check ${{ github.workspace }}/dash diff --git a/Makefile.am b/Makefile.am index 2ea6bb1f5245c..7cd688ba25ab3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,7 +36,6 @@ OSX_APP=Dash-Qt.app OSX_VOLNAME = $(subst $(space),-,$(PACKAGE_NAME)) OSX_DMG = $(OSX_VOLNAME).dmg OSX_TEMP_ISO = $(OSX_DMG:.dmg=).temp.iso -OSX_BACKGROUND_IMAGE=$(top_srcdir)/contrib/macdeploy/background.tiff OSX_DEPLOY_SCRIPT=$(top_srcdir)/contrib/macdeploy/macdeployqtplus OSX_INSTALLER_ICONS=$(top_srcdir)/src/qt/res/icons/dash.icns OSX_PLIST=$(top_builddir)/share/qt/Info.plist #not installed @@ -60,7 +59,6 @@ WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/dash.ico \ $(top_srcdir)/doc/README_windows.txt OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_INSTALLER_ICONS) \ - $(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \ $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh COVERAGE_INFO = baseline.info \ @@ -125,28 +123,17 @@ $(OSX_DMG): $(OSX_APP_BUILT) $(OSX_PACKAGING) deploydir: $(OSX_DMG) else !BUILD_DARWIN APP_DIST_DIR=$(top_builddir)/dist -APP_DIST_EXTRAS=$(APP_DIST_DIR)/.background/background.tiff $(APP_DIST_DIR)/.DS_Store $(APP_DIST_DIR)/Applications -$(APP_DIST_DIR)/Applications: - @rm -f $@ - @cd $(@D); $(LN_S) /Applications $(@F) - -$(APP_DIST_EXTRAS): $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Dash-Qt - -$(OSX_TEMP_ISO): $(APP_DIST_EXTRAS) +$(OSX_TEMP_ISO): $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Dash-Qt $(XORRISOFS) -D -l -V "$(OSX_VOLNAME)" -no-pad -r -dir-mode 0755 -o $@ $(APP_DIST_DIR) -- $(if $(SOURCE_DATE_EPOCH),-volume_date all_file_dates =$(SOURCE_DATE_EPOCH)) $(OSX_DMG): $(OSX_TEMP_ISO) $(DMG) dmg "$<" "$@" -$(APP_DIST_DIR)/.background/background.tiff: - $(MKDIR_P) $(@D) - cp $(OSX_BACKGROUND_IMAGE) $@ - $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Dash-Qt: $(OSX_APP_BUILT) $(OSX_PACKAGING) INSTALLNAMETOOL=$(INSTALLNAMETOOL) OTOOL=$(OTOOL) STRIP=$(STRIP) $(PYTHON) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) $(OSX_VOLNAME) -translations-dir=$(QT_TRANSLATION_DIR) -deploydir: $(APP_DIST_EXTRAS) +deploydir: $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Dash-Qt endif !BUILD_DARWIN appbundle: $(OSX_APP_BUILT) diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh index 72c30e1336f97..093835275341b 100755 --- a/ci/test/00_setup_env_native_qt5.sh +++ b/ci/test/00_setup_env_native_qt5.sh @@ -15,5 +15,5 @@ export RUN_UNIT_TESTS_SEQUENTIAL="true" export RUN_UNIT_TESTS="false" export GOAL="install" export TEST_PREVIOUS_RELEASES=true -export PREVIOUS_RELEASES_TO_DOWNLOAD="v0.15.0.0 v0.16.1.1 v0.17.0.3 v18.2.2 v19.3.0" +export PREVIOUS_RELEASES_TO_DOWNLOAD="v0.15.0.0 v0.16.1.1 v0.17.0.3 v18.2.2 v19.3.0 v20.0.1" export BITCOIN_CONFIG="--enable-zmq --enable-reduce-exports --disable-fuzz-binary LDFLAGS=-static-libstdc++" diff --git a/configure.ac b/configure.ac index 4c88518214352..1dd19c2bde743 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.69]) define(_CLIENT_VERSION_MAJOR, 20) define(_CLIENT_VERSION_MINOR, 0) -define(_CLIENT_VERSION_BUILD, 1) +define(_CLIENT_VERSION_BUILD, 2) define(_CLIENT_VERSION_RC, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2023) diff --git a/contrib/containers/guix/Dockerfile b/contrib/containers/guix/Dockerfile index 12b531a7150a0..aa42ce41c29a8 100644 --- a/contrib/containers/guix/Dockerfile +++ b/contrib/containers/guix/Dockerfile @@ -79,9 +79,14 @@ COPY --from=docker_root ./scripts/entrypoint /usr/local/bin/entrypoint COPY --from=docker_root ./scripts/guix-check /usr/local/bin/guix-check COPY --from=docker_root ./scripts/guix-start /usr/local/bin/guix-start -# Create directory for mounting and grant necessary permissions -RUN mkdir -p /src/dash && \ - chown -R ${USER_ID}:${GROUP_ID} /src +# Create directories for mounting to save/restore cache and grant necessary permissions +RUN mkdir -p \ + /home/${USERNAME}/.cache \ + /src/dash/depends/{built,sources,work} && \ + chown -R ${USER_ID}:${GROUP_ID} \ + /home/${USERNAME}/.cache \ + /src + WORKDIR "/src/dash" # Switch to unprivileged context diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh index 395f885e31316..a85d9bc921d16 100644 --- a/contrib/guix/libexec/build.sh +++ b/contrib/guix/libexec/build.sh @@ -328,7 +328,7 @@ mkdir -p "$DISTSRC" mkdir -p "unsigned-app-${HOST}" cp --target-directory="unsigned-app-${HOST}" \ osx_volname \ - contrib/macdeploy/detached-sig-{apply,create}.sh \ + contrib/macdeploy/detached-sig-create.sh \ "${BASEPREFIX}/${HOST}"/native/bin/dmg mv --target-directory="unsigned-app-${HOST}" dist ( diff --git a/contrib/macdeploy/detached-sig-apply.sh b/contrib/macdeploy/detached-sig-apply.sh deleted file mode 100755 index ae83ccc3e1c9c..0000000000000 --- a/contrib/macdeploy/detached-sig-apply.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# Copyright (c) 2014-2015 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -export LC_ALL=C -set -e - -UNSIGNED="$1" -SIGNATURE="$2" -ROOTDIR=dist -OUTDIR=signed-app -SIGNAPPLE=signapple - -if [ -z "$UNSIGNED" ]; then - echo "usage: $0 " - exit 1 -fi - -if [ -z "$SIGNATURE" ]; then - echo "usage: $0 " - exit 1 -fi - -${SIGNAPPLE} apply ${UNSIGNED} ${SIGNATURE} -mv ${ROOTDIR} ${OUTDIR} -echo "Signed: ${OUTDIR}" diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index 249ce9c1876a4..293e0d09343aa 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -16,8 +16,7 @@ # along with this program. If not, see . # -import plistlib -import sys, re, os, shutil, stat, os.path +import sys, re, os, platform, shutil, stat, subprocess, os.path from argparse import ArgumentParser from ds_store import DSStore from mac_alias import Alias @@ -53,7 +52,7 @@ class FrameworkInfo(object): return False def __str__(self): - return f""" Framework name: {frameworkName} + return f""" Framework name: {self.frameworkName} Framework directory: {self.frameworkDirectory} Framework path: {self.frameworkPath} Binary name: {self.binaryName} @@ -85,8 +84,8 @@ class FrameworkInfo(object): if line == "": return None - # Don't deploy system libraries (exception for libQtuitools and libQtlucene). - if line.startswith("/System/Library/") or line.startswith("@executable_path") or (line.startswith("/usr/lib/") and "libQt" not in line): + # Don't deploy system libraries + if line.startswith("/System/Library/") or line.startswith("@executable_path") or line.startswith("/usr/lib/"): return None m = cls.reOLine.match(line) @@ -246,56 +245,46 @@ def copyFramework(framework: FrameworkInfo, path: str, verbose: int) -> Optional toDir = os.path.join(path, framework.destinationDirectory) toPath = os.path.join(toDir, framework.binaryName) - if not os.path.exists(fromPath): - raise RuntimeError(f"No file at {fromPath}") + if framework.isDylib(): + if not os.path.exists(fromPath): + raise RuntimeError(f"No file at {fromPath}") - if os.path.exists(toPath): - return None # Already there + if os.path.exists(toPath): + return None # Already there - if not os.path.exists(toDir): - os.makedirs(toDir) + if not os.path.exists(toDir): + os.makedirs(toDir) - shutil.copy2(fromPath, toPath) - if verbose: - print("Copied:", fromPath) - print(" to:", toPath) + shutil.copy2(fromPath, toPath) + if verbose: + print("Copied:", fromPath) + print(" to:", toPath) + else: + to_dir = os.path.join(path, "Contents", "Frameworks", framework.frameworkName) + if os.path.exists(to_dir): + return None # Already there + + from_dir = framework.frameworkPath + if not os.path.exists(from_dir): + raise RuntimeError(f"No directory at {from_dir}") + + shutil.copytree(from_dir, to_dir, symlinks=True) + if verbose: + print("Copied:", from_dir) + print(" to:", to_dir) + + headers_link = os.path.join(to_dir, "Headers") + if os.path.exists(headers_link): + os.unlink(headers_link) + + headers_dir = os.path.join(to_dir, framework.binaryDirectory, "Headers") + if os.path.exists(headers_dir): + shutil.rmtree(headers_dir) permissions = os.stat(toPath) if not permissions.st_mode & stat.S_IWRITE: os.chmod(toPath, permissions.st_mode | stat.S_IWRITE) - if not framework.isDylib(): # Copy resources for real frameworks - - linkfrom = os.path.join(path, "Contents","Frameworks", framework.frameworkName, "Versions", "Current") - linkto = framework.version - if not os.path.exists(linkfrom): - os.symlink(linkto, linkfrom) - print("Linked:", linkfrom, "->", linkto) - fromResourcesDir = framework.sourceResourcesDirectory - if os.path.exists(fromResourcesDir): - toResourcesDir = os.path.join(path, framework.destinationResourcesDirectory) - shutil.copytree(fromResourcesDir, toResourcesDir, symlinks=True) - if verbose: - print("Copied resources:", fromResourcesDir) - print(" to:", toResourcesDir) - fromContentsDir = framework.sourceVersionContentsDirectory - if not os.path.exists(fromContentsDir): - fromContentsDir = framework.sourceContentsDirectory - if os.path.exists(fromContentsDir): - toContentsDir = os.path.join(path, framework.destinationVersionContentsDirectory) - shutil.copytree(fromContentsDir, toContentsDir, symlinks=True) - if verbose: - print("Copied Contents:", fromContentsDir) - print(" to:", toContentsDir) - elif framework.frameworkName.startswith("libQtGui"): # Copy qt_menu.nib (applies to non-framework layout) - qtMenuNibSourcePath = os.path.join(framework.frameworkDirectory, "Resources", "qt_menu.nib") - qtMenuNibDestinationPath = os.path.join(path, "Contents", "Resources", "qt_menu.nib") - if os.path.exists(qtMenuNibSourcePath) and not os.path.exists(qtMenuNibDestinationPath): - shutil.copytree(qtMenuNibSourcePath, qtMenuNibDestinationPath, symlinks=True) - if verbose: - print("Copied for libQtGui:", qtMenuNibSourcePath) - print(" to:", qtMenuNibDestinationPath) - return toPath def deployFrameworks(frameworks: List[FrameworkInfo], bundlePath: str, binaryPath: str, strip: bool, verbose: int, deploymentInfo: Optional[DeploymentInfo] = None) -> DeploymentInfo: @@ -351,115 +340,20 @@ def deployFrameworksForAppBundle(applicationBundle: ApplicationBundleInfo, strip return deployFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, strip, verbose) def deployPlugins(appBundleInfo: ApplicationBundleInfo, deploymentInfo: DeploymentInfo, strip: bool, verbose: int): - # Lookup available plugins, exclude unneeded plugins = [] if deploymentInfo.pluginPath is None: return for dirpath, dirnames, filenames in os.walk(deploymentInfo.pluginPath): pluginDirectory = os.path.relpath(dirpath, deploymentInfo.pluginPath) - if pluginDirectory == "designer": - # Skip designer plugins - continue - elif pluginDirectory == "printsupport": - # Skip printsupport plugins - continue - elif pluginDirectory == "imageformats": - # Skip imageformats plugins + + if pluginDirectory not in ['styles', 'platforms']: continue - elif pluginDirectory == "sqldrivers": - # Deploy the sql plugins only if QtSql is in use - if not deploymentInfo.usesFramework("QtSql"): - continue - elif pluginDirectory == "script": - # Deploy the script plugins only if QtScript is in use - if not deploymentInfo.usesFramework("QtScript"): - continue - elif pluginDirectory == "qmltooling" or pluginDirectory == "qml1tooling": - # Deploy the qml plugins only if QtDeclarative is in use - if not deploymentInfo.usesFramework("QtDeclarative"): - continue - elif pluginDirectory == "bearer": - # Deploy the bearer plugins only if QtNetwork is in use - if not deploymentInfo.usesFramework("QtNetwork"): - continue - elif pluginDirectory == "position": - # Deploy the position plugins only if QtPositioning is in use - if not deploymentInfo.usesFramework("QtPositioning"): - continue - elif pluginDirectory == "sensors" or pluginDirectory == "sensorgestures": - # Deploy the sensor plugins only if QtSensors is in use - if not deploymentInfo.usesFramework("QtSensors"): - continue - elif pluginDirectory == "audio" or pluginDirectory == "playlistformats": - # Deploy the audio plugins only if QtMultimedia is in use - if not deploymentInfo.usesFramework("QtMultimedia"): - continue - elif pluginDirectory == "mediaservice": - # Deploy the mediaservice plugins only if QtMultimediaWidgets is in use - if not deploymentInfo.usesFramework("QtMultimediaWidgets"): - continue - elif pluginDirectory == "canbus": - # Deploy the canbus plugins only if QtSerialBus is in use - if not deploymentInfo.usesFramework("QtSerialBus"): - continue - elif pluginDirectory == "webview": - # Deploy the webview plugins only if QtWebView is in use - if not deploymentInfo.usesFramework("QtWebView"): - continue - elif pluginDirectory == "gamepads": - # Deploy the webview plugins only if QtGamepad is in use - if not deploymentInfo.usesFramework("QtGamepad"): - continue - elif pluginDirectory == "geoservices": - # Deploy the webview plugins only if QtLocation is in use - if not deploymentInfo.usesFramework("QtLocation"): - continue - elif pluginDirectory == "texttospeech": - # Deploy the texttospeech plugins only if QtTextToSpeech is in use - if not deploymentInfo.usesFramework("QtTextToSpeech"): - continue - elif pluginDirectory == "virtualkeyboard": - # Deploy the virtualkeyboard plugins only if QtVirtualKeyboard is in use - if not deploymentInfo.usesFramework("QtVirtualKeyboard"): - continue - elif pluginDirectory == "sceneparsers": - # Deploy the virtualkeyboard plugins only if Qt3DCore is in use - if not deploymentInfo.usesFramework("Qt3DCore"): - continue - elif pluginDirectory == "renderplugins": - # Deploy the renderplugins plugins only if Qt3DCore is in use - if not deploymentInfo.usesFramework("Qt3DCore"): - continue - elif pluginDirectory == "geometryloaders": - # Deploy the geometryloaders plugins only if Qt3DCore is in use - if not deploymentInfo.usesFramework("Qt3DCore"): - continue for pluginName in filenames: pluginPath = os.path.join(pluginDirectory, pluginName) - if pluginName.endswith("_debug.dylib"): - # Skip debug plugins + + if pluginName.split('.')[0] not in ['libqminimal', 'libqcocoa', 'libqmacstyle']: continue - elif pluginPath == "imageformats/libqsvg.dylib" or pluginPath == "iconengines/libqsvgicon.dylib": - # Deploy the svg plugins only if QtSvg is in use - if not deploymentInfo.usesFramework("QtSvg"): - continue - elif pluginPath == "accessible/libqtaccessiblecompatwidgets.dylib": - # Deploy accessibility for Qt3Support only if the Qt3Support is in use - if not deploymentInfo.usesFramework("Qt3Support"): - continue - elif pluginPath == "graphicssystems/libqglgraphicssystem.dylib": - # Deploy the opengl graphicssystem plugin only if QtOpenGL is in use - if not deploymentInfo.usesFramework("QtOpenGL"): - continue - elif pluginPath == "accessible/libqtaccessiblequick.dylib": - # Deploy the accessible qtquick plugin only if QtQuick is in use - if not deploymentInfo.usesFramework("QtQuick"): - continue - elif pluginPath == "platforminputcontexts/libqtvirtualkeyboardplugin.dylib": - # Deploy the virtualkeyboardplugin plugin only if QtVirtualKeyboard is in use - if not deploymentInfo.usesFramework("QtVirtualKeyboard"): - continue plugins.append((pluginDirectory, pluginName)) @@ -527,6 +421,9 @@ if os.path.exists(appname + ".dmg"): print("+ Removing existing DMG +") os.unlink(appname + ".dmg") +if os.path.exists(appname + ".temp.dmg"): + os.unlink(appname + ".temp.dmg") + # ------------------------------------------------ target = os.path.join("dist", "Dash-Qt.app") @@ -644,6 +541,25 @@ ds.close() # ------------------------------------------------ +if platform.system() == "Darwin": + subprocess.check_call(f"codesign --deep --force --sign - {target}", shell=True) + +print("+ Installing background.tiff +") + +bg_path = os.path.join('dist', '.background', 'background.tiff') +os.mkdir(os.path.dirname(bg_path)) + +tiff_path = os.path.join('contrib', 'macdeploy', 'background.tiff') +shutil.copy2(tiff_path, bg_path) + +# ------------------------------------------------ + +print("+ Generating symlink for /Applications +") + +os.symlink("/Applications", os.path.join('dist', "Applications")) + +# ------------------------------------------------ + if config.dmg is not None: print("+ Preparing .dmg disk image +") @@ -667,19 +583,6 @@ if config.dmg is not None: print("Attaching temp image...") output = run(["hdiutil", "attach", tempname, "-readwrite"], check=True, universal_newlines=True, stdout=PIPE).stdout - m = re.search(r"/Volumes/(.+$)", output) - disk_root = m.group(0) - - print("+ Applying fancy settings +") - - bg_path = os.path.join(disk_root, ".background", os.path.basename('background.tiff')) - os.mkdir(os.path.dirname(bg_path)) - if verbose: - print('background.tiff', "->", bg_path) - shutil.copy2('contrib/macdeploy/background.tiff', bg_path) - - os.symlink("/Applications", os.path.join(disk_root, "Applications")) - print("+ Finalizing .dmg disk image +") run(["hdiutil", "detach", f"/Volumes/{appname}"], universal_newlines=True) diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 37fbb28943519..80b2300f73d9b 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -134,6 +134,9 @@ $(package)_config_opts_darwin += -no-feature-corewlan $(package)_config_opts_darwin += -no-freetype $(package)_config_opts_darwin += QMAKE_MACOSX_DEPLOYMENT_TARGET=$(OSX_MIN_VERSION) +# Optimizing using > -O1 causes non-determinism when building across arches. +$(package)_config_opts_aarch64_darwin += "QMAKE_CFLAGS_OPTIMIZE_FULL = -O1" + ifneq ($(build_os),darwin) $(package)_config_opts_darwin += -xplatform macx-clang-linux $(package)_config_opts_darwin += -device-option MAC_SDK_PATH=$(OSX_SDK) diff --git a/doc/release-notes.md b/doc/release-notes.md index d31ae9642f385..d72df7f9e7c41 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,4 +1,4 @@ -# Dash Core version v20.0.1 +# Dash Core version v20.0.2 Release is now available from: @@ -35,17 +35,20 @@ reindex or re-sync the whole chain. # Notable changes -## Qt Testnet Crash +## Masternode fix -A crash has been fixed which has only been seen on testnet and only affects QT clients. +A problem has been fixed in the old quorum data cleanup mechanism. It was slowing down masternodes during DKG sessions and causing them to get PoSe scored. -## Guix Build System Enhancements -The Guix build system has been enhanced to enable building with custom options when needed. -This will be used to support custom builds such as for nightly builds with extra debug options. +## Testnet Crash -Additionally, the Guix system will now produce debug symbols for MacOS. +A fix has been implemented for the reported crash that could occur when upgrading from v19.x to v20.0.0 after v20 activation without re-indexing. -# v20.0.1 Change log +## Other changes + +Implemented improvements in Github CI and build system for macOS. Fixed compilation issues on FreeBSD. + + +# v20.0.2 Change log See detailed [set of changes][set-of-changes]. @@ -54,6 +57,7 @@ See detailed [set of changes][set-of-changes]. Thanks to everyone who directly contributed to this release: - Konstantin Akimov (knst) +- Odysseas Gabrielides (ogabrielides) - PastaPastaPasta - UdjinM6 @@ -82,6 +86,7 @@ Dash Core tree 0.12.1.x was a fork of Bitcoin Core tree 0.12. These release are considered obsolete. Old release notes can be found here: +- [v20.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.1.md) released November/18/2023 - [v20.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.0.md) released November/15/2023 - [v19.3.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.3.0.md) released July/31/2023 - [v19.2.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.2.0.md) released June/19/2023 @@ -125,4 +130,4 @@ These release are considered obsolete. Old release notes can be found here: - [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014 - [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014 -[set-of-changes]: https://github.com/dashpay/dash/compare/v20.0.0...dashpay:v20.0.1 +[set-of-changes]: https://github.com/dashpay/dash/compare/v20.0.1...dashpay:v20.0.2 diff --git a/doc/release-notes/dash/release-notes-20.0.1.md b/doc/release-notes/dash/release-notes-20.0.1.md new file mode 100644 index 0000000000000..d31ae9642f385 --- /dev/null +++ b/doc/release-notes/dash/release-notes-20.0.1.md @@ -0,0 +1,128 @@ +# Dash Core version v20.0.1 + +Release is now available from: + + + +This is a new patch version release, bringing small bug fixes and build system enhancements. + +This release is optional for all nodes. + +Please report bugs using the issue tracker at GitHub: + + + + +# Upgrading and downgrading + +## How to Upgrade + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over /Applications/Dash-Qt (on Mac) or +dashd/dash-qt (on Linux). If you upgrade after DIP0003 activation and you were +using version < 0.13 you will have to reindex (start with -reindex-chainstate +or -reindex) to make sure your wallet has all the new data synced. Upgrading +from version 0.13 should not require any additional actions. + +## Downgrade warning + +### Downgrade to a version < v19.2.0 + +Downgrading to a version older than v19.2.0 is not supported due to changes +in the evodb database. If you need to use an older version, you must either +reindex or re-sync the whole chain. + +# Notable changes + +## Qt Testnet Crash + +A crash has been fixed which has only been seen on testnet and only affects QT clients. + +## Guix Build System Enhancements +The Guix build system has been enhanced to enable building with custom options when needed. +This will be used to support custom builds such as for nightly builds with extra debug options. + +Additionally, the Guix system will now produce debug symbols for MacOS. + +# v20.0.1 Change log + +See detailed [set of changes][set-of-changes]. + +# Credits + +Thanks to everyone who directly contributed to this release: + +- Konstantin Akimov (knst) +- PastaPastaPasta +- UdjinM6 + +As well as everyone that submitted issues, reviewed pull requests and helped +debug the release candidates. + +# Older releases + +Dash was previously known as Darkcoin. + +Darkcoin tree 0.8.x was a fork of Litecoin tree 0.8, original name was XCoin +which was first released on Jan/18/2014. + +Darkcoin tree 0.9.x was the open source implementation of masternodes based on +the 0.8.x tree and was first released on Mar/13/2014. + +Darkcoin tree 0.10.x used to be the closed source implementation of Darksend +which was released open source on Sep/25/2014. + +Dash Core tree 0.11.x was a fork of Bitcoin Core tree 0.9, +Darkcoin was rebranded to Dash. + +Dash Core tree 0.12.0.x was a fork of Bitcoin Core tree 0.10. + +Dash Core tree 0.12.1.x was a fork of Bitcoin Core tree 0.12. + +These release are considered obsolete. Old release notes can be found here: + +- [v20.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.0.md) released November/15/2023 +- [v19.3.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.3.0.md) released July/31/2023 +- [v19.2.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.2.0.md) released June/19/2023 +- [v19.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.1.0.md) released May/22/2023 +- [v19.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.0.0.md) released Apr/14/2023 +- [v18.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.2.md) released Mar/21/2023 +- [v18.2.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.1.md) released Jan/17/2023 +- [v18.2.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.0.md) released Jan/01/2023 +- [v18.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.1.1.md) released January/08/2023 +- [v18.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.1.0.md) released October/09/2022 +- [v18.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.0.2.md) released October/09/2022 +- [v18.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.0.1.md) released August/17/2022 +- [v0.17.0.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.17.0.3.md) released June/07/2021 +- [v0.17.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.17.0.2.md) released May/19/2021 +- [v0.16.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.1.1.md) released November/17/2020 +- [v0.16.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.1.0.md) released November/14/2020 +- [v0.16.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.0.1.md) released September/30/2020 +- [v0.15.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.15.0.0.md) released Febrary/18/2020 +- [v0.14.0.5](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.5.md) released December/08/2019 +- [v0.14.0.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.4.md) released November/22/2019 +- [v0.14.0.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.3.md) released August/15/2019 +- [v0.14.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.2.md) released July/4/2019 +- [v0.14.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.1.md) released May/31/2019 +- [v0.14.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.md) released May/22/2019 +- [v0.13.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.3.md) released Apr/04/2019 +- [v0.13.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.2.md) released Mar/15/2019 +- [v0.13.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.1.md) released Feb/9/2019 +- [v0.13.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.0.md) released Jan/14/2019 +- [v0.12.3.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.4.md) released Dec/14/2018 +- [v0.12.3.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.3.md) released Sep/19/2018 +- [v0.12.3.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.2.md) released Jul/09/2018 +- [v0.12.3.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.1.md) released Jul/03/2018 +- [v0.12.2.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.3.md) released Jan/12/2018 +- [v0.12.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.2.md) released Dec/17/2017 +- [v0.12.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.md) released Nov/08/2017 +- [v0.12.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.1.md) released Feb/06/2017 +- [v0.12.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.0.md) released Aug/15/2015 +- [v0.11.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.2.md) released Mar/04/2015 +- [v0.11.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.1.md) released Feb/10/2015 +- [v0.11.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.0.md) released Jan/15/2015 +- [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014 +- [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014 + +[set-of-changes]: https://github.com/dashpay/dash/compare/v20.0.0...dashpay:v20.0.1 diff --git a/src/Makefile.am b/src/Makefile.am index c8cdc24e576fc..a9354896e56a5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -169,6 +169,8 @@ BITCOIN_CORE_H = \ cuckoocache.h \ ctpl_stl.h \ cxxtimer.hpp \ + deploymentinfo.h \ + deploymentstatus.h \ evo/assetlocktx.h \ evo/dmn_types.h \ evo/cbtx.h \ @@ -349,7 +351,6 @@ BITCOIN_CORE_H = \ validation.h \ validationinterface.h \ versionbits.h \ - versionbitsinfo.h \ walletinitinterface.h \ wallet/bdb.h \ wallet/coincontrol.h \ @@ -403,6 +404,7 @@ libbitcoin_server_a_SOURCES = \ coinjoin/server.cpp \ consensus/tx_verify.cpp \ dbwrapper.cpp \ + deploymentstatus.cpp \ dsnotificationinterface.cpp \ evo/assetlocktx.cpp \ evo/cbtx.cpp \ @@ -696,6 +698,7 @@ libbitcoin_common_a_SOURCES = \ compressor.cpp \ core_read.cpp \ core_write.cpp \ + deploymentinfo.cpp \ key.cpp \ key_io.cpp \ merkleblock.cpp \ @@ -715,7 +718,6 @@ libbitcoin_common_a_SOURCES = \ script/sign.cpp \ script/signingprovider.cpp \ script/standard.cpp \ - versionbitsinfo.cpp \ warnings.cpp \ $(BITCOIN_CORE_H) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 34ded8f7c244f..9e485057de75e 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -8,12 +8,12 @@ #include #include +#include #include #include #include #include #include -#include #include diff --git a/src/consensus/params.h b/src/consensus/params.h index 6c0c25c75da05..5962b2028e0d6 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -14,13 +14,32 @@ namespace Consensus { -enum DeploymentPos { +enum BuriedDeployment : int16_t +{ + DEPLOYMENT_HEIGHTINCB = std::numeric_limits::min(), + DEPLOYMENT_DERSIG, + DEPLOYMENT_CLTV, + DEPLOYMENT_BIP147, + DEPLOYMENT_CSV, + DEPLOYMENT_DIP0001, + DEPLOYMENT_DIP0003, + DEPLOYMENT_DIP0008, + DEPLOYMENT_DIP0020, + DEPLOYMENT_DIP0024, + DEPLOYMENT_BRR, + DEPLOYMENT_V19, +}; +constexpr bool ValidDeployment(BuriedDeployment dep) { return DEPLOYMENT_HEIGHTINCB <= dep && dep <= DEPLOYMENT_V19; } + +enum DeploymentPos : uint16_t +{ DEPLOYMENT_TESTDUMMY, DEPLOYMENT_V20, // Deployment of EHF, LLMQ Randomness Beacon DEPLOYMENT_MN_RR, // Deployment of Masternode Reward Location Reallocation - // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp + // NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp MAX_VERSION_BITS_DEPLOYMENTS }; +constexpr bool ValidDeployment(DeploymentPos dep) { return DEPLOYMENT_TESTDUMMY <= dep && dep <= DEPLOYMENT_MN_RR; } /** * Struct for each individual consensus rule change using BIP9. @@ -145,7 +164,39 @@ struct Params { LLMQType llmqTypePlatform{LLMQType::LLMQ_NONE}; LLMQType llmqTypeMnhf{LLMQType::LLMQ_NONE}; LLMQType llmqTypeAssetLocks{LLMQType::LLMQ_NONE}; + + int DeploymentHeight(BuriedDeployment dep) const + { + switch (dep) { + case DEPLOYMENT_HEIGHTINCB: + return BIP34Height; + case DEPLOYMENT_DERSIG: + return BIP66Height; + case DEPLOYMENT_CLTV: + return BIP65Height; + case DEPLOYMENT_BIP147: + return BIP147Height; + case DEPLOYMENT_CSV: + return CSVHeight; + case DEPLOYMENT_DIP0001: + return DIP0001Height; + case DEPLOYMENT_DIP0003: + return DIP0003Height; + case DEPLOYMENT_DIP0008: + return DIP0008Height; + case DEPLOYMENT_DIP0020: + return DIP0020Height; + case DEPLOYMENT_DIP0024: + return DIP0024Height; + case DEPLOYMENT_BRR: + return BRRHeight; + case DEPLOYMENT_V19: + return V19Height; + } // no default case, so the compiler can warn about missing cases + return std::numeric_limits::max(); + } }; + } // namespace Consensus #endif // BITCOIN_CONSENSUS_PARAMS_H diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp new file mode 100644 index 0000000000000..17da5d3d36fa5 --- /dev/null +++ b/src/deploymentinfo.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2016-2020 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 + +#include + +const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { + { + /*.name =*/ "testdummy", + /*.gbt_force =*/ true, + }, + { + /*.name =*/"v20", + /*.gbt_force =*/true, + }, + { + /*.name =*/"mn_rr", + /*.gbt_force =*/true, + }, +}; + +std::string DeploymentName(Consensus::BuriedDeployment dep) +{ + assert(ValidDeployment(dep)); + switch (dep) { + case Consensus::DEPLOYMENT_HEIGHTINCB: + return "bip34"; + case Consensus::DEPLOYMENT_CLTV: + return "bip65"; + case Consensus::DEPLOYMENT_DERSIG: + return "bip66"; + case Consensus::DEPLOYMENT_BIP147: + return "bip147"; + case Consensus::DEPLOYMENT_CSV: + return "csv"; + case Consensus::DEPLOYMENT_DIP0001: + return "dip0001"; + case Consensus::DEPLOYMENT_DIP0003: + return "dip0003"; + case Consensus::DEPLOYMENT_DIP0008: + return "dip0008"; + case Consensus::DEPLOYMENT_DIP0020: + return "dip0020"; + case Consensus::DEPLOYMENT_DIP0024: + return "dip0024"; + case Consensus::DEPLOYMENT_BRR: + return "realloc"; + case Consensus::DEPLOYMENT_V19: + return "v19"; + } // no default case, so the compiler can warn about missing cases + return ""; +} diff --git a/src/deploymentinfo.h b/src/deploymentinfo.h new file mode 100644 index 0000000000000..63d58a7da2539 --- /dev/null +++ b/src/deploymentinfo.h @@ -0,0 +1,29 @@ +// Copyright (c) 2016-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_DEPLOYMENTINFO_H +#define BITCOIN_DEPLOYMENTINFO_H + +#include + +#include + +struct VBDeploymentInfo { + /** Deployment name */ + const char *name; + /** Whether GBT clients can safely ignore this rule in simplified usage */ + bool gbt_force; +}; + +extern const VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS]; + +std::string DeploymentName(Consensus::BuriedDeployment dep); + +inline std::string DeploymentName(Consensus::DeploymentPos pos) +{ + assert(Consensus::ValidDeployment(pos)); + return VersionBitsDeploymentInfo[pos].name; +} + +#endif // BITCOIN_DEPLOYMENTINFO_H diff --git a/src/deploymentstatus.cpp b/src/deploymentstatus.cpp new file mode 100644 index 0000000000000..9007800421870 --- /dev/null +++ b/src/deploymentstatus.cpp @@ -0,0 +1,17 @@ +// Copyright (c) 2020 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 + +#include +#include + +VersionBitsCache g_versionbitscache; + +/* Basic sanity checking for BuriedDeployment/DeploymentPos enums and + * ValidDeployment check */ + +static_assert(ValidDeployment(Consensus::DEPLOYMENT_TESTDUMMY), "sanity check of DeploymentPos failed (TESTDUMMY not valid)"); +static_assert(!ValidDeployment(Consensus::MAX_VERSION_BITS_DEPLOYMENTS), "sanity check of DeploymentPos failed (MAX value considered valid)"); +static_assert(!ValidDeployment(static_cast(Consensus::DEPLOYMENT_TESTDUMMY)), "sanity check of BuriedDeployment failed (overlaps with DeploymentPos)"); diff --git a/src/deploymentstatus.h b/src/deploymentstatus.h new file mode 100644 index 0000000000000..84c5e54698c99 --- /dev/null +++ b/src/deploymentstatus.h @@ -0,0 +1,55 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_DEPLOYMENTSTATUS_H +#define BITCOIN_DEPLOYMENTSTATUS_H + +#include +#include + +#include + +/** Global cache for versionbits deployment status */ +extern VersionBitsCache g_versionbitscache; + +/** Determine if a deployment is active for the next block */ +inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::BuriedDeployment dep) +{ + assert(Consensus::ValidDeployment(dep)); + return (pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1) >= params.DeploymentHeight(dep); +} + +inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos dep) +{ + assert(Consensus::ValidDeployment(dep)); + return ThresholdState::ACTIVE == g_versionbitscache.State(pindexPrev, params, dep); +} + +/** Determine if a deployment is active for this block */ +inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::BuriedDeployment dep) +{ + assert(Consensus::ValidDeployment(dep)); + return index.nHeight >= params.DeploymentHeight(dep); +} + +inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::DeploymentPos dep) +{ + assert(Consensus::ValidDeployment(dep)); + return DeploymentActiveAfter(index.pprev, params, dep); +} + +/** Determine if a deployment is enabled (can ever be active) */ +inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::BuriedDeployment dep) +{ + assert(Consensus::ValidDeployment(dep)); + return params.DeploymentHeight(dep) != std::numeric_limits::max(); +} + +inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::DeploymentPos dep) +{ + assert(Consensus::ValidDeployment(dep)); + return params.vDeployments[dep].nTimeout != 0; +} + +#endif // BITCOIN_DEPLOYMENTSTATUS_H diff --git a/src/evo/mnhftx.cpp b/src/evo/mnhftx.cpp index 34a031e23ec3b..853ff3efc2df9 100644 --- a/src/evo/mnhftx.cpp +++ b/src/evo/mnhftx.cpp @@ -3,12 +3,14 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include #include #include #include #include +#include #include #include @@ -16,6 +18,7 @@ #include #include +#include #include #include @@ -52,7 +55,7 @@ CMNHFManager::~CMNHFManager() CMNHFManager::Signals CMNHFManager::GetSignalsStage(const CBlockIndex* const pindexPrev) { - Signals signals = GetFromCache(pindexPrev); + Signals signals = GetForBlock(pindexPrev); const int height = pindexPrev->nHeight + 1; for (auto it = signals.begin(); it != signals.end(); ) { bool found{false}; @@ -98,7 +101,7 @@ bool MNHFTx::Verify(const uint256& quorumHash, const uint256& requestId, const u return true; } -bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) { if (tx.nVersion != 3 || tx.nType != TRANSACTION_MNHF_SIGNAL) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-type"); @@ -113,7 +116,7 @@ bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValida return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-version"); } - const CBlockIndex* pindexQuorum = g_chainman.m_blockman.LookupBlockIndex(mnhfTx.signal.quorumHash); + const CBlockIndex* pindexQuorum = WITH_LOCK(::cs_main, return g_chainman.m_blockman.LookupBlockIndex(mnhfTx.signal.quorumHash)); if (!pindexQuorum) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-quorum-hash"); } @@ -159,8 +162,6 @@ std::optional extractEHFSignal(const CTransaction& tx) static bool extractSignals(const CBlock& block, const CBlockIndex* const pindex, std::vector& new_signals, BlockValidationState& state) { - AssertLockHeld(cs_main); - // we skip the coinbase for (size_t i = 1; i < block.vtx.size(); ++i) { const CTransaction& tx = *block.vtx[i]; @@ -189,13 +190,13 @@ static bool extractSignals(const CBlock& block, const CBlockIndex* const pindex, return true; } -bool CMNHFManager::ProcessBlock(const CBlock& block, const CBlockIndex* const pindex, bool fJustCheck, BlockValidationState& state) +std::optional CMNHFManager::ProcessBlock(const CBlock& block, const CBlockIndex* const pindex, bool fJustCheck, BlockValidationState& state) { try { std::vector new_signals; if (!extractSignals(block, pindex, new_signals, state)) { // state is set inside extractSignals - return false; + return std::nullopt; } Signals signals = GetSignalsStage(pindex->pprev); if (new_signals.empty()) { @@ -203,25 +204,27 @@ bool CMNHFManager::ProcessBlock(const CBlock& block, const CBlockIndex* const pi AddToCache(signals, pindex); } LogPrint(BCLog::EHF, "CMNHFManager::ProcessBlock: no new signals; number of known signals: %d\n", signals.size()); - return true; + return signals; } - int mined_height = pindex->nHeight; + const int mined_height = pindex->nHeight; // Extra validation of signals to be sure that it can succeed for (const auto& versionBit : new_signals) { LogPrintf("CMNHFManager::ProcessBlock: add mnhf bit=%d block:%s number of known signals:%lld\n", versionBit, pindex->GetBlockHash().ToString(), signals.size()); if (signals.find(versionBit) != signals.end()) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-mnhf-duplicate"); + state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-mnhf-duplicate"); + return std::nullopt; } if (!Params().IsValidMNActivation(versionBit, pindex->GetMedianTimePast())) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-mnhf-non-mn-fork"); + state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-mnhf-non-mn-fork"); + return std::nullopt; } } if (fJustCheck) { // We are done, no need actually update any params - return true; + return signals; } for (const auto& versionBit : new_signals) { if (Params().IsValidMNActivation(versionBit, pindex->GetMedianTimePast())) { @@ -231,10 +234,11 @@ bool CMNHFManager::ProcessBlock(const CBlock& block, const CBlockIndex* const pi } AddToCache(signals, pindex); - return true; + return signals; } catch (const std::exception& e) { LogPrintf("CMNHFManager::ProcessBlock -- failed: %s\n", e.what()); - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-proc-mnhf-inblock"); + state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-proc-mnhf-inblock"); + return std::nullopt; } } @@ -250,7 +254,7 @@ bool CMNHFManager::UndoBlock(const CBlock& block, const CBlockIndex* const pinde return true; } - const Signals signals = GetFromCache(pindex); + const Signals signals = GetForBlock(pindex); for (const auto& versionBit : excluded_signals) { LogPrintf("%s: exclude mnhf bit=%d block:%s number of known signals:%lld\n", __func__, versionBit, pindex->GetBlockHash().ToString(), signals.size()); assert(signals.find(versionBit) != signals.end()); @@ -260,34 +264,68 @@ bool CMNHFManager::UndoBlock(const CBlock& block, const CBlockIndex* const pinde return true; } -CMNHFManager::Signals CMNHFManager::GetFromCache(const CBlockIndex* const pindex) +CMNHFManager::Signals CMNHFManager::GetForBlock(const CBlockIndex* pindex) { if (pindex == nullptr) return {}; + std::stack to_calculate; + + std::optional signalsTmp; + while (!(signalsTmp = GetFromCache(pindex)).has_value()) { + to_calculate.push(pindex); + pindex = pindex->pprev; + } + + const Consensus::Params& consensusParams{Params().GetConsensus()}; + while (!to_calculate.empty()) { + const CBlockIndex* pindex_top{to_calculate.top()}; + CBlock block; + if (!ReadBlockFromDisk(block, pindex_top, consensusParams)) { + throw std::runtime_error("failed-getehfforblock-read"); + } + BlockValidationState state; + signalsTmp = ProcessBlock(block, pindex_top, false, state); + if (!signalsTmp.has_value()) { + LogPrintf("%s: process block failed due to %s\n", __func__, state.ToString()); + throw std::runtime_error("failed-getehfforblock-construct"); + } + + to_calculate.pop(); + } + return *signalsTmp; +} + +std::optional CMNHFManager::GetFromCache(const CBlockIndex* const pindex) +{ + Signals signals{}; + if (pindex == nullptr) return signals; + // TODO: remove this check of phashBlock to nullptr // This check is needed only because unit test 'versionbits_tests.cpp' // lets `phashBlock` to be nullptr - if (pindex->phashBlock == nullptr) return {}; + if (pindex->phashBlock == nullptr) return signals; const uint256& blockHash = pindex->GetBlockHash(); - Signals signals{}; { LOCK(cs_cache); if (mnhfCache.get(blockHash, signals)) { return signals; } } - if (VersionBitsState(pindex->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_V20, versionbitscache) != ThresholdState::ACTIVE) { + { LOCK(cs_cache); - mnhfCache.insert(blockHash, {}); - return {}; + if (ThresholdState::ACTIVE != v20_activation.State(pindex->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_V20)) { + mnhfCache.insert(blockHash, signals); + return signals; + } } - bool ok = m_evoDb.Read(std::make_pair(DB_SIGNALS, blockHash), signals); - assert(ok); - LOCK(cs_cache); - mnhfCache.insert(blockHash, signals); - return signals; + if (m_evoDb.Read(std::make_pair(DB_SIGNALS, blockHash), signals)) { + LOCK(cs_cache); + mnhfCache.insert(blockHash, signals); + return signals; + } + return std::nullopt; } void CMNHFManager::AddToCache(const Signals& signals, const CBlockIndex* const pindex) @@ -297,15 +335,17 @@ void CMNHFManager::AddToCache(const Signals& signals, const CBlockIndex* const p LOCK(cs_cache); mnhfCache.insert(blockHash, signals); } - if (VersionBitsState(pindex->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_V20, versionbitscache) != ThresholdState::ACTIVE) { - return; + assert(pindex != nullptr); + { + LOCK(cs_cache); + if (ThresholdState::ACTIVE != v20_activation.State(pindex->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_V20)) return; } m_evoDb.Write(std::make_pair(DB_SIGNALS, blockHash), signals); } void CMNHFManager::AddSignal(const CBlockIndex* const pindex, int bit) { - auto signals = GetFromCache(pindex->pprev); + auto signals = GetForBlock(pindex->pprev); signals.emplace(bit, pindex->nHeight); AddToCache(signals, pindex); } diff --git a/src/evo/mnhftx.h b/src/evo/mnhftx.h index c130045643ef6..73983a5540456 100644 --- a/src/evo/mnhftx.h +++ b/src/evo/mnhftx.h @@ -22,7 +22,6 @@ class CBlock; class CBlockIndex; class CEvoDB; class TxValidationState; -extern RecursiveMutex cs_main; // mnhf signal special transaction class MNHFTx @@ -102,20 +101,25 @@ class CMNHFManager : public AbstractEHFManager // versionBit <-> height unordered_lru_cache mnhfCache GUARDED_BY(cs_cache) {MNHFCacheSize}; + // This cache is used only for v20 activation to avoid double lock throught VersionBitsConditionChecker::SignalHeight + VersionBitsCache v20_activation GUARDED_BY(cs_cache); public: explicit CMNHFManager(CEvoDB& evoDb); ~CMNHFManager(); explicit CMNHFManager(const CMNHFManager&) = delete; /** - * Every new block should be processed when Tip() is updated by calling of CMNHFManager::ProcessBlock + * Every new block should be processed when Tip() is updated by calling of CMNHFManager::ProcessBlock. + * This function actually does only validate EHF transaction for this block and update internal caches/evodb state */ - bool ProcessBlock(const CBlock& block, const CBlockIndex* const pindex, bool fJustCheck, BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + std::optional ProcessBlock(const CBlock& block, const CBlockIndex* const pindex, bool fJustCheck, BlockValidationState& state); /** * Every undo block should be processed when Tip() is updated by calling of CMNHFManager::UndoBlock + * This function actually does nothing at the moment, because status of ancester block is already know. + * Altough it should be still called to do some sanity checks */ - bool UndoBlock(const CBlock& block, const CBlockIndex* const pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool UndoBlock(const CBlock& block, const CBlockIndex* const pindex); // Implements interface @@ -130,13 +134,20 @@ class CMNHFManager : public AbstractEHFManager /** * This function returns list of signals available on previous block. + * if the signals for previous block is not available in cache it would read blocks from disk + * until state won't be recovered. * NOTE: that some signals could expired between blocks. - * validate them by */ - Signals GetFromCache(const CBlockIndex* const pindex); + Signals GetForBlock(const CBlockIndex* const pindex); + + /** + * This function access to in-memory cache or to evo db but does not calculate anything + * NOTE: that some signals could expired between blocks. + */ + std::optional GetFromCache(const CBlockIndex* const pindex); }; std::optional extractEHFSignal(const CTransaction& tx); -bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state); #endif // BITCOIN_EVO_MNHFTX_H diff --git a/src/governance/classes.cpp b/src/governance/classes.cpp index d3444a90f06be..2182f2ad93b8c 100644 --- a/src/governance/classes.cpp +++ b/src/governance/classes.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include diff --git a/src/init.cpp b/src/init.cpp index 8a5d30fa271a4..e59bab8d3074a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1912,6 +1912,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc LOCK(cs_main); node.evodb.reset(); node.evodb = std::make_unique(nEvoDbCache, false, fReset || fReindexChainState); + node.mnhf_manager.reset(); node.mnhf_manager = std::make_unique(*node.evodb); chainman.Reset(); @@ -1933,8 +1934,15 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc creditPoolManager.reset(new CCreditPoolManager(*node.evodb)); llmq::quorumSnapshotManager.reset(); llmq::quorumSnapshotManager.reset(new llmq::CQuorumSnapshotManager(*node.evodb)); + + if (node.llmq_ctx) { + node.llmq_ctx->Interrupt(); + node.llmq_ctx->Stop(); + } node.llmq_ctx.reset(); node.llmq_ctx.reset(new LLMQContext(chainman.ActiveChainstate(), *node.connman, *node.evodb, *::sporkManager, *node.mempool, node.peerman, false, fReset || fReindexChainState)); + // Have to start it early to let VerifyDB check ChainLock signatures in coinbase + node.llmq_ctx->Start(); if (fReset) { pblocktree->WriteReindexing(true); @@ -2120,6 +2128,10 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load()); } + if (args.GetArg("-checklevel", DEFAULT_CHECKLEVEL) >= 3) { + chainstate->ResetBlockFailureFlags(nullptr); + } + } else { // TODO: CEvoDB instance should probably be a part of CChainState // (for multiple chainstates to actually work in parallel) @@ -2131,10 +2143,6 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc break; } } - - if (args.GetArg("-checklevel", DEFAULT_CHECKLEVEL) >= 3) { - ::ChainstateActive().ResetBlockFailureFlags(nullptr); - } } } catch (const std::exception& e) { LogPrintf("%s\n", e.what()); @@ -2306,8 +2314,6 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc node.scheduler->scheduleEvery(std::bind(&PeriodicStats, std::ref(*node.args), std::cref(*node.mempool)), std::chrono::seconds{nStatsPeriod}); } - node.llmq_ctx->Start(); - // ********************************************************* Step 11: import blocks if (!CheckDiskSpace(GetDataDir())) { diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index 2162f36ed3669..35836755619aa 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -60,10 +60,7 @@ LLMQContext::~LLMQContext() { llmq::chainLocksHandler.reset(); llmq::quorumManager.reset(); llmq::quorumBlockProcessor.reset(); - { - LOCK(llmq::cs_llmq_vbc); - llmq::llmq_versionbitscache.Clear(); - } + llmq::llmq_versionbitscache.Clear(); } void LLMQContext::Interrupt() { diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index fb20dbb36889d..5f8f5ccee03a9 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -466,7 +466,8 @@ void CDKGSessionManager::CleanupOldContributions() const for (const auto& params : Params().GetConsensus().llmqs) { // For how many blocks recent DKG info should be kept - const int MAX_STORE_DEPTH = 2 * params.signingActiveQuorumCount * params.dkgInterval; + const int MAX_CYCLES = params.useRotation ? params.keepOldKeys / params.signingActiveQuorumCount : params.keepOldKeys; + const int MAX_STORE_DEPTH = MAX_CYCLES * params.dkgInterval; LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- looking for old entries for llmq type %d\n", __func__, ToUnderlying(params.type)); diff --git a/src/llmq/params.h b/src/llmq/params.h index 3076674fdb9e1..4d2cfae9e0f2c 100644 --- a/src/llmq/params.h +++ b/src/llmq/params.h @@ -104,6 +104,13 @@ struct LLMQParams { // For rotated quorums it should be equal to 2 x active quorums set. int keepOldConnections; + // The number of quorums for which we should keep keys. Usually it's equal to signingActiveQuorumCount * 2. + // Unlike for other quorum types we want to keep data (secret key shares and vvec) + // for Platform quorums for much longer because Platform can be restarted and + // it must be able to re-sign stuff. + + int keepOldKeys; + // How many members should we try to send all sigShares to before we give up. int recoveryMembers; }; @@ -138,6 +145,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 2, // just a few ones to allow easier testing .keepOldConnections = 3, + .keepOldKeys = 4, .recoveryMembers = 3, }, @@ -163,6 +171,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 2, // just a few ones to allow easier testing .keepOldConnections = 3, + .keepOldKeys = 4, .recoveryMembers = 3, }, @@ -188,6 +197,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 2, // just a few ones to allow easier testing .keepOldConnections = 3, + .keepOldKeys = 4, .recoveryMembers = 3, }, @@ -213,6 +223,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 2, // just a few ones to allow easier testing .keepOldConnections = 4, + .keepOldKeys = 4, .recoveryMembers = 3, }, @@ -238,6 +249,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 2, // just a few ones to allow easier testing .keepOldConnections = 4, + .keepOldKeys = 24 * 30 * 2, // 2 months of quorums .recoveryMembers = 3, }, @@ -263,6 +275,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 4, // just a few ones to allow easier testing .keepOldConnections = 5, + .keepOldKeys = 8, .recoveryMembers = 6, }, @@ -288,6 +301,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 2, // just a few ones to allow easier testing .keepOldConnections = 4, + .keepOldKeys = 4, .recoveryMembers = 4, }, @@ -313,6 +327,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 4, // just a few ones to allow easier testing .keepOldConnections = 5, + .keepOldKeys = 24 * 30 * 2, // 2 months of quorums .recoveryMembers = 6, }, @@ -338,6 +353,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 24, // a full day worth of LLMQs .keepOldConnections = 25, + .keepOldKeys = 48, .recoveryMembers = 25, }, @@ -363,6 +379,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 32, .keepOldConnections = 64, + .keepOldKeys = 64, .recoveryMembers = 25, }, @@ -389,6 +406,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 4, // two days worth of LLMQs .keepOldConnections = 5, + .keepOldKeys = 8, .recoveryMembers = 100, }, @@ -416,6 +434,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 4, // four days worth of LLMQs .keepOldConnections = 5, + .keepOldKeys = 8, .recoveryMembers = 100, }, @@ -443,6 +462,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 24, // a full day worth of LLMQs .keepOldConnections = 25, + .keepOldKeys = 24 * 30 * 2, // 2 months of quorums .recoveryMembers = 50, }, @@ -470,6 +490,7 @@ static constexpr std::array available_llmqs = { .signingActiveQuorumCount = 24, // a full day worth of LLMQs .keepOldConnections = 25, + .keepOldKeys = 24 * 30 * 2, // 2 months of quorums .recoveryMembers = 12, }, diff --git a/src/llmq/quorums.cpp b/src/llmq/quorums.cpp index 1d0991f942366..49c6fcd215c89 100644 --- a/src/llmq/quorums.cpp +++ b/src/llmq/quorums.cpp @@ -200,8 +200,8 @@ CQuorumManager::CQuorumManager(CBLSWorker& _blsWorker, CChainState& chainstate, m_mn_sync(mn_sync), m_peerman(peerman) { - utils::InitQuorumsCache(mapQuorumsCache); - utils::InitQuorumsCache(scanQuorumsCache); + utils::InitQuorumsCache(mapQuorumsCache, false); + utils::InitQuorumsCache(scanQuorumsCache, false); quorumThreadInterrupt.reset(); } @@ -296,7 +296,7 @@ void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitial } TriggerQuorumDataRecoveryThreads(pindexNew); - CleanupOldQuorumData(pindexNew); + StartCleanupOldQuorumDataThread(pindexNew); } void CQuorumManager::CheckQuorumConnections(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pindexNew) const @@ -824,7 +824,7 @@ void CQuorumManager::StartCachePopulatorThread(const CQuorumCPtr pQuorum) const // when then later some other thread tries to get keys, it will be much faster workerPool.push([pQuorum, t, this](int threadId) { for (const auto i : irange::range(pQuorum->members.size())) { - if (!quorumThreadInterrupt) { + if (quorumThreadInterrupt) { break; } if (pQuorum->qc->validMembers[i]) { @@ -956,7 +956,7 @@ void CQuorumManager::StartQuorumDataRecoveryThread(const CQuorumCPtr pQuorum, co }); } -static void DataCleanupHelper(CDBWrapper& db, std::set skip_list) +static void DataCleanupHelper(CDBWrapper& db, std::set skip_list, bool compact = false) { const auto prefixes = {DB_QUORUM_QUORUM_VVEC, DB_QUORUM_SK_SHARE}; @@ -990,39 +990,54 @@ static void DataCleanupHelper(CDBWrapper& db, std::set skip_list) db.WriteBatch(batch); - LogPrint(BCLog::LLMQ, "CQuorumManager::%d -- %s removed %d\n", __func__, prefix, count); + LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- %s removed %d\n", __func__, prefix, count); } pcursor.reset(); - db.CompactFull(); + + if (compact) { + // Avoid using this on regular cleanups, use on db migrations only + LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- compact start\n", __func__); + db.CompactFull(); + LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- compact end\n", __func__); + } } -void CQuorumManager::CleanupOldQuorumData(const CBlockIndex* pIndex) const +void CQuorumManager::StartCleanupOldQuorumDataThread(const CBlockIndex* pIndex) const { - if (!fMasternodeMode || pIndex == nullptr || (pIndex->nHeight % 576 != 0)) { + // Note: this function is CPU heavy and we don't want it to be running during DKGs. + // The largest dkgMiningWindowStart for a related quorum type is 42 (LLMQ_60_75). + // At the same time most quorums use dkgInterval = 24 so the next DKG for them + // (after block 576 + 42) will start at block 576 + 24 * 2. That's only a 6 blocks + // window and it's better to have more room so we pick next cycle. + // dkgMiningWindowStart for small quorums is 10 i.e. a safe block to start + // these calculations is at height 576 + 24 * 2 + 10 = 576 + 58. + if (!fMasternodeMode || pIndex == nullptr || (pIndex->nHeight % 576 != 58)) { return; } - std::set dbKeysToSkip; + cxxtimer::Timer t(/*start=*/ true); + LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- start\n", __func__); - LogPrint(BCLog::LLMQ, "CQuorumManager::%d -- start\n", __func__); - // Platform quorums in all networks are created every 24 blocks (~1h). - // Unlike for other quorum types we want to keep data (secret key shares and vvec) - // for Platform quorums for at least 2 months because Platform can be restarted and - // it must be able to re-sign stuff. During a month, 24 * 30 quorums are created. - constexpr auto numPlatformQuorumsDataToKeep = 24 * 30 * 2; + // do not block the caller thread + workerPool.push([pIndex, t, this](int threadId) { + std::set dbKeysToSkip; - for (const auto& params : Params().GetConsensus().llmqs) { - auto nQuorumsToKeep = params.type == Params().GetConsensus().llmqTypePlatform ? numPlatformQuorumsDataToKeep : params.keepOldConnections; - const auto vecQuorums = ScanQuorums(params.type, pIndex, nQuorumsToKeep); - for (const auto& pQuorum : vecQuorums) { - dbKeysToSkip.insert(MakeQuorumKey(*pQuorum)); + for (const auto& params : Params().GetConsensus().llmqs) { + if (quorumThreadInterrupt) { + break; + } + for (const auto& pQuorum : ScanQuorums(params.type, pIndex, params.keepOldKeys)) { + dbKeysToSkip.insert(MakeQuorumKey(*pQuorum)); + } } - } - DataCleanupHelper(m_evoDb.GetRawDB(), dbKeysToSkip); + if (!quorumThreadInterrupt) { + DataCleanupHelper(m_evoDb.GetRawDB(), dbKeysToSkip); + } - LogPrint(BCLog::LLMQ, "CQuorumManager::%d -- done\n", __func__); + LogPrint(BCLog::LLMQ, "CQuorumManager::StartCleanupOldQuorumDataThread -- done. time=%d\n", t.count()); + }); } } // namespace llmq diff --git a/src/llmq/quorums.h b/src/llmq/quorums.h index bc2eba6ae4250..976ba0f300789 100644 --- a/src/llmq/quorums.h +++ b/src/llmq/quorums.h @@ -277,7 +277,7 @@ class CQuorumManager void StartCachePopulatorThread(const CQuorumCPtr pQuorum) const; void StartQuorumDataRecoveryThread(const CQuorumCPtr pQuorum, const CBlockIndex* pIndex, uint16_t nDataMask) const; - void CleanupOldQuorumData(const CBlockIndex* pIndex) const; + void StartCleanupOldQuorumDataThread(const CBlockIndex* pIndex) const; }; extern std::unique_ptr quorumManager; diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index 83fbe9b214022..8105ac552079c 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -35,7 +35,6 @@ std::optional> GetNonNullCoinbaseChainlock(co namespace llmq { -Mutex cs_llmq_vbc; VersionBitsCache llmq_versionbitscache; namespace utils @@ -708,28 +707,24 @@ bool IsV19Active(gsl::not_null pindex) bool IsV20Active(gsl::not_null pindex) { - LOCK(cs_llmq_vbc); - return VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20, llmq_versionbitscache) == ThresholdState::ACTIVE; + return llmq_versionbitscache.State(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20) == ThresholdState::ACTIVE; } bool IsMNRewardReallocationActive(gsl::not_null pindex) { if (!IsV20Active(pindex)) return false; - LOCK(cs_llmq_vbc); - return VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_MN_RR, llmq_versionbitscache) == ThresholdState::ACTIVE; + return llmq_versionbitscache.State(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_MN_RR) == ThresholdState::ACTIVE; } ThresholdState GetV20State(gsl::not_null pindex) { - LOCK(cs_llmq_vbc); - return VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20, llmq_versionbitscache); + return llmq_versionbitscache.State(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20); } int GetV20Since(gsl::not_null pindex) { - LOCK(cs_llmq_vbc); - return VersionBitsStateSinceHeight(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20, llmq_versionbitscache); + return llmq_versionbitscache.StateSinceHeight(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20); } bool IsInstantSendLLMQTypeShared() @@ -1005,8 +1000,7 @@ bool IsQuorumTypeEnabledInternal(Consensus::LLMQType llmqType, const CQuorumMana return true; case Consensus::LLMQType::LLMQ_TEST_V17: { - LOCK(cs_llmq_vbc); - return VersionBitsState(pindex, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY, llmq_versionbitscache) == ThresholdState::ACTIVE; + return llmq_versionbitscache.State(pindex, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY) == ThresholdState::ACTIVE; } case Consensus::LLMQType::LLMQ_100_67: return pindex->nHeight + 1 >= consensusParams.DIP0020Height; @@ -1110,17 +1104,17 @@ std::map GetEnabledQuorumVvecSyncEntries() } template -void InitQuorumsCache(CacheType& cache) +void InitQuorumsCache(CacheType& cache, bool limit_by_connections) { for (const auto& llmq : Params().GetConsensus().llmqs) { cache.emplace(std::piecewise_construct, std::forward_as_tuple(llmq.type), - std::forward_as_tuple(llmq.keepOldConnections)); + std::forward_as_tuple(limit_by_connections ? llmq.keepOldConnections : llmq.keepOldKeys)); } } -template void InitQuorumsCache>>(std::map>& cache); -template void InitQuorumsCache, StaticSaltedHasher>>>(std::map, StaticSaltedHasher>>& cache); -template void InitQuorumsCache, StaticSaltedHasher, 0ul, 0ul>, std::less, std::allocator, StaticSaltedHasher, 0ul, 0ul>>>>>(std::map, StaticSaltedHasher, 0ul, 0ul>, std::less, std::allocator, StaticSaltedHasher, 0ul, 0ul>>>>&); -template void InitQuorumsCache>>(std::map>& cache); +template void InitQuorumsCache>>(std::map>& cache, bool limit_by_connections); +template void InitQuorumsCache, StaticSaltedHasher>>>(std::map, StaticSaltedHasher>>& cache, bool limit_by_connections); +template void InitQuorumsCache, StaticSaltedHasher, 0ul, 0ul>, std::less, std::allocator, StaticSaltedHasher, 0ul, 0ul>>>>>(std::map, StaticSaltedHasher, 0ul, 0ul>, std::less, std::allocator, StaticSaltedHasher, 0ul, 0ul>>>>&cache, bool limit_by_connections); +template void InitQuorumsCache>>(std::map>& cache, bool limit_by_connections); } // namespace utils diff --git a/src/llmq/utils.h b/src/llmq/utils.h index 535e279afca67..2db8da2725d61 100644 --- a/src/llmq/utils.h +++ b/src/llmq/utils.h @@ -30,10 +30,10 @@ namespace llmq class CQuorumManager; class CQuorumSnapshot; -// Use a separate cache instance instead of versionbitscache to avoid locking cs_main +// A separate cache instance instead of versionbitscache has been introduced to avoid locking cs_main // and dealing with all kinds of deadlocks. -extern Mutex cs_llmq_vbc; -extern VersionBitsCache llmq_versionbitscache GUARDED_BY(cs_llmq_vbc); +// TODO: drop llmq_versionbitscache completely so far as VersionBitsCache do not uses anymore cs_main +extern VersionBitsCache llmq_versionbitscache; static const bool DEFAULT_ENABLE_QUORUM_DATA_RECOVERY = true; @@ -120,7 +120,7 @@ void IterateNodesRandom(NodesContainer& nodeStates, Continue&& cont, Callback&& } template -void InitQuorumsCache(CacheType& cache); +void InitQuorumsCache(CacheType& cache, bool limit_by_connections = true); } // namespace utils diff --git a/src/masternode/meta.cpp b/src/masternode/meta.cpp index 987fc3ded8c38..44b94a80f9be6 100644 --- a/src/masternode/meta.cpp +++ b/src/masternode/meta.cpp @@ -34,7 +34,7 @@ UniValue CMasternodeMetaInfo::ToJson() const { UniValue ret(UniValue::VOBJ); - auto now = GetTime().count(); + int64_t now = GetTime().count(); ret.pushKV("lastDSQ", nLastDsq); ret.pushKV("mixingTxCount", nMixingTxCount); diff --git a/src/miner.cpp b/src/miner.cpp index a3b7272022888..a9170ca8348dc 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -132,11 +133,11 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc assert(pindexPrev != nullptr); nHeight = pindexPrev->nHeight + 1; - bool fDIP0003Active_context = nHeight >= chainparams.GetConsensus().DIP0003Height; - bool fDIP0008Active_context = nHeight >= chainparams.GetConsensus().DIP0008Height; + bool fDIP0003Active_context = DeploymentActiveAfter(pindexPrev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_DIP0003); + bool fDIP0008Active_context = DeploymentActiveAfter(pindexPrev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_DIP0008); bool fV20Active_context = llmq::utils::IsV20Active(pindexPrev); - pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + pblock->nVersion = g_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); // Non-mainnet only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (Params().NetworkIDString() != CBaseChainParams::MAIN) diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index cb94992a27abb..e0169310384bc 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index de5a74cccbc39..0f3c3a2caa904 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -12,7 +12,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -35,7 +38,7 @@ #include #include #include -#include +#include #include #include @@ -1572,25 +1575,25 @@ static UniValue verifychain(const JSONRPCRequest& request) active_chainstate, Params(), active_chainstate.CoinsTip(), *node.evodb, check_level, check_depth); } -static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int softfork_height, int tip_height) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const Consensus::Params& params, Consensus::BuriedDeployment dep) { // For buried deployments. // A buried deployment is one where the height of the activation has been hardcoded into // the client implementation long after the consensus change has activated. See BIP 90. // Buried deployments with activation height value of // std::numeric_limits::max() are disabled and thus hidden. - if (softfork_height == std::numeric_limits::max()) return; + if (!DeploymentEnabled(params, dep)) return; UniValue rv(UniValue::VOBJ); rv.pushKV("type", "buried"); // getblockchaininfo reports the softfork as active from when the chain height is // one below the activation height - rv.pushKV("active", tip_height + 1 >= softfork_height); - rv.pushKV("height", softfork_height); - softforks.pushKV(name, rv); + rv.pushKV("active", DeploymentActiveAfter(active_chain_tip, params, dep)); + rv.pushKV("height", params.DeploymentHeight(dep)); + softforks.pushKV(DeploymentName(dep), rv); } -static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, const std::unordered_map& signals, UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, const std::unordered_map& signals, UniValue& softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) { // For BIP9 deployments. // Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden. @@ -1599,7 +1602,7 @@ static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, const if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return; UniValue bip9(UniValue::VOBJ); - const ThresholdState thresholdState = VersionBitsState(active_chain_tip, consensusParams, id, versionbitscache); + const ThresholdState thresholdState = g_versionbitscache.State(active_chain_tip, consensusParams, id); switch (thresholdState) { case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break; case ThresholdState::STARTED: bip9.pushKV("status", "started"); break; @@ -1617,12 +1620,12 @@ static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, const if (auto it = signals.find(consensusParams.vDeployments[id].bit); it != signals.end()) { bip9.pushKV("ehf_height", it->second); } - int64_t since_height = VersionBitsStateSinceHeight(active_chain_tip, consensusParams, id, versionbitscache); + int64_t since_height = g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id); bip9.pushKV("since", since_height); if (ThresholdState::STARTED == thresholdState) { UniValue statsUV(UniValue::VOBJ); - BIP9Stats statsStruct = VersionBitsStatistics(active_chain_tip, consensusParams, id, versionbitscache); + BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id); statsUV.pushKV("period", statsStruct.period); statsUV.pushKV("threshold", statsStruct.threshold); statsUV.pushKV("elapsed", statsStruct.elapsed); @@ -1642,7 +1645,7 @@ static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, const } rv.pushKV("active", ThresholdState::ACTIVE == thresholdState); - softforks.pushKV(name, rv); + softforks.pushKV(DeploymentName(id), rv); } UniValue getblockchaininfo(const JSONRPCRequest& request) @@ -1743,23 +1746,23 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) const Consensus::Params& consensusParams = Params().GetConsensus(); UniValue softforks(UniValue::VOBJ); // sorted by activation block - BuriedForkDescPushBack(softforks,"bip34", consensusParams.BIP34Height, height); - BuriedForkDescPushBack(softforks,"bip66", consensusParams.BIP66Height, height); - BuriedForkDescPushBack(softforks,"bip65", consensusParams.BIP65Height, height); - BuriedForkDescPushBack(softforks,"bip147", consensusParams.BIP147Height, height); - BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight, height); - BuriedForkDescPushBack(softforks, "dip0001", consensusParams.DIP0001Height, height); - BuriedForkDescPushBack(softforks, "dip0003", consensusParams.DIP0003Height, height); - BuriedForkDescPushBack(softforks, "dip0008", consensusParams.DIP0008Height, height); - BuriedForkDescPushBack(softforks, "dip0020", consensusParams.DIP0020Height, height); - BuriedForkDescPushBack(softforks, "dip0024", consensusParams.DIP0024Height, height); - BuriedForkDescPushBack(softforks, "realloc", consensusParams.BRRHeight, height); - BuriedForkDescPushBack(softforks, "v19", consensusParams.V19Height, height); - BIP9SoftForkDescPushBack(tip, ehfSignals, softforks, "v20", consensusParams, Consensus::DEPLOYMENT_V20); - BIP9SoftForkDescPushBack(tip, ehfSignals, softforks, "mn_rr", consensusParams, Consensus::DEPLOYMENT_MN_RR); - BIP9SoftForkDescPushBack(tip, ehfSignals, softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); - - obj.pushKV("softforks", softforks); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_CLTV); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_BIP147); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_CSV); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DIP0001); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DIP0003); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DIP0008); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DIP0020); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DIP0024); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_BRR); + SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_V19); + SoftForkDescPushBack(tip, ehfSignals, softforks, consensusParams, Consensus::DEPLOYMENT_V20); + SoftForkDescPushBack(tip, ehfSignals, softforks, consensusParams, Consensus::DEPLOYMENT_MN_RR); + SoftForkDescPushBack(tip, ehfSignals, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); + + obj.pushKV("softforks", softforks); obj.pushKV("warnings", GetWarnings(false)); return obj; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 4a4cb503dcefa..02057ce65a3bf 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,7 +41,6 @@ #include #include #include -#include #include #include @@ -851,7 +852,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) UniValue vbavailable(UniValue::VOBJ); for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { Consensus::DeploymentPos pos = Consensus::DeploymentPos(j); - ThresholdState state = VersionBitsState(pindexPrev, consensusParams, pos, versionbitscache); + ThresholdState state = g_versionbitscache.State(pindexPrev, consensusParams, pos); switch (state) { case ThresholdState::DEFINED: case ThresholdState::FAILED: @@ -859,7 +860,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) break; case ThresholdState::LOCKED_IN: // Ensure bit is set in block version - pblock->nVersion |= VersionBitsMask(consensusParams, pos); + pblock->nVersion |= g_versionbitscache.Mask(consensusParams, pos); // FALL THROUGH to get vbavailable set... case ThresholdState::STARTED: { @@ -868,7 +869,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) if (setClientRules.find(vbinfo.name) == setClientRules.end()) { if (!vbinfo.gbt_force) { // If the client doesn't support this, don't indicate it in the [default] version - pblock->nVersion &= ~VersionBitsMask(consensusParams, pos); + pblock->nVersion &= ~g_versionbitscache.Mask(consensusParams, pos); } } break; @@ -955,7 +956,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) return result; } -class submitblock_StateCatcher : public CValidationInterface +class submitblock_StateCatcher final : public CValidationInterface { public: uint256 hash; @@ -1016,17 +1017,17 @@ static UniValue submitblock(const JSONRPCRequest& request) } bool new_block; - submitblock_StateCatcher sc(block.GetHash()); - RegisterValidationInterface(&sc); + auto sc = std::make_shared(block.GetHash()); + RegisterSharedValidationInterface(sc); bool accepted = chainman.ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block); - UnregisterValidationInterface(&sc); + UnregisterSharedValidationInterface(sc); if (!new_block && accepted) { return "duplicate"; } - if (!sc.found) { + if (!sc->found) { return "inconclusive"; } - return BIP22ValidationResult(sc.state); + return BIP22ValidationResult(sc->state); } static UniValue submitheader(const JSONRPCRequest& request) diff --git a/src/test/dynamic_activation_thresholds_tests.cpp b/src/test/dynamic_activation_thresholds_tests.cpp index 7e32d903d2639..e002229c37068 100644 --- a/src/test/dynamic_activation_thresholds_tests.cpp +++ b/src/test/dynamic_activation_thresholds_tests.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include