diff --git a/.travis.yml b/.travis.yml index dcf8dd202748..de92303e5555 100644 --- a/.travis.yml +++ b/.travis.yml @@ -232,7 +232,7 @@ after_success: FILE_ENV="./ci/test/00_setup_env_win64.sh" - stage: test - name: '32-bit + dash [GOAL: install] [GUI: no BIP70]' + name: '32-bit + dash [GOAL: install] [GUI]' env: >- FILE_ENV="./ci/test/00_setup_env_i686.sh" @@ -250,6 +250,7 @@ after_success: name: 'x86_64 Linux [GOAL: install] [xenial] [no depends, only system libs, sanitizers: thread (TSan), no wallet]' env: >- FILE_ENV="./ci/test/00_setup_env_native_tsan.sh" + TEST_RUNNER_EXTRA="--exclude feature_block" # Not enough memory on travis machines # x86_64 Linux (no depends, only system libs, sanitizers: address/leak (ASan + LSan) + undefined (UBSan) + integer) - stage: test name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, sanitizers: address/leak (ASan + LSan) + undefined (UBSan) + integer]' @@ -276,7 +277,7 @@ after_success: FILE_ENV="./ci/test/00_setup_env_mac.sh" - stage: test - name: 'macOS 10.14 native [GOAL: install] [GUI: BIP70 enabled] [no depends]' + name: 'macOS 10.14 native [GOAL: install] [GUI] [no depends]' os: osx # Use the most recent version: # Xcode 11, macOS 10.14, JDK 12.0.1 diff --git a/ci/test/00_setup_env_mac_host.sh b/ci/test/00_setup_env_mac_host.sh index 33a4da5bcfd4..7779a34e7f69 100755 --- a/ci/test/00_setup_env_mac_host.sh +++ b/ci/test/00_setup_env_mac_host.sh @@ -7,7 +7,7 @@ export LC_ALL=C.UTF-8 export HOST=x86_64-apple-darwin19 -export BREW_PACKAGES="automake berkeley-db4 libtool boost miniupnpc pkg-config protobuf qt qrencode python3 ccache zeromq" +export BREW_PACKAGES="automake berkeley-db4 libtool boost miniupnpc pkg-config qt qrencode python3 ccache zeromq" export PIP_PACKAGES="zmq" export RUN_CI_ON_HOST=true export RUN_UNIT_TESTS=true diff --git a/ci/test/00_setup_env_native_cxx20.sh b/ci/test/00_setup_env_native_cxx20.sh index ca89949270eb..c17fd1e42e27 100755 --- a/ci/test/00_setup_env_native_cxx20.sh +++ b/ci/test/00_setup_env_native_cxx20.sh @@ -6,7 +6,7 @@ export LC_ALL=C.UTF-8 -export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev" +export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libharfbuzz-dev" export DEP_OPTS="NO_UPNP=1 DEBUG=1" export CPPFLAGS="-DDEBUG_LOCKORDER -DENABLE_DASH_DEBUG -DARENA_DEBUG" export PYZMQ=true diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index 8142bc37b0d6..1b3adb9c156c 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -19,6 +19,7 @@ import subprocess, sys, re, os, shutil, stat, os.path, time from string import Template from argparse import ArgumentParser +from typing import List, Optional # This is ported from the original macdeployqt with modifications @@ -48,18 +49,18 @@ class FrameworkInfo(object): return False def __str__(self): - return """ Framework name: %s - Framework directory: %s - Framework path: %s - Binary name: %s - Binary directory: %s - Binary path: %s - Version: %s - Install name: %s - Deployed install name: %s - Source file Path: %s - Deployed Directory (relative to bundle): %s -""" % (self.frameworkName, + return """ Framework name: {} + Framework directory: {} + Framework path: {} + Binary name: {} + Binary directory: {} + Binary path: {} + Version: {} + Install name: {} + Deployed install name: {} + Source file Path: {} + Deployed Directory (relative to bundle): {} +""".format(self.frameworkName, self.frameworkDirectory, self.frameworkPath, self.binaryName, @@ -85,7 +86,7 @@ class FrameworkInfo(object): bundleBinaryDirectory = "Contents/MacOS" @classmethod - def fromOtoolLibraryLine(cls, line): + def fromOtoolLibraryLine(cls, line: str) -> Optional['FrameworkInfo']: # Note: line must be trimmed if line == "": return None @@ -146,13 +147,12 @@ class FrameworkInfo(object): info.sourceContentsDirectory = os.path.join(info.frameworkPath, "Contents") info.sourceVersionContentsDirectory = os.path.join(info.frameworkPath, "Versions", info.version, "Contents") info.destinationResourcesDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Resources") - info.destinationContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Contents") info.destinationVersionContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Versions", info.version, "Contents") return info class ApplicationBundleInfo(object): - def __init__(self, path): + def __init__(self, path: str): self.path = path appName = "Dash-Qt" self.binaryPath = os.path.join(path, "Contents", "MacOS", appName) @@ -167,7 +167,7 @@ class DeploymentInfo(object): self.pluginPath = None self.deployedFrameworks = [] - def detectQtPath(self, frameworkDirectory): + def detectQtPath(self, frameworkDirectory: str): parentDir = os.path.dirname(frameworkDirectory) if os.path.exists(os.path.join(parentDir, "translations")): # Classic layout, e.g. "/usr/local/Trolltech/Qt-4.x.x" @@ -180,9 +180,9 @@ class DeploymentInfo(object): if os.path.exists(pluginPath): self.pluginPath = pluginPath - def usesFramework(self, name): - nameDot = "%s." % name - libNameDot = "lib%s." % name + def usesFramework(self, name: str) -> bool: + nameDot = "{}.".format(name) + libNameDot = "lib{}.".format(name) for framework in self.deployedFrameworks: if framework.endswith(".framework"): if framework.startswith(nameDot): @@ -192,7 +192,7 @@ class DeploymentInfo(object): return True return False -def getFrameworks(binaryPath, verbose): +def getFrameworks(binaryPath: str, verbose: int) -> List[FrameworkInfo]: if verbose >= 3: print("Inspecting with otool: " + binaryPath) otoolbin=os.getenv("OTOOL", "otool") @@ -202,7 +202,7 @@ def getFrameworks(binaryPath, verbose): if verbose >= 1: sys.stderr.write(o_stderr) sys.stderr.flush() - raise RuntimeError("otool failed with return code %d" % otool.returncode) + raise RuntimeError("otool failed with return code {}".format(otool.returncode)) otoolLines = o_stdout.split("\n") otoolLines.pop(0) # First line is the inspected binary @@ -221,11 +221,11 @@ def getFrameworks(binaryPath, verbose): return libraries -def runInstallNameTool(action, *args): +def runInstallNameTool(action: str, *args): installnametoolbin=os.getenv("INSTALLNAMETOOL", "install_name_tool") subprocess.check_call([installnametoolbin, "-"+action] + list(args)) -def changeInstallName(oldName, newName, binaryPath, verbose): +def changeInstallName(oldName: str, newName: str, binaryPath: str, verbose: int): if verbose >= 3: print("Using install_name_tool:") print(" in", binaryPath) @@ -233,21 +233,21 @@ def changeInstallName(oldName, newName, binaryPath, verbose): print(" to", newName) runInstallNameTool("change", oldName, newName, binaryPath) -def changeIdentification(id, binaryPath, verbose): +def changeIdentification(id: str, binaryPath: str, verbose: int): if verbose >= 3: print("Using install_name_tool:") print(" change identification in", binaryPath) print(" to", id) runInstallNameTool("id", id, binaryPath) -def runStrip(binaryPath, verbose): +def runStrip(binaryPath: str, verbose: int): stripbin=os.getenv("STRIP", "strip") if verbose >= 3: print("Using strip:") print(" stripped", binaryPath) subprocess.check_call([stripbin, "-x", binaryPath]) -def copyFramework(framework, path, verbose): +def copyFramework(framework: FrameworkInfo, path: str, verbose: int) -> Optional[str]: if framework.sourceFilePath.startswith("Qt"): #standard place for Nokia Qt installer's frameworks fromPath = "/Library/Frameworks/" + framework.sourceFilePath @@ -309,7 +309,7 @@ def copyFramework(framework, path, verbose): return toPath -def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploymentInfo=None): +def deployFrameworks(frameworks: List[FrameworkInfo], bundlePath: str, binaryPath: str, strip: bool, verbose: int, deploymentInfo: Optional[DeploymentInfo] = None) -> DeploymentInfo: if deploymentInfo is None: deploymentInfo = DeploymentInfo() @@ -323,7 +323,7 @@ def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploym # Get the Qt path from one of the Qt frameworks if deploymentInfo.qtPath is None and framework.isQtFramework(): deploymentInfo.detectQtPath(framework.frameworkDirectory) - + if framework.installName.startswith("@executable_path") or framework.installName.startswith(bundlePath): if verbose >= 2: print(framework.frameworkName, "already deployed, skipping.") @@ -355,15 +355,15 @@ def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploym return deploymentInfo -def deployFrameworksForAppBundle(applicationBundle, strip, verbose): +def deployFrameworksForAppBundle(applicationBundle: ApplicationBundleInfo, strip: bool, verbose: int) -> DeploymentInfo: frameworks = getFrameworks(applicationBundle.binaryPath, verbose) if len(frameworks) == 0 and verbose >= 1: - print("Warning: Could not find any external frameworks to deploy in %s." % (applicationBundle.path)) + print("Warning: Could not find any external frameworks to deploy in {}.".format(applicationBundle.path)) return DeploymentInfo() else: return deployFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, strip, verbose) -def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose): +def deployPlugins(appBundleInfo: ApplicationBundleInfo, deploymentInfo: DeploymentInfo, strip: bool, verbose: int): # Lookup available plugins, exclude unneeded plugins = [] if deploymentInfo.pluginPath is None: @@ -373,10 +373,12 @@ def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose): if pluginDirectory == "designer": # Skip designer plugins continue - elif pluginDirectory == "phonon" or pluginDirectory == "phonon_backend": - # Deploy the phonon plugins only if phonon is in use - if not deploymentInfo.usesFramework("phonon"): - continue + elif pluginDirectory == "printsupport": + # Skip printsupport plugins + continue + elif pluginDirectory == "imageformats": + # Skip imageformats plugins + continue elif pluginDirectory == "sqldrivers": # Deploy the sql plugins only if QtSql is in use if not deploymentInfo.usesFramework("QtSql"): @@ -409,6 +411,42 @@ def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose): # 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) @@ -431,6 +469,10 @@ def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose): # 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)) @@ -499,7 +541,7 @@ app_bundle = config.app_bundle[0] if not os.path.exists(app_bundle): if verbose >= 1: - sys.stderr.write("Error: Could not find app bundle \"%s\"\n" % (app_bundle)) + sys.stderr.write("Error: Could not find app bundle \"{}\"\n".format(app_bundle)) sys.exit(1) app_bundle_name = os.path.splitext(os.path.basename(app_bundle))[0] @@ -511,7 +553,7 @@ if config.translations_dir and config.translations_dir[0]: translations_dir = config.translations_dir[0] else: if verbose >= 1: - sys.stderr.write("Error: Could not find translation dir \"%s\"\n" % (translations_dir)) + sys.stderr.write("Error: Could not find translation dir \"{}\"\n".format(translations_dir)) sys.exit(1) # ------------------------------------------------ @@ -520,7 +562,7 @@ for p in config.add_resources: print("Checking for \"%s\"..." % p) if not os.path.exists(p): if verbose >= 1: - sys.stderr.write("Error: Could not find additional resource file \"%s\"\n" % (p)) + sys.stderr.write("Error: Could not find additional resource file \"{}\"\n".format(p)) sys.exit(1) # ------------------------------------------------ @@ -534,13 +576,13 @@ if len(config.fancy) == 1: if verbose >= 1: sys.stderr.write("Error: Could not import plistlib which is required for fancy disk images.\n") sys.exit(1) - + p = config.fancy[0] if verbose >= 3: - print("Fancy: Loading \"%s\"..." % p) + print("Fancy: Loading \"{}\"...".format(p)) if not os.path.exists(p): if verbose >= 1: - sys.stderr.write("Error: Could not find fancy disk image plist at \"%s\"\n" % (p)) + sys.stderr.write("Error: Could not find fancy disk image plist at \"{}\"\n".format(p)) sys.exit(1) try: @@ -548,7 +590,7 @@ if len(config.fancy) == 1: fancy = plistlib.load(fp, fmt=plistlib.FMT_XML) except: if verbose >= 1: - sys.stderr.write("Error: Could not parse fancy disk image plist at \"%s\"\n" % (p)) + sys.stderr.write("Error: Could not parse fancy disk image plist at \"{}\"\n".format(p)) sys.exit(1) try: @@ -562,18 +604,18 @@ if len(config.fancy) == 1: assert isinstance(value, list) and len(value) == 2 and isinstance(value[0], int) and isinstance(value[1], int) except: if verbose >= 1: - sys.stderr.write("Error: Bad format of fancy disk image plist at \"%s\"\n" % (p)) + sys.stderr.write("Error: Bad format of fancy disk image plist at \"{}\"\n".format(p)) sys.exit(1) if "background_picture" in fancy: bp = fancy["background_picture"] if verbose >= 3: - print("Fancy: Resolving background picture \"%s\"..." % bp) + print("Fancy: Resolving background picture \"{}\"...".format(bp)) if not os.path.exists(bp): bp = os.path.join(os.path.dirname(p), bp) if not os.path.exists(bp): if verbose >= 1: - sys.stderr.write("Error: Could not find background picture at \"%s\" or \"%s\"\n" % (fancy["background_picture"], bp)) + sys.stderr.write("Error: Could not find background picture at \"{}\" or \"{}\"\n".format(fancy["background_picture"], bp)) sys.exit(1) else: fancy["background_picture"] = bp @@ -624,7 +666,7 @@ try: config.plugins = False except RuntimeError as e: if verbose >= 1: - sys.stderr.write("Error: %s\n" % str(e)) + sys.stderr.write("Error: {}\n".format(str(e))) sys.exit(1) # ------------------------------------------------ @@ -637,7 +679,7 @@ if config.plugins: deployPlugins(applicationBundle, deploymentInfo, config.strip, verbose) except RuntimeError as e: if verbose >= 1: - sys.stderr.write("Error: %s\n" % str(e)) + sys.stderr.write("Error: {}\n".format(str(e))) sys.exit(1) # ------------------------------------------------ @@ -653,14 +695,14 @@ else: else: sys.stderr.write("Error: Could not find Qt translation path\n") sys.exit(1) - add_qt_tr = ["qt_%s.qm" % lng for lng in config.add_qt_tr[0].split(",")] + add_qt_tr = ["qt_{}.qm".format(lng) for lng in config.add_qt_tr[0].split(",")] for lng_file in add_qt_tr: p = os.path.join(qt_tr_dir, lng_file) if verbose >= 3: - print("Checking for \"%s\"..." % p) + print("Checking for \"{}\"...".format(p)) if not os.path.exists(p): if verbose >= 1: - sys.stderr.write("Error: Could not find Qt translation file \"%s\"\n" % (lng_file)) + sys.stderr.write("Error: Could not find Qt translation file \"{}\"\n".format(lng_file)) sys.exit(1) # ------------------------------------------------ @@ -701,14 +743,14 @@ if config.sign and 'CODESIGNARGS' not in os.environ: print("You must set the CODESIGNARGS environment variable. Skipping signing.") elif config.sign: if verbose >= 1: - print("Code-signing app bundle %s"%(target,)) - subprocess.check_call("codesign --force %s %s"%(os.environ['CODESIGNARGS'], target), shell=True) + print("Code-signing app bundle {}".format(target)) + subprocess.check_call("codesign --force {} {}".format(os.environ['CODESIGNARGS'], target), shell=True) # ------------------------------------------------ if config.dmg is not None: - def runHDIUtil(verb, image_basename, **kwargs): + def runHDIUtil(verb: str, image_basename: str, **kwargs) -> int: hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"] if "capture_stdout" in kwargs: del kwargs["capture_stdout"] @@ -722,7 +764,7 @@ if config.dmg is not None: for key, value in kwargs.items(): hdiutil_args.append("-" + key) - if not value is True: + if value is not True: hdiutil_args.append(str(value)) return run(hdiutil_args, universal_newlines=True) @@ -851,7 +893,7 @@ if config.dmg is not None: if verbose >= 2: print("+ Finalizing .dmg disk image +") time.sleep(5) - + try: runHDIUtil("convert", dmg_name + ".temp", format="UDBZ", o=dmg_name + ".dmg", ov=True) except subprocess.CalledProcessError as e: diff --git a/doc/productivity.md b/doc/productivity.md index 7997eca073a8..aa949a0ea2c1 100644 --- a/doc/productivity.md +++ b/doc/productivity.md @@ -12,7 +12,7 @@ Table of Contents * [Multiple working directories with `git worktrees`](#multiple-working-directories-with-git-worktrees) * [Interactive "dummy rebases" for fixups and execs with `git merge-base`](#interactive-dummy-rebases-for-fixups-and-execs-with-git-merge-base) * [Writing code](#writing-code) - * [Format C/C++/Protobuf diffs with `clang-format-diff.py`](#format-ccprotobuf-diffs-with-clang-format-diffpy) + * [Format C/C++ diffs with `clang-format-diff.py`](#format-cc-diffs-with-clang-format-diffpy) * [Format Python diffs with `yapf-diff.py`](#format-python-diffs-with-yapf-diffpy) * [Rebasing/Merging code](#rebasingmerging-code) * [More conflict context with `merge.conflictstyle diff3`](#more-conflict-context-with-mergeconflictstyle-diff3) @@ -119,13 +119,13 @@ You can also set up [upstream refspecs](#reference-prs-easily-with-refspecs) to Writing code ------------ -### Format C/C++/Protobuf diffs with `clang-format-diff.py` +### Format C/C++ diffs with `clang-format-diff.py` See [contrib/devtools/README.md](/contrib/devtools/README.md#clang-format-diff.py). ### Format Python diffs with `yapf-diff.py` -Usage is exactly the same as [`clang-format-diff.py`](#format-ccprotobuf-diffs-with-clang-format-diffpy). You can get it [here](https://github.com/MarcoFalke/yapf-diff). +Usage is exactly the same as [`clang-format-diff.py`](#format-cc-diffs-with-clang-format-diffpy). You can get it [here](https://github.com/MarcoFalke/yapf-diff). Rebasing/Merging code ------------- diff --git a/doc/release-notes-16060.md b/doc/release-notes-16060.md new file mode 100644 index 000000000000..7fdf9daf90ef --- /dev/null +++ b/doc/release-notes-16060.md @@ -0,0 +1,15 @@ +Low-level RPC changes +---------------------- + +- Soft fork reporting in the `getblockchaininfo` return object has been + updated. For full details, see the RPC help text. In summary: + - The `bip9_softforks` sub-object is no longer returned + - The `softforks` sub-object now returns an object keyed by soft fork name, + rather than an array + - Each softfork object in the `softforks` object contains a `type` value which + is either `buried` (for soft fork deployments where the activation height is + hard-coded into the client implementation), or `bip9` (for soft fork deployments + where activation is controlled by BIP 9 signaling). + +- `getblocktemplate` no longer returns a `rules` array containing `CSV` + (the BIP 9 deployments that are currently in active state). diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 1884d1c2a200..bd3ef610dfc5 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -148,7 +148,7 @@ static int AppInitRPC(int argc, char* argv[]) tfm::format(std::cerr, "Error reading configuration file: %s\n", error); return EXIT_FAILURE; } - // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause) + // Check for -chain, -testnet or -regtest parameter (BaseParams() calls are only valid after this clause) try { SelectBaseParams(gArgs.GetChainName()); } catch (const std::exception& e) { diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 9fc4c2f7b93c..b5890316653b 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -88,7 +88,7 @@ static int AppInitRawTx(int argc, char* argv[]) return true; } - // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) + // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause) try { SelectParams(gArgs.GetChainName()); } catch (const std::exception& e) { diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 25c54be437b0..97b2b67531ef 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -88,7 +88,7 @@ static bool AppInit(int argc, char* argv[]) if (!args.ReadConfigFiles(error, true)) { return InitError(Untranslated(strprintf("Error reading configuration file: %s\n", error))); } - // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) + // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause) try { SelectParams(args.GetChainName()); } catch (const std::exception& e) { diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 35874b5c8970..d389f8e2e5ad 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -156,7 +156,8 @@ class CMainParams : public CChainParams { consensus.BIP34Hash = uint256S("0x000001f35e70f7c5705f64c6c5cc3dea9449e74d5b5c7cf74dad1bcca14a8012"); consensus.BIP65Height = 619382; // 00000000000076d8fcea02ec0963de4abfd01e771fec0863f960c2c64fe6f357 consensus.BIP66Height = 245817; // 00000000000b1fa2dfa312863570e13fae9ca7b5566cb27e55422620b469aefa - consensus.DIP0001Height = 782208; + consensus.CSVHeight = 622944; // 00000000000002e3d3a6224cfce80bae367fd3283d1e5a8ba50e5e60b2d2905d + consensus.DIP0001Height = 782208; // 000000000000000cbc9cb551e8ee1ac7aa223585cbdfb755d3683bafd93679e4 consensus.DIP0003Height = 1028160; consensus.DIP0003EnforcementHeight = 1047200; consensus.DIP0003EnforcementHash = uint256S("000000000000002d1734087b4c5afc3133e4e1c3e1a89218f62bcd9bb3d17f81"); @@ -176,18 +177,6 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008 consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008 - // Deployment of BIP68, BIP112, and BIP113. - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1486252800; // Feb 5th, 2017 - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1517788800; // Feb 5th, 2018 - - // Deployment of DIP0001 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].bit = 1; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nStartTime = 1508025600; // Oct 15th, 2017 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nTimeout = 1539561600; // Oct 15th, 2018 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nWindowSize = 4032; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nThresholdStart = 3226; // 80% of 4032 - // Deployment of BIP147 consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 1524477600; // Apr 23th, 2018 @@ -202,13 +191,6 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nWindowSize = 4032; consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThresholdStart = 3226; // 80% of 4032 - // Deployment of DIP0008 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].bit = 4; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nStartTime = 1557878400; // May 15th, 2019 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nTimeout = 1589500800; // May 15th, 2020 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nWindowSize = 4032; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThresholdStart = 3226; // 80% of 4032 - // Deployment of Block Reward Reallocation consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5; consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 1601510400; // Oct 1st, 2020 @@ -404,7 +386,8 @@ class CTestNetParams : public CChainParams { consensus.BIP34Hash = uint256S("0x000008ebb1db2598e897d17275285767717c6acfeac4c73def49fbea1ddcbcb6"); consensus.BIP65Height = 2431; // 0000039cf01242c7f921dcb4806a5994bc003b48c1973ae0c89b67809c2bb2ab consensus.BIP66Height = 2075; // 0000002acdd29a14583540cb72e1c5cc83783560e38fa7081495d474fe1671f7 - consensus.DIP0001Height = 5500; + consensus.CSVHeight = 8064; // 00000005eb94d027e34649373669191188858a22c70f4a6d29105e559124cec7 + consensus.DIP0001Height = 5500; // 00000001d60a01d8f1f39011cc6b26e3a1c97a24238cab856c2da71a4dd801a9 consensus.DIP0003Height = 7000; consensus.DIP0003EnforcementHeight = 7300; consensus.DIP0003EnforcementHash = uint256S("00000055ebc0e974ba3a3fb785c5ad4365a39637d4df168169ee80d313612f8f"); @@ -424,18 +407,6 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008 consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008 - // Deployment of BIP68, BIP112, and BIP113. - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1544655600; // Dec 13th, 2018 - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL; - - // Deployment of DIP0001 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].bit = 1; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nStartTime = 1544655600; // Dec 13th, 2018 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nWindowSize = 100; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nThresholdStart = 50; // 50% of 100 - // Deployment of BIP147 consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 1544655600; // Dec 13th, 2018 @@ -450,13 +421,6 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nWindowSize = 100; consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThresholdStart = 50; // 50% of 100 - // Deployment of DIP0008 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].bit = 4; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nStartTime = 1553126400; // Mar 21st, 2019 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nWindowSize = 100; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThresholdStart = 50; // 50% of 100 - // Deployment of Block Reward Reallocation consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5; consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 1598918400; // Sep 1st, 2020 @@ -625,6 +589,7 @@ class CDevNetParams : public CChainParams { consensus.BIP34Height = 1; // BIP34 activated immediately on devnet consensus.BIP65Height = 1; // BIP65 activated immediately on devnet consensus.BIP66Height = 1; // BIP66 activated immediately on devnet + consensus.CSVHeight = 1; // BIP68 activated immediately on devnet consensus.DIP0001Height = 2; // DIP0001 activated immediately on devnet consensus.DIP0003Height = 2; // DIP0003 activated immediately on devnet consensus.DIP0003EnforcementHeight = 2; // DIP0003 activated immediately on devnet @@ -645,18 +610,6 @@ class CDevNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008 consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008 - // Deployment of BIP68, BIP112, and BIP113. - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1506556800; // September 28th, 2017 - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL; - - // Deployment of DIP0001 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].bit = 1; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nStartTime = 1505692800; // Sep 18th, 2017 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nWindowSize = 100; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nThresholdStart = 50; // 50% of 100 - // Deployment of BIP147 consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 1517792400; // Feb 5th, 2018 @@ -671,13 +624,6 @@ class CDevNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nWindowSize = 100; consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThresholdStart = 50; // 50% of 100 - // Deployment of DIP0008 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].bit = 4; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nStartTime = 1553126400; // Mar 21st, 2019 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nWindowSize = 100; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThresholdStart = 50; // 50% of 100 - // Deployment of Block Reward Reallocation consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5; consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 1598918400; // Sep 1st, 2020 @@ -917,6 +863,7 @@ class CRegTestParams : public CChainParams { consensus.BIP34Hash = uint256(); consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in functional tests) consensus.BIP66Height = 1251; // BIP66 activated on regtest (Used in functional tests) + consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests) consensus.DIP0001Height = 2000; consensus.DIP0003Height = 432; consensus.DIP0003EnforcementHeight = 500; @@ -936,21 +883,12 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].bit = 1; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nTimeout = 999999999999ULL; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 999999999999ULL; consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].bit = 3; consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].bit = 4; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nTimeout = 999999999999ULL; consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5; consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nTimeout = 999999999999ULL; @@ -1009,7 +947,7 @@ class CRegTestParams : public CChainParams { m_assumed_blockchain_size = 0; m_assumed_chain_state_size = 0; - UpdateVersionBitsParametersFromArgs(args); + UpdateActivationParametersFromArgs(args); UpdateDIP3ParametersFromArgs(args); UpdateDIP8ParametersFromArgs(args); UpdateBudgetParametersFromArgs(args); @@ -1105,7 +1043,7 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[d].nFalloffCoeff = nFalloffCoeff; } } - void UpdateVersionBitsParametersFromArgs(const ArgsManager& args); + void UpdateActivationParametersFromArgs(const ArgsManager& args); /** * Allows modifying the DIP3 activation and enforcement height @@ -1171,7 +1109,7 @@ class CRegTestParams : public CChainParams { void UpdateLLMQInstantSendDIP0024FromArgs(const ArgsManager& args); }; -void CRegTestParams::UpdateVersionBitsParametersFromArgs(const ArgsManager& args) +void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args) { if (!args.IsArgSet("-vbparams")) return; diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 74a9039c6f55..9a6e33b9823c 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -32,9 +32,10 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) argsman.AddArg("-llmqtestinstantsendparams=:", "Override the default LLMQ size for the LLMQ_TEST_INSTANTSEND quorums (default: 3:2, regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-powtargetspacing=", "Override the default PowTargetSpacing value in seconds (default: 2.5 minutes, devnet-only)", ArgsManager::ALLOW_INT, OptionsCategory::CHAINPARAMS); argsman.AddArg("-minimumdifficultyblocks=", "The number of blocks that can be mined with the minimum difficulty at the start of a chain (default: 0, devnet-only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-chain=", "Use the chain (default: main). Allowed values: main, test, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " - "This is intended for regression testing tools and app development.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); - argsman.AddArg("-testnet", "Use the test chain", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + "This is intended for regression testing tools and app development. Equivalent to -chain=regtest", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-vbparams=::(::(::))", "Use given start/end times for specified version bits deployment (regtest-only). " "Specifying window, threshold/thresholdstart, thresholdmin and falloffcoeff is optional.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); diff --git a/src/consensus/params.h b/src/consensus/params.h index d9f238ea823e..2dfc2f0436ff 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -15,11 +15,8 @@ namespace Consensus { enum DeploymentPos { DEPLOYMENT_TESTDUMMY, - DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113. - DEPLOYMENT_DIP0001, // Deployment of DIP0001 and lower transaction fees. DEPLOYMENT_BIP147, // Deployment of BIP147 (NULLDUMMY) DEPLOYMENT_DIP0003, // Deployment of DIP0002 and DIP0003 (txv3 and deterministic MN lists) - DEPLOYMENT_DIP0008, // Deployment of ChainLock enforcement DEPLOYMENT_REALLOC, // Deployment of Block Reward Reallocation DEPLOYMENT_DIP0020, // Deployment of DIP0020, DIP0021 and LMQ_100_67 quorums DEPLOYMENT_DIP0024, // Deployment of DIP0024 (Quorum Rotation) and decreased governance proposal fee @@ -78,6 +75,8 @@ struct Params { int BIP65Height; /** Block height at which BIP66 becomes active */ int BIP66Height; + /** Block height at which CSV (BIP68, BIP112 and BIP113) becomes active */ + int CSVHeight; /** Block height at which DIP0001 becomes active */ int DIP0001Height; /** Block height at which DIP0003 becomes active */ diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 8aee777821d4..1b8f0e81c223 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -125,6 +125,9 @@ class Node //! Set command line arguments. virtual bool parseParameters(int argc, const char* const argv[], std::string& error) = 0; + //! Set a command line argument + virtual void forceSetArg(const std::string& arg, const std::string& value) = 0; + //! Set a command line argument if it doesn't already have a value virtual bool softSetArg(const std::string& arg, const std::string& value) = 0; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index e821709e7fc5..27e86c6d6860 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -198,6 +198,7 @@ class NodeImpl : public Node return gArgs.ParseParameters(argc, argv, error); } bool readConfigFiles(std::string& error) override { return gArgs.ReadConfigFiles(error, true); } + void forceSetArg(const std::string& arg, const std::string& value) override { gArgs.ForceSetArg(arg, value); } bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); } bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } void selectParams(const std::string& network) override { SelectParams(network); } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 915863e84d4b..ec700cb23656 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -298,6 +298,13 @@ void BitcoinApplication::parameterSetup() m_node.initParameterInteraction(); } +void BitcoinApplication::InitializePruneSetting(bool prune) +{ + // If prune is set, intentionally override existing prune size with + // the default size since this is called when choosing a new datadir. + optionsModel->SetPruneTargetGB(prune ? DEFAULT_PRUNE_TARGET_GB : 0, true); +} + void BitcoinApplication::requestInitialize() { qDebug() << __func__ << ": Requesting initialize"; @@ -517,8 +524,10 @@ int GuiMain(int argc, char* argv[]) /// 5. Now that settings and translations are available, ask user for data directory // User language is set up: pick a data directory - if (!Intro::pickDataDirectory(*node)) - return EXIT_SUCCESS; + bool did_show_intro = false; + bool prune = false; // Intro dialog prune check box + // Gracefully exit if the user cancels + if (!Intro::showIfNeeded(*node, did_show_intro, prune)) return EXIT_SUCCESS; /// 6. Determine availability of data directory and parse dash.conf /// - Do not call GetDataDir(true) before this step finishes @@ -541,7 +550,7 @@ int GuiMain(int argc, char* argv[]) // - QSettings() will use the new application name after this, resulting in network-specific settings // - Needs to be done before createOptionsModel - // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) + // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause) try { node->selectParams(gArgs.GetChainName()); } catch(std::exception &e) { @@ -559,7 +568,7 @@ int GuiMain(int argc, char* argv[]) return EXIT_FAILURE; } - QScopedPointer networkStyle(NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString()))); + QScopedPointer networkStyle(NetworkStyle::instantiate(Params().NetworkIDString())); assert(!networkStyle.isNull()); // Allow for separate UI settings for testnets // QApplication::setApplicationName(networkStyle->getAppName()); // moved to NetworkStyle::NetworkStyle @@ -688,6 +697,11 @@ int GuiMain(int argc, char* argv[]) "Warning: UI debug mode (-debug-ui) enabled" + QString(gArgs.IsArgSet("-custom-css-dir") ? "." : " without a custom css directory set with -custom-css-dir.")); } + if (did_show_intro) { + // Store intro dialog settings other than datadir (network specific) + app.InitializePruneSetting(prune); + } + if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) app.createSplashScreen(networkStyle.data()); diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h index 82f1535a477b..78a5e419a11b 100644 --- a/src/qt/bitcoin.h +++ b/src/qt/bitcoin.h @@ -65,6 +65,8 @@ class BitcoinApplication: public QApplication void parameterSetup(); /// Create options model void createOptionsModel(bool resetSettings); + /// Initialize prune setting + void InitializePruneSetting(bool prune); /// Create main window void createWindow(const NetworkStyle *networkStyle); /// Create splash screen diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui index 91d4553ce70f..659e2c8b23db 100644 --- a/src/qt/forms/intro.ui +++ b/src/qt/forms/intro.ui @@ -210,6 +210,16 @@ + + + + Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features. + + + + + + diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 4e5b8db3bb57..0b03d80eb1e6 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -47,4 +47,7 @@ static const int TOOLTIP_WRAP_THRESHOLD = 80; /* One gigabyte (GB) in bytes */ static constexpr uint64_t GB_BYTES{1000000000}; +// Default prune target displayed in GUI. +static constexpr int DEFAULT_PRUNE_TARGET_GB{2}; + #endif // BITCOIN_QT_GUICONSTANTS_H diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 9ee068276ff6..d60a2d898ed5 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -838,7 +838,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) // Start client minimized QString strArgs = "-min"; // Set -testnet /-regtest options - strArgs += QString::fromStdString(strprintf(" -testnet=%d -regtest=%d", gArgs.GetBoolArg("-testnet", false), gArgs.GetBoolArg("-regtest", false))); + strArgs += QString::fromStdString(strprintf(" -chain=%s", gArgs.GetChainName())); // Set the path to the shortcut target psl->SetPath(pszExePath); @@ -933,7 +933,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) optionFile << "Name=Dash Core\n"; else optionFile << strprintf("Name=Dash Core (%s)\n", chain); - optionFile << "Exec=" << pszExePath << strprintf(" -min -testnet=%d -regtest=%d\n", gArgs.GetBoolArg("-testnet", false), gArgs.GetBoolArg("-regtest", false)); + optionFile << "Exec=" << pszExePath << strprintf(" -min -chain=%s\n", chain); optionFile << "Terminal=false\n"; optionFile << "Hidden=false\n"; optionFile.close(); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 6794abce5c39..e84a614f77f2 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -23,9 +24,6 @@ #include -/* Total required space (in GB) depending on user choice (prune, not prune) */ -static uint64_t requiredSpace; - /* Check free space asynchronously to prevent hanging the UI thread. Up to one request to check a path is in flight to this thread; when the check() @@ -110,14 +108,24 @@ void FreespaceChecker::check() Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable); } +namespace { +//! Return pruning size that will be used if automatic pruning is enabled. +int GetPruneTargetGB() +{ + int64_t prune_target_mib = gArgs.GetArg("-prune", 0); + // >1 means automatic pruning is enabled by config, 1 means manual pruning, 0 means no pruning. + return prune_target_mib > 1 ? PruneMiBtoGB(prune_target_mib) : DEFAULT_PRUNE_TARGET_GB; +} +} // namespace -Intro::Intro(QWidget *parent, uint64_t blockchain_size, uint64_t chain_state_size) : +Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_size_gb) : QDialog(parent), ui(new Ui::Intro), thread(nullptr), signalled(false), - m_blockchain_size(blockchain_size), - m_chain_state_size(chain_state_size) + m_blockchain_size_gb(blockchain_size_gb), + m_chain_state_size_gb(chain_state_size_gb), + m_prune_target_gb{GetPruneTargetGB()} { ui->setupUi(this); ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(PACKAGE_NAME)); @@ -125,32 +133,24 @@ Intro::Intro(QWidget *parent, uint64_t blockchain_size, uint64_t chain_state_siz ui->lblExplanation1->setText(ui->lblExplanation1->text() .arg(PACKAGE_NAME) - .arg(m_blockchain_size) + .arg(m_blockchain_size_gb) .arg(2014) .arg("Dash") ); ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME)); - uint64_t pruneTarget = std::max(0, gArgs.GetArg("-prune", 0)); - requiredSpace = m_blockchain_size; - QString storageRequiresMsg = tr("At least %1 GB of data will be stored in this directory, and it will grow over time."); - if (pruneTarget) { - uint64_t prunedGBs = std::ceil(pruneTarget * 1024 * 1024.0 / GB_BYTES); - if (prunedGBs <= requiredSpace) { - requiredSpace = prunedGBs; - storageRequiresMsg = tr("Approximately %1 GB of data will be stored in this directory."); - } - ui->lblExplanation3->setVisible(true); - } else { - ui->lblExplanation3->setVisible(false); + if (gArgs.GetArg("-prune", 0) > 1) { // -prune=1 means enabled, above that it's a size in MiB + ui->prune->setChecked(true); + ui->prune->setEnabled(false); } - requiredSpace += m_chain_state_size; - ui->sizeWarningLabel->setText( - tr("%1 will download and store a copy of the Dash block chain.").arg(PACKAGE_NAME) + " " + - storageRequiresMsg.arg(requiredSpace) + " " + - tr("The wallet will also be stored in this directory.") - ); - this->adjustSize(); + ui->prune->setText(tr("Discard blocks after verification, except most recent %1 GB (prune)").arg(m_prune_target_gb)); + UpdatePruneLabels(ui->prune->isChecked()); + + connect(ui->prune, &QCheckBox::toggled, [this](bool prune_checked) { + UpdatePruneLabels(prune_checked); + UpdateFreeSpaceLabel(); + }); + startThread(); } @@ -182,8 +182,10 @@ void Intro::setDataDirectory(const QString &dataDir) } } -bool Intro::pickDataDirectory(interfaces::Node& node) +bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune) { + did_show_intro = false; + QSettings settings; /* If data directory provided on command line, no need to look at settings or show a picking dialog */ @@ -211,6 +213,7 @@ bool Intro::pickDataDirectory(interfaces::Node& node) GUIUtil::loadStyleSheet(true); intro.setDataDirectory(dataDirDefaultCurrent); intro.setWindowIcon(QIcon(":icons/dash")); + did_show_intro = true; while(true) { @@ -233,6 +236,9 @@ bool Intro::pickDataDirectory(interfaces::Node& node) } } + // Additional preferences: + prune = intro.ui->prune->isChecked(); + settings.setValue("strDataDir", dataDir); settings.setValue("strDataDirDefault", dataDirDefaultCurrent); settings.setValue("fReset", false); @@ -265,20 +271,31 @@ void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable { ui->freeSpace->setText(""); } else { - QString freeString = tr("%1 GB of free space available").arg(bytesAvailable/GB_BYTES); - if(bytesAvailable < requiredSpace * GB_BYTES) - { - freeString += " " + tr("(of %1 GB needed)").arg(requiredSpace); - ui->freeSpace->setStyleSheet(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR)); - } else { - ui->freeSpace->setStyleSheet(""); + m_bytes_available = bytesAvailable; + if (ui->prune->isEnabled()) { + ui->prune->setChecked(m_bytes_available < (m_blockchain_size_gb + m_chain_state_size_gb + 10) * GB_BYTES); } - ui->freeSpace->setText(freeString + "."); + UpdateFreeSpaceLabel(); } /* Don't allow confirm in ERROR state */ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR); } +void Intro::UpdateFreeSpaceLabel() +{ + QString freeString = tr("%n GB of free space available", "", m_bytes_available / GB_BYTES); + if (m_bytes_available < m_required_space_gb * GB_BYTES) { + freeString += " " + tr("(of %n GB needed)", "", m_required_space_gb); + ui->freeSpace->setStyleSheet("QLabel { color: #800000 }"); + } else if (m_bytes_available / GB_BYTES - m_required_space_gb < 10) { + freeString += " " + tr("(%n GB needed for full chain)", "", m_required_space_gb); + ui->freeSpace->setStyleSheet("QLabel { color: #999900 }"); + } else { + ui->freeSpace->setStyleSheet(""); + } + ui->freeSpace->setText(freeString + "."); +} + void Intro::on_dataDirectory_textChanged(const QString &dataDirStr) { /* Disable OK button until check result comes in */ @@ -339,3 +356,20 @@ QString Intro::getPathToCheck() mutex.unlock(); return retval; } + +void Intro::UpdatePruneLabels(bool prune_checked) +{ + m_required_space_gb = m_blockchain_size_gb + m_chain_state_size_gb; + QString storageRequiresMsg = tr("At least %1 GB of data will be stored in this directory, and it will grow over time."); + if (prune_checked && m_prune_target_gb <= m_blockchain_size_gb) { + m_required_space_gb = m_prune_target_gb + m_chain_state_size_gb; + storageRequiresMsg = tr("Approximately %1 GB of data will be stored in this directory."); + } + ui->lblExplanation3->setVisible(prune_checked); + ui->sizeWarningLabel->setText( + tr("%1 will download and store a copy of the Dash block chain.").arg(PACKAGE_NAME) + " " + + storageRequiresMsg.arg(m_required_space_gb) + " " + + tr("The wallet will also be stored in this directory.") + ); + this->adjustSize(); +} diff --git a/src/qt/intro.h b/src/qt/intro.h index 9e178b7a3ae8..30f941598758 100644 --- a/src/qt/intro.h +++ b/src/qt/intro.h @@ -31,7 +31,7 @@ class Intro : public QDialog public: explicit Intro(QWidget *parent = nullptr, - uint64_t blockchain_size = 0, uint64_t chain_state_size = 0); + int64_t blockchain_size_gb = 0, int64_t chain_state_size_gb = 0); ~Intro(); QString getDataDirectory(); @@ -39,6 +39,7 @@ class Intro : public QDialog /** * Determine data directory. Let the user choose if the current one doesn't exist. + * Let the user configure additional preferences such as pruning. * * @returns true if a data directory was selected, false if the user cancelled the selection * dialog. @@ -46,7 +47,7 @@ class Intro : public QDialog * @note do NOT call global GetDataDir() before calling this function, this * will cause the wrong path to be cached. */ - static bool pickDataDirectory(interfaces::Node& node); + static bool showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune); Q_SIGNALS: void requestCheck(); @@ -66,12 +67,18 @@ private Q_SLOTS: QMutex mutex; bool signalled; QString pathToCheck; - uint64_t m_blockchain_size; - uint64_t m_chain_state_size; + const int64_t m_blockchain_size_gb; + const int64_t m_chain_state_size_gb; + //! Total required space (in GB) depending on user choice (prune or not prune). + int64_t m_required_space_gb{0}; + uint64_t m_bytes_available{0}; + const int64_t m_prune_target_gb; void startThread(); void checkPath(const QString &dataDir); QString getPathToCheck(); + void UpdatePruneLabels(bool prune_checked); + void UpdateFreeSpaceLabel(); friend class FreespaceChecker; }; diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index 47f8f3e7eb74..cb0151e943fb 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include static const struct { @@ -22,9 +24,9 @@ static const struct { const std::string titleAddText; } network_styles[] = { {"main", QAPP_APP_NAME_DEFAULT, 0, 0, ""}, - {"test", QAPP_APP_NAME_TESTNET, 190, 20, QT_TRANSLATE_NOOP("SplashScreen", "[testnet]")}, + {"test", QAPP_APP_NAME_TESTNET, 190, 20}, {"devnet", QAPP_APP_NAME_DEVNET, 190, 20, "[devnet: %s]"}, - {"regtest", QAPP_APP_NAME_REGTEST, 160, 30, "[regtest]"} + {"regtest", QAPP_APP_NAME_REGTEST, 160, 30} }; void NetworkStyle::rotateColor(QColor& col, const int iconColorHueShift, const int iconColorSaturationReduction) @@ -86,8 +88,9 @@ NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift, splashImage = QPixmap(":/images/splash"); } -const NetworkStyle *NetworkStyle::instantiate(const QString &networkId) +const NetworkStyle* NetworkStyle::instantiate(const std::string& networkId) { + std::string titleAddText = networkId == CBaseChainParams::MAIN ? "" : strprintf("[%s]", networkId); for (const auto& network_style : network_styles) { if (networkId == network_style.networkId) @@ -95,7 +98,7 @@ const NetworkStyle *NetworkStyle::instantiate(const QString &networkId) std::string appName = network_style.appName; std::string titleAddText = network_style.titleAddText; - if (networkId == QString(CBaseChainParams::DEVNET.c_str())) { + if (networkId == CBaseChainParams::DEVNET.c_str()) { appName = strprintf(appName, gArgs.GetDevNetName()); titleAddText = strprintf(titleAddText, gArgs.GetDevNetName()); } diff --git a/src/qt/networkstyle.h b/src/qt/networkstyle.h index 96f76a420d13..a01964cc2aa4 100644 --- a/src/qt/networkstyle.h +++ b/src/qt/networkstyle.h @@ -15,7 +15,7 @@ class NetworkStyle { public: /** Get style associated with provided network id, or 0 if not known */ - static const NetworkStyle *instantiate(const QString &networkId); + static const NetworkStyle* instantiate(const std::string& networkId); const QString &getAppName() const { return appName; } const QIcon &getAppIcon() const { return appIcon; } diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 24413017cb25..6911dadbd5ab 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -185,12 +185,8 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("bPrune")) settings.setValue("bPrune", false); if (!settings.contains("nPruneSize")) - settings.setValue("nPruneSize", 2); - // Convert prune size from GB to MiB: - const uint64_t nPruneSizeMiB = (settings.value("nPruneSize").toInt() * GB_BYTES) >> 20; - if (!m_node.softSetArg("-prune", settings.value("bPrune").toBool() ? ToString(nPruneSizeMiB) : "0")) { - addOverriddenOption("-prune"); - } + settings.setValue("nPruneSize", DEFAULT_PRUNE_TARGET_GB); + SetPruneEnabled(settings.value("bPrune").toBool()); // If GUI is setting prune, then we also must set disablegovernance and txindex if (settings.value("bPrune").toBool()) { @@ -377,6 +373,31 @@ static const QString GetDefaultProxyAddress() return QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST).arg(DEFAULT_GUI_PROXY_PORT); } +void OptionsModel::SetPruneEnabled(bool prune, bool force) +{ + QSettings settings; + settings.setValue("bPrune", prune); + const int64_t prune_target_mib = PruneGBtoMiB(settings.value("nPruneSize").toInt()); + std::string prune_val = prune ? ToString(prune_target_mib) : "0"; + if (force) { + m_node.forceSetArg("-prune", prune_val); + return; + } + if (!m_node.softSetArg("-prune", prune_val)) { + addOverriddenOption("-prune"); + } +} + +void OptionsModel::SetPruneTargetGB(int prune_target_gb, bool force) +{ + const bool prune = prune_target_gb > 0; + if (prune) { + QSettings settings; + settings.setValue("nPruneSize", prune_target_gb); + } + SetPruneEnabled(prune, force); +} + // read QSettings values and return them QVariant OptionsModel::data(const QModelIndex & index, int role) const { diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 5d105106d011..ac334a4df252 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_OPTIONSMODEL_H #include +#include #include @@ -18,6 +19,16 @@ class Node; extern const char *DEFAULT_GUI_PROXY_HOST; static constexpr uint16_t DEFAULT_GUI_PROXY_PORT = 9050; +/** + * Convert configured prune target MiB to displayed GB. Round up to avoid underestimating max disk usage. + */ +static inline int PruneMiBtoGB(int64_t mib) { return (mib * 1024 * 1024 + GB_BYTES - 1) / GB_BYTES; } + +/** + * Convert displayed prune target GB to configured MiB. Round down so roundtrip GB -> MiB -> GB conversion is stable. + */ +static inline int64_t PruneGBtoMiB(int gb) { return gb * GB_BYTES / 1024 / 1024; } + /** Interface from Qt to configuration data structure for Bitcoin client. To Qt, the options are presented as a list with the different options laid out vertically. @@ -94,6 +105,10 @@ class OptionsModel : public QAbstractListModel const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; } void emitCoinJoinEnabledChanged(); + /* Explicit setters */ + void SetPruneEnabled(bool prune, bool force = false); + void SetPruneTargetGB(int prune_target_gb, bool force = false); + /* Restart flag helper */ void setRestartRequired(bool fRequired); bool isRestartRequired() const; diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp index d4c4f3874974..e2fdf0c49e07 100644 --- a/src/qt/test/apptests.cpp +++ b/src/qt/test/apptests.cpp @@ -73,7 +73,7 @@ void AppTests::appTests() GUIUtil::loadFonts(); m_app.createOptionsModel(true /* reset settings */); QScopedPointer style( - NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString()))); + NetworkStyle::instantiate(Params().NetworkIDString())); m_app.createWindow(style.data()); connect(&m_app, &BitcoinApplication::windowShown, this, &AppTests::guiTests); expectCallback("guiTests"); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 732e89066ed8..fc61889dcf32 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1418,54 +1418,49 @@ static UniValue verifychain(const JSONRPCRequest& request) ::ChainstateActive(), Params(), ::ChainstateActive().CoinsTip(), *node_context.evodb, check_level, check_depth); } -/** Implementation of IsSuperMajority with better feedback */ -static UniValue SoftForkMajorityDesc(int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams) +static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int height) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { - UniValue rv(UniValue::VOBJ); - bool activated = false; - switch(version) - { - case 2: - activated = pindex->nHeight >= consensusParams.BIP34Height; - break; - case 3: - activated = pindex->nHeight >= consensusParams.BIP66Height; - break; - case 4: - activated = pindex->nHeight >= consensusParams.BIP65Height; - break; - } - rv.pushKV("status", activated); - return rv; -} + // 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 (height == std::numeric_limits::max()) return; -static UniValue SoftForkDesc(const std::string &name, int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams) -{ UniValue rv(UniValue::VOBJ); - rv.pushKV("id", name); - rv.pushKV("version", version); - rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams)); - return rv; + rv.pushKV("type", "buried"); + // getblockchaininfo reports the softfork as active from when the chain height is + // one below the activation height + rv.pushKV("active", ::ChainActive().Tip()->nHeight + 1 >= height); + rv.pushKV("height", height); + softforks.pushKV(name, rv); } -static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { - UniValue rv(UniValue::VOBJ); + // For BIP9 deployments. + // Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden. + // A timeout value of 0 guarantees a softfork will never be activated. + // This is used when merging logic to implement a proposed softfork without a specified deployment schedule. + if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return; + + UniValue bip9(UniValue::VOBJ); const ThresholdState thresholdState = VersionBitsState(::ChainActive().Tip(), consensusParams, id, versionbitscache); switch (thresholdState) { - case ThresholdState::DEFINED: rv.pushKV("status", "defined"); break; - case ThresholdState::STARTED: rv.pushKV("status", "started"); break; - case ThresholdState::LOCKED_IN: rv.pushKV("status", "locked_in"); break; - case ThresholdState::ACTIVE: rv.pushKV("status", "active"); break; - case ThresholdState::FAILED: rv.pushKV("status", "failed"); break; + case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break; + case ThresholdState::STARTED: bip9.pushKV("status", "started"); break; + case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break; + case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break; + case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break; } if (ThresholdState::STARTED == thresholdState) { - rv.pushKV("bit", consensusParams.vDeployments[id].bit); + bip9.pushKV("bit", consensusParams.vDeployments[id].bit); } - rv.pushKV("start_time", consensusParams.vDeployments[id].nStartTime); - rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); - rv.pushKV("since", VersionBitsStateSinceHeight(::ChainActive().Tip(), consensusParams, id, versionbitscache)); + bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime); + bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); + int64_t since_height = VersionBitsStateSinceHeight(::ChainActive().Tip(), consensusParams, id, versionbitscache); + bip9.pushKV("since", since_height); if (ThresholdState::STARTED == thresholdState) { UniValue statsUV(UniValue::VOBJ); @@ -1475,18 +1470,18 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse statsUV.pushKV("elapsed", statsStruct.elapsed); statsUV.pushKV("count", statsStruct.count); statsUV.pushKV("possible", statsStruct.possible); - rv.pushKV("statistics", statsUV); + bip9.pushKV("statistics", statsUV); } - return rv; -} -static void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) -{ - // Deployments with timeout value of 0 are hidden. - // A timeout value of 0 guarantees a softfork will never be activated. - // This is used when softfork codes are merged without specifying the deployment schedule. - if (consensusParams.vDeployments[id].nTimeout > 0) - bip9_softforks.pushKV(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id)); + UniValue rv(UniValue::VOBJ); + rv.pushKV("type", "bip9"); + rv.pushKV("bip9", bip9); + if (ThresholdState::ACTIVE == thresholdState) { + rv.pushKV("height", since_height); + } + rv.pushKV("active", ThresholdState::ACTIVE == thresholdState); + + softforks.pushKV(name, rv); } UniValue getblockchaininfo(const JSONRPCRequest& request) @@ -1512,28 +1507,17 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) {RPCResult::Type::NUM, "pruneheight", "lowest-height complete block stored (only present if pruning is enabled)"}, {RPCResult::Type::BOOL, "automatic_pruning", "whether automatic pruning is enabled (only present if pruning is enabled)"}, {RPCResult::Type::NUM, "prune_target_size", "the target size used by pruning (only present if automatic pruning is enabled)"}, - {RPCResult::Type::ARR, "softforks", "status of softforks in progress", - { - {RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::STR, "xxxx", "name of the softfork"}, - {RPCResult::Type::STR, "version", "block version"}, - {RPCResult::Type::OBJ, "reject", "progress toward rejecting pre-softfork blocks", - { - {RPCResult::Type::BOOL, "status", "true if threshold reached"}, - }}, - }}, - }}, - {RPCResult::Type::OBJ_DYN, "bip9_softforks", "status of BIP9 softforks in progress", + {RPCResult::Type::OBJ, "softforks", "status of softforks in progress", { - {RPCResult::Type::OBJ, "xxxx", "name of the softfork", + {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""}, + {RPCResult::Type::OBJ, "bip9", "status of bip9 softforks (only for \"bip9\" type)", { {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""}, {RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)"}, {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"}, {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"}, {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"}, - {RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)", + {RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork", { {RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"}, {RPCResult::Type::NUM, "threshold", "the number of blocks with the version bit set required to activate the feature"}, @@ -1542,6 +1526,8 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) {RPCResult::Type::BOOL, "possible", "returns false if there are not enough blocks left in this period to pass activation threshold"}, }}, }}, + {RPCResult::Type::NUM, "height", "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"}, + {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"}, }}, {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"}, }}, @@ -1586,17 +1572,19 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) } const Consensus::Params& consensusParams = Params().GetConsensus(); - UniValue softforks(UniValue::VARR); - UniValue bip9_softforks(UniValue::VOBJ); + UniValue softforks(UniValue::VOBJ); // sorted by activation block - softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); - softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); - softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); - for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) { - BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast(pos)); - } + BuriedForkDescPushBack(softforks,"bip34", consensusParams.BIP34Height); + BuriedForkDescPushBack(softforks,"bip66", consensusParams.BIP66Height); + BuriedForkDescPushBack(softforks,"bip65", consensusParams.BIP65Height); + BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight); + BuriedForkDescPushBack(softforks, "dip0001", consensusParams.DIP0001Height); + BuriedForkDescPushBack(softforks, "dip0008", consensusParams.DIP0008Height); + for (int pos = Consensus::DEPLOYMENT_TESTDUMMY + 1; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) { + BIP9SoftForkDescPushBack(softforks, VersionBitsDeploymentInfo[pos].name, consensusParams, static_cast(pos)); + } + BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); obj.pushKV("softforks", softforks); - obj.pushKV("bip9_softforks", bip9_softforks); obj.pushKV("warnings", GetWarnings(false)); return obj; diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 6eae5901551d..550d8b861cd2 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1135,7 +1135,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup) // Results file is formatted like: // // || - BOOST_CHECK_EQUAL(out_sha_hex, "f8cff01ad967dfc82cf71208aa2b60f01a0aa621c28356af5ca91d1d7370c217"); + BOOST_CHECK_EQUAL(out_sha_hex, "1cd2697d1665d086f15ed18b3333bc356c3d39ba490589dc58afd8fc2f457731"); } BOOST_AUTO_TEST_CASE(util_ReadWriteSettings) diff --git a/src/util/system.cpp b/src/util/system.cpp index 59bf0b14434b..2c3d9c59b3a3 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -976,10 +976,11 @@ std::string ArgsManager::GetChainName() const const bool fRegTest = get_net("-regtest"); const bool fTestNet = get_net("-testnet"); const bool fDevNet = get_net("-devnet", false); + const bool is_chain_arg_set = IsArgSet("-chain"); - int nameParamsCount = (fRegTest ? 1 : 0) + (fDevNet ? 1 : 0) + (fTestNet ? 1 : 0); + int nameParamsCount = (fRegTest ? 1 : 0) + (fDevNet ? 1 : 0) + (fTestNet ? 1 : 0) + (is_chain_arg_set ? 1 : 0); if (nameParamsCount > 1) - throw std::runtime_error("Only one of -regtest, -testnet or -devnet can be used."); + throw std::runtime_error("Only one of -regtest, -testnet, -devnet or -chain can be used."); if (fDevNet) return CBaseChainParams::DEVNET; @@ -987,7 +988,7 @@ std::string ArgsManager::GetChainName() const return CBaseChainParams::REGTEST; if (fTestNet) return CBaseChainParams::TESTNET; - return CBaseChainParams::MAIN; + return GetArg("-chain", CBaseChainParams::MAIN); } std::string ArgsManager::GetDevNetName() const diff --git a/src/validation.cpp b/src/validation.cpp index 5c2bcc3d2415..f48f81cb9c2b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2040,8 +2040,8 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; } - // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic. - if (VersionBitsState(pindex->pprev, consensusparams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) { + // Start enforcing BIP112 (CHECKSEQUENCEVERIFY) + if (pindex->nHeight >= consensusparams.CSVHeight) { flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; } @@ -2224,9 +2224,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, } /// END DASH - // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic. + // Start enforcing BIP68 (sequence locks) int nLockTimeFlags = 0; - if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) { + if (pindex->nHeight >= chainparams.GetConsensus().CSVHeight) { nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; } @@ -3976,9 +3976,9 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat AssertLockHeld(cs_main); const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; - // Start enforcing BIP113 (Median Time Past) using versionbits logic. + // Start enforcing BIP113 (Median Time Past). int nLockTimeFlags = 0; - if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) { + if (nHeight >= consensusParams.CSVHeight) { assert(pindexPrev != nullptr); nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST; } diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp index c2a3d1c15a3b..bcec42dbbdf5 100644 --- a/src/versionbitsinfo.cpp +++ b/src/versionbitsinfo.cpp @@ -12,16 +12,6 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B /*.gbt_force =*/ true, /*.check_mn_protocol =*/ false, }, - { - /*.name =*/ "csv", - /*.gbt_force =*/ true, - /*.check_mn_protocol =*/ false, - }, - { - /*.name =*/ "dip0001", - /*.gbt_force =*/ true, - /*.check_mn_protocol =*/ true, - }, { /*.name =*/ "bip147", /*.gbt_force =*/ true, @@ -32,11 +22,6 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B /*.gbt_force =*/ true, /*.check_mn_protocol =*/ false, }, - { - /*.name =*/ "dip0008", - /*.gbt_force =*/ true, - /*.check_mn_protocol =*/ false, - }, { /*.name =*/ "realloc", /*.gbt_force =*/ true, diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py index faf5843b2d5e..cc2972b0618f 100755 --- a/test/functional/feature_bip68_sequence.py +++ b/test/functional/feature_bip68_sequence.py @@ -11,8 +11,8 @@ assert_equal, assert_greater_than, assert_raises_rpc_error, - get_bip9_status, - satoshi_round + satoshi_round, + softfork_active, ) from test_framework.script_util import DUMMY_P2SH_SCRIPT @@ -50,7 +50,7 @@ def run_test(self): self.log.info("Running test sequence-lock-unconfirmed-inputs") self.test_sequence_lock_unconfirmed_inputs() - self.log.info("Running test BIP68 not consensus before versionbits activation") + self.log.info("Running test BIP68 not consensus before activation") self.test_bip68_not_consensus() self.log.info("Activating BIP68 (and 112/113)") @@ -334,12 +334,12 @@ def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock): self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1)) self.nodes[0].generate(10) - # Make sure that BIP68 isn't being used to validate blocks, prior to - # versionbits activation. If more blocks are mined prior to this test + # Make sure that BIP68 isn't being used to validate blocks prior to + # activation height. If more blocks are mined prior to this test # being run, then it's possible the test has activated the soft fork, and # this test should be moved to run earlier, or deleted. def test_bip68_not_consensus(self): - assert get_bip9_status(self.nodes[0], 'csv')['status'] != 'active' + assert not softfork_active(self.nodes[0], 'csv') txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2) tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid)) @@ -388,9 +388,9 @@ def activateCSV(self): height = self.nodes[0].getblockcount() assert_greater_than(min_activation_height - height, 2) self.nodes[0].generate(min_activation_height - height - 2) - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "locked_in") + assert not softfork_active(self.nodes[0], 'csv') self.nodes[0].generate(1) - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "active") + assert softfork_active(self.nodes[0], 'csv') self.sync_blocks() # Use self.nodes[1] to test that version 2 transactions are standard. diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index 971cd6ecf591..82969347fb08 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -65,14 +65,11 @@ def set_test_params(self): self.rpc_timeout = 120 def test_cltv_info(self, *, is_active): - assert_equal( - next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip65'), + assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip65'], { - "id": "bip65", - "version": 4, - "reject": { - "status": is_active - } + "active": is_active, + "height": CLTV_HEIGHT, + "type": "buried", }, ) @@ -103,9 +100,9 @@ def run_test(self): block.hashMerkleRoot = block.calc_merkle_root() block.solve() - self.test_cltv_info(is_active=False) + self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules self.nodes[0].p2p.send_and_ping(msg_block(block)) - self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules + self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 4") @@ -151,7 +148,7 @@ def run_test(self): block.hashMerkleRoot = block.calc_merkle_root() block.solve() - self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules + self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules self.nodes[0].p2p.send_and_ping(msg_block(block)) self.test_cltv_info(is_active=True) # Active as of current tip assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index 04d5e7d30f01..57ac50d11f38 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -2,23 +2,17 @@ # Copyright (c) 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. -"""Test activation of the first version bits soft fork. +"""Test CSV soft fork activation. This soft fork will activate the following BIPS: BIP 68 - nSequence relative lock times BIP 112 - CHECKSEQUENCEVERIFY BIP 113 - MedianTimePast semantics for nLockTime -regtest lock-in with 108/144 block signalling -activation after a further 144 blocks - mine 82 blocks whose coinbases will be used to generate inputs for our tests -mine 61 blocks to transition from DEFINED to STARTED -mine 144 blocks only 100 of which are signaling readiness in order to fail to change state this period -mine 144 blocks with 108 signaling and verify STARTED->LOCKED_IN -mine 140 blocks and seed block chain with the 82 inputs will use for our tests at height 572 -mine 3 blocks and verify still at LOCKED_IN and test that enforcement has not triggered -mine 1 block and test that enforcement has triggered (which triggers ACTIVE) +mine 345 blocks and seed block chain with the 82 inputs will use for our tests at height 427 +mine 2 blocks and verify soft fork not yet activated +mine 1 block and test that soft fork is activated (rules enforced for next block) Test BIP 113 is enforced Mine 4 blocks so next height is 580 and test BIP 68 is enforced for time and height Mine 1 block so next height is 581 and test BIP 68 now passes time but not height @@ -58,13 +52,14 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - get_bip9_status, hex_str_to_bytes, + softfork_active, ) TESTING_TX_COUNT = 83 # Number of testing transactions: 1 BIP113 tx, 16 BIP68 txs, 66 BIP112 txs (see comments above) COINBASE_BLOCK_COUNT = TESTING_TX_COUNT # Number of coinbase blocks we need to generate as inputs for our txs BASE_RELATIVE_LOCKTIME = 10 +CSV_ACTIVATION_HEIGHT = 432 SEQ_DISABLE_FLAG = 1 << 31 SEQ_RANDOM_HIGH_BIT = 1 << 25 SEQ_TYPE_FLAG = 1 << 22 @@ -168,20 +163,19 @@ def setup_network(self): def skip_test_if_missing_module(self): self.skip_if_no_wallet() - def generate_blocks(self, number, version, test_blocks=None): - if test_blocks is None: - test_blocks = [] + def generate_blocks(self, number): + test_blocks = [] for i in range(number): - block = self.create_test_block([], version) + block = self.create_test_block([]) test_blocks.append(block) self.last_block_time += 600 self.tip = block.sha256 self.tipheight += 1 return test_blocks - def create_test_block(self, txs, version=536870912): + def create_test_block(self, txs): block = create_block(self.tip, create_coinbase(self.tipheight + 1), self.last_block_time + 600) - block.nVersion = version + block.nVersion = 4 block.vtx.extend(txs) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() @@ -207,50 +201,15 @@ def run_test(self): self.tip = int(self.nodes[0].getbestblockhash(), 16) self.nodeaddress = self.nodes[0].getnewaddress() - # TODO: uncomment the code below when bitcoin#16060 is backported, - # should go right below `# Activation height is hardcoded` line - # # We advance to block height five below BIP112 activation for the following tests - # test_blocks = self.generate_blocks(CSV_ACTIVATION_HEIGHT-5 - COINBASE_BLOCK_COUNT) - - self.log.info("Test that the csv softfork is DEFINED") - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'defined') - test_blocks = self.generate_blocks(60, 4) - self.send_blocks(test_blocks) - - self.log.info("Advance from DEFINED to STARTED, height = 143") - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started') - - self.log.info("Fail to achieve LOCKED_IN") - # 100 out of 144 signal bit 0. Use a variety of bits to simulate multiple parallel softforks - - test_blocks = self.generate_blocks(50, 536870913) # 0x20000001 (signalling ready) - test_blocks = self.generate_blocks(20, 4, test_blocks) # 0x00000004 (signalling not) - test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready) - test_blocks = self.generate_blocks(24, 536936448, test_blocks) # 0x20010000 (signalling not) - self.send_blocks(test_blocks) - - self.log.info("Failed to advance past STARTED, height = 287") - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started') - - self.log.info("Generate blocks to achieve LOCK-IN") - # 108 out of 144 signal bit 0 to achieve lock-in - # using a variety of bits to simulate multiple parallel softforks - test_blocks = self.generate_blocks(58, 536870913) # 0x20000001 (signalling ready) - test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not) - test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready) - test_blocks = self.generate_blocks(10, 536936448, test_blocks) # 0x20010000 (signalling not) - self.send_blocks(test_blocks) - - self.log.info("Advanced from STARTED to LOCKED_IN, height = 431") - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in') - - # Generate 140 more version 4 blocks - test_blocks = self.generate_blocks(140, 4) + # Activation height is hardcoded + test_blocks = self.generate_blocks(CSV_ACTIVATION_HEIGHT-5 - COINBASE_BLOCK_COUNT) + #test_blocks = self.generate_blocks(345) self.send_blocks(test_blocks) + assert not softfork_active(self.nodes[0], 'csv') - # Inputs at height = 572 + # Inputs at height = 431 # - # Put inputs for all tests in the chain at height 572 (tip now = 571) (time increases by 600s per block) + # Put inputs for all tests in the chain at height 431 (tip now = 430) (time increases by 600s per block) # Note we reuse inputs for v1 and v2 txs so must test these separately # 16 normal inputs bip68inputs = [] @@ -282,7 +241,7 @@ def run_test(self): bip113input = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress) self.nodes[0].setmocktime(self.last_block_time + 600) - inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 572 + inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 431 self.nodes[0].setmocktime(TIME_GENESIS_BLOCK + 600 * 1000 + 100) self.tip = int(inputblockhash, 16) self.tipheight += 1 @@ -290,11 +249,12 @@ def run_test(self): assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]), TESTING_TX_COUNT + 1) # 2 more version 4 blocks - test_blocks = self.generate_blocks(2, 4) + test_blocks = self.generate_blocks(2) self.send_blocks(test_blocks) - self.log.info("Not yet advanced to ACTIVE, height = 574 (will activate for block 576, not 575)") - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in') + assert_equal(self.tipheight, CSV_ACTIVATION_HEIGHT - 2) + self.log.info("Height = {}, CSV not yet active (will activate for block {}, not {})".format(self.tipheight, CSV_ACTIVATION_HEIGHT, CSV_ACTIVATION_HEIGHT - 1)) + assert not softfork_active(self.nodes[0], 'csv') # Test both version 1 and version 2 transactions for all tests # BIP113 test transaction will be modified before each use to put in appropriate block time @@ -372,10 +332,11 @@ def run_test(self): self.send_blocks([self.create_test_block(success_txs)]) self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) - # 1 more version 4 block to get us to height 575 so the fork should now be active for the next block - test_blocks = self.generate_blocks(1, 4) + # 1 more version 4 block to get us to height 432 so the fork should now be active for the next block + assert not softfork_active(self.nodes[0], 'csv') + test_blocks = self.generate_blocks(1) self.send_blocks(test_blocks) - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'active') + assert softfork_active(self.nodes[0], 'csv') self.log.info("Post-Soft Fork Tests.") @@ -396,8 +357,8 @@ def run_test(self): self.send_blocks([self.create_test_block([bip113tx])]) self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) - # Next block height = 580 after 4 blocks of random version - test_blocks = self.generate_blocks(4, 1234) + # Next block height = 437 after 4 blocks of random version + test_blocks = self.generate_blocks(4) self.send_blocks(test_blocks) self.log.info("BIP 68 tests") @@ -424,8 +385,8 @@ def run_test(self): for tx in bip68heighttxs: self.send_blocks([self.create_test_block([tx])], success=False, reject_reason='bad-txns-nonfinal') - # Advance one block to 581 - test_blocks = self.generate_blocks(1, 1234) + # Advance one block to 438 + test_blocks = self.generate_blocks(1) self.send_blocks(test_blocks) # Height txs should fail and time txs should now pass 9 * 600 > 10 * 512 @@ -435,8 +396,8 @@ def run_test(self): for tx in bip68heighttxs: self.send_blocks([self.create_test_block([tx])], success=False, reject_reason='bad-txns-nonfinal') - # Advance one block to 582 - test_blocks = self.generate_blocks(1, 1234) + # Advance one block to 439 + test_blocks = self.generate_blocks(1) self.send_blocks(test_blocks) # All BIP 68 txs should pass diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 5ab9dfbba761..740a208beebb 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -45,14 +45,11 @@ def set_test_params(self): self.rpc_timeout = 240 def test_dersig_info(self, *, is_active): - assert_equal( - next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip66'), + assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'], { - "id": "bip66", - "version": 3, - "reject": { - "status": is_active - } + "active": is_active, + "height": DERSIG_HEIGHT, + "type": "buried", }, ) @@ -84,9 +81,9 @@ def run_test(self): block.rehash() block.solve() - self.test_dersig_info(is_active=False) + self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules self.nodes[0].p2p.send_and_ping(msg_block(block)) - self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules + self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 3") @@ -131,7 +128,7 @@ def run_test(self): block.rehash() block.solve() - self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules + self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules self.nodes[0].p2p.send_and_ping(msg_block(block)) self.test_dersig_info(is_active=True) # Active as of current tip assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) diff --git a/test/functional/feature_dip0020_activation.py b/test/functional/feature_dip0020_activation.py index 471e4fdc667a..df3fcbd7b1c3 100755 --- a/test/functional/feature_dip0020_activation.py +++ b/test/functional/feature_dip0020_activation.py @@ -5,7 +5,7 @@ from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, ToHex from test_framework.script import CScript, OP_CAT, OP_DROP, OP_TRUE from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_rpc_error, get_bip9_status, satoshi_round +from test_framework.util import assert_equal, assert_raises_rpc_error, get_bip9_details, softfork_active, satoshi_round ''' feature_dip0020_activation.py @@ -55,12 +55,12 @@ def run_test(self): tx0_hex = ToHex(tx0) # This tx isn't valid yet - assert_equal(get_bip9_status(self.nodes[0], 'dip0020')['status'], 'locked_in') + assert_equal(get_bip9_details(self.nodes[0], 'dip0020')['status'], 'locked_in') assert_raises_rpc_error(-26, DISABLED_OPCODE_ERROR, self.node.sendrawtransaction, tx0_hex) # Generate enough blocks to activate DIP0020 opcodes self.node.generate(98) - assert_equal(get_bip9_status(self.nodes[0], 'dip0020')['status'], 'active') + assert softfork_active(self.nodes[0], 'dip0020') # Still need 1 more block for mempool to accept new opcodes assert_raises_rpc_error(-26, DISABLED_OPCODE_ERROR, self.node.sendrawtransaction, tx0_hex) diff --git a/test/functional/feature_dip3_deterministicmns.py b/test/functional/feature_dip3_deterministicmns.py index 570436a35719..9a39ab1040a5 100755 --- a/test/functional/feature_dip3_deterministicmns.py +++ b/test/functional/feature_dip3_deterministicmns.py @@ -12,7 +12,7 @@ from test_framework.blocktools import create_block, create_coinbase, get_masternode_payment from test_framework.messages import CCbTx, COIN, CTransaction, FromHex, ToHex, uint256_to_string from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, force_finish_mnsync, get_bip9_status, p2p_port +from test_framework.util import assert_equal, force_finish_mnsync, p2p_port class Masternode(object): pass @@ -390,10 +390,10 @@ def mine_block(self, node, vtx=None, miner_address=None, mn_payee=None, mn_amoun coinbasevalue += new_fees if mn_amount is None: - realloc_info = get_bip9_status(self.nodes[0], 'realloc') + realloc_info = node.getblockchaininfo()['softforks']['realloc'] realloc_height = 99999999 - if realloc_info['status'] == 'active': - realloc_height = realloc_info['since'] + if realloc_info['active']: + realloc_height = realloc_info['height'] mn_amount = get_masternode_payment(height, coinbasevalue, realloc_height) miner_amount = coinbasevalue - mn_amount diff --git a/test/functional/feature_llmq_is_cl_conflicts.py b/test/functional/feature_llmq_is_cl_conflicts.py index 9616cd32a2ba..dd3deaa9023e 100755 --- a/test/functional/feature_llmq_is_cl_conflicts.py +++ b/test/functional/feature_llmq_is_cl_conflicts.py @@ -18,7 +18,7 @@ from test_framework.messages import CCbTx, CInv, COIN, CTransaction, FromHex, hash256, msg_clsig, msg_inv, ser_string, ToHex, uint256_from_str, uint256_to_string from test_framework.mininode import P2PInterface from test_framework.test_framework import DashTestFramework -from test_framework.util import assert_equal, assert_raises_rpc_error, hex_str_to_bytes, get_bip9_status, wait_until +from test_framework.util import assert_equal, assert_raises_rpc_error, hex_str_to_bytes, wait_until class TestP2PConn(P2PInterface): @@ -308,10 +308,10 @@ def create_block(self, node, vtx=None): coinbasevalue -= bt_fees coinbasevalue += new_fees - realloc_info = get_bip9_status(self.nodes[0], 'realloc') + realloc_info = self.nodes[0].getblockchaininfo()['softforks']['realloc'] realloc_height = 99999999 - if realloc_info['status'] == 'active': - realloc_height = realloc_info['since'] + if realloc_info['active']: + realloc_height = realloc_info['height'] mn_amount = get_masternode_payment(height, coinbasevalue, realloc_height) miner_amount = coinbasevalue - mn_amount diff --git a/test/functional/feature_new_quorum_type_activation.py b/test/functional/feature_new_quorum_type_activation.py index d70aa78a4bef..fc42ad36b50c 100755 --- a/test/functional/feature_new_quorum_type_activation.py +++ b/test/functional/feature_new_quorum_type_activation.py @@ -3,7 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, get_bip9_status +from test_framework.util import assert_equal, get_bip9_details ''' feature_new_quorum_type_activation.py @@ -20,19 +20,20 @@ def set_test_params(self): self.extra_args = [["-vbparams=dip0020:0:999999999999:10:8:6:5"]] def run_test(self): - assert_equal(get_bip9_status(self.nodes[0], 'dip0020')['status'], 'defined') + self.log.info(get_bip9_details(self.nodes[0], 'dip0020')) + assert_equal(get_bip9_details(self.nodes[0], 'dip0020')['status'], 'defined') self.nodes[0].generate(9) - assert_equal(get_bip9_status(self.nodes[0], 'dip0020')['status'], 'started') + assert_equal(get_bip9_details(self.nodes[0], 'dip0020')['status'], 'started') ql = self.nodes[0].quorum("list") assert_equal(len(ql), 3) assert "llmq_test_v17" not in ql self.nodes[0].generate(10) - assert_equal(get_bip9_status(self.nodes[0], 'dip0020')['status'], 'locked_in') + assert_equal(get_bip9_details(self.nodes[0], 'dip0020')['status'], 'locked_in') ql = self.nodes[0].quorum("list") assert_equal(len(ql), 3) assert "llmq_test_v17" not in ql self.nodes[0].generate(10) - assert_equal(get_bip9_status(self.nodes[0], 'dip0020')['status'], 'active') + assert_equal(get_bip9_details(self.nodes[0], 'dip0020')['status'], 'active') ql = self.nodes[0].quorum("list") assert_equal(len(ql), 4) assert "llmq_test_v17" in ql diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 0220069cb35d..444f6f94ab46 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -83,7 +83,6 @@ def _test_getblockchaininfo(self): keys = [ 'bestblockhash', - 'bip9_softforks', 'blocks', 'chain', 'chainwork', @@ -128,6 +127,128 @@ def _test_getblockchaininfo(self): assert res['automatic_pruning'] assert_equal(res['prune_target_size'], 576716800) assert_greater_than(res['size_on_disk'], 0) + { + 'testdummy': { + 'type': 'bip9', + 'bip9': { + 'status': 'started', + 'bit': 28, + 'start_time': 0, + 'timeout': 999999999999, + 'since': 144, + 'statistics': { + 'period': 144, + 'threshold': 108, + 'elapsed': 57, + 'count': 57, + 'possible': True + } + }, + 'active': False} + } + assert_equal(res['softforks'], { + 'bip34': {'type': 'buried', 'active': False, 'height': 500}, + 'bip66': {'type': 'buried', 'active': False, 'height': 1251}, + 'bip65': {'type': 'buried', 'active': False, 'height': 1351}, + 'csv': {'type': 'buried', 'active': False, 'height': 432}, + 'dip0001': { 'type': 'buried', 'active': False, 'height': 2000}, + 'dip0008': { 'type': 'buried', 'active': False, 'height': 432}, + 'bip147': { + 'type': 'bip9', + 'bip9': { + 'status': 'started', + 'bit': 2, + 'start_time': 0, + 'timeout': 999999999999, + 'since': 144, + 'statistics': { + 'period': 144, + 'threshold': 108, + 'elapsed': 57, + 'count': 57, + 'possible': True + }, + }, + 'active': False}, + 'dip0003': { + 'type': 'bip9', + 'bip9': { + 'status': 'started', + 'bit': 3, + 'start_time': 0, + 'timeout': 999999999999, + 'since': 144, + 'statistics': { + 'period': 144, + 'threshold': 108, + 'elapsed': 57, + 'count': 57, + 'possible': True + }, + }, + 'active': False}, + 'realloc': { + 'type': 'bip9', + 'bip9': { + 'status': 'defined', + 'start_time': 0, + 'timeout': 999999999999, + 'since': 0 + }, + 'active': False}, + 'dip0020': { + 'type': 'bip9', + 'bip9': { + 'status': 'locked_in', + 'start_time': 0, + 'timeout': 999999999999, + 'since': 200 + }, + 'active': False}, + 'dip0024': { + 'type': 'bip9', + 'bip9': { + 'status': 'defined', + 'start_time': 0, + 'timeout': 999999999999, + 'since': 0 + }, + 'active': False}, + 'v19': { + 'type': 'bip9', + 'bip9': { + 'status': 'defined', + 'start_time': 0, + 'timeout': 999999999999, + 'since': 0 + }, + 'active': False}, + 'v20': { + 'type': 'bip9', + 'bip9': { + 'status': 'defined', + 'start_time': 0, + 'timeout': 999999999999, + 'since': 0 + }, 'active': False}, + 'testdummy': { + 'type': 'bip9', + 'bip9': { + 'status': 'started', + 'bit': 28, + 'start_time': 0, + 'timeout': 999999999999, # testdummy does not have a timeout so is set to the max int64 value + 'since': 144, + 'statistics': { + 'period': 144, + 'threshold': 108, + 'elapsed': 57, + 'count': 57, + 'possible': True, + }, + }, + 'active': False}, + }) def _test_getchaintxstats(self): self.log.info("Test getchaintxstats") diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 953681adee01..12752e52a08e 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -17,13 +17,11 @@ wait_until, ) from test_framework.mininode import P2PInterface +import test_framework.messages from test_framework.messages import ( CAddress, msg_addr, NODE_NETWORK, - NODE_GETUTXO,NODE_BLOOM, - NODE_NETWORK_LIMITED, - NODE_HEADERS_COMPRESSED, ) @@ -34,16 +32,10 @@ def assert_net_servicesnames(servicesflag, servicenames): :param servicesflag: The services as an integer. :param servicenames: The list of decoded services names, as strings. """ - if servicesflag & NODE_NETWORK: - assert "NETWORK" in servicenames - if servicesflag & NODE_GETUTXO: - assert "GETUTXO" in servicenames - if servicesflag & NODE_BLOOM: - assert "BLOOM" in servicenames - if servicesflag & NODE_NETWORK_LIMITED: - assert "NETWORK_LIMITED" in servicenames - if servicesflag & NODE_HEADERS_COMPRESSED: - assert "HEADERS_COMPRESSED" in servicenames + servicesflag_generated = 0 + for servicename in servicenames: + servicesflag_generated |= getattr(test_framework.messages, 'NODE_' + servicename) + assert servicesflag_generated == servicesflag class NetTest(DashTestFramework): @@ -126,7 +118,7 @@ def _test_getnetworkinfo(self): # check the `servicesnames` field network_info = [node.getnetworkinfo() for node in self.nodes] for info in network_info: - assert_net_servicesnames(int(info["localservices"], 16), info["localservicesnames"]) + assert_net_servicesnames(int(info["localservices"], 0x10), info["localservicesnames"]) self.log.info('Test extended connections info') self.connect_nodes(1, 2) @@ -166,7 +158,7 @@ def _test_getpeerinfo(self): assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr']) # check the `servicesnames` field for info in peer_info: - assert_net_servicesnames(int(info[0]["services"], 16), info[0]["servicesnames"]) + assert_net_servicesnames(int(info[0]["services"], 0x10), info[0]["servicesnames"]) def test_service_flags(self): self.nodes[0].add_p2p_connection(P2PInterface(), services=(1 << 4) | (1 << 63)) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index c84c83f41f54..8741b59f0050 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -50,6 +50,7 @@ set_node_times, set_timeout_scale, satoshi_round, + softfork_active, wait_until, get_chain_folder, rpc_port, ) @@ -1046,9 +1047,9 @@ def activate_by_name(self, name, expected_activation_height=None): self.bump_mocktime(blocks_left) self.nodes[0].generate(blocks_left) self.sync_blocks() - assert self.nodes[0].getblockchaininfo()['bip9_softforks'][name]['status'] != 'active' + assert not softfork_active(self.nodes[0], name) - while self.nodes[0].getblockchaininfo()['bip9_softforks'][name]['status'] != 'active': + while not softfork_active(self.nodes[0], name): self.bump_mocktime(batch_size) self.nodes[0].generate(batch_size) self.sync_blocks() diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 43bdf366bae9..312d650644cd 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -430,9 +430,13 @@ def get_chain_folder(datadir, chain): pass return chain -def get_bip9_status(node, key): - info = node.getblockchaininfo() - return info['bip9_softforks'][key] +def get_bip9_details(node, key): + """Return extra info about bip9 softfork""" + return node.getblockchaininfo()['softforks'][key]['bip9'] + +def softfork_active(node, key): + """Return whether a softfork is active.""" + return node.getblockchaininfo()['softforks'][key]['active'] def set_node_times(nodes, t): for node in nodes: diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index 550646fa7c31..7015a61dcdd3 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -26,8 +26,10 @@ ) import collections +from decimal import Decimal import enum import itertools +import random Call = enum.Enum("Call", "single multiaddress multiscript") Data = enum.Enum("Data", "address pub priv") @@ -50,7 +52,7 @@ def do_import(self, timestamp): assert_equal(response, None) elif self.call in (Call.multiaddress, Call.multiscript): - response = self.node.importmulti([{ + request = { "scriptPubKey": { "address": self.address["address"] } if self.call == Call.multiaddress else self.address["scriptPubKey"], @@ -59,13 +61,18 @@ def do_import(self, timestamp): "keys": [self.key] if self.data == Data.priv else [], "label": self.label, "watchonly": self.data != Data.priv - }], {"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)}) + } + response = self.node.importmulti( + requests=[request], + options={"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)}, + ) assert_equal(response, [{"success": True}]) - def check(self, txid=None, amount=None, confirmations=None): + def check(self, txid=None, amount=None, confirmation_height=None): """Verify that listtransactions/listreceivedbyaddress return expected values.""" txs = self.node.listtransactions(label=self.label, count=10000, include_watchonly=True) + current_height = self.node.getblockcount() assert_equal(len(txs), self.expected_txs) addresses = self.node.listreceivedbyaddress(minconf=0, include_watchonly=True, address_filter=self.address['address']) @@ -80,13 +87,13 @@ def check(self, txid=None, amount=None, confirmations=None): assert_equal(tx["category"], "receive") assert_equal(tx["label"], self.label) assert_equal(tx["txid"], txid) - assert_equal(tx["confirmations"], confirmations) + assert_equal(tx["confirmations"], 1 + current_height - confirmation_height) assert_equal("trusted" not in tx, True) address, = [ad for ad in addresses if txid in ad["txids"]] assert_equal(address["address"], self.address["address"]) assert_equal(address["amount"], self.expected_balance) - assert_equal(address["confirmations"], confirmations) + assert_equal(address["confirmations"], 1 + current_height - confirmation_height) # Verify the transaction is correctly marked watchonly depending on # whether the transaction pays to an imported public key or # imported private key. The test setup ensures that transaction @@ -114,6 +121,13 @@ def check(self, txid=None, amount=None, confirmations=None): # Rescans start at the earliest block up to 2 hours before the key timestamp. TIMESTAMP_WINDOW = 2 * 60 * 60 +AMOUNT_DUST = 0.00000546 + + +def get_rand_amount(): + r = random.uniform(AMOUNT_DUST, 1) + return Decimal(str(round(r, 8))) + class ImportRescanTest(BitcoinTestFramework): def set_test_params(self): @@ -125,13 +139,13 @@ def skip_test_if_missing_module(self): self.skip_if_no_wallet() def setup_network(self): - extra_args = [[] for _ in range(self.num_nodes)] + self.extra_args = [[] for _ in range(self.num_nodes)] for i, import_node in enumerate(IMPORT_NODES, 2): if import_node.prune: # txindex is enabled by default in Dash and needs to be disabled for import-rescan.py - extra_args[i] += ["-prune=1", "-txindex=0", "-reindex"] + self.extra_args[i] += ["-prune=1", "-txindex=0", "-reindex"] - self.add_nodes(self.num_nodes, extra_args=extra_args) + self.add_nodes(self.num_nodes, extra_args=self.extra_args) # Import keys with pruning disabled self.start_nodes(extra_args=[[]] * self.num_nodes) @@ -149,15 +163,18 @@ def run_test(self): variant.label = "label {} {}".format(i, variant) variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress(variant.label)) variant.key = self.nodes[1].dumpprivkey(variant.address["address"]) - variant.initial_amount = 1 - (i + 1) / 64 + variant.initial_amount = get_rand_amount() variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount) + self.nodes[0].generate(1) # Generate one block for each send + variant.confirmation_height = self.nodes[0].getblockcount() + variant.timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"] - # Generate a block containing the initial transactions, then another - # block further in the future (past the rescan window). - self.nodes[0].generate(1) + # Generate a block further in the future (past the rescan window). assert_equal(self.nodes[0].getrawmempool(), []) - timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"] - set_node_times(self.nodes, timestamp + TIMESTAMP_WINDOW + 1) + set_node_times( + self.nodes, + self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"] + TIMESTAMP_WINDOW + 1, + ) self.nodes[0].generate(1) self.sync_all() @@ -167,11 +184,11 @@ def run_test(self): self.log.info('Run import for variant {}'.format(variant)) expect_rescan = variant.rescan == Rescan.yes variant.node = self.nodes[2 + IMPORT_NODES.index(ImportNode(variant.prune, expect_rescan))] - variant.do_import(timestamp) + variant.do_import(variant.timestamp) if expect_rescan: variant.expected_balance = variant.initial_amount variant.expected_txs = 1 - variant.check(variant.initial_txid, variant.initial_amount, 2) + variant.check(variant.initial_txid, variant.initial_amount, variant.confirmation_height) else: variant.expected_balance = 0 variant.expected_txs = 0 @@ -179,11 +196,11 @@ def run_test(self): # Create new transactions sending to each address. for i, variant in enumerate(IMPORT_VARIANTS): - variant.sent_amount = 1 - (2 * i + 1) / 128 + variant.sent_amount = get_rand_amount() variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount) + self.nodes[0].generate(1) # Generate one block for each send + variant.confirmation_height = self.nodes[0].getblockcount() - # Generate a block containing the new transactions. - self.nodes[0].generate(1) assert_equal(self.nodes[0].getrawmempool(), []) self.sync_all() @@ -192,7 +209,7 @@ def run_test(self): self.log.info('Run check for variant {}'.format(variant)) variant.expected_balance += variant.sent_amount variant.expected_txs += 1 - variant.check(variant.sent_txid, variant.sent_amount, 1) + variant.check(variant.sent_txid, variant.sent_amount, variant.confirmation_height) for i, import_node in enumerate(IMPORT_NODES, 2): if import_node.prune: self.stop_node(i, expected_stderr='Warning: You are starting with governance validation disabled. This is expected because you are running a pruned node.')