Compare commits
71 Commits
f8136f5839
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| bf3c33c53a | |||
| 5951ee118a | |||
| d2124a3038 | |||
| b5050d06c0 | |||
| a520441e3a | |||
| 11704e6023 | |||
| 7e9b2c6615 | |||
| fc06a43dd7 | |||
| 7e99311210 | |||
| 14e3fb6708 | |||
| b9fdc79818 | |||
| 4351d5b733 | |||
| 4a0a334649 | |||
| d52550a6fc | |||
| d159e72086 | |||
| adf2bacdbd | |||
| 1ec3dbfee3 | |||
| a568ab628e | |||
| 693d2290e0 | |||
| 389c8c7383 | |||
| 4e3c0f8f6f | |||
| 2e54d9fb4d | |||
| 4238da9bea | |||
| 4e67e687d7 | |||
| 762e25294f | |||
| 3bb4eb3a5a | |||
| 7914fca0f2 | |||
| 810bd6712f | |||
| bf5b066a8d | |||
| 19e1ce6f00 | |||
| dc45e7d904 | |||
| 1ed468e040 | |||
| 53b1fe332b | |||
| 9c6ccbb726 | |||
| 34432e5848 | |||
| 4caf2fc68f | |||
| 84aefb5475 | |||
| 1f2b109d95 | |||
| 78ea2aac5b | |||
| 2419ed7bf7 | |||
| a9b1b4085f | |||
| f763e3f1e7 | |||
| f8f13f9027 | |||
| bf1b4cffe0 | |||
| 82d77344d2 | |||
| 1673cfb6dc | |||
| 2f3f320d28 | |||
| 2b011d6ee2 | |||
|
|
d77088c1f2 | ||
| faa3e925cd | |||
| ddd851dc11 | |||
| 752590348b | |||
| f0cb958cac | |||
| 6d56ad8541 | |||
| 449a00434e | |||
| 5cda31b505 | |||
| ec517f86e6 | |||
| 33e5f646a7 | |||
| c1408871cc | |||
| 0a01ad8bba | |||
| 85c8d7f7dd | |||
| d6ba1aed4e | |||
|
|
7e1b5701a6 | ||
|
|
4cbad44688 | ||
|
|
876c32ed1f | ||
|
|
f889ded55e | ||
|
|
07738d75ab | ||
|
|
04916cdf57 | ||
|
|
0d139e0bdc | ||
|
|
4c86d11b13 | ||
|
|
978d4d739b |
27
.dockerignore
Normal file
27
.dockerignore
Normal file
@@ -0,0 +1,27 @@
|
||||
.git
|
||||
release
|
||||
depends/built
|
||||
depends/work
|
||||
depends/x86_64-unknown-linux-gnu
|
||||
depends/x86_64-w64-mingw32
|
||||
src/RandomX/build
|
||||
src/*.o
|
||||
src/*.a
|
||||
src/*.la
|
||||
src/*.lo
|
||||
src/.libs
|
||||
src/.deps
|
||||
src/univalue/.libs
|
||||
src/univalue/.deps
|
||||
src/cc/*.o
|
||||
src/cc/*.a
|
||||
src/dragonxd
|
||||
src/dragonx-cli
|
||||
src/dragonx-tx
|
||||
src/dragonxd.exe
|
||||
src/dragonx-cli.exe
|
||||
src/dragonx-tx.exe
|
||||
sapling-output.params
|
||||
sapling-spend.params
|
||||
config.status
|
||||
config.log
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -28,6 +28,7 @@ build-aux/test-driver
|
||||
config.log
|
||||
config.status
|
||||
configure
|
||||
configure~
|
||||
libtool
|
||||
src/config/bitcoin-config.h
|
||||
src/config/bitcoin-config.h.in
|
||||
@@ -166,3 +167,12 @@ REGTEST_7776
|
||||
src/cc/librogue.so
|
||||
src/cc/games/prices
|
||||
src/cc/games/tetris
|
||||
release-linux/
|
||||
release/
|
||||
src/dragonxd
|
||||
src/dragonx-cli
|
||||
src/dragonx-tx
|
||||
src/dragonxd.exe
|
||||
src/dragonx-cli.exe
|
||||
src/dragonx-tx.exe
|
||||
doc/relnotes/
|
||||
4
AUTHORS
4
AUTHORS
@@ -1,3 +1,7 @@
|
||||
# The DragonX Developers
|
||||
|
||||
Dan S https://git.dragonx.is/dan
|
||||
|
||||
# The Hush Developers
|
||||
|
||||
Duke Leto https://git.hush.is/duke https://github.com/leto
|
||||
|
||||
1
COPYING
1
COPYING
@@ -1,3 +1,4 @@
|
||||
Copyright (c) 2024-2026 The DragonX developers
|
||||
Copyright (c) 2018-2025 The Hush developers
|
||||
Copyright (c) 2009-2017 The Bitcoin Core developers
|
||||
Copyright (c) 2009-2018 Bitcoin Developers
|
||||
|
||||
31
Dockerfile.compat
Normal file
31
Dockerfile.compat
Normal file
@@ -0,0 +1,31 @@
|
||||
FROM ubuntu:20.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential pkg-config libc6-dev m4 g++-multilib autoconf libtool \
|
||||
ncurses-dev unzip python3 zlib1g-dev wget bsdmainutils automake cmake \
|
||||
libcurl4-openssl-dev curl git binutils \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /build
|
||||
COPY . /build/
|
||||
|
||||
# Clean host-built depends and src artifacts to force full rebuild inside container
|
||||
RUN rm -rf /build/depends/built /build/depends/work \
|
||||
/build/depends/x86_64-unknown-linux-gnu \
|
||||
/build/depends/x86_64-w64-mingw32 \
|
||||
/build/src/RandomX/build \
|
||||
&& find /build/src -name '*.o' -o -name '*.a' -o -name '*.la' -o -name '*.lo' \
|
||||
-o -name '*.lai' | xargs rm -f \
|
||||
&& rm -rf /build/src/univalue/.libs /build/src/univalue/.deps \
|
||||
&& rm -rf /build/src/.libs /build/src/.deps \
|
||||
&& rm -rf /build/src/cc/*.o /build/src/cc/*.a \
|
||||
&& rm -f /build/config.status /build/config.log
|
||||
|
||||
RUN cd /build && ./util/build.sh --disable-tests -j$(nproc)
|
||||
|
||||
# Strip binaries inside the container so extracted files are already small
|
||||
RUN strip /build/src/dragonxd /build/src/dragonx-cli /build/src/dragonx-tx
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
26
LICENSE
26
LICENSE
@@ -1,4 +1,4 @@
|
||||
GENERAL GENERAL PUBLIC LICENSE
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
@@ -7,15 +7,15 @@
|
||||
|
||||
Preamble
|
||||
|
||||
The GENERAL General Public License is a free, copyleft license for
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GENERAL General Public License is intended to guarantee your freedom to
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GENERAL General Public License for most of our software; it applies also to
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
@@ -37,7 +37,7 @@ freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GENERAL GPL protect your rights with two steps:
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
@@ -72,7 +72,7 @@ modification follow.
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GENERAL General Public License.
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
@@ -549,35 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GENERAL Affero General Public License.
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GENERAL Affero General Public License into a single
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GENERAL Affero General Public License,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GENERAL General Public License from time to time. Such new versions will
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GENERAL General
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GENERAL General Public License, you may choose any version ever published
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GENERAL General Public License can be used, that proxy's
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
|
||||
40
README.md
40
README.md
@@ -27,6 +27,21 @@ the entire history of Hush transactions; depending on the speed of your
|
||||
computer and network connection, it will likely take a few hours at least, but
|
||||
some people report full nodes syncing in less than 1.5 hours.
|
||||
|
||||
# Fastest way to sync (bootstrap)
|
||||
|
||||
The quickest way to get a fully-synced node is the signed bootstrap snapshot, which
|
||||
installs a pre-built blockchain so you skip re-validating the whole chain from genesis:
|
||||
|
||||
```sh
|
||||
# Stop dragonxd first if it is running, then:
|
||||
./util/bootstrap-dragonx.sh
|
||||
```
|
||||
|
||||
The script preserves your `wallet.dat` and `DRAGONX.conf`, verifies the download's
|
||||
checksums and (once a release key is published) its cryptographic signature, then starts
|
||||
you near the chain tip. If you prefer to sync from the network instead, a larger
|
||||
`-dbcache` (e.g. `-dbcache=2048`) noticeably speeds up the initial block download.
|
||||
|
||||
# Banned by GitHub
|
||||
|
||||
In working on this release, Duke Leto was suspended from Github, which gave Hush developers
|
||||
@@ -106,19 +121,32 @@ apt-get install -y gcc-7 g++-7 && \
|
||||
|
||||
# Build on Mac
|
||||
|
||||
```
|
||||
sudo port update
|
||||
sudo port upgrade outdated
|
||||
sudo port install qt5
|
||||
Install Xcode Command Line Tools and [Homebrew](https://brew.sh/), then install dependencies:
|
||||
|
||||
```sh
|
||||
xcode-select --install
|
||||
brew install gcc autoconf automake pkgconf libtool cmake curl
|
||||
|
||||
# Install Rust (needed for librustzcash on macOS Sequoia+)
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source "$HOME/.cargo/env"
|
||||
|
||||
# clone git repo
|
||||
git clone https://git.hush.is/hush/hush3
|
||||
cd hush3
|
||||
# Build
|
||||
# This uses 3 build processes, you need 2GB of RAM for each.
|
||||
# Build (uses 3 build processes, you need 2GB of RAM for each)
|
||||
# Make sure libtool gnubin and cargo are on PATH
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/opt/libtool/libexec/gnubin:$PATH"
|
||||
./build.sh -j3
|
||||
```
|
||||
|
||||
For a release build:
|
||||
|
||||
```sh
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/opt/libtool/libexec/gnubin:$PATH"
|
||||
./build.sh --mac-release -j$(sysctl -n hw.ncpu)
|
||||
```
|
||||
|
||||
# Installing Hush binaries
|
||||
|
||||
1. [Download the release](https://git.hush.is/hush/hush3/releases) with a .deb file extension.
|
||||
|
||||
204
build.sh
204
build.sh
@@ -1,19 +1,211 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2024-2026 The DragonX developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
set -eu -o pipefail
|
||||
|
||||
# run correct build script for detected OS
|
||||
VERSION="1.0.3"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
RELEASE_DIR="$SCRIPT_DIR/release"
|
||||
|
||||
# Parse release flags
|
||||
BUILD_LINUX_RELEASE=0
|
||||
BUILD_WIN_RELEASE=0
|
||||
BUILD_MAC_RELEASE=0
|
||||
BUILD_LINUX_COMPAT=0
|
||||
REMAINING_ARGS=()
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--linux-release)
|
||||
BUILD_LINUX_RELEASE=1
|
||||
;;
|
||||
--linux-compat)
|
||||
BUILD_LINUX_COMPAT=1
|
||||
;;
|
||||
--win-release)
|
||||
BUILD_WIN_RELEASE=1
|
||||
;;
|
||||
--mac-release)
|
||||
BUILD_MAC_RELEASE=1
|
||||
;;
|
||||
--all-release)
|
||||
BUILD_LINUX_RELEASE=1
|
||||
BUILD_WIN_RELEASE=1
|
||||
BUILD_MAC_RELEASE=1
|
||||
;;
|
||||
*)
|
||||
REMAINING_ARGS+=("$arg")
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Clean artifacts that may conflict between platform builds
|
||||
clean_for_platform() {
|
||||
local platform="$1"
|
||||
echo "Cleaning build artifacts for $platform build..."
|
||||
|
||||
# Use make clean if Makefile exists (safer than manual deletion)
|
||||
if [ -f src/Makefile ]; then
|
||||
make -C src clean 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Remove final binaries
|
||||
if [ -d src ]; then
|
||||
rm -f src/dragonxd src/dragonx-cli src/dragonx-tx 2>/dev/null || true
|
||||
rm -f src/dragonxd.exe src/dragonx-cli.exe src/dragonx-tx.exe 2>/dev/null || true
|
||||
rm -f src/hushd src/hush-cli src/hush-tx 2>/dev/null || true
|
||||
rm -f src/hushd.exe src/hush-cli.exe src/hush-tx.exe 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Clean RandomX build for cross-platform compatibility
|
||||
rm -rf src/RandomX/build 2>/dev/null || true
|
||||
|
||||
# Clean cryptoconditions
|
||||
rm -rf src/cc/*.o src/cc/*.a 2>/dev/null || true
|
||||
|
||||
# Clean config cache (forces reconfigure for cross-platform)
|
||||
rm -f config.status config.log 2>/dev/null || true
|
||||
|
||||
echo "Clean complete for $platform"
|
||||
}
|
||||
|
||||
# Package release for a platform
|
||||
package_release() {
|
||||
local platform="$1"
|
||||
local release_subdir="$RELEASE_DIR/dragonx-$VERSION-$platform"
|
||||
|
||||
echo "Packaging release for $platform..."
|
||||
mkdir -p "$release_subdir"
|
||||
|
||||
# Copy bootstrap script (platform-appropriate)
|
||||
if [ "$platform" = "win64" ]; then
|
||||
cp "$SCRIPT_DIR/util/bootstrap-dragonx.bat" "$release_subdir/"
|
||||
else
|
||||
cp "$SCRIPT_DIR/util/bootstrap-dragonx.sh" "$release_subdir/"
|
||||
fi
|
||||
|
||||
# Copy common files
|
||||
cp "$SCRIPT_DIR/contrib/asmap/asmap.dat" "$release_subdir/" 2>/dev/null || true
|
||||
cp "$SCRIPT_DIR/sapling-output.params" "$release_subdir/" 2>/dev/null || true
|
||||
cp "$SCRIPT_DIR/sapling-spend.params" "$release_subdir/" 2>/dev/null || true
|
||||
|
||||
case "$platform" in
|
||||
linux-amd64)
|
||||
cp "$SCRIPT_DIR/src/dragonxd" "$release_subdir/"
|
||||
cp "$SCRIPT_DIR/src/dragonx-cli" "$release_subdir/"
|
||||
cp "$SCRIPT_DIR/src/dragonx-tx" "$release_subdir/"
|
||||
strip "$release_subdir/dragonxd" "$release_subdir/dragonx-cli" "$release_subdir/dragonx-tx"
|
||||
;;
|
||||
win64)
|
||||
cp "$SCRIPT_DIR/src/dragonxd.exe" "$release_subdir/"
|
||||
cp "$SCRIPT_DIR/src/dragonx-cli.exe" "$release_subdir/"
|
||||
cp "$SCRIPT_DIR/src/dragonx-tx.exe" "$release_subdir/"
|
||||
x86_64-w64-mingw32-strip "$release_subdir/"*.exe 2>/dev/null || strip "$release_subdir/"*.exe 2>/dev/null || true
|
||||
;;
|
||||
macos)
|
||||
cp "$SCRIPT_DIR/src/dragonxd" "$release_subdir/"
|
||||
cp "$SCRIPT_DIR/src/dragonx-cli" "$release_subdir/"
|
||||
cp "$SCRIPT_DIR/src/dragonx-tx" "$release_subdir/"
|
||||
strip "$release_subdir/dragonxd" "$release_subdir/dragonx-cli" "$release_subdir/dragonx-tx" 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Release packaged: $release_subdir"
|
||||
ls -la "$release_subdir"
|
||||
}
|
||||
|
||||
# Handle release builds
|
||||
if [ $BUILD_LINUX_COMPAT -eq 1 ] || [ $BUILD_LINUX_RELEASE -eq 1 ] || [ $BUILD_WIN_RELEASE -eq 1 ] || [ $BUILD_MAC_RELEASE -eq 1 ]; then
|
||||
mkdir -p "$RELEASE_DIR"
|
||||
|
||||
if [ $BUILD_LINUX_COMPAT -eq 1 ]; then
|
||||
echo "=== Building Linux compat release (Ubuntu 20.04 via Docker) ==="
|
||||
if ! command -v docker &>/dev/null; then
|
||||
echo "Error: docker is required for --linux-compat builds"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use sudo for docker if the user isn't in the docker group
|
||||
DOCKER_CMD="docker"
|
||||
if ! docker info &>/dev/null 2>&1; then
|
||||
echo "Note: Using sudo for docker (add yourself to the docker group to avoid this)"
|
||||
DOCKER_CMD="sudo docker"
|
||||
fi
|
||||
|
||||
DOCKER_IMAGE="dragonx-compat-builder"
|
||||
COMPAT_PLATFORM="linux-amd64-ubuntu2004"
|
||||
COMPAT_RELEASE_DIR="$RELEASE_DIR/dragonx-$VERSION-$COMPAT_PLATFORM"
|
||||
|
||||
echo "Building Docker image (Ubuntu 20.04 base)..."
|
||||
$DOCKER_CMD build -f Dockerfile.compat -t "$DOCKER_IMAGE" .
|
||||
|
||||
echo "Extracting binaries from Docker image..."
|
||||
CONTAINER_ID=$($DOCKER_CMD create "$DOCKER_IMAGE")
|
||||
mkdir -p "$COMPAT_RELEASE_DIR"
|
||||
|
||||
for bin in dragonxd dragonx-cli dragonx-tx; do
|
||||
$DOCKER_CMD cp "$CONTAINER_ID:/build/src/$bin" "$COMPAT_RELEASE_DIR/$bin"
|
||||
done
|
||||
$DOCKER_CMD rm "$CONTAINER_ID" >/dev/null
|
||||
|
||||
# Fix ownership (docker cp creates root-owned files)
|
||||
# Binaries are already stripped inside the Docker container
|
||||
if [ "$(stat -c '%U' "$COMPAT_RELEASE_DIR/dragonxd")" = "root" ]; then
|
||||
sudo chown "$(id -u):$(id -g)" "$COMPAT_RELEASE_DIR"/dragonx*
|
||||
fi
|
||||
|
||||
# Copy common files
|
||||
cp "$SCRIPT_DIR/util/bootstrap-dragonx.sh" "$COMPAT_RELEASE_DIR/"
|
||||
cp "$SCRIPT_DIR/contrib/asmap/asmap.dat" "$COMPAT_RELEASE_DIR/" 2>/dev/null || true
|
||||
cp "$SCRIPT_DIR/sapling-output.params" "$COMPAT_RELEASE_DIR/" 2>/dev/null || true
|
||||
cp "$SCRIPT_DIR/sapling-spend.params" "$COMPAT_RELEASE_DIR/" 2>/dev/null || true
|
||||
|
||||
echo "Compat release packaged: $COMPAT_RELEASE_DIR"
|
||||
ls -la "$COMPAT_RELEASE_DIR"
|
||||
|
||||
# Show glibc version requirement
|
||||
echo ""
|
||||
echo "Binary compatibility info:"
|
||||
objdump -T "$COMPAT_RELEASE_DIR/dragonxd" | grep -oP 'GLIBC_\d+\.\d+' | sort -uV | tail -1 && echo "(max GLIBC version required)"
|
||||
fi
|
||||
|
||||
if [ $BUILD_LINUX_RELEASE -eq 1 ]; then
|
||||
echo "=== Building Linux release ==="
|
||||
clean_for_platform linux
|
||||
./util/build.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
||||
package_release linux-amd64
|
||||
fi
|
||||
|
||||
if [ $BUILD_WIN_RELEASE -eq 1 ]; then
|
||||
echo "=== Building Windows release ==="
|
||||
clean_for_platform windows
|
||||
./util/build-win.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
||||
package_release win64
|
||||
fi
|
||||
|
||||
if [ $BUILD_MAC_RELEASE -eq 1 ]; then
|
||||
echo "=== Building macOS release ==="
|
||||
clean_for_platform macos
|
||||
./util/build-mac.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
||||
package_release macos
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Release builds complete ==="
|
||||
ls -la "$RELEASE_DIR"/
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Standard build (auto-detect OS)
|
||||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
./util/build.sh --disable-tests $@
|
||||
./util/build.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
./util/build-mac.sh --disable-tests $@
|
||||
./util/build-mac.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
||||
elif [[ "$OSTYPE" == "msys"* ]]; then
|
||||
./util/build-win.sh --disable-tests $@
|
||||
#elif [[ "$OSTYPE" == "freebsd"* ]]; then
|
||||
# placeholder
|
||||
./util/build-win.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
||||
else
|
||||
echo "Unable to detect your OS. What are you using?"
|
||||
fi
|
||||
|
||||
14
configure.ac
14
configure.ac
@@ -1,23 +1,23 @@
|
||||
dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
|
||||
AC_PREREQ([2.60])
|
||||
define(_CLIENT_VERSION_MAJOR, 3)
|
||||
define(_CLIENT_VERSION_MAJOR, 1)
|
||||
dnl Must be kept in sync with src/clientversion.h , ugh!
|
||||
define(_CLIENT_VERSION_MINOR, 10)
|
||||
define(_CLIENT_VERSION_REVISION, 5)
|
||||
define(_CLIENT_VERSION_MINOR, 0)
|
||||
define(_CLIENT_VERSION_REVISION, 3)
|
||||
define(_CLIENT_VERSION_BUILD, 50)
|
||||
define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50)))
|
||||
define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1)))
|
||||
define(_CLIENT_VERSION_IS_RELEASE, true)
|
||||
define(_COPYRIGHT_YEAR, 2026)
|
||||
AC_INIT([Hush],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_SUFFIX(_ZC_BUILD_VAL)],[https://git.hush.is/hush/hush3],[hush])
|
||||
AC_INIT([DragonX],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_SUFFIX(_ZC_BUILD_VAL)],[https://git.dragonx.is/DragonX/dragonx],[dragonx])
|
||||
AC_CONFIG_SRCDIR([src/main.cpp])
|
||||
AC_CONFIG_HEADERS([src/config/bitcoin-config.h])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_MACRO_DIR([build-aux/m4])
|
||||
|
||||
BITCOIN_DAEMON_NAME=hushd
|
||||
BITCOIN_CLI_NAME=hush-cli
|
||||
BITCOIN_TX_NAME=hush-tx
|
||||
BITCOIN_DAEMON_NAME=dragonxd
|
||||
BITCOIN_CLI_NAME=dragonx-cli
|
||||
BITCOIN_TX_NAME=dragonx-tx
|
||||
|
||||
dnl Unless the user specified ARFLAGS, force it to be cr
|
||||
AC_ARG_VAR(ARFLAGS, [Flags for the archiver, defaults to <cr> if not set])
|
||||
|
||||
@@ -1,3 +1,50 @@
|
||||
dragonx (1.0.3) stable; urgency=medium
|
||||
|
||||
* IBD/sync speedups: parallel RandomX pre-verification, adaptive -dbcache, P2P download fixes
|
||||
* Fix Sapling witness desync and parallelize witness cache rebuild
|
||||
* Opt-in bulk block streaming (-bulkblocksync) for faster initial sync
|
||||
* BIP39 seed phrases (SilentDragonXLite-compatible) and HD transparent keys
|
||||
* Remove assumeutxo / UTXO-snapshot feature
|
||||
|
||||
-- DragonX <dan-s-dev@proton.me> Tue, 07 Jul 2026 05:49:59 +0200
|
||||
|
||||
dragonx (1.0.0) stable; urgency=medium
|
||||
|
||||
* Initial release of DragonX, forked from Hush Full Node
|
||||
* Full legal-compliant rebrand: binaries, config, documentation
|
||||
* RandomX proof-of-work, 36-second block time, fully shielded transactions
|
||||
* New binary names: dragonxd, dragonx-cli, dragonx-tx
|
||||
|
||||
-- DragonX <dan-s-dev@proton.me> Mon, 03 Mar 2026 00:00:00 +0000
|
||||
|
||||
hush (3.10.5) stable; urgency=medium
|
||||
|
||||
* DragonX is no longer supported by this codebase
|
||||
* DragonX devs have forked our code and we wish them the best
|
||||
* Find the latest place to download a DragonX full node via dragonx.is
|
||||
* Concurrent `z_sendmany` now works
|
||||
* A longstanding bug relating to run multiple `z_sendmany` operations at
|
||||
once has been fixed. You can now queue up many `z_sendmany` operations
|
||||
and they will succeed because they now understand how to avoid spending
|
||||
coins that another `z_sendmany` process is trying to spend.
|
||||
* Updated Autonomous System Map (asmap)
|
||||
* New RPC `z_listlockunspent`
|
||||
* Lists shielded notes (coins inside a zaddr) which are temporarily unspendable because an RPC process is currently trying to spend them.
|
||||
* If that operation succeeds, they will become spent. If it fails they will be unlocked and become spendable again.
|
||||
* New option to `z_shieldcoinbase` allows privately donating a percentage of coinbase funds during shielding
|
||||
* This new option defaults to OFF and allows CLI users to opt-in to donating between 0% and 10% of coinbase funds to developers
|
||||
* No GUI currently utilizes this but that feature is planned for SD
|
||||
* The donation has extremely good privacy:
|
||||
* It cannot be determined from public blockchain data if a donation is being made, as it takes the place of a Sietch output
|
||||
* It cannot be determined from public blockchain data the amount of the donation
|
||||
* Donations do not create new transactions, do not use additional blockspace and cannot be detected by anyone but the sender or reciever
|
||||
* New HAC option `ac_clearnet` can be used to disable clearnet networking for an entire blockchain instead of just a single node
|
||||
* Updated test framework and tests which allowed the fixing of the `z_sendmany` bug above
|
||||
* Faster compiling of RandomX internals
|
||||
* GMP dependency removed, as it is no longer needed
|
||||
* Hush is now compatible with GCC15 and now correctly supports customizing the compiler for a build via the CC env var
|
||||
* New HTML man pages are now available at doc/man/hushd.html and doc/man/hush-cli.html
|
||||
|
||||
hush (3.10.4) stable; urgency=medium
|
||||
|
||||
* Updated seed node list
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
Source: hush
|
||||
Source: dragonx
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: Hush <myhushteam@gmail.com>
|
||||
Homepage: https://hush.is
|
||||
Maintainer: DragonX <dan-s-dev@proton.me>
|
||||
Homepage: https://dragonx.is
|
||||
Build-Depends: autoconf, automake, bsdmainutils, build-essential,
|
||||
cmake, curl, git, g++-multilib, libc6-dev, libsodium-dev,
|
||||
libtool, m4, ncurses-dev, pkg-config, python,
|
||||
unzip, wget, zlib1g-dev
|
||||
Vcs-Git: https://git.hush.is/hush/hush3.git
|
||||
Vcs-Browser: https://git.hush.is/hush/hush3
|
||||
Vcs-Git: https://git.dragonx.is/DragonX/dragonx.git
|
||||
Vcs-Browser: https://git.dragonx.is/DragonX/dragonx
|
||||
|
||||
Package: hush
|
||||
Package: dragonx
|
||||
Architecture: amd64 arm64
|
||||
Depends: ${shlibs:Depends}
|
||||
Description: Cryptocoin full node for Hush
|
||||
Speak And Transact Freely with Hush, which inherits from Bitcoin Protocol and
|
||||
Zcash Protocol and is focused on private communications.
|
||||
Description: Privacy-focused cryptocurrency full node for DragonX
|
||||
DragonX is a privacy-focused cryptocurrency using RandomX proof-of-work.
|
||||
All transactions are shielded by default. Fork of the Hush Full Node.
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
Files: *
|
||||
Copyright: 2016-2026, The Hush developers
|
||||
Copyright: 2024-2026, The DragonX developers
|
||||
2016-2026, The Hush developers
|
||||
2009-2016, Bitcoin Core developers
|
||||
License: GPLv3
|
||||
Comment: https://hush.is
|
||||
Comment: https://dragonx.is
|
||||
|
||||
Files: depends/sources/libsodium-*.tar.gz
|
||||
Copyright: 2013-2016 Frank Denis
|
||||
|
||||
1
contrib/debian/dragonx.example
Normal file
1
contrib/debian/dragonx.example
Normal file
@@ -0,0 +1 @@
|
||||
DEBIAN/examples/DRAGONX.conf
|
||||
3
contrib/debian/dragonx.install
Normal file
3
contrib/debian/dragonx.install
Normal file
@@ -0,0 +1,3 @@
|
||||
usr/bin/dragonxd
|
||||
usr/bin/dragonx-cli
|
||||
usr/bin/dragonx-tx
|
||||
3
contrib/debian/dragonx.manpages
Normal file
3
contrib/debian/dragonx.manpages
Normal file
@@ -0,0 +1,3 @@
|
||||
DEBIAN/manpages/dragonx-cli.1
|
||||
DEBIAN/manpages/dragonx-tx.1
|
||||
DEBIAN/manpages/dragonxd.1
|
||||
209
contrib/debian/examples/DRAGONX.conf
Normal file
209
contrib/debian/examples/DRAGONX.conf
Normal file
@@ -0,0 +1,209 @@
|
||||
## DRAGONX.conf configuration file. Lines beginning with # are comments.
|
||||
|
||||
# Network-related settings:
|
||||
|
||||
# Run a regression test network
|
||||
#regtest=0
|
||||
# Run a test node (which means you can mine with no peers)
|
||||
#testnode=1
|
||||
|
||||
#set a custom client name/user agent
|
||||
#clientName=GoldenSandtrout
|
||||
|
||||
# Rescan from block height
|
||||
#rescan=123
|
||||
|
||||
# Connect via a SOCKS5 proxy
|
||||
#proxy=127.0.0.1:9050
|
||||
|
||||
# Automatically create Tor hidden service
|
||||
#listenonion=1
|
||||
|
||||
#Use separate SOCKS5 proxy to reach peers via Tor hidden services
|
||||
#onion=1.2.3.4:9050
|
||||
|
||||
# Only connect to nodes in network <net> (ipv4, ipv6, onion or i2p)"));
|
||||
#onlynet=<net>
|
||||
|
||||
#Tor control port to use if onion listening enabled
|
||||
#torcontrol=127.0.0.1:9051
|
||||
|
||||
# Bind to given address and always listen on it. Use [host]:port notation for IPv6
|
||||
#bind=<addr>
|
||||
|
||||
# Bind to given address and allowlist peers connecting to it. Use [host]:port notation for IPv6
|
||||
#allowbind=<addr>
|
||||
|
||||
##############################################################
|
||||
## Quick Primer on addnode vs connect ##
|
||||
## Let's say for instance you use addnode=4.2.2.4 ##
|
||||
## addnode will connect you to and tell you about the ##
|
||||
## nodes connected to 4.2.2.4. In addition it will tell ##
|
||||
## the other nodes connected to it that you exist so ##
|
||||
## they can connect to you. ##
|
||||
## connect will not do the above when you 'connect' to it. ##
|
||||
## It will *only* connect you to 4.2.2.4 and no one else.##
|
||||
## ##
|
||||
## So if you're behind a firewall, or have other problems ##
|
||||
## finding nodes, add some using 'addnode'. ##
|
||||
## ##
|
||||
## If you want to stay private, use 'connect' to only ##
|
||||
## connect to "trusted" nodes. ##
|
||||
## ##
|
||||
## If you run multiple nodes on a LAN, there's no need for ##
|
||||
## all of them to open lots of connections. Instead ##
|
||||
## 'connect' them all to one node that is port forwarded ##
|
||||
## and has lots of connections. ##
|
||||
## Thanks goes to [Noodle] on Freenode. ##
|
||||
##############################################################
|
||||
|
||||
# Use as many addnode= settings as you like to connect to specific peers
|
||||
#addnode=69.164.218.197
|
||||
#addnode=10.0.0.2:8233
|
||||
|
||||
# Alternatively use as many connect= settings as you like to connect ONLY to specific peers
|
||||
#connect=69.164.218.197
|
||||
#connect=10.0.0.1:8233
|
||||
|
||||
# Listening mode, enabled by default except when 'connect' is being used
|
||||
#listen=1
|
||||
|
||||
# Maximum number of inbound+outbound connections.
|
||||
#maxconnections=
|
||||
|
||||
#
|
||||
# JSON-RPC options (for controlling a running dragonxd process)
|
||||
#
|
||||
|
||||
# server=1 tells node to accept JSON-RPC commands (set as default if not specified)
|
||||
#server=1
|
||||
|
||||
# Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6.
|
||||
# This option can be specified multiple times (default: bind to all interfaces)
|
||||
#rpcbind=<addr>
|
||||
|
||||
# You must set rpcuser and rpcpassword to secure the JSON-RPC api
|
||||
# These will automatically be created for you
|
||||
#rpcuser=user
|
||||
#rpcpassword=supersecretpassword
|
||||
|
||||
# How many seconds node will wait for a complete RPC HTTP request.
|
||||
# after the HTTP connection is established.
|
||||
#rpcclienttimeout=30
|
||||
|
||||
# By default, only RPC connections from localhost are allowed.
|
||||
# Specify as many rpcallowip= settings as you like to allow connections from other hosts,
|
||||
# either as a single IPv4/IPv6 or with a subnet specification.
|
||||
|
||||
# NOTE: opening up the RPC port to hosts outside your local trusted network is NOT RECOMMENDED,
|
||||
# because the rpcpassword is transmitted over the network unencrypted and also because anyone
|
||||
# that can authenticate on the RPC port can steal your keys + take over the account running dragonxd
|
||||
|
||||
#rpcallowip=10.1.1.34/255.255.255.0
|
||||
#rpcallowip=1.2.3.4/24
|
||||
#rpcallowip=2001:db8:85a3:0:0:8a2e:370:7334/96
|
||||
|
||||
# Listen for RPC connections on this TCP port:
|
||||
#rpcport=1234
|
||||
|
||||
# You can use dragonxd to send commands to dragonxd
|
||||
# running on another host using this option:
|
||||
#rpcconnect=127.0.0.1
|
||||
|
||||
# Transaction Fee
|
||||
|
||||
# Send transactions as zero-fee transactions if possible (default: 0)
|
||||
#sendfreetransactions=0
|
||||
|
||||
# Create transactions that have enough fees (or priority) so they are likely to # begin confirmation within n blocks (default: 1).
|
||||
# This setting is overridden by the -paytxfee option.
|
||||
#txconfirmtarget=n
|
||||
|
||||
# Miscellaneous options
|
||||
|
||||
# Enable mining at startup
|
||||
#gen=1
|
||||
|
||||
# Set the number of threads to be used for mining (-1 = all cores).
|
||||
#genproclimit=1
|
||||
|
||||
# Specify a different Equihash solver (e.g. "tromp") to try to mine
|
||||
# faster when gen=1.
|
||||
#equihashsolver=default
|
||||
|
||||
# Pre-generate this many public/private key pairs, so wallet backups will be valid for
|
||||
# both prior transactions and several dozen future transactions.
|
||||
#keypool=100
|
||||
|
||||
# Pay an optional transaction fee every time you send a tx. Transactions with fees
|
||||
# are more likely than free transactions to be included in generated blocks, so may
|
||||
# be validated sooner. This setting does not affect private transactions created with
|
||||
# 'z_sendmany'.
|
||||
#paytxfee=0.00
|
||||
|
||||
#Rewind the chain to specific block height. This is useful for creating snapshots at a given block height.
|
||||
#rewind=555
|
||||
|
||||
#Stop the chain a specific block height. This is useful for creating snapshots at a given block height.
|
||||
#stopat=1000000
|
||||
|
||||
#Set an address to use as change address for all transactions. This value must be set to a 33 byte pubkey. All mined coins will also be sent to this address.
|
||||
#pubkey=027dc7b5cfb5efca96674b45e9fda18df069d040b9fd9ff32c35df56005e330392
|
||||
|
||||
# Disable clearnet (ipv4 and ipv6) connections to this node
|
||||
#clearnet=0
|
||||
|
||||
# Disable ipv4
|
||||
#disableipv4=1
|
||||
# Disable ipv6
|
||||
#disableipv6=1
|
||||
|
||||
# Enable transaction index
|
||||
#txindex=1
|
||||
# Enable address index
|
||||
#addressindex=1
|
||||
# Enable timestamp index
|
||||
#timestampindex=1
|
||||
# Enable spent index
|
||||
#spentindex=1
|
||||
|
||||
# Enable shielded stats index
|
||||
#zindex=1
|
||||
|
||||
# Attempt to salvage a corrupt wallet
|
||||
# salvagewallet=1
|
||||
|
||||
# Mine all blocks to this address (not good for your privacy and not recommended!)
|
||||
# Disallowed if clearnet=0
|
||||
# mineraddress=XXX
|
||||
|
||||
# Disable wallet
|
||||
#disablewallet=1
|
||||
|
||||
# Allow mining to an address that is not in the current wallet
|
||||
#minetolocalwallet=0
|
||||
|
||||
# Delete all wallet transactions
|
||||
#zapwallettxes=1
|
||||
|
||||
# Enable sapling consolidation
|
||||
# consolidation=1
|
||||
|
||||
# Enable stratum server
|
||||
# stratum=1
|
||||
|
||||
# Run a command each time a new block is seen
|
||||
# %s in command is replaced by block hash
|
||||
#blocknotify=/my/awesome/script.sh %s
|
||||
|
||||
# Run a command when wallet gets a new tx
|
||||
# %s in command is replaced with txid
|
||||
#walletnotify=/my/cool/script.sh %s
|
||||
|
||||
# Run a command when tx expires
|
||||
# %s in command is replaced with txid
|
||||
#txexpirynotify=/my/elite/script.sh %s
|
||||
|
||||
# Execute this commend to send a tx
|
||||
# %s is replaced with tx hex
|
||||
#txsend=/send/it.sh %s
|
||||
59
contrib/init/dragonxd.conf
Normal file
59
contrib/init/dragonxd.conf
Normal file
@@ -0,0 +1,59 @@
|
||||
description "Hush Daemon"
|
||||
|
||||
start on runlevel [2345]
|
||||
stop on starting rc RUNLEVEL=[016]
|
||||
|
||||
env HUSHD_BIN="/usr/bin/dragonxd"
|
||||
env HUSHD_USER="hush"
|
||||
env HUSHD_GROUP="hush"
|
||||
env HUSHD_PIDDIR="/var/run/dragonxd"
|
||||
# upstart can't handle variables constructed with other variables
|
||||
env HUSHD_PIDFILE="/var/run/dragonxd/dragonxd.pid"
|
||||
env HUSHD_CONFIGFILE="/etc/hush/hush.conf"
|
||||
env HUSHD_DATADIR="/var/lib/dragonxd"
|
||||
|
||||
expect fork
|
||||
|
||||
respawn
|
||||
respawn limit 5 120
|
||||
kill timeout 60
|
||||
|
||||
pre-start script
|
||||
# this will catch non-existent config files
|
||||
# dragonxd will check and exit with this very warning, but it can do so
|
||||
# long after forking, leaving upstart to think everything started fine.
|
||||
# since this is a commonly encountered case on install, just check and
|
||||
# warn here.
|
||||
if ! grep -qs '^rpcpassword=' "$HUSHD_CONFIGFILE" ; then
|
||||
echo "ERROR: You must set a secure rpcpassword to run dragonxd."
|
||||
echo "The setting must appear in $HUSHD_CONFIGFILE"
|
||||
echo
|
||||
echo "This password is security critical to securing wallets "
|
||||
echo "and must not be the same as the rpcuser setting."
|
||||
echo "You can generate a suitable random password using the following"
|
||||
echo "command from the shell:"
|
||||
echo
|
||||
echo "bash -c 'tr -dc a-zA-Z0-9 < /dev/urandom | head -c32 && echo'"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$HUSHD_PIDDIR"
|
||||
chmod 0755 "$HUSHD_PIDDIR"
|
||||
chown $HUSHD_USER:$HUSHD_GROUP "$HUSHD_PIDDIR"
|
||||
chown $HUSHD_USER:$HUSHD_GROUP "$HUSHD_CONFIGFILE"
|
||||
chmod 0660 "$HUSHD_CONFIGFILE"
|
||||
end script
|
||||
|
||||
exec start-stop-daemon \
|
||||
--start \
|
||||
--pidfile "$HUSHD_PIDFILE" \
|
||||
--chuid $HUSHD_USER:$HUSHD_GROUP \
|
||||
--exec "$HUSHD_BIN" \
|
||||
-- \
|
||||
-pid="$HUSHD_PIDFILE" \
|
||||
-conf="$HUSHD_CONFIGFILE" \
|
||||
-datadir="$HUSHD_DATADIR" \
|
||||
-disablewallet \
|
||||
-daemon
|
||||
|
||||
67
contrib/init/dragonxd.init
Normal file
67
contrib/init/dragonxd.init
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# dragonxd The hush core server.
|
||||
#
|
||||
#
|
||||
# chkconfig: 345 80 20
|
||||
# description: dragonxd
|
||||
# processname: dragonxd
|
||||
#
|
||||
|
||||
# Source function library.
|
||||
. /etc/init.d/functions
|
||||
|
||||
# you can override defaults in /etc/sysconfig/dragonxd, see below
|
||||
if [ -f /etc/sysconfig/dragonxd ]; then
|
||||
. /etc/sysconfig/dragonxd
|
||||
fi
|
||||
|
||||
RETVAL=0
|
||||
|
||||
prog=dragonxd
|
||||
# you can override the lockfile via HUSHD_LOCKFILE in /etc/sysconfig/dragonxd
|
||||
lockfile=${HUSHD_LOCKFILE-/var/lock/subsys/dragonxd}
|
||||
|
||||
# dragonxd defaults to /usr/bin/dragonxd, override with HUSHD_BIN
|
||||
dragonxd=${HUSHD_BIN-/usr/bin/dragonxd}
|
||||
|
||||
# dragonxd opts default to -disablewallet, override with HUSHD_OPTS
|
||||
dragonxd_opts=${HUSHD_OPTS--disablewallet}
|
||||
|
||||
start() {
|
||||
echo -n $"Starting $prog: "
|
||||
daemon $DAEMONOPTS $dragonxd $dragonxd_opts
|
||||
RETVAL=$?
|
||||
echo
|
||||
[ $RETVAL -eq 0 ] && touch $lockfile
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo -n $"Stopping $prog: "
|
||||
killproc $prog
|
||||
RETVAL=$?
|
||||
echo
|
||||
[ $RETVAL -eq 0 ] && rm -f $lockfile
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status $prog
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: service $prog {start|stop|status|restart}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
87
contrib/init/dragonxd.openrc
Normal file
87
contrib/init/dragonxd.openrc
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/sbin/runscript
|
||||
|
||||
# backward compatibility for existing gentoo layout
|
||||
#
|
||||
if [ -d "/var/lib/hush/.hush" ]; then
|
||||
HUSHD_DEFAULT_DATADIR="/var/lib/hush/.hush"
|
||||
else
|
||||
HUSHD_DEFAULT_DATADIR="/var/lib/dragonxd"
|
||||
fi
|
||||
|
||||
HUSHD_CONFIGFILE=${HUSHD_CONFIGFILE:-/etc/hush/hush.conf}
|
||||
HUSHD_PIDDIR=${HUSHD_PIDDIR:-/var/run/dragonxd}
|
||||
HUSHD_PIDFILE=${HUSHD_PIDFILE:-${HUSHD_PIDDIR}/dragonxd.pid}
|
||||
HUSHD_DATADIR=${HUSHD_DATADIR:-${HUSHD_DEFAULT_DATADIR}}
|
||||
HUSHD_USER=${HUSHD_USER:-${HUSH_USER:-hush}}
|
||||
HUSHD_GROUP=${HUSHD_GROUP:-hush}
|
||||
HUSHD_BIN=${HUSHD_BIN:-/usr/bin/dragonxd}
|
||||
HUSHD_NICE=${HUSHD_NICE:-${NICELEVEL:-0}}
|
||||
HUSHD_OPTS="${HUSHD_OPTS:-${HUSH_OPTS}}"
|
||||
|
||||
name="Hush Full Node Daemon"
|
||||
description="Hush cryptocurrency P2P network daemon"
|
||||
|
||||
command="/usr/bin/dragonxd"
|
||||
command_args="-pid=\"${HUSHD_PIDFILE}\" \
|
||||
-conf=\"${HUSHD_CONFIGFILE}\" \
|
||||
-datadir=\"${HUSHD_DATADIR}\" \
|
||||
-daemon \
|
||||
${HUSHD_OPTS}"
|
||||
|
||||
required_files="${HUSHD_CONFIGFILE}"
|
||||
start_stop_daemon_args="-u ${HUSHD_USER} \
|
||||
-N ${HUSHD_NICE} -w 2000"
|
||||
pidfile="${HUSHD_PIDFILE}"
|
||||
|
||||
# The retry schedule to use when stopping the daemon. Could be either
|
||||
# a timeout in seconds or multiple signal/timeout pairs (like
|
||||
# "SIGKILL/180 SIGTERM/300")
|
||||
retry="${HUSHD_SIGTERM_TIMEOUT}"
|
||||
|
||||
depend() {
|
||||
need localmount net
|
||||
}
|
||||
|
||||
# verify
|
||||
# 1) that the datadir exists and is writable (or create it)
|
||||
# 2) that a directory for the pid exists and is writable
|
||||
# 3) ownership and permissions on the config file
|
||||
start_pre() {
|
||||
checkpath \
|
||||
-d \
|
||||
--mode 0750 \
|
||||
--owner "${HUSHD_USER}:${HUSHD_GROUP}" \
|
||||
"${HUSHD_DATADIR}"
|
||||
|
||||
checkpath \
|
||||
-d \
|
||||
--mode 0755 \
|
||||
--owner "${HUSHD_USER}:${HUSHD_GROUP}" \
|
||||
"${HUSHD_PIDDIR}"
|
||||
|
||||
checkpath -f \
|
||||
-o ${HUSHD_USER}:${HUSHD_GROUP} \
|
||||
-m 0660 \
|
||||
${HUSHD_CONFIGFILE}
|
||||
|
||||
checkconfig || return 1
|
||||
}
|
||||
|
||||
checkconfig()
|
||||
{
|
||||
if ! grep -qs '^rpcpassword=' "${HUSHD_CONFIGFILE}" ; then
|
||||
eerror ""
|
||||
eerror "ERROR: You must set a secure rpcpassword to run dragonxd."
|
||||
eerror "The setting must appear in ${HUSHD_CONFIGFILE}"
|
||||
eerror ""
|
||||
eerror "This password is security critical to securing wallets "
|
||||
eerror "and must not be the same as the rpcuser setting."
|
||||
eerror "You can generate a suitable random password using the following"
|
||||
eerror "command from the shell:"
|
||||
eerror ""
|
||||
eerror "bash -c 'tr -dc a-zA-Z0-9 < /dev/urandom | head -c32 && echo'"
|
||||
eerror ""
|
||||
eerror ""
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
33
contrib/init/dragonxd.openrcconf
Normal file
33
contrib/init/dragonxd.openrcconf
Normal file
@@ -0,0 +1,33 @@
|
||||
# /etc/conf.d/dragonxd: config file for /etc/init.d/dragonxd
|
||||
|
||||
# Config file location
|
||||
#HUSHD_CONFIGFILE="/etc/hush/hush.conf"
|
||||
|
||||
# What directory to write pidfile to? (created and owned by $HUSHD_USER)
|
||||
#HUSHD_PIDDIR="/var/run/dragonxd"
|
||||
|
||||
# What filename to give the pidfile
|
||||
#HUSHD_PIDFILE="${HUSHD_PIDDIR}/dragonxd.pid"
|
||||
|
||||
# Where to write dragonxd data (be mindful that the blockchain is large)
|
||||
#HUSHD_DATADIR="/var/lib/dragonxd"
|
||||
|
||||
# User and group to own dragonxd process
|
||||
#HUSHD_USER="hush"
|
||||
#HUSHD_GROUP="hush"
|
||||
|
||||
# Path to dragonxd executable
|
||||
#HUSHD_BIN="/usr/bin/dragonxd"
|
||||
|
||||
# Nice value to run dragonxd under
|
||||
#HUSHD_NICE=0
|
||||
|
||||
# Additional options (avoid -conf and -datadir, use flags above)
|
||||
HUSHD_OPTS="-disablewallet"
|
||||
|
||||
# The timeout in seconds OpenRC will wait for dragonxd to terminate
|
||||
# after a SIGTERM has been raised.
|
||||
# Note that this will be mapped as argument to start-stop-daemon's
|
||||
# '--retry' option, which means you can specify a retry schedule
|
||||
# here. For more information see man 8 start-stop-daemon.
|
||||
HUSHD_SIGTERM_TIMEOUT=60
|
||||
22
contrib/init/dragonxd.service
Normal file
22
contrib/init/dragonxd.service
Normal file
@@ -0,0 +1,22 @@
|
||||
[Unit]
|
||||
Description=Hush: Speak And Transact Freely
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=hush
|
||||
Group=hush
|
||||
|
||||
Type=forking
|
||||
PIDFile=/var/lib/dragonxd/dragonxd.pid
|
||||
ExecStart=/usr/bin/dragonxd -daemon -pid=/var/lib/dragonxd/dragonxd.pid \
|
||||
-conf=/etc/hush/hush.conf -datadir=/var/lib/dragonxd -disablewallet
|
||||
|
||||
Restart=always
|
||||
PrivateTmp=true
|
||||
TimeoutStopSec=60s
|
||||
TimeoutStartSec=2s
|
||||
StartLimitInterval=120s
|
||||
StartLimitBurst=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,5 +1,5 @@
|
||||
build_darwin_CC = gcc-8
|
||||
build_darwin_CXX = g++-8
|
||||
build_darwin_CC = gcc-15
|
||||
build_darwin_CXX = g++-15
|
||||
build_darwin_AR: = $(shell xcrun -f ar)
|
||||
build_darwin_RANLIB: = $(shell xcrun -f ranlib)
|
||||
build_darwin_STRIP: = $(shell xcrun -f strip)
|
||||
@@ -10,8 +10,8 @@ build_darwin_SHA256SUM = shasum -a 256
|
||||
build_darwin_DOWNLOAD = curl --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -L -f -o
|
||||
|
||||
#darwin host on darwin builder. overrides darwin host preferences.
|
||||
darwin_CC= gcc-8
|
||||
darwin_CXX= g++-8
|
||||
darwin_CC= gcc-15
|
||||
darwin_CXX= g++-15
|
||||
darwin_AR:=$(shell xcrun -f ar)
|
||||
darwin_RANLIB:=$(shell xcrun -f ranlib)
|
||||
darwin_STRIP:=$(shell xcrun -f strip)
|
||||
|
||||
@@ -2,8 +2,8 @@ OSX_MIN_VERSION=10.12
|
||||
OSX_SDK_VERSION=10.12
|
||||
OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk
|
||||
LD64_VERSION=253.9
|
||||
darwin_CC=gcc-8 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION)
|
||||
darwin_CXX=g++-8 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION)
|
||||
darwin_CC=gcc-15 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION)
|
||||
darwin_CXX=g++-15 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION)
|
||||
|
||||
darwin_CFLAGS=-pipe
|
||||
darwin_CXXFLAGS=$(darwin_CFLAGS)
|
||||
|
||||
@@ -40,9 +40,15 @@ define $(package)_preprocess_cmds
|
||||
cat $($(package)_patch_dir)/cargo.config | sed 's|CRATE_REGISTRY|$(host_prefix)/$(CRATE_REGISTRY)|' > .cargo/config
|
||||
endef
|
||||
|
||||
ifeq ($(build_os),darwin)
|
||||
define $(package)_build_cmds
|
||||
CARGO=$(HOME)/.cargo/bin/cargo RUSTC=$(HOME)/.cargo/bin/rustc $(HOME)/.cargo/bin/cargo build --package librustzcash $($(package)_build_opts)
|
||||
endef
|
||||
else
|
||||
define $(package)_build_cmds
|
||||
$(host_prefix)/native/bin/cargo build --package librustzcash $($(package)_build_opts)
|
||||
endef
|
||||
endif
|
||||
|
||||
define $(package)_stage_cmds
|
||||
mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \
|
||||
|
||||
8
depends/strip-rlib-metadata.sh
Executable file
8
depends/strip-rlib-metadata.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
# Strip rust.metadata.bin from .rlib archives for macOS linker compatibility
|
||||
RLIB_DIR="$1"
|
||||
if [ -d "$RLIB_DIR" ]; then
|
||||
for rlib in "$RLIB_DIR"/*.rlib; do
|
||||
[ -f "$rlib" ] && ar d "$rlib" rust.metadata.bin 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
7
doc/beefy-DRAGONX.conf
Normal file
7
doc/beefy-DRAGONX.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
rpcuser=dontuseweakusernameoryougetrobbed
|
||||
rpcpassword=dontuseweakpasswordoryougetrobbed
|
||||
txindex=1
|
||||
server=1
|
||||
rpcworkqueue=64
|
||||
addnode=1.2.3.4
|
||||
addnode=5.6.7.8
|
||||
29
doc/dragonxd-systemd.md
Normal file
29
doc/dragonxd-systemd.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Systemd script for the DragonX daemon
|
||||
|
||||
## Set it up
|
||||
|
||||
First set it up as follows:
|
||||
* Copy dragonxd.service to the systemd user directory, which is /usr/lib/systemd/user directory
|
||||
|
||||
## Basic Usage
|
||||
|
||||
How to start the script:
|
||||
`systemctl start --user dragonxd.service`
|
||||
|
||||
How to stop the script:
|
||||
`systemctl stop --user dragonxd.service`
|
||||
|
||||
How to restart the script:
|
||||
`systemctl restart --user dragonxd.service`
|
||||
|
||||
## How to watch it as it starts
|
||||
|
||||
Use the following on most Linux distros:
|
||||
`watch systemctl status --user dragonxd.service`
|
||||
|
||||
Or watch the log directly:
|
||||
`tail -f ~/.hush/DRAGONX/debug.log`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* Don't run it with sudo or root, or it won't work with the wallet.
|
||||
9
doc/dragonxd.service
Normal file
9
doc/dragonxd.service
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=DragonX daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/dragonxd
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
87
doc/hd-transparent-keys.md
Normal file
87
doc/hd-transparent-keys.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# HD transparent keys
|
||||
|
||||
DragonX derives **transparent** (t-address) keys deterministically from the
|
||||
wallet's HD seed, so they can be recovered from the seed alone — the same way
|
||||
Sapling (shielded) keys already are.
|
||||
|
||||
## Derivation
|
||||
|
||||
Transparent keys are derived over secp256k1 using BIP32/BIP44:
|
||||
|
||||
```
|
||||
m / 44' / coin_type' / 0' / 0 / i
|
||||
```
|
||||
|
||||
* `coin_type` is `Params().BIP44CoinType()` — **141** on mainnet, **1** on
|
||||
test/regtest.
|
||||
* Account is fixed at `0'` and the chain at `0` (external). The internal/change
|
||||
chain (`1`) is **not** used: on this `ac_private=1` chain a non-coinbase
|
||||
transparent output is consensus-invalid, so transparent change can never carry
|
||||
value.
|
||||
* `i` is `CHDChain.transparentChildCounter`, a monotonic index persisted in the
|
||||
wallet so the same addresses regenerate after a seed-only restore.
|
||||
|
||||
Each derived key records its `hdKeypath` and the seed fingerprint (`seedFp`) in
|
||||
its `CKeyMetadata`, matching the Sapling scheme.
|
||||
|
||||
## Why this matters on a private chain
|
||||
|
||||
On DragonX (`ac_private=1` from genesis) a normal user can never *receive* to a
|
||||
transparent address — inbound t-payments are rejected by consensus. The only
|
||||
thing that legitimately lands spendable value on a t-address is a **mining
|
||||
coinbase** (plus notary/burn special cases). There is no "coinbase must be
|
||||
shielded" rule, so mature coinbase is directly spendable.
|
||||
|
||||
So HD transparent keys exist to let a **miner recover coinbase rewards** that
|
||||
were paid to wallet-derived t-addresses, using only the seed.
|
||||
|
||||
## Enabling / disabling
|
||||
|
||||
Controlled by `-hdtransparent` (default **on**). When on and the wallet has an
|
||||
HD seed, every newly generated transparent key (receive address, change,
|
||||
coinbase payout drawn from the keypool) is HD-derived.
|
||||
|
||||
```
|
||||
-hdtransparent=0 # keep the legacy behaviour (random transparent keys)
|
||||
```
|
||||
|
||||
## Backing up and restoring
|
||||
|
||||
* **Back up the seed.** `z_exportwallet <file>` writes the 32-byte HD seed as a
|
||||
`# HDSeed=<hex>` line. Guard this value like a private key.
|
||||
* **Restore into a fresh/empty wallet** by starting the node with:
|
||||
|
||||
```
|
||||
-hdseed=<64-hex-character seed>
|
||||
-hdtransparentgaplimit=<n> # HD transparent keys to pre-derive (default 1000)
|
||||
```
|
||||
|
||||
On restore the node injects the seed, pre-derives `n` transparent keys with a
|
||||
genesis birthday, and the normal startup rescan finds any coinbase paid to
|
||||
them. Raise `-hdtransparentgaplimit` if the wallet minted more than `n`
|
||||
distinct coinbase addresses.
|
||||
|
||||
> **Warning:** passing `-hdseed` on the command line exposes the seed to your
|
||||
> shell history and the process list. Prefer putting it in `DRAGONX.conf` with
|
||||
> tight file permissions, and remove it after the restore completes.
|
||||
|
||||
## Limitations (read before relying on recovery)
|
||||
|
||||
* **Legacy random keys are not recoverable.** Any transparent key created before
|
||||
this feature (or with `-hdtransparent=0`) came from the CSPRNG, not the seed,
|
||||
and the phrase/seed will **not** regenerate it. Keep `wallet.dat` /
|
||||
`dumpwallet` backups for those. A wallet that predates the feature and then
|
||||
enables it becomes a *mix* of random (old) and HD (new) keys.
|
||||
* **Gap limit.** A rescan only discovers keys already present in the wallet.
|
||||
Restore pre-derives `-hdtransparentgaplimit` keys; coinbase paid to an index
|
||||
beyond that window is not found until you derive further and rescan again.
|
||||
* **Scope.** Recovers transparent **coinbase** value only, per the consensus
|
||||
rules above. Shielded funds are recovered separately via the Sapling HD keys.
|
||||
|
||||
## On-disk compatibility
|
||||
|
||||
The transparent counter is stored in `CHDChain` under a new serialization
|
||||
version (`VERSION_HD_TRANSPARENT = 2`). Existing v1 `wallet.dat` records load
|
||||
unchanged (the counter defaults to 0); the record is rewritten as v2 the first
|
||||
time an HD transparent key is derived. Downgrading a v2 wallet to an older
|
||||
binary is not supported.
|
||||
@@ -1 +1 @@
|
||||
dist_man1_MANS=hushd.1 hush-cli.1 hush-tx.1
|
||||
dist_man1_MANS=dragonxd.1 dragonx-cli.1 dragonx-tx.1
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
|
||||
.TH HUSH-CLI "1" "July 2025" "hush-cli v3.10.4" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||
.TH DRAGONX "1" "July 2026" "DragonX RPC client version v1.0.3-95aeaed0c-dirty" "User Commands"
|
||||
.SH NAME
|
||||
hush-cli \- manual page for hush-cli v3.10.4
|
||||
DragonX \- manual page for DragonX RPC client version v1.0.3-95aeaed0c-dirty
|
||||
.SH DESCRIPTION
|
||||
Hush RPC client version v3.10.4\-7e63e2f01\-dirty
|
||||
DragonX RPC client version v1.0.3\-95aeaed0c\-dirty
|
||||
.PP
|
||||
In order to ensure you are adequately protecting your privacy when using Hush,
|
||||
please see <https://hush.is/security/>.
|
||||
In order to ensure you are adequately protecting your privacy when using
|
||||
DragonX, please see <https://dragonx.is/security/>.
|
||||
.SS "Usage:"
|
||||
.TP
|
||||
hush\-cli [options] <command> [params]
|
||||
Send command to Hush
|
||||
dragonx\-cli [options] <command> [params]
|
||||
Send command to DragonX
|
||||
.TP
|
||||
hush\-cli [options] help
|
||||
dragonx\-cli [options] help
|
||||
List commands
|
||||
.TP
|
||||
hush\-cli [options] help <command>
|
||||
dragonx\-cli [options] help <command>
|
||||
Get help for a command
|
||||
.SH OPTIONS
|
||||
.HP
|
||||
@@ -25,7 +25,7 @@ This help message
|
||||
.HP
|
||||
\fB\-conf=\fR<file>
|
||||
.IP
|
||||
Specify configuration file (default: HUSH3.conf)
|
||||
Specify configuration file (default: DRAGONX.conf)
|
||||
.HP
|
||||
\fB\-datadir=\fR<dir>
|
||||
.IP
|
||||
@@ -47,7 +47,7 @@ Send commands to node running on <ip> (default: 127.0.0.1)
|
||||
.HP
|
||||
\fB\-rpcport=\fR<port>
|
||||
.IP
|
||||
Connect to JSON\-RPC on <port> (default: 18030 )
|
||||
Connect to JSON\-RPC on <port> (default: 21769 )
|
||||
.HP
|
||||
\fB\-rpcwait\fR
|
||||
.IP
|
||||
@@ -70,20 +70,25 @@ Timeout in seconds during HTTP requests, or 0 for no timeout. (default:
|
||||
.IP
|
||||
Read extra arguments from standard input, one per line until EOF/Ctrl\-D
|
||||
(recommended for sensitive information such as passphrases)
|
||||
.PP
|
||||
In order to ensure you are adequately protecting your privacy when using
|
||||
DragonX, please see <https://dragonx.is/security/>.
|
||||
.SH COPYRIGHT
|
||||
|
||||
In order to ensure you are adequately protecting your privacy when using Hush,
|
||||
please see <https://hush.is/security/>.
|
||||
|
||||
Copyright (C) 2016-2025 Duke Leto and The Hush Developers
|
||||
|
||||
Copyright (C) 2016-2020 jl777 and SuperNET developers
|
||||
|
||||
Copyright (C) 2016-2018 The Zcash developers
|
||||
|
||||
Copyright (C) 2009-2014 The Bitcoin Core developers
|
||||
|
||||
Copyright \(co 2024\-2026 The DragonX Developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2024 Duke Leto and The Hush Developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2020 jl777 and SuperNET developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2018 The Zcash developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2009\-2014 The Bitcoin Core developers
|
||||
.PP
|
||||
This is experimental Free Software! Fuck Yeah!!!!!
|
||||
|
||||
.PP
|
||||
Distributed under the GPLv3 software license, see the accompanying file COPYING
|
||||
or <https://www.gnu.org/licenses/gpl-3.0.en.html>.
|
||||
or <https://www.gnu.org/licenses/gpl\-3.0.en.html>.
|
||||
@@ -1,9 +1,9 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
|
||||
.TH HUSH-TX "1" "July 2025" "hush-tx v3.10.4" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||
.TH DRAGONX-TX "1" "July 2026" "dragonx-tx v1.0.3-4caf2fc68" "User Commands"
|
||||
.SH NAME
|
||||
hush-tx \- manual page for hush-tx v3.10.4
|
||||
dragonx-tx \- DragonX transaction utility
|
||||
.SH DESCRIPTION
|
||||
hush\-tx utility version v3.10.4\-7e63e2f01\-dirty
|
||||
hush\-tx utility version v1.0.3\-4caf2fc68
|
||||
.SS "Usage:"
|
||||
.TP
|
||||
hush\-tx [options] <hex\-tx> [commands]
|
||||
@@ -84,20 +84,3 @@ Load JSON file FILENAME into register NAME
|
||||
set=NAME:JSON\-STRING
|
||||
.IP
|
||||
Set register NAME to given JSON\-STRING
|
||||
.SH COPYRIGHT
|
||||
|
||||
In order to ensure you are adequately protecting your privacy when using Hush,
|
||||
please see <https://hush.is/security/>.
|
||||
|
||||
Copyright (C) 2016-2025 Duke Leto and The Hush Developers
|
||||
|
||||
Copyright (C) 2016-2020 jl777 and SuperNET developers
|
||||
|
||||
Copyright (C) 2016-2018 The Zcash developers
|
||||
|
||||
Copyright (C) 2009-2014 The Bitcoin Core developers
|
||||
|
||||
This is experimental Free Software! Fuck Yeah!!!!!
|
||||
|
||||
Distributed under the GPLv3 software license, see the accompanying file COPYING
|
||||
or <https://www.gnu.org/licenses/gpl-3.0.en.html>.
|
||||
@@ -1,16 +1,16 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
|
||||
.TH HUSHD "1" "July 2025" "hushd v3.10.4" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||
.TH DRAGONX "1" "July 2026" "DragonX Daemon version v1.0.3-4caf2fc68" "User Commands"
|
||||
.SH NAME
|
||||
hushd \- manual page for hushd v3.10.4
|
||||
DragonX \- manual page for DragonX Daemon version v1.0.3-4caf2fc68
|
||||
.SH DESCRIPTION
|
||||
Hush Daemon version v3.10.4\-7e63e2f01\-dirty
|
||||
DragonX Daemon version v1.0.3\-4caf2fc68
|
||||
.PP
|
||||
In order to ensure you are adequately protecting your privacy when using Hush,
|
||||
please see <https://hush.is/security/>.
|
||||
In order to ensure you are adequately protecting your privacy when using
|
||||
DragonX, please see <https://dragonx.is/security/>.
|
||||
.SS "Usage:"
|
||||
.TP
|
||||
hushd [options]
|
||||
Start a Hush Daemon
|
||||
dragonxd [options]
|
||||
Start DragonX Daemon
|
||||
.SH OPTIONS
|
||||
.HP
|
||||
\-?
|
||||
@@ -32,11 +32,11 @@ How thorough the block verification of \fB\-checkblocks\fR is (0\-4, default: 3)
|
||||
.HP
|
||||
\fB\-clientname=\fR<SomeName>
|
||||
.IP
|
||||
Full node client name, default 'GoldenSandtrout'
|
||||
Full node client name, default 'DragonX'
|
||||
.HP
|
||||
\fB\-conf=\fR<file>
|
||||
.IP
|
||||
Specify configuration file (default: HUSH3.conf)
|
||||
Specify configuration file (default: DRAGONX.conf)
|
||||
.HP
|
||||
\fB\-daemon\fR
|
||||
.IP
|
||||
@@ -52,7 +52,11 @@ Specify directory to be used when exporting data
|
||||
.HP
|
||||
\fB\-dbcache=\fR<n>
|
||||
.IP
|
||||
Set database cache size in megabytes (4 to 16384, default: 512)
|
||||
Set database cache size in megabytes (4 to 16384). Default: adaptive \-
|
||||
uses most free RAM to speed up initial block download (far fewer
|
||||
UTXO flushes to disk) and automatically shrinks if other
|
||||
applications need memory, always leaving a reserve free. Setting
|
||||
a fixed value disables adaptive sizing.
|
||||
.HP
|
||||
\fB\-loadblock=\fR<file>
|
||||
.IP
|
||||
@@ -78,9 +82,15 @@ applied)
|
||||
.HP
|
||||
\fB\-par=\fR<n>
|
||||
.IP
|
||||
Set the number of script verification threads (\fB\-8\fR to 16, 0 = auto, <0 =
|
||||
Set the number of script verification threads (\fB\-4\fR to 16, 0 = auto, <0 =
|
||||
leave that many cores free, default: 0)
|
||||
.HP
|
||||
\fB\-randomxverifythreads=\fR<n>
|
||||
.IP
|
||||
Number of threads for parallel RandomX PoW pre\-verification of
|
||||
post\-checkpoint blocks during sync (0 = inline only, max 16,
|
||||
default: same as \fB\-par\fR)
|
||||
.HP
|
||||
\fB\-pid=\fR<file>
|
||||
.IP
|
||||
Specify pid file (default: hushd.pid)
|
||||
@@ -337,6 +347,40 @@ Do not load the wallet and disable wallet RPC calls
|
||||
.IP
|
||||
Set key pool size to <n> (default: 100)
|
||||
.HP
|
||||
\fB\-hdtransparent\fR
|
||||
.IP
|
||||
Derive transparent addresses from the HD seed so they can be recovered
|
||||
from it (default: 1)
|
||||
.HP
|
||||
\fB\-hdseed=\fR<hex>
|
||||
.IP
|
||||
Restore a fresh/empty wallet from a 32\- or 64\-byte HD seed hex (the
|
||||
value shown in z_exportwallet's '# HDSeed=' line). WARNING:
|
||||
exposes the seed to your shell history and process list.
|
||||
.HP
|
||||
\fB\-mnemonic=\fR<words>
|
||||
.IP
|
||||
Restore/create a fresh/empty wallet from a BIP39 seed phrase, compatible
|
||||
with SilentDragonXLite (English, no passphrase). WARNING: exposes
|
||||
the phrase to your shell history and process list; prefer
|
||||
DRAGONX.conf with tight permissions.
|
||||
.HP
|
||||
\fB\-usemnemonic\fR
|
||||
.IP
|
||||
Create new wallets from a fresh BIP39 seed phrase so the 24 words can be
|
||||
exported (z_exportmnemonic) and used in SilentDragonXLite
|
||||
(default: 0)
|
||||
.HP
|
||||
\fB\-hdtransparentgaplimit=\fR<n>
|
||||
.IP
|
||||
On \fB\-mnemonic\fR/\-hdseed restore, pre\-derive this many HD transparent keys
|
||||
so a rescan can find coinbase paid to them (default: 1000)
|
||||
.HP
|
||||
\fB\-mnemonicsaplinggap=\fR<n>
|
||||
.IP
|
||||
On \fB\-mnemonic\fR/\-hdseed restore, pre\-derive this many shielded (Sapling)
|
||||
addresses so a rescan can find notes sent to them (default: 100)
|
||||
.HP
|
||||
\fB\-consolidation\fR
|
||||
.IP
|
||||
Enable auto Sapling note consolidation (default: false)
|
||||
@@ -649,8 +693,8 @@ multiple times (default: bind to all interfaces)
|
||||
.HP
|
||||
\fB\-stratumport=\fR<port>
|
||||
.IP
|
||||
Listen for Stratum work requests on <port> (default: 19031 or testnet:
|
||||
19031)
|
||||
Listen for Stratum work requests on <port> (default: 22769 or testnet:
|
||||
22769)
|
||||
.HP
|
||||
\fB\-stratumallowip=\fR<ip>
|
||||
.IP
|
||||
@@ -659,7 +703,7 @@ single IP (e.g. 1.2.3.4), a network/netmask (e.g.
|
||||
1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This
|
||||
option can be specified multiple times
|
||||
.PP
|
||||
Hush Arrakis Chain options:
|
||||
DragonX Chain options:
|
||||
.HP
|
||||
\fB\-ac_algo\fR
|
||||
.IP
|
||||
@@ -686,6 +730,12 @@ OP_RETURN minimum fee per tx, regardless of tx size, default is 1 coin
|
||||
.IP
|
||||
CODA integration
|
||||
.HP
|
||||
\fB\-ac_clearnet\fR
|
||||
.IP
|
||||
Enable or disable clearnet connections for the entire blockchain.
|
||||
Setting to 0 will disable clearnet and use sane defaults for
|
||||
Tor/i2p and require all nodes to do the same (default: 1)
|
||||
.HP
|
||||
\fB\-ac_decay\fR
|
||||
.IP
|
||||
Percentage of block reward decrease at each halving
|
||||
@@ -754,20 +804,25 @@ Starting supply, default is 10
|
||||
\fB\-ac_txpow\fR
|
||||
.IP
|
||||
Enforce transaction\-rate limit, default 0
|
||||
.PP
|
||||
In order to ensure you are adequately protecting your privacy when using
|
||||
DragonX, please see <https://dragonx.is/security/>.
|
||||
.SH COPYRIGHT
|
||||
|
||||
In order to ensure you are adequately protecting your privacy when using Hush,
|
||||
please see <https://hush.is/security/>.
|
||||
|
||||
Copyright (C) 2016-2025 Duke Leto and The Hush Developers
|
||||
|
||||
Copyright (C) 2016-2020 jl777 and SuperNET developers
|
||||
|
||||
Copyright (C) 2016-2018 The Zcash developers
|
||||
|
||||
Copyright (C) 2009-2014 The Bitcoin Core developers
|
||||
|
||||
Copyright \(co 2024\-2026 The DragonX Developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2024 Duke Leto and The Hush Developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2020 jl777 and SuperNET developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2018 The Zcash developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2009\-2014 The Bitcoin Core developers
|
||||
.PP
|
||||
This is experimental Free Software! Fuck Yeah!!!!!
|
||||
|
||||
.PP
|
||||
Distributed under the GPLv3 software license, see the accompanying file COPYING
|
||||
or <https://www.gnu.org/licenses/gpl-3.0.en.html>.
|
||||
or <https://www.gnu.org/licenses/gpl\-3.0.en.html>.
|
||||
@@ -71,11 +71,11 @@ Install deps on Linux:
|
||||
- To make a pre-release "beta" you can modify `CLIENT_VERSION_BUILD` but that is rarely done in Hush world.
|
||||
- A `CLIENT_VERSION_BUILD` of 50 means "actual non-beta release"
|
||||
- Make sure to keep the values in configure.ac and src/clientversion.h the same. The variables are prefixed wth an underscore in configure.ac
|
||||
- Run `make manpages`, commit + push results
|
||||
- hushd must be running so the script can automatically get the correct version number
|
||||
- There is a hack in the script where you can hardcode a version number if hushd isn't running.
|
||||
- Comment out the HUSHVER line and uncomment the line above it with a hardcoded version number
|
||||
- Run `./util/gen-manpages.sh`, commit + push results
|
||||
- There is a hack in the script where you can hardcode a version number if hushd isn't compiled on this machine
|
||||
- Comment out the HUSHVER line and uncomment the line above it with a hardcoded version number
|
||||
- PROTIP: Man page creation must be done after updating the version number and recompiling and before Debian package creation
|
||||
- TODO: How to regenerate html man pages?
|
||||
- Update checkpoints in src/chainparams.cpp via util/checkpoints.pl
|
||||
- Run "./util/checkpoints.pl help" to get example usage
|
||||
- hushd must be running to run this script, since it uses hush-cli to get the data
|
||||
@@ -97,7 +97,6 @@ Install deps on Linux:
|
||||
- They only provide limited security, because they talk about the past, not future block heights.
|
||||
- Try to generate checkpoints as close to the release as possible, so you can have a recent block height be protected.
|
||||
- For instance, don't update checkpoints and then do a release a month later. You can always update checkpoint data again or multiple times
|
||||
- Update copyright years if applicable. Example: `./util/update-copyrights.h 2022 2023`
|
||||
- Update doc/relnotes/README.md
|
||||
- To get the stats of file changes: `git diff --stat master...dev`
|
||||
- Do a fresh clone and fresh sync with new checkpoints
|
||||
|
||||
@@ -10,24 +10,35 @@ and no longer on Github, since they banned Duke Leto and
|
||||
also because they censor many people around the world and work with
|
||||
evil organizations. They also use all your "private" repos to train their AI.
|
||||
|
||||
# Hush 3.10.5 ""
|
||||
# Hush 3.10.5 "Atelic Alpaca"
|
||||
|
||||
This is an OPTIONAL but RECOMMENDED upgrade.
|
||||
|
||||
* DragonX is no longer supported by this codebase
|
||||
* DragonX devs have forked our code and we wish them the best
|
||||
* Find the latest place to download a DragonX full node via dragonx.is
|
||||
* Concurrent `z_sendmany` now works
|
||||
* A longstanding bug relating to run multiple `z_sendmany` operations at
|
||||
once has been fixed. You can now queue up many `z_sendmany` operations
|
||||
and they will succeed because they now understand how to avoid spending
|
||||
coins that another `z_sendmany` process is trying to spend.
|
||||
* Updated Autonomous System Map (asmap)
|
||||
* New RPC `z_listlockunspent`
|
||||
* Lists shielded notes (coins inside a zaddr) which are temporarily unspendable because an RPC process is currently trying to spend them.
|
||||
* If that operation succeeds, they will become spent. If it fails they will be unlocked and become spendable again.
|
||||
* Fixed DragonX checkpoints
|
||||
* Hush checkpoints were mistakenly listed as checkpoints in the 3.10.4
|
||||
release, which caused some nodes to be unable to sync.
|
||||
* This release fixes this issue.
|
||||
* New option to `z_shieldcoinbase` allows privately donating a percentage of coinbase funds during shielding
|
||||
* This new option defaults to OFF and allows CLI users to opt-in to donating between 0% and 10% of coinbase funds to developers
|
||||
* No GUI currently utilizes this but that feature is planned for SD
|
||||
* The donation has extremely good privacy:
|
||||
* It cannot be determined from public blockchain data if a donation is being made, as it takes the place of a Sietch output
|
||||
* It cannot be determined from public blockchain data the amount of the donation
|
||||
* Donations do not create new transactions, do not use additional blockspace and cannot be detected by anyone but the sender or reciever
|
||||
* New HAC option `ac_clearnet` can be used to disable clearnet networking for an entire blockchain instead of just a single node
|
||||
* Updated test framework and tests which allowed the fixing of the `z_sendmany` bug above
|
||||
* Faster compiling of RandomX internals
|
||||
* GMP dependency removed, as it is no longer needed
|
||||
* Hush is now compatible with GCC15 and now correctly supports customizing the compiler for a build via the CC env var
|
||||
* New HTML man pages are now available at doc/man/hushd.html and doc/man/hush-cli.html
|
||||
|
||||
# Hush 3.10.4 "Hazy Hākuturi"
|
||||
|
||||
|
||||
90
doc/seed-phrase.md
Normal file
90
doc/seed-phrase.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# BIP39 seed phrases (SilentDragonXLite-compatible)
|
||||
|
||||
DragonX full-node wallets can be created from and restored to a **BIP39 24-word
|
||||
seed phrase** that is **byte-for-byte compatible with SilentDragonXLite**: the
|
||||
same words produce the same transparent (t-) and shielded (z-) addresses in
|
||||
either wallet, so funds move between the light wallet and the full node with one
|
||||
backup.
|
||||
|
||||
## What makes them compatible
|
||||
|
||||
Compatibility requires the mnemonic, the seed derivation, and every HD path to
|
||||
match exactly. They do:
|
||||
|
||||
| Detail | Value (both wallets) |
|
||||
|---|---|
|
||||
| Word list | BIP39 English, 2048 words |
|
||||
| Passphrase | empty (no "25th word") |
|
||||
| Mnemonic → seed | PBKDF2-HMAC-SHA512, 2048 rounds, salt `"mnemonic"`, 64-byte output |
|
||||
| Coin type | 141 (KMD SLIP-0044) |
|
||||
| Shielded path | `m/32'/141'/i'` (ZIP-32) |
|
||||
| Transparent path | `m/44'/141'/0'/0/i` (BIP44) |
|
||||
|
||||
The node stores the 32-byte BIP39 **entropy** (SilentDragonXLite's on-disk
|
||||
convention) and expands it to the 64-byte seed on demand for derivation. The
|
||||
node's vendored BIP39 library (`src/crypto/bip39`) is byte-identical to
|
||||
SilentDragonXLite's `tiny-bip39` 0.6.2, and the derivation is anchored by a
|
||||
known-answer test (`src/gtest/test_mnemonic_compat.cpp`).
|
||||
|
||||
## Restore from a phrase
|
||||
|
||||
Start the node once, on a **fresh/empty datadir**, with the phrase:
|
||||
|
||||
```
|
||||
dragonxd -mnemonic="word1 word2 ... word24"
|
||||
```
|
||||
|
||||
or, preferably (keeps the phrase out of your shell history and process list),
|
||||
put it in `DRAGONX.conf` with tight permissions:
|
||||
|
||||
```
|
||||
mnemonic=word1 word2 ... word24
|
||||
```
|
||||
|
||||
On restore the node pre-derives keys and rescans from genesis to recover funds:
|
||||
|
||||
* `-hdtransparentgaplimit=<n>` — HD transparent keys to pre-derive (default 1000)
|
||||
* `-mnemonicsaplinggap=<n>` — shielded addresses to pre-derive (default 100)
|
||||
|
||||
Raise these if the wallet used many addresses. Restore only works on a wallet
|
||||
with no seed yet (a brand-new datadir); it refuses to overwrite an existing seed.
|
||||
|
||||
## Create a new phrase on the node
|
||||
|
||||
By default new node wallets use a random (non-mnemonic) seed. To create a new
|
||||
wallet from a fresh 24-word phrase instead — so you can export it and use it in
|
||||
SilentDragonXLite — start with:
|
||||
|
||||
```
|
||||
dragonxd -usemnemonic
|
||||
```
|
||||
|
||||
## Show / back up the phrase
|
||||
|
||||
For a mnemonic wallet (created with `-usemnemonic` or restored with `-mnemonic`):
|
||||
|
||||
```
|
||||
dragonx-cli z_exportmnemonic
|
||||
```
|
||||
|
||||
returns the 24 words and the seed fingerprint. The wallet must be unlocked.
|
||||
Guard the phrase like a private key.
|
||||
|
||||
## Limitations
|
||||
|
||||
* **English + empty passphrase only.** Any other word list or a BIP39 passphrase
|
||||
would break compatibility, so they are not accepted.
|
||||
* **Legacy / random-seed wallets have no phrase.** A wallet created before this
|
||||
feature (or without `-usemnemonic`) has a random seed; `z_exportmnemonic`
|
||||
returns an error for it — use `z_exportwallet` to back up the raw seed. Such
|
||||
wallets are not SilentDragonXLite-compatible.
|
||||
* **Scope.** Recovers HD-derived shielded funds and transparent coinbase (see
|
||||
[hd-transparent-keys.md](hd-transparent-keys.md) for why only coinbase lands on
|
||||
t-addresses on this `ac_private=1` chain). Keys imported with `z_importkey` are
|
||||
not seed-derived and are not recovered by the phrase.
|
||||
|
||||
## On-disk compatibility
|
||||
|
||||
Mnemonic wallets set `CHDChain` version 3 (`VERSION_HD_MNEMONIC`). Older wallet
|
||||
records load unchanged. Downgrading a mnemonic wallet to an older binary is not
|
||||
supported.
|
||||
@@ -39,15 +39,22 @@ BITCOIN_INCLUDES += -I$(srcdir)/univalue/include
|
||||
BITCOIN_INCLUDES += -I$(srcdir)/leveldb/include
|
||||
|
||||
if TARGET_WINDOWS
|
||||
LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl
|
||||
LIBBITCOIN_SERVER=libbitcoin_server.a
|
||||
endif
|
||||
if TARGET_DARWIN
|
||||
LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl
|
||||
LIBBITCOIN_SERVER=libbitcoin_server.a
|
||||
endif
|
||||
if TARGET_LINUX
|
||||
LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl
|
||||
LIBBITCOIN_SERVER=libbitcoin_server.a
|
||||
endif
|
||||
|
||||
# libcurl is a linker flag, not a buildable library file. It must NOT live inside
|
||||
# LIBBITCOIN_SERVER, which is also fed into EXTRA_LIBRARIES (a list of files automake
|
||||
# builds) where a -l flag is illegal and triggers a portability error. Keep it as its
|
||||
# own variable, added to each binary's _LDADD after libbitcoin_server.a (whose objects
|
||||
# reference curl symbols) so static link order stays correct.
|
||||
LIBCURL = -lcurl
|
||||
|
||||
LIBBITCOIN_WALLET=libbitcoin_wallet.a
|
||||
LIBBITCOIN_COMMON=libbitcoin_common.a
|
||||
LIBBITCOIN_CLI=libbitcoin_cli.a
|
||||
@@ -94,11 +101,11 @@ noinst_PROGRAMS =
|
||||
TESTS =
|
||||
|
||||
#if BUILD_BITCOIND
|
||||
bin_PROGRAMS += hushd
|
||||
bin_PROGRAMS += dragonxd
|
||||
#endif
|
||||
|
||||
if BUILD_BITCOIN_UTILS
|
||||
bin_PROGRAMS += hush-cli hush-tx
|
||||
bin_PROGRAMS += dragonx-cli dragonx-tx
|
||||
endif
|
||||
if ENABLE_WALLET
|
||||
bin_PROGRAMS += wallet-utility
|
||||
@@ -318,6 +325,7 @@ libbitcoin_wallet_a_SOURCES = \
|
||||
wallet/asyncrpcoperation_shieldcoinbase.cpp \
|
||||
wallet/crypter.cpp \
|
||||
wallet/db.cpp \
|
||||
wallet/mnemonic.cpp \
|
||||
zcash/Note.cpp \
|
||||
transaction_builder.cpp \
|
||||
wallet/rpcdump.cpp \
|
||||
@@ -354,6 +362,23 @@ crypto_libbitcoin_crypto_a_SOURCES = \
|
||||
crypto/sha512.cpp \
|
||||
crypto/sha512.h
|
||||
|
||||
# Vendored trezor-crypto BIP39 (mnemonic seed phrases). Kept self-contained so
|
||||
# the same 24 words are compatible with SilentDragonXLite (tiny-bip39 0.6.2).
|
||||
crypto_libbitcoin_crypto_a_SOURCES += \
|
||||
crypto/bip39/bip39.c \
|
||||
crypto/bip39/bip39.h \
|
||||
crypto/bip39/bip39_english.h \
|
||||
crypto/bip39/pbkdf2.c \
|
||||
crypto/bip39/pbkdf2.h \
|
||||
crypto/bip39/hmac.c \
|
||||
crypto/bip39/hmac.h \
|
||||
crypto/bip39/sha2.c \
|
||||
crypto/bip39/sha2.h \
|
||||
crypto/bip39/memzero.c \
|
||||
crypto/bip39/memzero.h \
|
||||
crypto/bip39/options.h \
|
||||
crypto/bip39/rand.h
|
||||
|
||||
if EXPERIMENTAL_ASM
|
||||
crypto_libbitcoin_crypto_a_SOURCES += crypto/sha256_sse4.cpp
|
||||
endif
|
||||
@@ -453,17 +478,18 @@ nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
|
||||
#
|
||||
|
||||
# hushd binary #
|
||||
hushd_SOURCES = bitcoind.cpp
|
||||
hushd_CPPFLAGS = -fPIC $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
hushd_CXXFLAGS = -fPIC $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
hushd_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
dragonxd_SOURCES = bitcoind.cpp
|
||||
dragonxd_CPPFLAGS = -fPIC $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
dragonxd_CXXFLAGS = -fPIC $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
dragonxd_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
|
||||
if TARGET_WINDOWS
|
||||
hushd_SOURCES += bitcoind-res.rc
|
||||
dragonxd_SOURCES += bitcoind-res.rc
|
||||
endif
|
||||
|
||||
hushd_LDADD = \
|
||||
dragonxd_LDADD = \
|
||||
$(LIBBITCOIN_SERVER) \
|
||||
$(LIBCURL) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
@@ -476,10 +502,10 @@ hushd_LDADD = \
|
||||
$(LIBRANDOMX)
|
||||
|
||||
if ENABLE_WALLET
|
||||
hushd_LDADD += $(LIBBITCOIN_WALLET)
|
||||
dragonxd_LDADD += $(LIBBITCOIN_WALLET)
|
||||
endif
|
||||
|
||||
hushd_LDADD += \
|
||||
dragonxd_LDADD += \
|
||||
$(BOOST_LIBS) \
|
||||
$(BDB_LIBS) \
|
||||
$(SSL_LIBS) \
|
||||
@@ -490,27 +516,27 @@ hushd_LDADD += \
|
||||
$(LIBZCASH_LIBS)
|
||||
|
||||
if TARGET_DARWIN
|
||||
hushd_LDADD += libcc.dylib $(LIBSECP256K1)
|
||||
dragonxd_LDADD += libcc.dylib $(LIBSECP256K1)
|
||||
endif
|
||||
if TARGET_WINDOWS
|
||||
hushd_LDADD += libcc.dll $(LIBSECP256K1)
|
||||
dragonxd_LDADD += libcc.dll $(LIBSECP256K1)
|
||||
endif
|
||||
if TARGET_LINUX
|
||||
hushd_LDADD += libcc.so $(LIBSECP256K1)
|
||||
dragonxd_LDADD += libcc.so $(LIBSECP256K1)
|
||||
endif
|
||||
|
||||
# [+] Decker: use static linking for libstdc++.6.dylib, libgomp.1.dylib, libgcc_s.1.dylib
|
||||
if TARGET_DARWIN
|
||||
hushd_LDFLAGS += -static-libgcc
|
||||
dragonxd_LDFLAGS += -static-libgcc
|
||||
endif
|
||||
|
||||
# hush-cli binary #
|
||||
hush_cli_SOURCES = bitcoin-cli.cpp
|
||||
hush_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS)
|
||||
hush_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
hush_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
dragonx_cli_SOURCES = bitcoin-cli.cpp
|
||||
dragonx_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS)
|
||||
dragonx_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
dragonx_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
if TARGET_DARWIN
|
||||
hush_cli_LDFLAGS += -static-libgcc
|
||||
dragonx_cli_LDFLAGS += -static-libgcc
|
||||
endif
|
||||
|
||||
# wallet-utility binary #
|
||||
@@ -522,10 +548,10 @@ wallet_utility_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
endif
|
||||
|
||||
if TARGET_WINDOWS
|
||||
hush_cli_SOURCES += bitcoin-cli-res.rc
|
||||
dragonx_cli_SOURCES += bitcoin-cli-res.rc
|
||||
endif
|
||||
|
||||
hush_cli_LDADD = \
|
||||
dragonx_cli_LDADD = \
|
||||
$(LIBBITCOIN_CLI) \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
@@ -554,16 +580,16 @@ wallet_utility_LDADD = \
|
||||
endif
|
||||
|
||||
# hush-tx binary #
|
||||
hush_tx_SOURCES = hush-tx.cpp
|
||||
hush_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
hush_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
hush_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
dragonx_tx_SOURCES = hush-tx.cpp
|
||||
dragonx_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
dragonx_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
dragonx_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
|
||||
if TARGET_WINDOWS
|
||||
hush_tx_SOURCES += bitcoin-tx-res.rc
|
||||
dragonx_tx_SOURCES += bitcoin-tx-res.rc
|
||||
endif
|
||||
|
||||
hush_tx_LDADD = \
|
||||
dragonx_tx_LDADD = \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
@@ -574,7 +600,7 @@ hush_tx_LDADD = \
|
||||
$(LIBZCASH_LIBS) \
|
||||
$(LIBRANDOMX)
|
||||
|
||||
hush_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
|
||||
dragonx_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
|
||||
|
||||
# Zcash Protocol Primitives
|
||||
libzcash_a_SOURCES = \
|
||||
@@ -594,7 +620,7 @@ libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBOOST_SPIRIT_THREADSAFE -DHAV
|
||||
#libzcash_a_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
#libzcash_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMONTGOMERY_OUTPUT
|
||||
|
||||
libzcash_a_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing -std=gnu17
|
||||
libzcash_a_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing -std=gnu++17
|
||||
libzcash_a_LDFLAGS = $(SAN_LDFLAGS) $(HARDENED_LDFLAGS)
|
||||
libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT
|
||||
|
||||
@@ -636,7 +662,7 @@ libhush_a_SOURCES = \
|
||||
|
||||
libhush_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) -pipe -O1 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES)
|
||||
|
||||
libhush_a_CXXFLAGS = $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing -std=gnu17
|
||||
libhush_a_CXXFLAGS = $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing -std=gnu++17
|
||||
|
||||
libhush_a_LDFLAGS = $(HARDENED_LDFLAGS)
|
||||
|
||||
@@ -685,5 +711,5 @@ endif
|
||||
if ENABLE_TESTS
|
||||
#include Makefile.test-hush.include
|
||||
#include Makefile.test.include
|
||||
#include Makefile.gtest.include
|
||||
include Makefile.gtest.include
|
||||
endif
|
||||
|
||||
@@ -4,65 +4,61 @@ TESTS += hush-gtest
|
||||
bin_PROGRAMS += hush-gtest
|
||||
|
||||
# tool for generating our public parameters
|
||||
# NOTE: the original test list used an invalid automake form (comment after a trailing
|
||||
# backslash, and `zcash_gtest_SOURCES +=` with no prior `=`), which is why the whole
|
||||
# gtest harness was disabled via a `#include`. Minimal valid set: the harness + the
|
||||
# Re-add other gtest sources here as they are revived.
|
||||
hush_gtest_SOURCES = \
|
||||
gtest/main.cpp \
|
||||
gtest/utils.cpp \
|
||||
gtest/test_checktransaction.cpp \
|
||||
gtest/json_test_vectors.cpp \
|
||||
gtest/json_test_vectors.h \
|
||||
gtest/test_wallet_zkeys.cpp \
|
||||
# These tests are order-dependent, because they
|
||||
# depend on global state (see #1539)
|
||||
if ENABLE_WALLET
|
||||
zcash_gtest_SOURCES += \
|
||||
wallet/gtest/test_wallet_zkeys.cpp
|
||||
endif
|
||||
zcash_gtest_SOURCES += \
|
||||
gtest/test_tautology.cpp \
|
||||
gtest/test_deprecation.cpp \
|
||||
gtest/test_equihash.cpp \
|
||||
gtest/test_httprpc.cpp \
|
||||
gtest/test_keys.cpp \
|
||||
gtest/test_keystore.cpp \
|
||||
gtest/test_noteencryption.cpp \
|
||||
gtest/test_mempool.cpp \
|
||||
gtest/test_merkletree.cpp \
|
||||
gtest/test_metrics.cpp \
|
||||
gtest/test_miner.cpp \
|
||||
gtest/test_pow.cpp \
|
||||
gtest/test_random.cpp \
|
||||
gtest/test_rpc.cpp \
|
||||
gtest/test_sapling_note.cpp \
|
||||
gtest/test_transaction.cpp \
|
||||
gtest/test_transaction_builder.cpp \
|
||||
gtest/test_upgrades.cpp \
|
||||
gtest/test_validation.cpp \
|
||||
gtest/test_circuit.cpp \
|
||||
gtest/test_txid.cpp \
|
||||
gtest/test_libzcash_utils.cpp \
|
||||
gtest/test_proofs.cpp \
|
||||
gtest/test_pedersen_hash.cpp \
|
||||
gtest/test_checkblock.cpp \
|
||||
gtest/test_zip32.cpp
|
||||
if ENABLE_WALLET
|
||||
zcash_gtest_SOURCES += \
|
||||
wallet/gtest/test_wallet.cpp
|
||||
endif
|
||||
gtest/test_randomx_preverify.cpp \
|
||||
gtest/test_hdtransparent.cpp \
|
||||
gtest/test_mnemonic_compat.cpp
|
||||
|
||||
hush_gtest_CPPFLAGS = $(AM_CPPFLAGS) -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC $(BITCOIN_INCLUDES)
|
||||
hush_gtest_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
|
||||
hush_gtest_LDADD = -lgtest -lgmock $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
|
||||
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)
|
||||
# Mirror dragonxd_LDADD's working library set/order (the old list used a non-existent
|
||||
# $(LIBBITCOIN_UNIVALUE) so univalue was never linked, and omitted LIBHUSH/LIBRANDOMX/libcc).
|
||||
hush_gtest_LDADD = -lgtest -lgmock \
|
||||
$(LIBBITCOIN_SERVER) \
|
||||
$(LIBCURL) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBZCASH) \
|
||||
$(LIBHUSH) \
|
||||
$(LIBLEVELDB) \
|
||||
$(LIBMEMENV) \
|
||||
$(LIBSECP256K1) \
|
||||
$(LIBRANDOMX)
|
||||
if ENABLE_WALLET
|
||||
hush_gtest_LDADD += $(LIBBITCOIN_WALLET)
|
||||
endif
|
||||
|
||||
hush_gtest_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS)
|
||||
hush_gtest_LDADD += \
|
||||
$(BOOST_LIBS) \
|
||||
$(BOOST_UNIT_TEST_FRAMEWORK_LIB) \
|
||||
$(BDB_LIBS) \
|
||||
$(SSL_LIBS) \
|
||||
$(CRYPTO_LIBS) \
|
||||
$(EVENT_PTHREADS_LIBS) \
|
||||
$(EVENT_LIBS) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBZCASH_LIBS)
|
||||
|
||||
hush_gtest_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
|
||||
if TARGET_DARWIN
|
||||
hush_gtest_LDADD += libcc.dylib $(LIBSECP256K1)
|
||||
endif
|
||||
if TARGET_WINDOWS
|
||||
hush_gtest_LDADD += libcc.dll $(LIBSECP256K1)
|
||||
endif
|
||||
if TARGET_LINUX
|
||||
hush_gtest_LDADD += libcc.so $(LIBSECP256K1)
|
||||
endif
|
||||
|
||||
hush_gtest_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
|
||||
hush_gtest_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
|
||||
hush-gtest-expected-failures: hush-gtest FORCE
|
||||
./hush-gtest --gtest_filter=*DISABLED_* --gtest_also_run_disabled_tests
|
||||
|
||||
@@ -112,13 +112,13 @@ endif
|
||||
|
||||
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
|
||||
test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) -fopenmp $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS)
|
||||
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
|
||||
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBCURL) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
|
||||
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
|
||||
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
if ENABLE_WALLET
|
||||
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
|
||||
endif
|
||||
test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
|
||||
test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBCURL) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
|
||||
$(LIBLEVELDB) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
|
||||
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"ac_perc": "11111111",
|
||||
"ac_eras": "3",
|
||||
"ac_script": "76a9145eb10cf64f2bab1b457f1f25e658526155928fac88ac",
|
||||
"clientname": "GoldenSandtrout",
|
||||
"clientname": "DragonX",
|
||||
"addnode": [
|
||||
"node1.hush.is",
|
||||
"node2.hush.is",
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
#include <event2/keyvalq_struct.h>
|
||||
#include "support/events.h"
|
||||
|
||||
uint16_t ASSETCHAINS_RPCPORT = 18031;
|
||||
uint16_t BITCOIND_RPCPORT = 18031;
|
||||
uint16_t ASSETCHAINS_RPCPORT = 21769;
|
||||
uint16_t BITCOIND_RPCPORT = 21769;
|
||||
char SMART_CHAIN_SYMBOL[65];
|
||||
|
||||
extern uint16_t ASSETCHAINS_RPCPORT;
|
||||
@@ -47,13 +47,13 @@ std::string HelpMessageCli()
|
||||
std::string strUsage;
|
||||
strUsage += HelpMessageGroup(_("Options:"));
|
||||
strUsage += HelpMessageOpt("-?", _("This help message"));
|
||||
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "HUSH3.conf"));
|
||||
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "DRAGONX.conf"));
|
||||
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory (this path cannot use '~')"));
|
||||
strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
|
||||
strUsage += HelpMessageOpt("-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."));
|
||||
strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1"));
|
||||
strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u )"), 18030));
|
||||
strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u )"), 21769));
|
||||
strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
|
||||
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
|
||||
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
|
||||
@@ -87,19 +87,19 @@ static int AppInitRPC(int argc, char* argv[])
|
||||
ParseParameters(argc, argv);
|
||||
std:string name;
|
||||
|
||||
// default HAC is HUSH3 itself, which to the internals, is also a HAC
|
||||
name = GetArg("-ac_name","HUSH3");
|
||||
// default HAC is DRAGONX itself, which to the internals, is also a HAC
|
||||
name = GetArg("-ac_name","DRAGONX");
|
||||
|
||||
if ( !name.empty() )
|
||||
strncpy(SMART_CHAIN_SYMBOL,name.c_str(),sizeof(SMART_CHAIN_SYMBOL)-1);
|
||||
|
||||
if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) {
|
||||
std::string strUsage = _("Hush RPC client version") + " " + FormatFullVersion() + "\n" + PrivacyInfo();
|
||||
std::string strUsage = _("DragonX RPC client version") + " " + FormatFullVersion() + "\n" + PrivacyInfo();
|
||||
if (!mapArgs.count("-version")) {
|
||||
strUsage += "\n" + _("Usage:") + "\n" +
|
||||
" hush-cli [options] <command> [params] " + _("Send command to Hush") + "\n" +
|
||||
" hush-cli [options] help " + _("List commands") + "\n" +
|
||||
" hush-cli [options] help <command> " + _("Get help for a command") + "\n";
|
||||
" dragonx-cli [options] <command> [params] " + _("Send command to DragonX") + "\n" +
|
||||
" dragonx-cli [options] help " + _("List commands") + "\n" +
|
||||
" dragonx-cli [options] help <command> " + _("Get help for a command") + "\n";
|
||||
|
||||
strUsage += "\n" + HelpMessageCli();
|
||||
} else {
|
||||
|
||||
@@ -117,14 +117,14 @@ bool AppInit(int argc, char* argv[])
|
||||
// Process help and version before taking care about datadir
|
||||
if (mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version"))
|
||||
{
|
||||
std::string strUsage = _("Hush Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n" + PrivacyInfo();
|
||||
std::string strUsage = _("DragonX Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n" + PrivacyInfo();
|
||||
|
||||
if (mapArgs.count("-version"))
|
||||
{
|
||||
strUsage += LicenseInfo();
|
||||
} else {
|
||||
strUsage += "\n" + _("Usage:") + "\n" +
|
||||
" hushd [options] " + _("Start a Hush Daemon") + "\n";
|
||||
" dragonxd [options] " + _("Start DragonX Daemon") + "\n";
|
||||
|
||||
strUsage += "\n" + HelpMessage(HMM_BITCOIND);
|
||||
}
|
||||
@@ -167,13 +167,13 @@ bool AppInit(int argc, char* argv[])
|
||||
"\n"
|
||||
"You can look at the example configuration file for suggestions of default\n"
|
||||
"options that you may want to change. It should be in one of these locations,\n"
|
||||
"depending on how you installed Hush\n") +
|
||||
"depending on how you installed DragonX\n") +
|
||||
_("- Source code: %s\n"
|
||||
"- .deb package: %s\n")).c_str(),
|
||||
GetConfigFile().string().c_str(),
|
||||
"contrib/debian/examples/HUSH3.conf",
|
||||
"/usr/share/doc/hush/examples/HUSH3.conf",
|
||||
"https://git.hush.is/hush/hush3/src/branch/master/contrib/debian/examples/HUSH3.conf");
|
||||
"contrib/debian/examples/DRAGONX.conf",
|
||||
"/usr/share/doc/dragonx/examples/DRAGONX.conf",
|
||||
"https://git.dragonx.is/DragonX/dragonx/src/branch/main/contrib/debian/examples/DRAGONX.conf");
|
||||
return false;
|
||||
} catch (const std::exception& e) {
|
||||
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
|
||||
@@ -183,15 +183,15 @@ bool AppInit(int argc, char* argv[])
|
||||
// Command-line RPC
|
||||
bool fCommandLine = false;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
// detect accidental use of RPC in hushd
|
||||
if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "hush:")) {
|
||||
// detect accidental use of RPC in dragonxd
|
||||
if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "dragonx:")) {
|
||||
fCommandLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fCommandLine)
|
||||
{
|
||||
fprintf(stderr, "Error: Ooops! There is no RPC client functionality in hushd. Use the hush-cli utility instead.\n");
|
||||
fprintf(stderr, "Error: Ooops! There is no RPC client functionality in dragonxd. Use the dragonx-cli utility instead.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ bool AppInit(int argc, char* argv[])
|
||||
fDaemon = GetBoolArg("-daemon", false);
|
||||
if (fDaemon)
|
||||
{
|
||||
fprintf(stdout, "Hush %s server starting\n",SMART_CHAIN_SYMBOL);
|
||||
fprintf(stdout, "DragonX %s server starting\n",SMART_CHAIN_SYMBOL);
|
||||
|
||||
// Daemonize
|
||||
pid_t pid = fork();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SHELL = /bin/sh
|
||||
CC_DARWIN = g++-8
|
||||
CC_DARWIN = g++-15
|
||||
CC_WIN = x86_64-w64-mingw32-gcc-posix
|
||||
CC_AARCH64 = aarch64-linux-gnu-g++
|
||||
CFLAGS_DARWIN = -DBUILD_CUSTOMCC -std=c++11 -arch x86_64 -I../secp256k1/include -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../leveldb/include -I.. -I. -fPIC -Wl,-undefined -Wl,dynamic_lookup -Wno-write-strings -shared -dynamiclib
|
||||
|
||||
BIN
src/cc/customcc.dylib
Normal file
BIN
src/cc/customcc.dylib
Normal file
Binary file not shown.
56
src/chain.h
56
src/chain.h
@@ -32,8 +32,18 @@ class CChainPower;
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
extern bool fZindex;
|
||||
static const int SPROUT_VALUE_VERSION = 1001400;
|
||||
static const int SAPLING_VALUE_VERSION = 1010100;
|
||||
// These version thresholds control whether nSproutValue/nSaplingValue are
|
||||
// serialized in the block index. They must be <= CLIENT_VERSION or the
|
||||
// values will never be persisted, causing nChainSaplingValue to reset
|
||||
// to 0 after node restart. DragonX CLIENT_VERSION is 1000350 (v1.0.3.50).
|
||||
static const int SPROUT_VALUE_VERSION = 1000000;
|
||||
static const int SAPLING_VALUE_VERSION = 1000000;
|
||||
// Block-index records written at >= this version store nSaplingValue as a boost::optional
|
||||
// (1-byte discriminant + value). Earlier records stored it as a raw 8-byte CAmount; the
|
||||
// deserializer consumes those bytes but reads the value as boost::none (untrusted) so the
|
||||
// turnstile guard stays dormant until such a node reindexes. Keep == the CLIENT_VERSION that
|
||||
// introduced the optional format (v1.0.3, CLIENT_VERSION 1000350).
|
||||
static const int SAPLING_VALUE_OPTIONAL_VERSION = 1000350;
|
||||
extern int32_t ASSETCHAINS_LWMAPOS;
|
||||
extern char SMART_CHAIN_SYMBOL[65];
|
||||
extern uint64_t ASSETCHAINS_NOTARY_PAY[];
|
||||
@@ -369,10 +379,10 @@ public:
|
||||
//! Will be boost::none if nChainTx is zero.
|
||||
boost::optional<CAmount> nChainSproutValue;
|
||||
|
||||
//! Change in value held by the Sapling circuit over this block.
|
||||
//! Not a boost::optional because this was added before Sapling activated, so we can
|
||||
//! rely on the invariant that every block before this was added had nSaplingValue = 0.
|
||||
CAmount nSaplingValue;
|
||||
//! Change in value held by the Sapling circuit over this block. boost::none for blocks
|
||||
//! before nSaplingValue was tracked, or on nodes that loaded an older-format block index
|
||||
//! (see SAPLING_VALUE_OPTIONAL_VERSION) -- propagates to nChainSaplingValue == none.
|
||||
boost::optional<CAmount> nSaplingValue;
|
||||
|
||||
//! (memory only) Total value held by the Sapling circuit up to and including this block.
|
||||
//! Will be boost::none if nChainTx is zero.
|
||||
@@ -395,7 +405,16 @@ public:
|
||||
|
||||
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
|
||||
uint32_t nSequenceId;
|
||||
|
||||
|
||||
//! (memory only) Set true once this block's RandomX PoW has been verified by the parallel
|
||||
//! pre-verification pool, letting the inline check in CheckBlockHeader skip the recompute.
|
||||
//! Written by exactly one pre-verify worker (1:1 with the block) and read by the connect
|
||||
//! thread only AFTER the pool barrier (CCheckQueue::Wait provides the happens-before), so a
|
||||
//! plain bool is race-free here. NOT serialized — a pure optimization hint; the inline
|
||||
//! CheckRandomXSolution remains the consensus authority. (Plain bool, not std::atomic, so
|
||||
//! CBlockIndex stays copyable for CDiskBlockIndex's `CBlockIndex(*pindex)` construction.)
|
||||
bool fRandomXVerified;
|
||||
|
||||
void SetNull()
|
||||
{
|
||||
phashBlock = NULL;
|
||||
@@ -410,6 +429,7 @@ public:
|
||||
chainPower = CChainPower();
|
||||
nTx = 0;
|
||||
nChainTx = 0;
|
||||
fRandomXVerified = false;
|
||||
|
||||
// Shieldex Index chain stats
|
||||
nChainPayments = 0;
|
||||
@@ -446,7 +466,7 @@ public:
|
||||
nSequenceId = 0;
|
||||
nSproutValue = boost::none;
|
||||
nChainSproutValue = boost::none;
|
||||
nSaplingValue = 0;
|
||||
nSaplingValue = boost::none;
|
||||
nChainSaplingValue = boost::none;
|
||||
|
||||
nVersion = 0;
|
||||
@@ -653,10 +673,22 @@ public:
|
||||
READWRITE(nSproutValue);
|
||||
}
|
||||
|
||||
// Only read/write nSaplingValue if the client version used to create
|
||||
// this index was storing them.
|
||||
if ((s.GetType() & SER_DISK) && (nVersion >= SAPLING_VALUE_VERSION)) {
|
||||
READWRITE(nSaplingValue);
|
||||
// nSaplingValue is a boost::optional so "not reliably tracked" reads back as none,
|
||||
// keeping the turnstile guard dormant on old/snapshot-bootstrapped DBs. Records written
|
||||
// before SAPLING_VALUE_OPTIONAL_VERSION stored it as a raw 8-byte CAmount: consume those
|
||||
// bytes for alignment but DISCARD the value (read as none), since it may be understated on
|
||||
// a node that never tracked the pool from genesis. That node stays dormant until it
|
||||
// reindexes, which rewrites records at the current version -> the optional path below.
|
||||
if (s.GetType() & SER_DISK) {
|
||||
if (nVersion >= SAPLING_VALUE_OPTIONAL_VERSION) {
|
||||
READWRITE(nSaplingValue); // new format: boost::optional<CAmount>
|
||||
} else if (nVersion >= SAPLING_VALUE_VERSION) {
|
||||
CAmount nLegacySaplingValue = 0; // old format: raw 8 bytes present in the stream
|
||||
READWRITE(nLegacySaplingValue); // consume for alignment; value is discarded
|
||||
if (ser_action.ForRead())
|
||||
nSaplingValue = boost::none;
|
||||
}
|
||||
// else (< SAPLING_VALUE_VERSION): field was never stored -> nSaplingValue stays none
|
||||
}
|
||||
|
||||
// These values only serialized when -zindex enabled
|
||||
|
||||
2852
src/chainparams.cpp
2852
src/chainparams.cpp
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ class CBaseMainParams : public CBaseChainParams
|
||||
public:
|
||||
CBaseMainParams()
|
||||
{
|
||||
nRPCPort = 18031;
|
||||
nRPCPort = 21769;
|
||||
}
|
||||
};
|
||||
static CBaseMainParams mainParams;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
* for both bitcoind and bitcoin-core, to make it harder for attackers to
|
||||
* target servers or GUI users specifically.
|
||||
*/
|
||||
const std::string CLIENT_NAME = GetArg("-clientname", "GoldenSandtrout");
|
||||
const std::string CLIENT_NAME = GetArg("-clientname", "DragonX");
|
||||
|
||||
/**
|
||||
* Client version number
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
// client versioning and copyright year
|
||||
//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
|
||||
// Must be kept in sync with configure.ac , ugh!
|
||||
#define CLIENT_VERSION_MAJOR 3
|
||||
#define CLIENT_VERSION_MINOR 10
|
||||
#define CLIENT_VERSION_REVISION 5
|
||||
#define CLIENT_VERSION_MAJOR 1
|
||||
#define CLIENT_VERSION_MINOR 0
|
||||
#define CLIENT_VERSION_REVISION 3
|
||||
#define CLIENT_VERSION_BUILD 50
|
||||
|
||||
//! Set to true for release, false for prerelease or test build
|
||||
@@ -40,7 +40,7 @@
|
||||
* Copyright year (2009-this)
|
||||
* Todo: update this when changing our copyright comments in the source
|
||||
*/
|
||||
#define COPYRIGHT_YEAR 2024
|
||||
#define COPYRIGHT_YEAR 2026
|
||||
|
||||
#endif //HAVE_CONFIG_H
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ bool sanity_test_range_fmt()
|
||||
{
|
||||
std::string test;
|
||||
try {
|
||||
test.at(1);
|
||||
(void)test.at(1);
|
||||
} catch (const std::out_of_range&) {
|
||||
return true;
|
||||
} catch (...) {
|
||||
|
||||
@@ -33,10 +33,13 @@
|
||||
#include "rand.h"
|
||||
#include "sha2.h"
|
||||
|
||||
#if USE_BIP39_CACHE
|
||||
|
||||
// BIP39_WORDS is used unconditionally by the wordlist helpers below, so it must
|
||||
// be defined even when the BIP39 cache is disabled (upstream places it inside
|
||||
// the cache block by mistake).
|
||||
int BIP39_WORDS = 2048;
|
||||
|
||||
#if USE_BIP39_CACHE
|
||||
|
||||
static int bip39_cache_index = 0;
|
||||
|
||||
static CONFIDENTIAL struct {
|
||||
|
||||
@@ -56,8 +56,10 @@
|
||||
#endif
|
||||
|
||||
// implement BIP39 caching
|
||||
// Disabled: caching keeps the plaintext mnemonic/passphrase/seed in a static
|
||||
// process-lifetime buffer, which we do not want in a wallet daemon.
|
||||
#ifndef USE_BIP39_CACHE
|
||||
#define USE_BIP39_CACHE 1
|
||||
#define USE_BIP39_CACHE 0
|
||||
#define BIP39_CACHE_SIZE 4
|
||||
#endif
|
||||
|
||||
|
||||
171
src/gtest/test_hdtransparent.cpp
Normal file
171
src/gtest/test_hdtransparent.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2016-2024 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
//
|
||||
// Tests for HD-derived transparent keys (m/44'/coin'/0'/0/i) and the
|
||||
// version-gated CHDChain serialization used to persist the transparent counter.
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "key.h"
|
||||
#include "chainparams.h"
|
||||
#include "streams.h"
|
||||
#include "uint256.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
#include "wallet/wallet.h"
|
||||
#include "wallet/walletdb.h"
|
||||
#include "zcash/zip32.h"
|
||||
|
||||
// Build an in-memory wallet with a known seed + hdChain so that the
|
||||
// HD-transparent path (IsHDTransparentEnabled) is active.
|
||||
static void LoadSeedForTest(CWallet& wallet, const HDSeed& seed)
|
||||
{
|
||||
wallet.LoadHDSeed(seed);
|
||||
CHDChain chain;
|
||||
chain.nVersion = CHDChain::VERSION_HD_TRANSPARENT;
|
||||
chain.seedFp = seed.Fingerprint();
|
||||
chain.nCreateTime = 1;
|
||||
wallet.SetHDChain(chain, true /* memonly */);
|
||||
}
|
||||
|
||||
// Same seed must reproduce the same transparent addresses in the same order:
|
||||
// this is the recovery guarantee that lets a seed-only restore find coinbase.
|
||||
TEST(hdtransparent_tests, DeterministicFromSeed)
|
||||
{
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
RawHDSeed rawSeed(HD_WALLET_SEED_LENGTH, 0x42);
|
||||
HDSeed seed(rawSeed);
|
||||
|
||||
std::vector<CKeyID> keysA;
|
||||
{
|
||||
CWallet wallet;
|
||||
LoadSeedForTest(wallet, seed);
|
||||
ASSERT_TRUE(wallet.IsHDTransparentEnabled());
|
||||
|
||||
LOCK(wallet.cs_wallet);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
CPubKey pk = wallet.GenerateNewKey();
|
||||
keysA.push_back(pk.GetID());
|
||||
|
||||
const CKeyMetadata& md = wallet.mapKeyMetadata[pk.GetID()];
|
||||
EXPECT_EQ(md.seedFp, seed.Fingerprint());
|
||||
EXPECT_EQ(md.hdKeypath, std::string("m/44'/141'/0'/0/") + std::to_string(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Fresh wallet, same seed -> identical keys.
|
||||
{
|
||||
CWallet wallet;
|
||||
LoadSeedForTest(wallet, seed);
|
||||
|
||||
LOCK(wallet.cs_wallet);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
CPubKey pk = wallet.GenerateNewKey();
|
||||
EXPECT_EQ(pk.GetID(), keysA[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pin the exact derivation path so it can never silently change.
|
||||
TEST(hdtransparent_tests, KnownDerivationPath)
|
||||
{
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
RawHDSeed rawSeed(HD_WALLET_SEED_LENGTH, 0x42);
|
||||
HDSeed seed(rawSeed);
|
||||
|
||||
// Independently derive m/44'/141'/0'/0/0.
|
||||
RawHDSeed raw = seed.RawSeed();
|
||||
CExtKey m, purpose, coinType, account, external, child;
|
||||
m.SetMaster(raw.data(), raw.size());
|
||||
m.Derive(purpose, 44 | BIP32_HARDENED_KEY_LIMIT);
|
||||
purpose.Derive(coinType, 141 | BIP32_HARDENED_KEY_LIMIT);
|
||||
coinType.Derive(account, 0 | BIP32_HARDENED_KEY_LIMIT);
|
||||
account.Derive(external, 0);
|
||||
external.Derive(child, 0);
|
||||
CKeyID expected = child.key.GetPubKey().GetID();
|
||||
|
||||
CWallet wallet;
|
||||
LoadSeedForTest(wallet, seed);
|
||||
LOCK(wallet.cs_wallet);
|
||||
CPubKey pk = wallet.GenerateNewKey();
|
||||
EXPECT_EQ(pk.GetID(), expected);
|
||||
}
|
||||
|
||||
// A pre-existing v1 CHDChain record (no transparent counter) must still
|
||||
// deserialize under v2 code, leaving transparentChildCounter at 0; and a v2
|
||||
// record must round-trip the counter.
|
||||
TEST(hdtransparent_tests, HDChainVersionCompat)
|
||||
{
|
||||
CHDChain v1;
|
||||
v1.nVersion = CHDChain::VERSION_HD_BASE; // 1: transparentChildCounter not serialized
|
||||
v1.seedFp = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
|
||||
v1.nCreateTime = 12345;
|
||||
v1.saplingAccountCounter = 7;
|
||||
|
||||
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
|
||||
ss << v1;
|
||||
|
||||
CHDChain out; // default-constructed: SetNull() zeroes transparentChildCounter
|
||||
ss >> out;
|
||||
EXPECT_EQ(out.nVersion, +CHDChain::VERSION_HD_BASE); // unary + -> rvalue, avoid ODR-use of static const
|
||||
EXPECT_EQ(out.seedFp, v1.seedFp);
|
||||
EXPECT_EQ(out.nCreateTime, (int64_t)12345);
|
||||
EXPECT_EQ(out.saplingAccountCounter, (uint32_t)7);
|
||||
EXPECT_EQ(out.transparentChildCounter, (uint32_t)0);
|
||||
|
||||
CHDChain v2;
|
||||
v2.nVersion = CHDChain::VERSION_HD_TRANSPARENT;
|
||||
v2.saplingAccountCounter = 3;
|
||||
v2.transparentChildCounter = 42;
|
||||
CDataStream ss2(SER_DISK, PROTOCOL_VERSION);
|
||||
ss2 << v2;
|
||||
|
||||
CHDChain out2;
|
||||
ss2 >> out2;
|
||||
EXPECT_EQ(out2.nVersion, +CHDChain::VERSION_HD_TRANSPARENT);
|
||||
EXPECT_EQ(out2.saplingAccountCounter, (uint32_t)3);
|
||||
EXPECT_EQ(out2.transparentChildCounter, (uint32_t)42);
|
||||
}
|
||||
|
||||
// Restoring from a 32-byte seed hex reproduces the same keys as the source
|
||||
// wallet, and refuses to run when a seed already exists.
|
||||
TEST(hdtransparent_tests, RestoreFromSeedHex)
|
||||
{
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
RawHDSeed rawSeed(HD_WALLET_SEED_LENGTH, 0x7a);
|
||||
HDSeed seed(rawSeed);
|
||||
std::string seedHex = HexStr(seed.RawSeed());
|
||||
|
||||
// Source wallet: derive some keys.
|
||||
std::vector<CKeyID> expected;
|
||||
{
|
||||
CWallet wallet;
|
||||
LoadSeedForTest(wallet, seed);
|
||||
LOCK(wallet.cs_wallet);
|
||||
for (int i = 0; i < 3; i++)
|
||||
expected.push_back(wallet.GenerateNewKey().GetID());
|
||||
}
|
||||
|
||||
// Restored wallet: inject the seed hex, pre-derive, and compare.
|
||||
{
|
||||
CWallet wallet;
|
||||
ASSERT_TRUE(wallet.SetHDSeedFromHex(seedHex));
|
||||
// Second attempt must fail: a seed already exists.
|
||||
EXPECT_FALSE(wallet.SetHDSeedFromHex(seedHex));
|
||||
|
||||
wallet.TopUpHDTransparentKeys(3, 1);
|
||||
LOCK(wallet.cs_wallet);
|
||||
for (int i = 0; i < 3; i++)
|
||||
EXPECT_TRUE(wallet.HaveKey(expected[i]));
|
||||
}
|
||||
|
||||
// Bad input is rejected.
|
||||
{
|
||||
CWallet wallet;
|
||||
EXPECT_FALSE(wallet.SetHDSeedFromHex("nothex"));
|
||||
EXPECT_FALSE(wallet.SetHDSeedFromHex("abcd")); // too short
|
||||
}
|
||||
}
|
||||
141
src/gtest/test_mnemonic_compat.cpp
Normal file
141
src/gtest/test_mnemonic_compat.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2016-2024 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
//
|
||||
// Proves that a BIP39 seed phrase produces the SAME transparent and shielded
|
||||
// addresses on the DragonX full node as in SilentDragonXLite. The proof chain:
|
||||
// phrase -> entropy (round-trip) -> 64-byte BIP39 seed (known-answer)
|
||||
// -> z/t addresses (wallet path == direct ZIP-32/BIP44 derivation).
|
||||
// The 64-byte seed is anchored to the well-known BIP39 value for the all-zero
|
||||
// "abandon...art" entropy with an EMPTY passphrase, which is exactly what
|
||||
// SilentDragonXLite's tiny-bip39 0.6.2 feeds into the same coin_type=141 paths.
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "chainparams.h"
|
||||
#include "key.h"
|
||||
#include "key_io.h"
|
||||
#include "util.h"
|
||||
#include "wallet/mnemonic.h"
|
||||
#include "wallet/wallet.h"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "zcash/zip32.h"
|
||||
|
||||
// The canonical 24-word phrase for 32 bytes of all-zero entropy.
|
||||
static const char* ABANDON_ART =
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon "
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon "
|
||||
"abandon abandon abandon abandon abandon abandon abandon art";
|
||||
|
||||
// The standard BIP39 seed for that phrase with an EMPTY passphrase
|
||||
// (PBKDF2-HMAC-SHA512, 2048 rounds, salt "mnemonic"). Matches tiny-bip39.
|
||||
static const char* SEED64_HEX =
|
||||
"408b285c123836004f4b8842c89324c1f01382450c0d439af345ba7fc49acf70"
|
||||
"5489c6fc77dbd4e3dc1dd8cc6bc9f043db8ada1e243c4a0eafb290d399480840";
|
||||
|
||||
// First shielded address for a 64-byte seed: m/32'/141'/0' default address.
|
||||
static std::string DeriveZAddrFromSeed64(RawHDSeed seed64)
|
||||
{
|
||||
HDSeed s(seed64);
|
||||
auto m = libzcash::SaplingExtendedSpendingKey::Master(s);
|
||||
auto xsk = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT)
|
||||
.Derive(141 | ZIP32_HARDENED_KEY_LIMIT)
|
||||
.Derive(0 | ZIP32_HARDENED_KEY_LIMIT);
|
||||
return EncodePaymentAddress(xsk.DefaultAddress());
|
||||
}
|
||||
|
||||
// First transparent address for a BIP32 master over `seedBytes`:
|
||||
// m/44'/141'/0'/0/0.
|
||||
static std::string DeriveTAddrFromSeedBytes(RawHDSeed seedBytes)
|
||||
{
|
||||
CExtKey master, purpose, coinType, account, external, child;
|
||||
master.SetMaster(seedBytes.data(), seedBytes.size());
|
||||
master.Derive(purpose, 44 | BIP32_HARDENED_KEY_LIMIT);
|
||||
purpose.Derive(coinType, 141 | BIP32_HARDENED_KEY_LIMIT);
|
||||
coinType.Derive(account, 0 | BIP32_HARDENED_KEY_LIMIT);
|
||||
account.Derive(external, 0);
|
||||
external.Derive(child, 0);
|
||||
return EncodeDestination(child.key.GetPubKey().GetID());
|
||||
}
|
||||
|
||||
// The 64-byte seed derived from the mnemonic must equal the known BIP39 value.
|
||||
// This is the cross-wallet anchor: SilentDragonXLite feeds the identical seed.
|
||||
TEST(mnemonic_compat, Bip39SeedKnownAnswer)
|
||||
{
|
||||
RawHDSeed entropy(32, 0);
|
||||
RawHDSeed seed64;
|
||||
ASSERT_TRUE(Bip39SeedFromEntropy(entropy, seed64));
|
||||
ASSERT_EQ(seed64.size(), (size_t)64);
|
||||
EXPECT_EQ(HexStr(seed64.begin(), seed64.end()), std::string(SEED64_HEX));
|
||||
}
|
||||
|
||||
TEST(mnemonic_compat, EntropyPhraseRoundTrip)
|
||||
{
|
||||
RawHDSeed zeros(32, 0);
|
||||
std::string phrase;
|
||||
ASSERT_TRUE(EntropyToMnemonic(zeros, phrase));
|
||||
EXPECT_EQ(phrase, std::string(ABANDON_ART));
|
||||
|
||||
EXPECT_TRUE(MnemonicIsValid(ABANDON_ART));
|
||||
|
||||
RawHDSeed entropy;
|
||||
ASSERT_TRUE(MnemonicToEntropy(ABANDON_ART, entropy));
|
||||
EXPECT_EQ(entropy.size(), (size_t)32);
|
||||
EXPECT_EQ(HexStr(entropy.begin(), entropy.end()), std::string(64, '0'));
|
||||
|
||||
// Bad checksum / unknown words are rejected.
|
||||
EXPECT_FALSE(MnemonicIsValid("abandon abandon abandon"));
|
||||
EXPECT_FALSE(MnemonicIsValid("clearly not valid bip39 words at all here"));
|
||||
RawHDSeed junk;
|
||||
EXPECT_FALSE(MnemonicToEntropy("clearly not valid bip39 words at all here", junk));
|
||||
}
|
||||
|
||||
// The wallet's mnemonic derivation must reproduce the exact addresses obtained
|
||||
// by driving ZIP-32 / BIP44 directly from the known 64-byte seed, and must be
|
||||
// deterministic across wallets.
|
||||
TEST(mnemonic_compat, WalletDerivesSdxliteAddresses)
|
||||
{
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
RawHDSeed zeros(32, 0), seed64;
|
||||
ASSERT_TRUE(Bip39SeedFromEntropy(zeros, seed64));
|
||||
const std::string expZ = DeriveZAddrFromSeed64(seed64);
|
||||
const std::string expT = DeriveTAddrFromSeedBytes(seed64);
|
||||
EXPECT_EQ(expZ.substr(0, 2), "zs"); // sapling HRP for mainnet
|
||||
|
||||
CWallet wallet;
|
||||
ASSERT_TRUE(wallet.SetHDSeedFromMnemonic(ABANDON_ART));
|
||||
ASSERT_TRUE(wallet.IsMnemonicSeed());
|
||||
{
|
||||
LOCK(wallet.cs_wallet);
|
||||
EXPECT_EQ(EncodePaymentAddress(wallet.GenerateNewSaplingZKey()), expZ);
|
||||
EXPECT_EQ(EncodeDestination(wallet.GenerateNewKey().GetID()), expT);
|
||||
}
|
||||
|
||||
// Same phrase, fresh wallet -> identical first addresses.
|
||||
CWallet wallet2;
|
||||
ASSERT_TRUE(wallet2.SetHDSeedFromMnemonic(ABANDON_ART));
|
||||
{
|
||||
LOCK(wallet2.cs_wallet);
|
||||
EXPECT_EQ(EncodePaymentAddress(wallet2.GenerateNewSaplingZKey()), expZ);
|
||||
EXPECT_EQ(EncodeDestination(wallet2.GenerateNewKey().GetID()), expT);
|
||||
}
|
||||
|
||||
// The phrase round-trips out of the wallet.
|
||||
std::string exported;
|
||||
ASSERT_TRUE(wallet.GetMnemonicPhrase(exported));
|
||||
EXPECT_EQ(exported, std::string(ABANDON_ART));
|
||||
}
|
||||
|
||||
// Negative: feeding the 32-byte entropy DIRECTLY as the seed (the classic
|
||||
// interop bug) must produce a different address than the 64-byte BIP39 seed.
|
||||
TEST(mnemonic_compat, RawEntropyDiffersFromMnemonicSeed)
|
||||
{
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
RawHDSeed zeros(32, 0), seed64;
|
||||
ASSERT_TRUE(Bip39SeedFromEntropy(zeros, seed64));
|
||||
|
||||
const std::string seedT = DeriveTAddrFromSeedBytes(seed64); // correct (64-byte)
|
||||
const std::string entropyT = DeriveTAddrFromSeedBytes(zeros); // wrong (32-byte)
|
||||
EXPECT_NE(seedT, entropyT);
|
||||
}
|
||||
172
src/gtest/test_randomx_preverify.cpp
Normal file
172
src/gtest/test_randomx_preverify.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) 2024-2026 The DragonX developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
//
|
||||
// Consensus-equivalence test for the parallel RandomX pre-verification pool. The pool is purely an
|
||||
// optimization: a block's transient fRandomXVerified flag (set by CRandomXCheck on a real hash
|
||||
// match) only lets CheckBlockHeader SKIP the inline recompute. So for every block the pool's
|
||||
// outcome must equal the inline CheckRandomXSolution outcome — `(preVerified || inline) == inline`.
|
||||
// We exercise a valid solution, a corrupted solution, and confirm the pool never "succeeds" on a
|
||||
// block the inline check would reject.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "arith_uint256.h"
|
||||
#include "chain.h"
|
||||
#include "chainparams.h"
|
||||
#include "pow.h"
|
||||
#include "primitives/block.h"
|
||||
#include "RandomX/src/randomx.h"
|
||||
#include "hush_defs.h"
|
||||
#include "util.h"
|
||||
#include <boost/thread.hpp>
|
||||
#include <memory>
|
||||
|
||||
extern int32_t HUSH_LOADINGBLOCKS;
|
||||
extern bool fCheckpointsEnabled;
|
||||
|
||||
namespace {
|
||||
// Compute the correct RandomX solution for a header using a standalone reference light VM, via the
|
||||
// SAME key + input helpers the validator uses (so the bytes/key match exactly).
|
||||
void ReferenceRandomXHash(const CBlockHeader& hdr, const std::string& key, unsigned char out[RANDOMX_HASH_SIZE])
|
||||
{
|
||||
std::vector<unsigned char> in = GetRandomXInput(hdr);
|
||||
randomx_flags flags = randomx_get_flags();
|
||||
randomx_cache* c = randomx_alloc_cache(flags);
|
||||
ASSERT_NE(c, nullptr);
|
||||
randomx_init_cache(c, key.data(), key.size());
|
||||
randomx_vm* vm = randomx_create_vm(flags, c, nullptr);
|
||||
ASSERT_NE(vm, nullptr);
|
||||
randomx_calculate_hash(vm, in.data(), in.size(), out);
|
||||
randomx_destroy_vm(vm);
|
||||
randomx_release_cache(c);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(RandomXPreVerify, ConsensusEquivalence)
|
||||
{
|
||||
// Force RandomX validation to actually run at low heights in the test harness.
|
||||
uint32_t savedAlgo = ASSETCHAINS_ALGO, savedRx = ASSETCHAINS_RANDOMX;
|
||||
int32_t savedVal = ASSETCHAINS_RANDOMX_VALIDATION, savedLoad = HUSH_LOADINGBLOCKS;
|
||||
bool savedCkpt = fCheckpointsEnabled;
|
||||
ASSETCHAINS_RANDOMX = 2; // a distinct nonzero algo id
|
||||
ASSETCHAINS_ALGO = ASSETCHAINS_RANDOMX;
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = 1; // enforce from height 1
|
||||
HUSH_LOADINGBLOCKS = 0; // not in initial-load (else RandomX skipped)
|
||||
fCheckpointsEnabled = false; // avoid the below-checkpoint skip
|
||||
|
||||
const int32_t height = 10; // < interval+lag -> the chain-params initial key (no chainActive needed)
|
||||
|
||||
CBlockHeader hdr;
|
||||
hdr.nVersion = 4;
|
||||
hdr.hashPrevBlock = uint256S("0x0000000000000000000000000000000000000000000000000000000000000001");
|
||||
hdr.hashMerkleRoot = uint256S("0x0000000000000000000000000000000000000000000000000000000000000002");
|
||||
hdr.hashFinalSaplingRoot = uint256S("0x0000000000000000000000000000000000000000000000000000000000000003");
|
||||
hdr.nTime = 1700000000;
|
||||
hdr.nBits = 0x200f0f0f;
|
||||
hdr.nNonce = uint256S("0x0000000000000000000000000000000000000000000000000000000000000004");
|
||||
|
||||
std::string key = GetRandomXKey(height);
|
||||
ASSERT_FALSE(key.empty());
|
||||
|
||||
unsigned char good[RANDOMX_HASH_SIZE];
|
||||
ReferenceRandomXHash(hdr, key, good);
|
||||
|
||||
// Run the pool path synchronously on this thread (CRandomXCheck creates its own thread_local VM).
|
||||
auto poolVerifies = [&](const CBlockHeader& h) -> bool {
|
||||
RandomXValidatorPrepareKey(key); // load the shared cache with this key
|
||||
bool slot = false;
|
||||
CRandomXCheck chk(key, GetRandomXInput(h), h.nSolution.data(), &slot);
|
||||
chk();
|
||||
return slot;
|
||||
};
|
||||
|
||||
// Case 1 — valid solution: both inline and pool accept; equivalence holds.
|
||||
hdr.nSolution.assign(good, good + RANDOMX_HASH_SIZE);
|
||||
EXPECT_TRUE(CheckRandomXSolution(&hdr, height));
|
||||
EXPECT_TRUE(poolVerifies(hdr));
|
||||
EXPECT_EQ(poolVerifies(hdr) || CheckRandomXSolution(&hdr, height), CheckRandomXSolution(&hdr, height));
|
||||
|
||||
// Case 2 — corrupted solution: both reject; the pool must NOT set verified.
|
||||
{
|
||||
CBlockHeader bad = hdr;
|
||||
bad.nSolution[0] ^= 0xff;
|
||||
EXPECT_FALSE(CheckRandomXSolution(&bad, height));
|
||||
EXPECT_FALSE(poolVerifies(bad));
|
||||
EXPECT_EQ(poolVerifies(bad) || CheckRandomXSolution(&bad, height), CheckRandomXSolution(&bad, height));
|
||||
}
|
||||
|
||||
// Case 3 — a verified flag on the block lets CheckBlockHeader skip, but verified is only ever set
|
||||
// by a real hash match, so it can never mask an invalid block. (Pool returns false for the bad
|
||||
// block above, so its fRandomXVerified stays false and the inline path rejects it at connect.)
|
||||
|
||||
ASSETCHAINS_ALGO = savedAlgo; ASSETCHAINS_RANDOMX = savedRx;
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = savedVal; HUSH_LOADINGBLOCKS = savedLoad;
|
||||
fCheckpointsEnabled = savedCkpt;
|
||||
}
|
||||
|
||||
// A/B: serial inline verification (single VM) vs the parallel pool (worker threads). Directly
|
||||
// measures the speedup the pool delivers. We don't care about validity here (mismatched solutions
|
||||
// still cost a full hash), only wall-clock. parallel must beat serial whenever >1 core is used.
|
||||
TEST(RandomXPreVerify, ParallelSpeedup)
|
||||
{
|
||||
uint32_t savedAlgo = ASSETCHAINS_ALGO, savedRx = ASSETCHAINS_RANDOMX;
|
||||
int32_t savedVal = ASSETCHAINS_RANDOMX_VALIDATION, savedLoad = HUSH_LOADINGBLOCKS;
|
||||
bool savedCkpt = fCheckpointsEnabled;
|
||||
ASSETCHAINS_RANDOMX = 2; ASSETCHAINS_ALGO = ASSETCHAINS_RANDOMX;
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = 1; HUSH_LOADINGBLOCKS = 0; fCheckpointsEnabled = false;
|
||||
|
||||
const int32_t height = 10;
|
||||
std::string key = GetRandomXKey(height);
|
||||
ASSERT_FALSE(key.empty());
|
||||
ASSERT_TRUE(RandomXValidatorPrepareKey(key));
|
||||
|
||||
const int M = 16; // blocks to verify in the window
|
||||
std::vector<CBlockHeader> hdrs(M);
|
||||
for (int i = 0; i < M; i++) {
|
||||
hdrs[i].nVersion = 4;
|
||||
hdrs[i].nTime = 1700000000 + i;
|
||||
hdrs[i].nBits = 0x200f0f0f;
|
||||
hdrs[i].nNonce = ArithToUint256(arith_uint256(i + 1)); // distinct inputs
|
||||
hdrs[i].nSolution.assign(RANDOMX_HASH_SIZE, 0); // arbitrary; we time the hash
|
||||
}
|
||||
|
||||
// Serial baseline: inline single-VM verification (each call hashes, then mismatches -> false).
|
||||
int64_t t0 = GetTimeMicros();
|
||||
for (int i = 0; i < M; i++) CheckRandomXSolution(&hdrs[i], height);
|
||||
int64_t serialUs = GetTimeMicros() - t0;
|
||||
|
||||
// Parallel: spawn K-1 workers + the master (this thread) joining via Wait().
|
||||
int K = std::min(8, std::max(2, (int)boost::thread::hardware_concurrency()));
|
||||
boost::thread_group workers;
|
||||
for (int i = 0; i < K - 1; i++) workers.create_thread(&ThreadRandomXVerify);
|
||||
|
||||
std::unique_ptr<bool[]> slots(new bool[M]());
|
||||
std::vector<CRandomXCheck> checks;
|
||||
checks.reserve(M);
|
||||
for (int i = 0; i < M; i++)
|
||||
checks.push_back(CRandomXCheck(key, GetRandomXInput(hdrs[i]), hdrs[i].nSolution.data(), &slots[i]));
|
||||
|
||||
int64_t t1 = GetTimeMicros();
|
||||
{
|
||||
CCheckQueueControl<CRandomXCheck> control(&rxCheckQueue);
|
||||
control.Add(checks);
|
||||
control.Wait();
|
||||
}
|
||||
int64_t parallelUs = GetTimeMicros() - t1;
|
||||
|
||||
workers.interrupt_all();
|
||||
workers.join_all();
|
||||
|
||||
printf("[ RandomX A/B ] %d blocks: serial(1 VM)=%ldms, parallel(%d threads)=%ldms, speedup=%.1fx\n",
|
||||
M, (long)(serialUs / 1000), K, (long)(parallelUs / 1000),
|
||||
(double)serialUs / (double)std::max<int64_t>(1, parallelUs));
|
||||
|
||||
EXPECT_LT(parallelUs, serialUs); // parallel must be faster than serial on a multi-core box
|
||||
|
||||
ASSETCHAINS_ALGO = savedAlgo; ASSETCHAINS_RANDOMX = savedRx;
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = savedVal; HUSH_LOADINGBLOCKS = savedLoad;
|
||||
fCheckpointsEnabled = savedCkpt;
|
||||
}
|
||||
14
src/hush.h
14
src/hush.h
@@ -510,7 +510,9 @@ int32_t hush_validate_chain(uint256 srchash,int32_t notarized_height)
|
||||
return(0);
|
||||
if ( IsInitialBlockDownload() == 0 && ((pindex= hush_getblockindex(srchash)) == 0 || pindex->GetHeight() != notarized_height) )
|
||||
{
|
||||
fprintf(stderr,"%s: Not in IBD, height=%d\n", __func__, pindex->GetHeight() );
|
||||
// SECURITY (null-deref crash DoS): this branch is entered when pindex==0 (srchash, taken
|
||||
// from an attacker-controlled notarization OP_RETURN, is not a known block). Guard the deref.
|
||||
fprintf(stderr,"%s: Not in IBD, height=%d\n", __func__, pindex != 0 ? pindex->GetHeight() : -1 );
|
||||
if ( sp->NOTARIZED_HEIGHT > 0 && sp->NOTARIZED_HEIGHT < notarized_height )
|
||||
rewindtarget = sp->NOTARIZED_HEIGHT - 1;
|
||||
else if ( notarized_height > 101 )
|
||||
@@ -589,6 +591,14 @@ int32_t hush_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryi
|
||||
opretlen += (scriptbuf[len++] << 8);
|
||||
}
|
||||
opoffset = len;
|
||||
// SECURITY (Finding #4): opretlen is attacker-controlled (up to 65535 via OP_PUSHDATA2)
|
||||
// and was previously used with no bounds check. scriptbuf is a fixed DRAGON_MAXSCRIPTSIZE
|
||||
// stack buffer in hush_connectblock, so an oversized opretlen drives out-of-bounds reads in
|
||||
// the downstream 'K'/KV and notarization paths (persisted to disk, leaked via kvsearch RPC,
|
||||
// reliable crash on block connect). Reject any opret claiming more bytes than actually
|
||||
// remain in the real script; this mirrors the no-OP_RETURN fall-through so nothing valid changes.
|
||||
if ( opretlen < 0 || opretlen > scriptlen - len )
|
||||
return(notaryid);
|
||||
matched = 0;
|
||||
if ( SMART_CHAIN_SYMBOL[0] == 0 )
|
||||
{
|
||||
@@ -931,7 +941,7 @@ int32_t hush_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block)
|
||||
if ( len >= sizeof(uint32_t) && len <= sizeof(scriptbuf) )
|
||||
{
|
||||
memcpy(scriptbuf,(uint8_t *)&block.vtx[i].vout[j].scriptPubKey[0],len);
|
||||
if ( len == 35 && scriptbuf[0] == 33 && scriptbuf[34] == 0xac )
|
||||
if ( len == 35 && scriptbuf[0] == 33 && scriptbuf[34] == 0xac && numvalid < (int32_t)(sizeof(pubkeys)/sizeof(pubkeys[0])) )
|
||||
{
|
||||
memcpy(pubkeys[numvalid++],scriptbuf+1,33);
|
||||
for (k=0; k<33; k++)
|
||||
|
||||
@@ -580,70 +580,103 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
|
||||
char pchBuf[0x10000];
|
||||
bool bIsSSL = false;
|
||||
int nBytes = 0, nRet = 0;
|
||||
// Drain the socket in a bounded loop rather than one read per select pass: a single
|
||||
// 64K read per pass underfills high-bandwidth/high-latency links. Cap the reads per
|
||||
// pass and honor the receive-flood back-pressure so one peer can neither exhaust
|
||||
// memory nor starve other peers within this pass.
|
||||
int nDrainReads = 0;
|
||||
const int MAX_DRAIN_READS = 16; // up to ~1 MiB per peer per pass (fairness across peers)
|
||||
// Pre-read back-pressure: gate on the flood ceiling BEFORE each read so the per-peer
|
||||
// recv buffer high-water stays at ReceiveFloodSize()+one read (matching the select()
|
||||
// FD_SET gate), and track bytes locally to avoid the O(n) GetTotalRecvSize() per pass.
|
||||
const int64_t nRecvBase = (int64_t)pnode->GetTotalRecvSize();
|
||||
int64_t nPassBytes = 0;
|
||||
bool fKeepReading = true;
|
||||
while (fKeepReading) {
|
||||
int nSSLPending = 0;
|
||||
if (nRecvBase + nPassBytes > (int64_t)ReceiveFloodSize())
|
||||
break;
|
||||
{
|
||||
LOCK(pnode->cs_hSocket);
|
||||
|
||||
{
|
||||
LOCK(pnode->cs_hSocket);
|
||||
if (pnode->hSocket == INVALID_SOCKET) {
|
||||
LogPrint("tls", "Receive: connection with %s is already closed\n", pnode->addr.ToString());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pnode->hSocket == INVALID_SOCKET) {
|
||||
LogPrint("tls", "Receive: connection with %s is already closed\n", pnode->addr.ToString());
|
||||
return -1;
|
||||
bIsSSL = (pnode->ssl != NULL);
|
||||
|
||||
if (bIsSSL) {
|
||||
wolfSSL_ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread
|
||||
nBytes = wolfSSL_read(pnode->ssl, pchBuf, sizeof(pchBuf));
|
||||
nRet = wolfSSL_get_error(pnode->ssl, nBytes);
|
||||
// Capture TLS buffered-byte count while still under cs_hSocket; the drain-continuation
|
||||
// check below runs unlocked, so touching pnode->ssl there would race with
|
||||
// SocketSendData / CloseSocketDisconnect (which free ssl) -> data race / use-after-free.
|
||||
nSSLPending = wolfSSL_pending(pnode->ssl);
|
||||
} else {
|
||||
nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
|
||||
nRet = WSAGetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
bIsSSL = (pnode->ssl != NULL);
|
||||
|
||||
if (bIsSSL) {
|
||||
wolfSSL_ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread
|
||||
nBytes = wolfSSL_read(pnode->ssl, pchBuf, sizeof(pchBuf));
|
||||
nRet = wolfSSL_get_error(pnode->ssl, nBytes);
|
||||
} else {
|
||||
nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
|
||||
nRet = WSAGetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
if (nBytes > 0) {
|
||||
if (!pnode->ReceiveMsgBytes(pchBuf, nBytes))
|
||||
pnode->CloseSocketDisconnect();
|
||||
pnode->nLastRecv = GetTime();
|
||||
pnode->nRecvBytes += nBytes;
|
||||
pnode->RecordBytesRecv(nBytes);
|
||||
} else if (nBytes == 0) {
|
||||
|
||||
if (bIsSSL) {
|
||||
unsigned long error = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_read err: %s\n",
|
||||
__FILE__, __func__, __LINE__, error_str);
|
||||
}
|
||||
// socket closed gracefully (peer disconnected)
|
||||
if (!pnode->fDisconnect)
|
||||
LogPrint("tls", "socket closed (%s)\n", pnode->addr.ToString());
|
||||
pnode->CloseSocketDisconnect();
|
||||
|
||||
} else if (nBytes < 0) {
|
||||
// error
|
||||
if (bIsSSL) {
|
||||
if (nRet != WOLFSSL_ERROR_WANT_READ && nRet != WOLFSSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
if (!pnode->fDisconnect)
|
||||
LogPrintf("TLS: ERROR: SSL_read %s\n", ERR_error_string(nRet, NULL));
|
||||
if (nBytes > 0) {
|
||||
if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) {
|
||||
pnode->CloseSocketDisconnect();
|
||||
fKeepReading = false;
|
||||
}
|
||||
pnode->nLastRecv = GetTime();
|
||||
pnode->nRecvBytes += nBytes;
|
||||
pnode->RecordBytesRecv(nBytes);
|
||||
nPassBytes += nBytes;
|
||||
// Keep draining only while the socket likely has more data (we filled the
|
||||
// buffer, or TLS has buffered decrypted bytes) and within the per-pass cap.
|
||||
// The flood ceiling is enforced pre-read at the top of the loop.
|
||||
if (fKeepReading) {
|
||||
bool fMore = (nBytes == (int)sizeof(pchBuf)) || (bIsSSL && nSSLPending > 0);
|
||||
if (!fMore || ++nDrainReads >= MAX_DRAIN_READS)
|
||||
fKeepReading = false;
|
||||
}
|
||||
} else if (nBytes == 0) {
|
||||
|
||||
if (bIsSSL) {
|
||||
unsigned long error = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_read - code[0x%x], err: %s\n",
|
||||
__FILE__, __func__, __LINE__, nRet, error_str);
|
||||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_read err: %s\n",
|
||||
__FILE__, __func__, __LINE__, error_str);
|
||||
}
|
||||
// socket closed gracefully (peer disconnected)
|
||||
if (!pnode->fDisconnect)
|
||||
LogPrint("tls", "socket closed (%s)\n", pnode->addr.ToString());
|
||||
pnode->CloseSocketDisconnect();
|
||||
fKeepReading = false;
|
||||
|
||||
} else if (nBytes < 0) {
|
||||
// error
|
||||
if (bIsSSL) {
|
||||
if (nRet != WOLFSSL_ERROR_WANT_READ && nRet != WOLFSSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
if (!pnode->fDisconnect)
|
||||
LogPrintf("TLS: ERROR: SSL_read %s\n", ERR_error_string(nRet, NULL));
|
||||
pnode->CloseSocketDisconnect();
|
||||
|
||||
unsigned long error = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_read - code[0x%x], err: %s\n",
|
||||
__FILE__, __func__, __LINE__, nRet, error_str);
|
||||
|
||||
} else {
|
||||
// preventive measure from exhausting CPU usage
|
||||
MilliSleep(1); // 1 msec
|
||||
}
|
||||
} else {
|
||||
// preventive measure from exhausting CPU usage
|
||||
MilliSleep(1); // 1 msec
|
||||
}
|
||||
} else {
|
||||
if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS) {
|
||||
if (!pnode->fDisconnect)
|
||||
LogPrintf("TLS: ERROR: socket recv %s\n", NetworkErrorString(nRet));
|
||||
pnode->CloseSocketDisconnect();
|
||||
if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS) {
|
||||
if (!pnode->fDisconnect)
|
||||
LogPrintf("TLS: ERROR: socket recv %s\n", NetworkErrorString(nRet));
|
||||
pnode->CloseSocketDisconnect();
|
||||
}
|
||||
}
|
||||
fKeepReading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *hushnotarized_hei
|
||||
params[0] = 0;
|
||||
*hushnotarized_heightp = 0;
|
||||
if ( strcmp(dest,"HUSH3") == 0 ) {
|
||||
port = HUSH3_PORT;
|
||||
port = DRAGONX_PORT;
|
||||
userpass = HUSHUSERPASS;
|
||||
} else if ( strcmp(dest,"BTC") == 0 )
|
||||
{
|
||||
@@ -498,7 +498,7 @@ int32_t hush_verifynotarization(char *symbol,char *dest,int32_t height,int32_t N
|
||||
{
|
||||
if ( SMART_CHAIN_SYMBOL[0] != 0 )
|
||||
{
|
||||
jsonstr = hush_issuemethod(HUSHUSERPASS,(char *)"getrawtransaction",params,HUSH3_PORT);
|
||||
jsonstr = hush_issuemethod(HUSHUSERPASS,(char *)"getrawtransaction",params,DRAGONX_PORT);
|
||||
//printf("userpass.(%s) got (%s)\n",HUSHUSERPASS,jsonstr);
|
||||
}
|
||||
}//else jsonstr = _dex_getrawtransaction();
|
||||
@@ -1693,6 +1693,11 @@ int32_t hush_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
|
||||
fprintf(stderr,"hush_checkPOW slowflag.%d ht.%d CheckEquihashSolution failed\n",slowflag,height);
|
||||
return(-1);
|
||||
}
|
||||
if ( !CheckRandomXSolution(pblock, height) )
|
||||
{
|
||||
fprintf(stderr,"hush_checkPOW slowflag.%d ht.%d CheckRandomXSolution failed\n",slowflag,height);
|
||||
return(-1);
|
||||
}
|
||||
hash = pblock->GetHash();
|
||||
bnTarget.SetCompact(pblock->nBits,&fNegative,&fOverflow);
|
||||
bhash = UintToArith256(hash);
|
||||
|
||||
@@ -568,6 +568,7 @@ extern uint64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_FOUNDERS_REWARD;
|
||||
extern int32_t ASSETCHAINS_LWMAPOS, ASSETCHAINS_SAPLING, ASSETCHAINS_OVERWINTER,ASSETCHAINS_BLOCKTIME;
|
||||
extern uint64_t ASSETCHAINS_TIMELOCKGTE;
|
||||
extern uint32_t ASSETCHAINS_ALGO,ASSETCHAINS_EQUIHASH,ASSETCHAINS_RANDOMX, HUSH_INITDONE;
|
||||
extern int32_t ASSETCHAINS_RANDOMX_VALIDATION;
|
||||
extern int32_t HUSH_MININGTHREADS,HUSH_LONGESTCHAIN,ASSETCHAINS_SEED,IS_HUSH_NOTARY,USE_EXTERNAL_PUBKEY,HUSH_CHOSEN_ONE,HUSH_ON_DEMAND,HUSH_PASSPORT_INITDONE,ASSETCHAINS_STAKED,HUSH_NSPV;
|
||||
extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_LASTERA,ASSETCHAINS_CBOPRET;
|
||||
extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS+1], ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS+1], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[],ASSETCHAINS_NK[2];
|
||||
|
||||
@@ -93,6 +93,7 @@ uint64_t ASSETCHAINS_NONCEMASK[] = {0xffff};
|
||||
uint32_t ASSETCHAINS_NONCESHIFT[] = {32};
|
||||
uint32_t ASSETCHAINS_HASHESPERROUND[] = {1};
|
||||
uint32_t ASSETCHAINS_ALGO = _ASSETCHAINS_EQUIHASH;
|
||||
int32_t ASSETCHAINS_RANDOMX_VALIDATION = -1; // activation height for RandomX validation (-1 = disabled)
|
||||
// min diff returned from GetNextWorkRequired needs to be added here for each algo, so they can work with ac_staked.
|
||||
uint32_t ASSETCHAINS_MINDIFF[] = {537857807};
|
||||
int32_t ASSETCHAINS_LWMAPOS = 0; // percentage of blocks should be PoS
|
||||
@@ -101,7 +102,7 @@ int32_t ASSETCHAINS_OVERWINTER = -1;
|
||||
int32_t ASSETCHAINS_STAKED;
|
||||
uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY = 10,ASSETCHAINS_FOUNDERS_REWARD;
|
||||
uint32_t HUSH_INITDONE;
|
||||
char HUSHUSERPASS[8192+512+1],BTCUSERPASS[8192]; uint16_t HUSH3_PORT = 18031,BITCOIND_RPCPORT = 18031;
|
||||
char HUSHUSERPASS[8192+512+1],BTCUSERPASS[8192]; uint16_t DRAGONX_PORT = 21769,BITCOIND_RPCPORT = 21769;
|
||||
uint64_t PENDING_HUSH_TX;
|
||||
extern int32_t HUSH_LOADINGBLOCKS;
|
||||
unsigned int MAX_BLOCK_SIGOPS = 20000;
|
||||
|
||||
@@ -324,7 +324,7 @@ int32_t NSPV_mempoolfuncs(bits256 *satoshisp,int32_t *vindexp,std::vector<uint25
|
||||
CScript scriptPubKey = tx.vout[tx.vout.size()-1].scriptPubKey;
|
||||
if ( GetOpReturnData(scriptPubKey,vopret) != 0 )
|
||||
{
|
||||
if ( vopret[0] == evalcode && vopret[1] == func )
|
||||
if ( vopret.size() >= 2 && vopret[0] == evalcode && vopret[1] == func )
|
||||
{
|
||||
txids.push_back(hash);
|
||||
num++;
|
||||
@@ -417,7 +417,9 @@ int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n)
|
||||
{
|
||||
request.read(json,n);
|
||||
jreq.parse(request);
|
||||
strcpy(ptr->method,jreq.strMethod.c_str());
|
||||
// SECURITY (stack overflow): strMethod is attacker-controlled; bound the copy to the fixed buffer.
|
||||
strncpy(ptr->method,jreq.strMethod.c_str(),sizeof(ptr->method)-1);
|
||||
ptr->method[sizeof(ptr->method)-1] = '\0';
|
||||
len+=sizeof(ptr->method);
|
||||
std::map<std::string, bool>::iterator it = nspv_remote_commands.find(jreq.strMethod);
|
||||
if (it==nspv_remote_commands.end())
|
||||
@@ -438,8 +440,10 @@ int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n)
|
||||
{
|
||||
rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
|
||||
response=rpc_result.write();
|
||||
memcpy(ptr->json,response.c_str(),response.size());
|
||||
len+=response.size();
|
||||
// SECURITY (stack overflow): clamp to the fixed json buffer.
|
||||
size_t rlen = response.size(); if ( rlen > sizeof(ptr->json) ) rlen = sizeof(ptr->json);
|
||||
memcpy(ptr->json,response.c_str(),rlen);
|
||||
len+=rlen;
|
||||
return (len);
|
||||
}
|
||||
else throw JSONRPCError(RPC_MISC_ERROR, "Error in executing RPC on remote node");
|
||||
@@ -459,8 +463,10 @@ int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n)
|
||||
rpc_result = JSONRPCReplyObj(NullUniValue,JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
|
||||
response=rpc_result.write();
|
||||
}
|
||||
memcpy(ptr->json,response.c_str(),response.size());
|
||||
len+=response.size();
|
||||
// SECURITY (stack overflow): the error path echoes attacker-controlled jreq.id; clamp to the buffer.
|
||||
size_t rlen = response.size(); if ( rlen > sizeof(ptr->json) ) rlen = sizeof(ptr->json);
|
||||
memcpy(ptr->json,response.c_str(),rlen);
|
||||
len+=rlen;
|
||||
return (len);
|
||||
}
|
||||
|
||||
@@ -651,10 +657,10 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
||||
if ( timestamp > pfrom->prevtimes[ind] )
|
||||
{
|
||||
struct NSPV_utxosresp U;
|
||||
if ( len < 64+5 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
||||
if ( len >= 2 && len < 64+5 && request[1] < 64 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
||||
{
|
||||
int32_t skipcount = 0; char coinaddr[64]; uint8_t filter; uint8_t isCC = 0;
|
||||
memcpy(coinaddr,&request[2],request[1]);
|
||||
memcpy(coinaddr,&request[2],request[1]); // request[1] < 64 bounds the copy + the terminator write
|
||||
coinaddr[request[1]] = 0;
|
||||
if ( request[1] == len-3 )
|
||||
isCC = (request[len-1] != 0);
|
||||
@@ -691,10 +697,10 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
||||
if ( timestamp > pfrom->prevtimes[ind] )
|
||||
{
|
||||
struct NSPV_txidsresp T;
|
||||
if ( len < 64+5 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
||||
if ( len >= 2 && len < 64+5 && request[1] < 64 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
||||
{
|
||||
int32_t skipcount = 0; char coinaddr[64]; uint32_t filter; uint8_t isCC = 0;
|
||||
memcpy(coinaddr,&request[2],request[1]);
|
||||
memcpy(coinaddr,&request[2],request[1]); // request[1] < 64 bounds the copy + the terminator write
|
||||
coinaddr[request[1]] = 0;
|
||||
if ( request[1] == len-3 )
|
||||
isCC = (request[len-1] != 0);
|
||||
@@ -724,7 +730,7 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
||||
}
|
||||
NSPV_txidsresp_purge(&T);
|
||||
}
|
||||
} else fprintf(stderr,"len.%d req1.%d\n",len,request[1]);
|
||||
} else fprintf(stderr,"len.%d\n",len);
|
||||
}
|
||||
}
|
||||
else if ( request[0] == NSPV_MEMPOOL )
|
||||
@@ -732,7 +738,7 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
||||
if ( timestamp > pfrom->prevtimes[ind] )
|
||||
{
|
||||
struct NSPV_mempoolresp M; char coinaddr[64];
|
||||
if ( len < sizeof(M)+64 )
|
||||
if ( len >= 40 && len < sizeof(M)+64 ) // SECURITY: lower bound guards the fixed-offset reads request[1..39]
|
||||
{
|
||||
int32_t vout; uint256 txid; uint8_t funcid,isCC = 0;
|
||||
n = 1;
|
||||
@@ -741,7 +747,7 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
||||
n += dragon_rwnum(0,&request[n],sizeof(vout),&vout);
|
||||
n += dragon_rwbignum(0,&request[n],sizeof(txid),(uint8_t *)&txid);
|
||||
slen = request[n++];
|
||||
if ( slen < 63 )
|
||||
if ( slen < 63 && n + slen <= len ) // SECURITY: bound the memcpy source read within request
|
||||
{
|
||||
memcpy(coinaddr,&request[n],slen), n += slen;
|
||||
coinaddr[slen] = 0;
|
||||
@@ -761,7 +767,7 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
||||
NSPV_mempoolresp_purge(&M);
|
||||
}
|
||||
}
|
||||
} else fprintf(stderr,"len.%d req1.%d\n",len,request[1]);
|
||||
} else fprintf(stderr,"len.%d\n",len);
|
||||
}
|
||||
}
|
||||
else if ( request[0] == NSPV_NTZS )
|
||||
|
||||
109
src/hush_utils.h
109
src/hush_utils.h
@@ -1413,20 +1413,20 @@ void hush_configfile(char *symbol,uint16_t rpcport)
|
||||
#ifdef _WIN32
|
||||
while ( fname[strlen(fname)-1] != '\\' )
|
||||
fname[strlen(fname)-1] = 0;
|
||||
strcat(fname,"HUSH3.conf");
|
||||
strcat(fname,"DRAGONX.conf");
|
||||
#else
|
||||
while ( fname[strlen(fname)-1] != '/' )
|
||||
fname[strlen(fname)-1] = 0;
|
||||
#ifdef __APPLE__
|
||||
strcat(fname,"HUSH3.conf");
|
||||
strcat(fname,"DRAGONX.conf");
|
||||
#else
|
||||
strcat(fname,"HUSH3.conf");
|
||||
strcat(fname,"DRAGONX.conf");
|
||||
#endif
|
||||
#endif
|
||||
if ( (fp= fopen(fname,"rb")) != 0 )
|
||||
{
|
||||
if ( (hushport= _hush_userpass(username,password,fp)) != 0 )
|
||||
HUSH3_PORT = hushport;
|
||||
DRAGONX_PORT = hushport;
|
||||
sprintf(HUSHUSERPASS,"%s:%s",username,password);
|
||||
fclose(fp);
|
||||
//printf("HUSH.(%s) -> userpass.(%s)\n",fname,HUSHUSERPASS);
|
||||
@@ -1790,38 +1790,28 @@ void hush_args(char *argv0)
|
||||
}
|
||||
|
||||
|
||||
name = GetArg("-ac_name","HUSH3");
|
||||
name = GetArg("-ac_name","DRAGONX");
|
||||
fprintf(stderr,".oO Starting %s Full Node (Extreme Privacy!) with genproc=%d notary=%d\n",name.c_str(),HUSH_MININGTHREADS, IS_HUSH_NOTARY);
|
||||
|
||||
vector<string> HUSH_nodes = {};
|
||||
// Only HUSH3 uses these by default, other HACs must opt-in via -connect/-addnode
|
||||
const bool ishush3 = strncmp(name.c_str(), "HUSH3",5) == 0 ? true : false;
|
||||
vector<string> DRAGONX_nodes = {};
|
||||
// Only DRAGONX connects to these by default, other chains must opt-in via -connect/-addnode
|
||||
const bool isdragonx = strncmp(name.c_str(), "DRAGONX",7) == 0 ? true : false;
|
||||
|
||||
LogPrint("net", "%s: ishush3=%d\n", __func__, ishush3);
|
||||
if (ishush3) {
|
||||
HUSH_nodes = {"node1.hush.is","node2.hush.is","node3.hush.is",
|
||||
"node4.hush.is","node5.hush.is","node6.hush.is",
|
||||
"node7.hush.is","node8.hush.is",
|
||||
"178.250.189.141",
|
||||
"31.202.19.157",
|
||||
"45.132.75.69",
|
||||
"45.63.58.167",
|
||||
"b2dln7mw7ydnuopls444tuixujhcw5kn5o22cna6gqfmw2fl6drb5nad.onion",
|
||||
"dslbaa5gut5kapqtd44pbg65tpl5ydsamfy62hjbldhfsvk64qs57pyd.onion",
|
||||
"vsqdumnh5khjbrzlxoeucbkiuaictdzyc3ezjpxpp2ph3gfwo2ptjmyd.onion",
|
||||
"plrobkepqjxs2cmig273mxnqh3qhuhdaioyb2n5kafn264ramb7tqxid.onion"
|
||||
LogPrint("net", "%s: isdragonx=%d\n", __func__, isdragonx);
|
||||
if (isdragonx) {
|
||||
DRAGONX_nodes = {"node1.dragonx.is","node2.dragonx.is","node3.dragonx.is",
|
||||
"node4.dragonx.is","node5.dragonx.is"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
vector<string> more_nodes = mapMultiArgs["-addnode"];
|
||||
if (more_nodes.size() > 0) {
|
||||
fprintf(stderr,"%s: Adding %lu more nodes via custom -addnode arguments\n", __func__, more_nodes.size() );
|
||||
}
|
||||
// Add default HUSH nodes after custom addnodes, if applicable
|
||||
if(HUSH_nodes.size() > 0) {
|
||||
LogPrint("net", "%s: adding %d HUSH3 hostname-based nodes\n", __func__, HUSH_nodes.size() );
|
||||
more_nodes.insert( more_nodes.end(), HUSH_nodes.begin(), HUSH_nodes.end() );
|
||||
// Add default DRAGONX nodes after custom addnodes, if applicable
|
||||
if(DRAGONX_nodes.size() > 0) {
|
||||
LogPrint("net", "%s: adding %d DRAGONX hostname-based nodes\n", __func__, DRAGONX_nodes.size() );
|
||||
more_nodes.insert( more_nodes.end(), DRAGONX_nodes.begin(), DRAGONX_nodes.end() );
|
||||
}
|
||||
|
||||
mapMultiArgs["-addnode"] = more_nodes;
|
||||
@@ -1830,10 +1820,15 @@ void hush_args(char *argv0)
|
||||
WITNESS_CACHE_SIZE = MAX_REORG_LENGTH+10;
|
||||
ASSETCHAINS_CC = GetArg("-ac_cc",0);
|
||||
HUSH_CCACTIVATE = GetArg("-ac_ccactivate",0);
|
||||
ASSETCHAINS_BLOCKTIME = GetArg("-ac_blocktime",60);
|
||||
// We do not support ac_public=1 chains, Hush is a platform for privacy
|
||||
|
||||
// Set defaults based on chain
|
||||
int default_blocktime = isdragonx ? 36 : 60;
|
||||
int default_private = isdragonx ? 1 : 0;
|
||||
|
||||
ASSETCHAINS_BLOCKTIME = GetArg("-ac_blocktime", default_blocktime);
|
||||
// We do not support ac_public=1 chains, DragonX is a platform for privacy
|
||||
ASSETCHAINS_PUBLIC = 0;
|
||||
ASSETCHAINS_PRIVATE = GetArg("-ac_private",0);
|
||||
ASSETCHAINS_PRIVATE = GetArg("-ac_private", default_private);
|
||||
HUSH_SNAPSHOT_INTERVAL = GetArg("-ac_snapshot",0);
|
||||
Split(GetArg("-ac_nk",""), sizeof(ASSETCHAINS_NK)/sizeof(*ASSETCHAINS_NK), ASSETCHAINS_NK, 0);
|
||||
|
||||
@@ -1871,7 +1866,9 @@ void hush_args(char *argv0)
|
||||
ASSETCHAINS_EARLYTXIDCONTRACT = GetArg("-ac_earlytxidcontract",0);
|
||||
if ( name.c_str()[0] != 0 )
|
||||
{
|
||||
std::string selectedAlgo = GetArg("-ac_algo", std::string(ASSETCHAINS_ALGORITHMS[0]));
|
||||
// Default algo is randomx for DRAGONX, equihash for others
|
||||
std::string default_algo = isdragonx ? "randomx" : std::string(ASSETCHAINS_ALGORITHMS[0]);
|
||||
std::string selectedAlgo = GetArg("-ac_algo", default_algo);
|
||||
|
||||
for ( int i = 0; i < ASSETCHAINS_NUMALGOS; i++ )
|
||||
{
|
||||
@@ -1900,12 +1897,20 @@ void hush_args(char *argv0)
|
||||
|
||||
// Set our symbol from -ac_name value
|
||||
strncpy(SMART_CHAIN_SYMBOL,name.c_str(),sizeof(SMART_CHAIN_SYMBOL)-1);
|
||||
const bool ishush3 = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? true : false;
|
||||
|
||||
// Set RandomX validation activation height per chain
|
||||
if (ASSETCHAINS_ALGO == ASSETCHAINS_RANDOMX) {
|
||||
if (strncmp(SMART_CHAIN_SYMBOL, "DRAGONX", 7) == 0) {
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = 2838976; // TBD: set to coordinated upgrade height
|
||||
} else if (strncmp(SMART_CHAIN_SYMBOL, "TUMIN", 5) == 0) {
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = 1200; // TBD: set to coordinated upgrade height
|
||||
} else {
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = 1; // all other RandomX HACs: enforce from height 1
|
||||
}
|
||||
printf("ASSETCHAINS_RANDOMX_VALIDATION set to %d for %s\n", ASSETCHAINS_RANDOMX_VALIDATION, SMART_CHAIN_SYMBOL);
|
||||
}
|
||||
|
||||
ASSETCHAINS_LASTERA = GetArg("-ac_eras", 1);
|
||||
if(ishush3) {
|
||||
ASSETCHAINS_LASTERA = 3;
|
||||
}
|
||||
if ( ASSETCHAINS_LASTERA < 1 || ASSETCHAINS_LASTERA > ASSETCHAINS_MAX_ERAS )
|
||||
{
|
||||
ASSETCHAINS_LASTERA = 1;
|
||||
@@ -1939,33 +1944,13 @@ void hush_args(char *argv0)
|
||||
ASSETCHAINS_SCRIPTPUB = GetArg("-ac_script","");
|
||||
|
||||
|
||||
fprintf(stderr,"%s: Setting custom %s reward HUSH3=%d reward,halving,subsidy chain values...\n",__func__, SMART_CHAIN_SYMBOL, ishush3);
|
||||
if(ishush3) {
|
||||
// Migrated from hushd script
|
||||
ASSETCHAINS_CC = 2;
|
||||
ASSETCHAINS_BLOCKTIME = 150; // this will change to 75 at the correct block
|
||||
ASSETCHAINS_COMMISSION = 11111111;
|
||||
// 6250000 - (Sprout pool at block 500,000)
|
||||
ASSETCHAINS_SUPPLY = 6178674;
|
||||
ASSETCHAINS_FOUNDERS = 1;
|
||||
fprintf(stderr,"%s: Setting custom %s reward isdragonx=%d reward,halving,subsidy chain values...\n",__func__, SMART_CHAIN_SYMBOL, isdragonx);
|
||||
if(isdragonx) {
|
||||
// DragonX chain parameters (previously set via wrapper script)
|
||||
// -ac_name=DRAGONX -ac_algo=randomx -ac_halving=3500000 -ac_reward=300000000 -ac_blocktime=36 -ac_private=1
|
||||
ASSETCHAINS_SAPLING = 1;
|
||||
// this corresponds to FR address RHushEyeDm7XwtaTWtyCbjGQumYyV8vMjn
|
||||
ASSETCHAINS_SCRIPTPUB = "76a9145eb10cf64f2bab1b457f1f25e658526155928fac88ac";
|
||||
// we do not want to change the magic of HUSH3 mainnet so we do not call devtax_scriptpub_for_height() here,
|
||||
// instead we call it whenever ASSETCHAINS_SCRIPTPUB is used later on
|
||||
|
||||
// Over-ride HUSH3 values from CLI params. Changing our blocktime to 75s changes things
|
||||
ASSETCHAINS_REWARD[0] = 0;
|
||||
ASSETCHAINS_REWARD[1] = 1125000000;
|
||||
ASSETCHAINS_REWARD[2] = 281250000; // 2.8125 HUSH goes to miners per block after 1st halving at Block 340K
|
||||
ASSETCHAINS_REWARD[3] = 140625000; // 1.40625 HUSH after 2nd halving at Block 2020000
|
||||
ASSETCHAINS_HALVING[0] = 129;
|
||||
ASSETCHAINS_HALVING[1] = GetArg("-z2zheight",340000);
|
||||
ASSETCHAINS_HALVING[2] = 2020000; // 2020000 = 340000 + 1680000 (1st halving block plus new halving interval)
|
||||
ASSETCHAINS_HALVING[3] = 3700000; // ASSETCHAINS_HALVING[2] + 1680000;
|
||||
ASSETCHAINS_ENDSUBSIDY[0] = 129;
|
||||
ASSETCHAINS_ENDSUBSIDY[1] = GetArg("-z2zheight",340000);
|
||||
ASSETCHAINS_ENDSUBSIDY[2] = 2*5422111; // TODO: Fix this, twice the previous end of rewards is an estimate
|
||||
ASSETCHAINS_REWARD[0] = 300000000; // 3 DRAGONX per block
|
||||
ASSETCHAINS_HALVING[0] = 3500000; // halving every 3.5M blocks
|
||||
}
|
||||
Split(GetArg("-ac_decay",""), sizeof(ASSETCHAINS_DECAY)/sizeof(*ASSETCHAINS_DECAY), ASSETCHAINS_DECAY, 0);
|
||||
Split(GetArg("-ac_notarypay",""), sizeof(ASSETCHAINS_NOTARY_PAY)/sizeof(*ASSETCHAINS_NOTARY_PAY), ASSETCHAINS_NOTARY_PAY, 0);
|
||||
@@ -2480,11 +2465,11 @@ void hush_args(char *argv0)
|
||||
void hush_nameset(char *symbol,char *dest,char *source)
|
||||
{
|
||||
if ( source[0] == 0 ) {
|
||||
strcpy(symbol,(char *)"HUSH3");
|
||||
strcpy(symbol,(char *)"DRAGONX");
|
||||
strcpy(dest,(char *)"BTC");
|
||||
} else {
|
||||
strcpy(symbol,source);
|
||||
strcpy(dest,(char *)"HUSH3");
|
||||
strcpy(dest,(char *)"DRAGONX");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
265
src/init.cpp
265
src/init.cpp
@@ -43,6 +43,7 @@
|
||||
#endif
|
||||
#include "main.h"
|
||||
#include "metrics.h"
|
||||
#include "pow.h"
|
||||
#include "miner.h"
|
||||
#include "net.h"
|
||||
#include "rpc/server.h"
|
||||
@@ -176,7 +177,7 @@ public:
|
||||
// Writes do not need similar protection, as failure to write is handled by the caller.
|
||||
};
|
||||
|
||||
static CCoinsViewDB *pcoinsdbview = NULL;
|
||||
CCoinsViewDB *pcoinsdbview = NULL; // global (declared extern in main.h) for UTXO-snapshot dump/load
|
||||
static CCoinsViewErrorCatcher *pcoinscatcher = NULL;
|
||||
static boost::scoped_ptr<ECCVerifyHandle> globalVerifyHandle;
|
||||
|
||||
@@ -282,6 +283,7 @@ void Shutdown()
|
||||
}
|
||||
#endif
|
||||
UnregisterAllValidationInterfaces();
|
||||
RandomXValidatorShutdown(); // release the ~256MB shared RandomX pre-verify cache (was leaked at exit)
|
||||
#ifdef ENABLE_WALLET
|
||||
delete pwalletMain;
|
||||
pwalletMain = NULL;
|
||||
@@ -377,8 +379,8 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)"));
|
||||
strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), 288));
|
||||
strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), 3));
|
||||
strUsage += HelpMessageOpt("-clientname=<SomeName>", _("Full node client name, default 'GoldenSandtrout'"));
|
||||
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "HUSH3.conf"));
|
||||
strUsage += HelpMessageOpt("-clientname=<SomeName>", _("Full node client name, default 'DragonX'"));
|
||||
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "DRAGONX.conf"));
|
||||
if (mode == HMM_BITCOIND)
|
||||
{
|
||||
#if !defined(WIN32)
|
||||
@@ -387,7 +389,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
}
|
||||
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory (this path cannot use '~')"));
|
||||
strUsage += HelpMessageOpt("-exportdir=<dir>", _("Specify directory to be used when exporting data"));
|
||||
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
|
||||
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d). Default: adaptive - uses most free RAM to speed up initial block download (far fewer UTXO flushes to disk) and automatically shrinks if other applications need memory, always leaving a reserve free. Setting a fixed value disables adaptive sizing."), nMinDbCache, nMaxDbCache));
|
||||
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file") + " " + _("on startup"));
|
||||
strUsage += HelpMessageOpt("-maxdebugfilesize=<n>", strprintf(_("Set the max size of the debug.log file (default: %u)"), 15));
|
||||
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
|
||||
@@ -395,6 +397,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
strUsage += HelpMessageOpt("-mempooltxinputlimit=<n>", _("[DEPRECATED/IGNORED] Set the maximum number of transparent inputs in a transaction that the mempool will accept (default: 0 = no limit applied)"));
|
||||
strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
|
||||
-(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS));
|
||||
strUsage += HelpMessageOpt("-randomxverifythreads=<n>", strprintf(_("Number of threads for parallel RandomX PoW pre-verification of post-checkpoint blocks during network sync; no effect on reindex (0 = inline only, max %d, default: same as -par)"), MAX_SCRIPTCHECK_THREADS));
|
||||
#ifndef _WIN32
|
||||
strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), "hushd.pid"));
|
||||
#endif
|
||||
@@ -465,6 +468,12 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
strUsage += HelpMessageGroup(_("Wallet options:"));
|
||||
strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"));
|
||||
strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), 100));
|
||||
strUsage += HelpMessageOpt("-hdtransparent", strprintf(_("Derive transparent addresses from the HD seed so they can be recovered from it (default: %u)"), 1));
|
||||
strUsage += HelpMessageOpt("-hdseed=<hex>", _("Restore a fresh/empty wallet from a 32- or 64-byte HD seed hex (the value shown in z_exportwallet's '# HDSeed=' line). WARNING: exposes the seed to your shell history and process list."));
|
||||
strUsage += HelpMessageOpt("-mnemonic=<words>", _("Restore/create a fresh/empty wallet from a BIP39 seed phrase, compatible with SilentDragonXLite (English, no passphrase; cross-wallet restore parity is mainnet-only -- testnet/regtest derive a different HD coin_type). WARNING: exposes the phrase to your shell history and process list; prefer DRAGONX.conf with tight permissions."));
|
||||
strUsage += HelpMessageOpt("-usemnemonic", strprintf(_("Create new wallets from a fresh BIP39 seed phrase so the 24 words can be exported (z_exportmnemonic) and used in SilentDragonXLite (default: %u)"), 0));
|
||||
strUsage += HelpMessageOpt("-hdtransparentgaplimit=<n>", strprintf(_("On -mnemonic/-hdseed restore, pre-derive this many HD transparent keys so a rescan can find coinbase paid to them (default: %u)"), 1000));
|
||||
strUsage += HelpMessageOpt("-mnemonicsaplinggap=<n>", strprintf(_("On -mnemonic/-hdseed restore, pre-derive this many shielded (Sapling) addresses so a rescan can find notes sent to them (default: %u)"), 100));
|
||||
strUsage += HelpMessageOpt("-consolidation", _("Enable auto Sapling note consolidation (default: false)"));
|
||||
strUsage += HelpMessageOpt("-consolidationinterval", _("Block interval between consolidations (default: 25)"));
|
||||
strUsage += HelpMessageOpt("-consolidatesaplingaddress=<zaddr>", _("Specify Sapling Address to Consolidate. (default: all)"));
|
||||
@@ -605,7 +614,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
strUsage += HelpMessageOpt("-stratumallowip=<ip>", _("Allow Stratum work requests from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"));
|
||||
|
||||
// "ac" stands for "affects consensus" or Arrakis Chain
|
||||
strUsage += HelpMessageGroup(_("Hush Arrakis Chain options:"));
|
||||
strUsage += HelpMessageGroup(_("DragonX Chain options:"));
|
||||
strUsage += HelpMessageOpt("-ac_algo", _("Choose PoW mining algorithm, either 'equihash' or 'randomx'. default is Equihash (200,9)"));
|
||||
strUsage += HelpMessageOpt("-ac_blocktime", _("Block time in seconds, default is 60"));
|
||||
strUsage += HelpMessageOpt("-ac_beam", _("BEAM integration"));
|
||||
@@ -987,6 +996,123 @@ bool AppInitServers(boost::thread_group& threadGroup)
|
||||
*/
|
||||
extern int32_t HUSH_REWIND;
|
||||
|
||||
// --- Adaptive coins-cache sizing -------------------------------------------------------------
|
||||
// The in-memory UTXO/coins cache (nCoinCacheUsage) is the biggest lever on IBD speed: a bigger
|
||||
// cache means far fewer chainstate flushes to disk. We size it to use most of RAM, but a scheduled
|
||||
// background task (AdjustCoinCacheForMemoryPressure, registered in AppInit2) shrinks the target when
|
||||
// free system memory runs low — e.g. the user opens other apps — and grows it back when memory frees
|
||||
// up, always leaving a reserve free for the rest of the system. The existing per-block flush
|
||||
// (FlushStateToDisk, FLUSH_STATE_IF_NEEDED, which fires when cacheSize > nCoinCacheUsage) enforces
|
||||
// whatever target is current, so the task only moves the threshold: it never touches cs_main or the
|
||||
// flush path. NOTE: the coins cache is application heap, not OS file cache — "freeing" it means an
|
||||
// early flush that clears the map; on Linux the allocator returns the pages, on Windows the heap
|
||||
// returns them best-effort (RSS may lag), but either way the node stops growing past the target.
|
||||
// windows.h / <unistd.h> arrive via compat.h (net.h). Memory helpers return 0 if undeterminable.
|
||||
static int64_t GetPhysicalMemoryMB()
|
||||
{
|
||||
#ifdef WIN32
|
||||
MEMORYSTATUSEX status;
|
||||
status.dwLength = sizeof(status);
|
||||
if (GlobalMemoryStatusEx(&status))
|
||||
return (int64_t)(status.ullTotalPhys / (1024 * 1024));
|
||||
return 0;
|
||||
#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
|
||||
long pages = sysconf(_SC_PHYS_PAGES);
|
||||
long pageSize = sysconf(_SC_PAGESIZE);
|
||||
if (pages > 0 && pageSize > 0)
|
||||
return (int64_t)((int64_t)pages * (int64_t)pageSize / (1024 * 1024));
|
||||
return 0;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Currently-available (allocatable) physical RAM in MiB. On Linux uses MemAvailable (counts
|
||||
// reclaimable page cache), falling back to truly-free pages.
|
||||
static int64_t GetAvailableMemoryMB()
|
||||
{
|
||||
#ifdef WIN32
|
||||
MEMORYSTATUSEX status;
|
||||
status.dwLength = sizeof(status);
|
||||
if (GlobalMemoryStatusEx(&status))
|
||||
return (int64_t)(status.ullAvailPhys / (1024 * 1024));
|
||||
return 0;
|
||||
#else
|
||||
FILE* f = fopen("/proc/meminfo", "r");
|
||||
if (f) {
|
||||
char line[256];
|
||||
long long availKB = -1;
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
if (sscanf(line, "MemAvailable: %lld kB", &availKB) == 1)
|
||||
break;
|
||||
}
|
||||
fclose(f);
|
||||
if (availKB >= 0)
|
||||
return (int64_t)(availKB / 1024);
|
||||
}
|
||||
#if defined(_SC_AVPHYS_PAGES) && defined(_SC_PAGESIZE)
|
||||
long pages = sysconf(_SC_AVPHYS_PAGES);
|
||||
long pageSize = sysconf(_SC_PAGESIZE);
|
||||
if (pages > 0 && pageSize > 0)
|
||||
return (int64_t)((int64_t)pages * (int64_t)pageSize / (1024 * 1024));
|
||||
#endif
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// RAM (MiB) to always keep free for the OS and other applications: 20% of total, at least 2 GiB.
|
||||
static int64_t GetMemoryReserveMB()
|
||||
{
|
||||
int64_t ramMB = GetPhysicalMemoryMB();
|
||||
int64_t reserve = (ramMB > 0) ? ramMB / 5 : 2048; // 20%
|
||||
if (reserve < 2048) reserve = 2048;
|
||||
return reserve;
|
||||
}
|
||||
|
||||
// Startup -dbcache default: use most of RAM (total minus the reserve), clamped to
|
||||
// [nDefaultDbCache, nMaxDbCache] MiB. Falls back to the fixed default if RAM can't be detected.
|
||||
static int64_t GetDefaultDbCacheMB()
|
||||
{
|
||||
int64_t ramMB = GetPhysicalMemoryMB();
|
||||
if (ramMB <= 0)
|
||||
return nDefaultDbCache;
|
||||
int64_t cacheMB = ramMB - GetMemoryReserveMB();
|
||||
if (cacheMB < nDefaultDbCache) cacheMB = nDefaultDbCache;
|
||||
if (cacheMB > nMaxDbCache) cacheMB = nMaxDbCache;
|
||||
return cacheMB;
|
||||
}
|
||||
|
||||
// Ceiling (bytes) the adaptive task may grow the coins cache back up to (the startup nCoinCacheUsage).
|
||||
static size_t g_nMaxCoinCacheUsage = 0;
|
||||
static const int64_t g_nMinCoinCacheMB = 256; // never thrash below this working set
|
||||
|
||||
// Scheduled task: nudge nCoinCacheUsage toward "use all RAM except the reserve". If free RAM is below
|
||||
// the reserve we shrink the target (the next per-block flush releases the excess); if there is spare
|
||||
// RAM we grow it back toward the startup ceiling. nCoinCacheUsage is std::atomic<size_t>, so this
|
||||
// cross-thread write (vs the cs_main-held reads in FlushStateToDisk/VerifyDB) is well-defined, no lock needed.
|
||||
static void AdjustCoinCacheForMemoryPressure()
|
||||
{
|
||||
if (g_nMaxCoinCacheUsage == 0)
|
||||
return; // adaptive sizing disabled (user pinned -dbcache) or RAM undetectable
|
||||
int64_t availMB = GetAvailableMemoryMB();
|
||||
if (availMB <= 0)
|
||||
return; // can't measure pressure; leave the target untouched
|
||||
int64_t reserveMB = GetMemoryReserveMB();
|
||||
// Error term: free RAM beyond the reserve. >0 => spare, grow; <0 => pressure, shrink.
|
||||
int64_t errMB = availMB - reserveMB;
|
||||
// Deadband: ignore small fluctuations so the target settles instead of oscillating.
|
||||
if (errMB > -256 && errMB < 256)
|
||||
return;
|
||||
int64_t curTargetMB = (int64_t)(nCoinCacheUsage >> 20);
|
||||
// Damped proportional step (gain 1/4) toward "free RAM == reserve"; the clamps bound it and the
|
||||
// per-block flush (FLUSH_STATE_IF_NEEDED) enforces a lowered target within ~one block during IBD.
|
||||
int64_t newTargetMB = curTargetMB + errMB / 4;
|
||||
int64_t ceilMB = (int64_t)(g_nMaxCoinCacheUsage >> 20);
|
||||
if (newTargetMB > ceilMB) newTargetMB = ceilMB;
|
||||
if (newTargetMB < g_nMinCoinCacheMB) newTargetMB = g_nMinCoinCacheMB;
|
||||
nCoinCacheUsage = (size_t)(newTargetMB << 20);
|
||||
}
|
||||
|
||||
bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
{
|
||||
//fprintf(stderr,"%s start\n", __FUNCTION__);
|
||||
@@ -1309,6 +1435,33 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)
|
||||
nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;
|
||||
|
||||
// Parallel RandomX pre-verification threads (speeds up post-checkpoint sync). Defaults to the
|
||||
// script-check thread count — RandomX pre-verify and script checks do not run simultaneously
|
||||
// within a single connect, so they can share the same budget. 0 disables (inline-only).
|
||||
nRandomXVerifyThreads = GetArg("-randomxverifythreads", nScriptCheckThreads);
|
||||
if (nRandomXVerifyThreads < 0)
|
||||
nRandomXVerifyThreads = 0;
|
||||
else if (nRandomXVerifyThreads > MAX_SCRIPTCHECK_THREADS)
|
||||
nRandomXVerifyThreads = MAX_SCRIPTCHECK_THREADS;
|
||||
|
||||
// Per-peer block-download window (see MAX_BLOCKS_IN_TRANSIT_PER_PEER). Raising this lifts
|
||||
// the bandwidth-delay-product ceiling on high-latency peers during IBD. Clamp to a sane range.
|
||||
MAX_BLOCKS_IN_TRANSIT_PER_PEER = GetArg("-maxblocksintransit", DEFAULT_MAX_BLOCKS_IN_TRANSIT_PER_PEER);
|
||||
if (MAX_BLOCKS_IN_TRANSIT_PER_PEER < 1)
|
||||
MAX_BLOCKS_IN_TRANSIT_PER_PEER = 1;
|
||||
else if (MAX_BLOCKS_IN_TRANSIT_PER_PEER > (int)BLOCK_DOWNLOAD_WINDOW) {
|
||||
// Values above BLOCK_DOWNLOAD_WINDOW are a silent no-op: FindNextBlocksToDownload never fetches
|
||||
// beyond pindexLastCommonBlock + BLOCK_DOWNLOAD_WINDOW, so clamp to the real effective ceiling.
|
||||
LogPrintf("-maxblocksintransit=%d exceeds the effective ceiling BLOCK_DOWNLOAD_WINDOW=%u; clamping\n", MAX_BLOCKS_IN_TRANSIT_PER_PEER, BLOCK_DOWNLOAD_WINDOW);
|
||||
MAX_BLOCKS_IN_TRANSIT_PER_PEER = (int)BLOCK_DOWNLOAD_WINDOW;
|
||||
}
|
||||
LogPrintf("Per-peer max blocks in transit: %d\n", MAX_BLOCKS_IN_TRANSIT_PER_PEER);
|
||||
|
||||
// Opt-in bulk block streaming (DragonX). Drives the requester branch in SendMessages and, when
|
||||
// set, also advertises NODE_BULKBLOCKS below so we serve bulk ranges to peers. OFF by default.
|
||||
fBulkBlockSync = GetBoolArg("-bulkblocksync", DEFAULT_BULKBLOCKSYNC);
|
||||
LogPrintf("Bulk block streaming: %s\n", fBulkBlockSync ? "enabled" : "disabled");
|
||||
|
||||
fServer = GetBoolArg("-server", false);
|
||||
//fprintf(stderr,"%s tik6\n", __FUNCTION__);
|
||||
|
||||
@@ -1545,6 +1698,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
threadGroup.create_thread(&ThreadScriptCheck);
|
||||
}
|
||||
|
||||
// Spawn the parallel RandomX pre-verification worker pool (the connect thread joins as the Nth
|
||||
// worker via CCheckQueueControl::Wait, so spawn N-1 here, mirroring ThreadScriptCheck).
|
||||
if (ASSETCHAINS_ALGO == ASSETCHAINS_RANDOMX && nRandomXVerifyThreads > 0) {
|
||||
LogPrintf("Using %u threads for parallel RandomX pre-verification\n", nRandomXVerifyThreads);
|
||||
for (int i = 0; i < nRandomXVerifyThreads - 1; i++)
|
||||
threadGroup.create_thread(&ThreadRandomXVerify);
|
||||
}
|
||||
|
||||
//fprintf(stderr,"%s tik13\n", __FUNCTION__);
|
||||
|
||||
// Start the lightweight task scheduler thread
|
||||
@@ -1619,7 +1780,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
return InitError(strprintf("User Agent comment (%s) contains unsafe characters.", cmt));
|
||||
uacomments.push_back(SanitizeString(cmt, SAFE_CHARS_UA_COMMENT));
|
||||
}
|
||||
strSubVersion = FormatSubVersion(GetArg("-clientname","GoldenSandtrout"), CLIENT_VERSION, uacomments);
|
||||
strSubVersion = FormatSubVersion(GetArg("-clientname","DragonX"), CLIENT_VERSION, uacomments);
|
||||
if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) {
|
||||
return InitError(strprintf("Total length of network version string %i exceeds maximum of %i characters. Reduce the number and/or size of uacomments.",
|
||||
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
|
||||
@@ -1840,14 +2001,20 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
LogPrintf("* Compression is %s\n", dbCompression ? "enabled" : "disabled");
|
||||
|
||||
// cache size calculations
|
||||
int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20);
|
||||
int64_t nTotalCache = (GetArg("-dbcache", GetDefaultDbCacheMB()) << 20);
|
||||
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
|
||||
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greated than nMaxDbcache
|
||||
int64_t nBlockTreeDBCache = nTotalCache / 8;
|
||||
|
||||
if (GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX) || GetBoolArg("-spentindex", DEFAULT_SPENTINDEX)) {
|
||||
// enable 3/4 of the cache if addressindex and/or spentindex is enabled
|
||||
// Give indexed (address/spent-index) nodes a larger index LevelDB read cache, but CAP it.
|
||||
// With adaptive dbcache, nTotalCache is now multi-GB, so 3/4 of it is several GB -- far more
|
||||
// than the index cache can use, while starving the in-memory UTXO set that actually speeds
|
||||
// IBD (and this chunk is not shrinkable by the memory-pressure controller, which only adjusts
|
||||
// the coins cache). Cap at 1 GiB so the adaptive budget flows to the coins cache. Tunable.
|
||||
nBlockTreeDBCache = nTotalCache * 3 / 4;
|
||||
if (nBlockTreeDBCache > ((int64_t)1024 << 20))
|
||||
nBlockTreeDBCache = ((int64_t)1024 << 20);
|
||||
} else {
|
||||
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false)) {
|
||||
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
|
||||
@@ -1857,6 +2024,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
|
||||
nTotalCache -= nCoinDBCache;
|
||||
nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
|
||||
// Adaptive sizing: unless the user pinned -dbcache, grow/shrink the coins cache with free system
|
||||
// memory (AdjustCoinCacheForMemoryPressure), using the startup size as the ceiling.
|
||||
if (!mapArgs.count("-dbcache")) {
|
||||
g_nMaxCoinCacheUsage = nCoinCacheUsage;
|
||||
scheduler.scheduleEvery(&AdjustCoinCacheForMemoryPressure, 5);
|
||||
LogPrintf("* Adaptive dbcache enabled: ceiling %.0fMiB, keeping >= %lldMiB RAM free for the system\n",
|
||||
nCoinCacheUsage * (1.0 / 1024 / 1024), (long long)GetMemoryReserveMB());
|
||||
}
|
||||
LogPrintf("Cache configuration:\n");
|
||||
LogPrintf("* Max cache setting possible %.1fMiB\n", nMaxDbCache);
|
||||
LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
|
||||
@@ -1908,7 +2083,25 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex);
|
||||
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
||||
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
||||
pnotarizations = new NotarizationDB(100*1024*1024, false, fReindex);
|
||||
try {
|
||||
pnotarizations = new NotarizationDB(100*1024*1024, false, fReindex);
|
||||
} catch (const std::exception& e) {
|
||||
// The notarizations (dPoW) DB is non-essential and node-regenerable. It has been seen to
|
||||
// snapshot/flush torn (0-byte log -> leveldb "Database I/O error" on reopen), which
|
||||
// previously aborted startup with a spurious "Error opening block database" and forced a
|
||||
// full resync. Wipe and recreate it instead of failing hard.
|
||||
LogPrintf("%s: notarizations DB failed to open (%s); moving aside and regenerating (non-fatal)\n", __FUNCTION__, e.what());
|
||||
// Move (do NOT delete) the old DB aside, so a transient open failure (fd
|
||||
// exhaustion, disk full, permissions) cannot permanently destroy notarization
|
||||
// history. If the recreate below also fails it propagates as fatal and the old
|
||||
// data survives in notarizations.corrupt for recovery.
|
||||
{
|
||||
boost::filesystem::path ndir = GetDataDir() / "notarizations";
|
||||
boost::filesystem::remove_all(ndir.string() + ".corrupt");
|
||||
boost::filesystem::rename(ndir, ndir.string() + ".corrupt");
|
||||
}
|
||||
pnotarizations = new NotarizationDB(100*1024*1024, false, true);
|
||||
}
|
||||
|
||||
|
||||
if (fReindex) {
|
||||
@@ -1938,6 +2131,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
strLoadError = _("Error initializing block database");
|
||||
break;
|
||||
}
|
||||
|
||||
HUSH_LOADINGBLOCKS = 0;
|
||||
// Check for changed -txindex state
|
||||
if (fTxIndex != GetBoolArg("-txindex", true)) {
|
||||
@@ -2107,8 +2301,54 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
|
||||
if (!pwalletMain->HaveHDSeed())
|
||||
{
|
||||
// generate a new HD seed
|
||||
pwalletMain->GenerateNewSeed();
|
||||
std::string mnemonic = GetArg("-mnemonic", "");
|
||||
std::string hdSeedHex = GetArg("-hdseed", "");
|
||||
bool restoring = false;
|
||||
|
||||
if (!mnemonic.empty() && !hdSeedHex.empty())
|
||||
return InitError(_("Specify only one of -mnemonic or -hdseed, not both"));
|
||||
|
||||
if (!mnemonic.empty())
|
||||
{
|
||||
// Restore/create a wallet from a BIP39 seed phrase, byte-compatible
|
||||
// with SilentDragonXLite. Must be a fresh/empty wallet.
|
||||
if (!pwalletMain->SetHDSeedFromMnemonic(mnemonic))
|
||||
return InitError(_("Invalid -mnemonic: expected a valid BIP39 English phrase on a fresh/empty wallet"));
|
||||
LogPrintf("%s: restoring wallet from -mnemonic seed phrase\n", __func__);
|
||||
restoring = true;
|
||||
}
|
||||
else if (!hdSeedHex.empty())
|
||||
{
|
||||
// Restore from a previously exported HD seed hex (z_exportwallet's
|
||||
// "# HDSeed=" line): 32 bytes (raw) or 64 bytes (BIP39-derived).
|
||||
if (!pwalletMain->SetHDSeedFromHex(hdSeedHex))
|
||||
return InitError(_("Invalid -hdseed: expected a 32- or 64-hex-character seed on a fresh/empty wallet"));
|
||||
LogPrintf("%s: restoring wallet from -hdseed\n", __func__);
|
||||
restoring = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// generate a new HD seed
|
||||
pwalletMain->GenerateNewSeed();
|
||||
}
|
||||
|
||||
if (restoring)
|
||||
{
|
||||
// Pre-derive keys (birthday = genesis) so the startup rescan finds
|
||||
// funds paid to them: transparent coinbase + shielded notes.
|
||||
int64_t tGap = GetArg("-hdtransparentgaplimit", 1000);
|
||||
if (tGap < 0) tGap = 0;
|
||||
pwalletMain->TopUpHDTransparentKeys((unsigned int)tGap, 1);
|
||||
|
||||
int64_t zGap = GetArg("-mnemonicsaplinggap", 100);
|
||||
if (zGap < 0) zGap = 0;
|
||||
{
|
||||
LOCK(pwalletMain->cs_wallet);
|
||||
for (int i = 0; i < (int)zGap; i++)
|
||||
pwalletMain->GenerateNewSaplingZKey();
|
||||
}
|
||||
LogPrintf("%s: pre-derived %d transparent and %d sapling keys for restore rescan\n", __func__, (int)tGap, (int)zGap);
|
||||
}
|
||||
}
|
||||
|
||||
//Set Sapling Consolidation
|
||||
@@ -2380,6 +2620,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
nLocalServices |= NODE_ADDRINDEX;
|
||||
if ( GetBoolArg("-spentindex", DEFAULT_SPENTINDEX) != 0 )
|
||||
nLocalServices |= NODE_SPENTINDEX;
|
||||
// Advertise willingness to SERVE bulk block streams (full nodes only) when opted in.
|
||||
if ( fBulkBlockSync )
|
||||
nLocalServices |= NODE_BULKBLOCKS;
|
||||
fprintf(stderr,"nLocalServices %llx %d, %d\n",(long long)nLocalServices,GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX),GetBoolArg("-spentindex", DEFAULT_SPENTINDEX));
|
||||
}
|
||||
// ********************************************************* Step 10: import blocks
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
*/
|
||||
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
|
||||
|
||||
/** BIP32: child indices at or above this are hardened. */
|
||||
const unsigned int BIP32_HARDENED_KEY_LIMIT = 0x80000000;
|
||||
|
||||
/** An encapsulated private key. */
|
||||
class CKey
|
||||
{
|
||||
|
||||
@@ -51,8 +51,9 @@ namespace port {
|
||||
|
||||
// Mac OS
|
||||
#elif defined(OS_MACOSX)
|
||||
#include <atomic>
|
||||
inline void MemoryBarrier() {
|
||||
OSMemoryBarrier();
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
}
|
||||
#define LEVELDB_HAVE_MEMORY_BARRIER
|
||||
|
||||
|
||||
BIN
src/libcc.dylib
Normal file
BIN
src/libcc.dylib
Normal file
Binary file not shown.
512
src/main.cpp
512
src/main.cpp
@@ -89,6 +89,12 @@ static int64_t nTimeBestReceived = 0;
|
||||
CWaitableCriticalSection csBestBlock;
|
||||
CConditionVariable cvBlockChange;
|
||||
int nScriptCheckThreads = 0;
|
||||
int MAX_BLOCKS_IN_TRANSIT_PER_PEER = DEFAULT_MAX_BLOCKS_IN_TRANSIT_PER_PEER;
|
||||
bool fBulkBlockSync = DEFAULT_BULKBLOCKSYNC;
|
||||
// Server-side flood throttle: minimum interval between bulk serves to the same peer (main.cpp-local
|
||||
// since only the serve handler uses it; kept out of main.h to avoid a full-tree recompile).
|
||||
static const int64_t BULK_MIN_SERVE_INTERVAL_US = 50000; // 50 ms => <= 20 bulk serves/s/peer
|
||||
int nRandomXVerifyThreads = 0; // parallel RandomX pre-verification worker count (0 = inline only)
|
||||
bool fExperimentalMode = true;
|
||||
bool fImporting = false;
|
||||
bool fReindex = false;
|
||||
@@ -103,7 +109,7 @@ bool fIsBareMultisigStd = true;
|
||||
bool fCheckBlockIndex = false;
|
||||
bool fCheckpointsEnabled = true;
|
||||
bool fCoinbaseEnforcedProtectionEnabled = true;
|
||||
size_t nCoinCacheUsage = 5000 * 300;
|
||||
std::atomic<size_t> nCoinCacheUsage(5000 * 300);
|
||||
uint64_t nPruneTarget = 0;
|
||||
// If the tip is older than this (in seconds), the node is considered to be in initial block download.
|
||||
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
|
||||
@@ -247,6 +253,7 @@ namespace {
|
||||
int64_t nTime; //! Time of "getdata" request in microseconds.
|
||||
bool fValidatedHeaders; //! Whether this block has validated headers at the time of request.
|
||||
int64_t nTimeDisconnect; //! The timeout for this block request (for disconnecting a slow peer)
|
||||
bool fBulk; //! Requested as part of a bulk stream range (exempt from the front() stall-disconnect).
|
||||
};
|
||||
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight;
|
||||
|
||||
@@ -306,6 +313,23 @@ namespace {
|
||||
int nBlocksInFlightValidHeaders;
|
||||
//! Whether we consider this a preferred download peer.
|
||||
bool fPreferredDownload;
|
||||
//! Opt-in bulk block streaming (DragonX): whether a bulk range request is outstanding to this peer.
|
||||
bool fBulkInFlight;
|
||||
//! Time (us) the outstanding bulk request was issued, for the response timeout/fallback.
|
||||
int64_t nBulkSince;
|
||||
//! Height of the first block in the outstanding bulk range.
|
||||
int nBulkRangeStart;
|
||||
//! Number of blocks requested in the outstanding bulk range.
|
||||
int nBulkRangeCount;
|
||||
//! Hash of the first block of the outstanding bulk range (request identity; the server echoes it
|
||||
//! in the BLOCKSTREAM header so a stale/duplicate header for an old request can be ignored).
|
||||
uint256 nBulkHashStart;
|
||||
//! Whether the (one-shot) trailing BLOCKSTREAM header for the outstanding request was processed.
|
||||
bool fBulkHeaderSeen;
|
||||
//! (server side) time (us) we last served a bulk stream to this peer, for flood throttling.
|
||||
int64_t nLastBulkServeTime;
|
||||
//! (#8 IBD header-flood cap) cumulative headers this peer made us process while in IBD.
|
||||
int64_t nHeadersProcessed;
|
||||
|
||||
CNodeState() {
|
||||
fCurrentlyConnected = false;
|
||||
@@ -319,6 +343,14 @@ namespace {
|
||||
nBlocksInFlight = 0;
|
||||
nBlocksInFlightValidHeaders = 0;
|
||||
fPreferredDownload = false;
|
||||
fBulkInFlight = false;
|
||||
nBulkSince = 0;
|
||||
nBulkRangeStart = 0;
|
||||
nBulkRangeCount = 0;
|
||||
nBulkHashStart.SetNull();
|
||||
fBulkHeaderSeen = false;
|
||||
nLastBulkServeTime = 0;
|
||||
nHeadersProcessed = 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -385,14 +417,14 @@ namespace {
|
||||
|
||||
void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
|
||||
{
|
||||
/* int expired = pool.Expire(GetTime() - age);
|
||||
if (expired != 0)
|
||||
LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired);
|
||||
int expired = pool.Expire(GetTime() - age);
|
||||
if (expired != 0)
|
||||
LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired);
|
||||
|
||||
std::vector<uint256> vNoSpendsRemaining;
|
||||
pool.TrimToSize(limit, &vNoSpendsRemaining);
|
||||
BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining)
|
||||
pcoinsTip->Uncache(removed);*/
|
||||
// Fee-order trim to the size limit. (Upstream also pcoinsTip->Uncache()s the coins freed
|
||||
// by eviction, but CCoinsViewCache has no Uncache() in this fork -- it is only a UTXO-cache
|
||||
// perf hint, not eviction correctness, so it is skipped.)
|
||||
pool.TrimToSize(limit);
|
||||
}
|
||||
|
||||
// Requires cs_main.
|
||||
@@ -413,7 +445,7 @@ namespace {
|
||||
}
|
||||
|
||||
// Requires cs_main.
|
||||
void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) {
|
||||
void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL, bool fBulk = false) {
|
||||
CNodeState *state = State(nodeid);
|
||||
assert(state != NULL);
|
||||
|
||||
@@ -421,7 +453,7 @@ namespace {
|
||||
MarkBlockAsReceived(hash);
|
||||
|
||||
int64_t nNow = GetTimeMicros();
|
||||
QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)};
|
||||
QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams), fBulk};
|
||||
nQueuedValidatedHeaders += newentry.fValidatedHeaders;
|
||||
list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry);
|
||||
state->nBlocksInFlight++;
|
||||
@@ -429,6 +461,36 @@ namespace {
|
||||
mapBlocksInFlight[hash] = std::make_pair(nodeid, it);
|
||||
}
|
||||
|
||||
// Opt-in bulk block streaming (DragonX): free this peer's still-in-flight bulk blocks whose height
|
||||
// falls in [hStart, hEnd), so the normal per-block path re-fetches them. We scan the peer's OWN
|
||||
// vBlocksInFlight by the LITERAL hash marked at request time (via the stored pindex) rather than
|
||||
// re-deriving hashes from the mutable pindexBestKnownBlock - the latter would miss the real entries
|
||||
// after a reorg (leaking in-flight slots) and can never touch another peer's blocks. Requires cs_main.
|
||||
void FreeBulkRangeInFlight(CNodeState* state, int hStart, int hEnd) {
|
||||
if (state == NULL) return;
|
||||
std::vector<uint256> toFree; // collect first: MarkBlockAsReceived erases from vBlocksInFlight
|
||||
BOOST_FOREACH(const QueuedBlock& q, state->vBlocksInFlight) {
|
||||
if (q.fBulk && q.pindex != NULL) {
|
||||
int h = q.pindex->GetHeight();
|
||||
if (h >= hStart && h < hEnd) toFree.push_back(q.hash);
|
||||
}
|
||||
}
|
||||
BOOST_FOREACH(const uint256& hh, toFree)
|
||||
MarkBlockAsReceived(hh);
|
||||
}
|
||||
// True if any of this peer's bulk blocks with height in [hStart, hEnd) is still in flight (range not
|
||||
// fully drained). Completion is decided by the RANGE draining, not the global per-peer window count.
|
||||
bool BulkRangeInFlight(CNodeState* state, int hStart, int hEnd) {
|
||||
if (state == NULL) return false;
|
||||
BOOST_FOREACH(const QueuedBlock& q, state->vBlocksInFlight) {
|
||||
if (q.fBulk && q.pindex != NULL) {
|
||||
int h = q.pindex->GetHeight();
|
||||
if (h >= hStart && h < hEnd) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Check whether the last unknown block a peer advertized is not yet known. */
|
||||
void ProcessBlockAvailability(NodeId nodeid) {
|
||||
CNodeState *state = State(nodeid);
|
||||
@@ -485,7 +547,7 @@ namespace {
|
||||
|
||||
/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
|
||||
* at most count entries. */
|
||||
void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller) {
|
||||
void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller, CBlockIndex** pFrontierStuck = NULL) {
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
@@ -562,8 +624,9 @@ namespace {
|
||||
return;
|
||||
}
|
||||
} else if (waitingfor == -1) {
|
||||
// This is the first already-in-flight block.
|
||||
// This is the first already-in-flight block (the download frontier).
|
||||
waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first;
|
||||
if (pFrontierStuck) *pFrontierStuck = pindex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1223,6 +1286,16 @@ bool ContextualCheckTransaction(int32_t slowflag,const CBlock *block, CBlockInde
|
||||
const bool overwinterActive = nHeight >=1 ? true : false; //NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER);
|
||||
const bool saplingActive = nHeight >=1 ? true : false; //NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING);
|
||||
|
||||
// SECURITY FIX (JoinSplit inflation): reject Sprout JoinSplits at consensus. DragonX is
|
||||
// Sapling-only from genesis (zero JoinSplits in its entire history, verified by the mainnet
|
||||
// supply audit); a tx carrying one is illegitimate. Their zk-proof/sig/nullifier/anchor are
|
||||
// never verified while vpub_new is counted as transparent value-in -> unlimited inflation.
|
||||
// Coinbase/notary (IsMint) exempt. Unconditional is safe: no historical block has a JoinSplit.
|
||||
if (!tx.IsMint() && !tx.vjoinsplit.empty()) {
|
||||
return state.DoS(100, error("ContextualCheckTransaction(): Sprout JoinSplits are disabled (inflation vector)"),
|
||||
REJECT_INVALID, "bad-txns-joinsplit-disabled");
|
||||
}
|
||||
|
||||
if (saplingActive) {
|
||||
// Reject transactions with valid version but missing overwintered flag
|
||||
if (tx.nVersion >= SAPLING_MIN_TX_VERSION && !tx.fOverwintered) {
|
||||
@@ -2024,6 +2097,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
if (fSpentIndex) {
|
||||
pool.addSpentIndex(entry, view);
|
||||
}
|
||||
|
||||
// Bound mempool memory with fee-ordered eviction. Now that the tx is in, if the pool
|
||||
// exceeds -maxmempool, TrimToSize drops the lowest-feerate txs -- so a higher-fee tx
|
||||
// DISPLACES lower-fee ones instead of being bluntly rejected. If this very tx was the one
|
||||
// evicted (its feerate was the lowest in the pool), it does not belong here -- reject it.
|
||||
size_t maxmempool = (size_t)GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
|
||||
if ( pool.DynamicMemoryUsage() > maxmempool ) {
|
||||
pool.TrimToSize(maxmempool);
|
||||
if ( !pool.exists(hash) )
|
||||
return state.DoS(0, error("AcceptToMemoryPool: mempool full, tx %s evicted (feerate too low)", hash.ToString()), REJECT_INSUFFICIENTFEE, "mempool-full");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -2375,6 +2459,17 @@ bool IsInitialBlockDownload()
|
||||
//fprintf(stderr,"nullptr in IsInitialDownload\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// SECURITY: enforce the known-good minimum chain work (defined in chainparams but previously
|
||||
// never checked). Keeps an eclipsed/bootstrapping node from trusting a cheap low-work fake
|
||||
// chain -- a recent tip timestamp alone (below) is not sufficient. Gated to the DRAGONX symbol
|
||||
// so ephemeral assetchains (fresh, low work) run from the same binary are not trapped in IBD.
|
||||
if (strncmp(SMART_CHAIN_SYMBOL, "DRAGONX", 7) == 0 &&
|
||||
ptr->chainPower.chainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
state = ((chainActive.Height() < ptr->GetHeight() - 24*60) ||
|
||||
ptr->GetBlockTime() < (GetTime() - nMaxTipAge));
|
||||
if ( HUSH_INSYNC != 0 )
|
||||
@@ -3472,6 +3567,19 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
error("ConnectBlock(): block's hashFinalSaplingRoot is incorrect"),
|
||||
REJECT_INVALID, "bad-sapling-root-in-block");
|
||||
}
|
||||
// Turnstile / inflation guard (belt-and-suspenders to the per-tx binding-signature check):
|
||||
// the cumulative Sapling value pool must never go negative -- a block cannot deshield more
|
||||
// value than was ever shielded. Enforced ONLY when the pool is reliably tracked: pprev's
|
||||
// nChainSaplingValue is engaged only if every ancestor since genesis had a known per-block
|
||||
// value (nSaplingValue). On old/snapshot-bootstrapped nodes it is none -> guard dormant
|
||||
// (until reindex), so this can never false-reject a valid block or split the chain.
|
||||
if (pindex->pprev && pindex->pprev->nChainSaplingValue) {
|
||||
CAmount blockSaplingValue = 0;
|
||||
for (const CTransaction& btx : block.vtx)
|
||||
blockSaplingValue += -btx.valueBalance;
|
||||
if (*pindex->pprev->nChainSaplingValue + blockSaplingValue < 0)
|
||||
return state.DoS(100, error("ConnectBlock(): Sapling value pool would go negative (turnstile/inflation violation)"), REJECT_INVALID, "bad-sapling-value-pool-negative");
|
||||
}
|
||||
}
|
||||
int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
|
||||
LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001);
|
||||
@@ -4004,6 +4112,10 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
|
||||
uiInterface.NotifyTxExpiration(id);
|
||||
}
|
||||
|
||||
// Bound mempool memory on each block: age-expire (-mempoolexpiry) then fee-order trim to
|
||||
// -maxmempool, evicting the lowest-feerate txs (+ descendants) and uncaching their coins.
|
||||
LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
|
||||
|
||||
// Update chainActive & related variables.
|
||||
UpdateTip(pindexNew);
|
||||
|
||||
@@ -4188,6 +4300,7 @@ static bool ActivateBestChainStep(bool fSkipdpow, CValidationState &state, CBloc
|
||||
return state.DoS(100, error("ActivateBestChainStep(): pindexOldTip->GetHeight().%d > notarizedht %d && pindexFork->GetHeight().%d is < notarizedht %d, so ignore it",(int32_t)pindexOldTip->GetHeight(),notarizedht,(int32_t)pindexFork->GetHeight(),notarizedht),
|
||||
REJECT_INVALID, "past-notarized-height");
|
||||
}
|
||||
|
||||
// - On ChainDB initialization, pindexOldTip will be null, so there are no removable blocks.
|
||||
// - If pindexMostWork is in a chain that doesn't have the same genesis block as our chain,
|
||||
// then pindexFork will be null, and we would need to remove the entire chain including
|
||||
@@ -4258,6 +4371,36 @@ static bool ActivateBestChainStep(bool fSkipdpow, CValidationState &state, CBloc
|
||||
}
|
||||
nHeight = nTargetHeight;
|
||||
|
||||
// Parallel RandomX pre-verification (Stage 4): verify this about-to-be-connected window's
|
||||
// PoW on the worker pool BEFORE the serial connect, so ConnectBlock rarely pays the
|
||||
// ~tens-of-ms light-mode hash. Pure optimization — CheckBlockHeader's inline
|
||||
// CheckRandomXSolution still verifies anything not pre-verified, so consensus is unchanged.
|
||||
// We hold cs_main; key derivation + the disk reads happen here on the main thread, and the
|
||||
// pool workers receive only value-type work items (no cs_main, no chainstate pointers).
|
||||
if (nRandomXVerifyThreads > 0 && rxCheckQueue.IsIdle()) {
|
||||
std::map<std::string, std::vector<CRandomXCheck> > rxGroups; // grouped by RandomX key
|
||||
BOOST_FOREACH(CBlockIndex *pidx, vpindexToConnect) {
|
||||
if (pidx->fRandomXVerified || !RandomXValidationRequired(pidx->GetHeight()))
|
||||
continue;
|
||||
std::string rxKey = GetRandomXKey(pidx->GetHeight());
|
||||
if (rxKey.empty())
|
||||
continue; // can't derive key -> inline fallback
|
||||
CBlock blk;
|
||||
if (!ReadBlockFromDisk(blk, pidx, false))
|
||||
continue; // -> inline fallback
|
||||
if (blk.nSolution.size() != 32) // RANDOMX_HASH_SIZE; wrong size -> inline (will error)
|
||||
continue;
|
||||
rxGroups[rxKey].push_back(CRandomXCheck(rxKey, GetRandomXInput(blk), blk.nSolution.data(), &pidx->fRandomXVerified));
|
||||
}
|
||||
for (std::map<std::string, std::vector<CRandomXCheck> >::iterator it = rxGroups.begin(); it != rxGroups.end(); ++it) {
|
||||
if (!RandomXValidatorPrepareKey(it->first))
|
||||
break; // cache alloc failed -> leave the rest for the inline fallback
|
||||
CCheckQueueControl<CRandomXCheck> control(&rxCheckQueue);
|
||||
control.Add(it->second);
|
||||
control.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
// Connect new blocks.
|
||||
BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
|
||||
if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) {
|
||||
@@ -4719,8 +4862,8 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
|
||||
} else {
|
||||
pindex->nChainSproutValue = boost::none;
|
||||
}
|
||||
if (pindex->pprev->nChainSaplingValue) {
|
||||
pindex->nChainSaplingValue = *pindex->pprev->nChainSaplingValue + pindex->nSaplingValue;
|
||||
if (pindex->pprev->nChainSaplingValue && pindex->nSaplingValue) {
|
||||
pindex->nChainSaplingValue = *pindex->pprev->nChainSaplingValue + *pindex->nSaplingValue;
|
||||
} else {
|
||||
pindex->nChainSaplingValue = boost::none;
|
||||
}
|
||||
@@ -4993,6 +5136,12 @@ bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,
|
||||
{
|
||||
if ( !CheckEquihashSolution(&blockhdr, Params()) )
|
||||
return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),REJECT_INVALID, "invalid-solution");
|
||||
// Skip the inline RandomX recompute only if the parallel pre-verify pool already verified
|
||||
// THIS block (fRandomXVerified set 1:1 on a real hash match). Every other case — pool miss,
|
||||
// straggler, disabled pool, or any pindex==NULL caller (TestBlockValidity/VerifyDB/header
|
||||
// accept) — falls through to the inline check, so consensus is unchanged.
|
||||
if ( !(pindex && pindex->fRandomXVerified) && !CheckRandomXSolution(&blockhdr, height) )
|
||||
return state.DoS(100, error("CheckBlockHeader(): RandomX solution invalid"),REJECT_INVALID, "invalid-randomx-solution");
|
||||
}
|
||||
// Check proof of work matches claimed amount
|
||||
/*hush_index2pubkey33(pubkey33,pindex,height);
|
||||
@@ -5003,6 +5152,14 @@ bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,
|
||||
|
||||
int32_t hush_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height);
|
||||
|
||||
// RAII: save+restore the thread-local RandomX-skip flag around the verify-once dedup in CheckBlock,
|
||||
// so it can never clobber the miner's own fSkipRandomXValidation (TestBlockValidity -> ConnectBlock
|
||||
// re-entry) nor leak TRUE on an exception thrown out of hush_checkPOW.
|
||||
struct ScopedRandomXSkip {
|
||||
bool prev;
|
||||
ScopedRandomXSkip() : prev(GetSkipRandomXValidation()) { SetSkipRandomXValidation(true); }
|
||||
~ScopedRandomXSkip() { SetSkipRandomXValidation(prev); }
|
||||
};
|
||||
bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state,
|
||||
libzcash::ProofVerifier& verifier,
|
||||
bool fCheckPOW, bool fCheckMerkleRoot)
|
||||
@@ -5033,8 +5190,16 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C
|
||||
fprintf(stderr," failed hash ht.%d\n",height);
|
||||
return state.DoS(50, error("CheckBlock: proof of work failed"),REJECT_INVALID, "high-hash");
|
||||
}
|
||||
if ( ASSETCHAINS_STAKED == 0 && hush_checkPOW(1,(CBlock *)&block,height) < 0 ) // checks Equihash
|
||||
return state.DoS(100, error("CheckBlock: failed slow_checkPOW"),REJECT_INVALID, "failed-slow_checkPOW");
|
||||
if ( ASSETCHAINS_STAKED == 0 ) {
|
||||
// verify-once: CheckBlockHeader above already verified this block RandomX solution; skip the
|
||||
// redundant recompute inside hush_checkPOW (the un-deduped 2nd verify, ~half the RandomX cost
|
||||
// that dominates IBD). The scoped guard saves/restores the skip flag (never hardcodes false)
|
||||
// so the miner's own skip is preserved and nothing leaks on throw. Equihash + PoW-target in
|
||||
// hush_checkPOW still run.
|
||||
ScopedRandomXSkip _rxskip;
|
||||
if ( hush_checkPOW(1,(CBlock *)&block,height) < 0 )
|
||||
return state.DoS(100, error("CheckBlock: failed slow_checkPOW"),REJECT_INVALID, "failed-slow_checkPOW");
|
||||
}
|
||||
}
|
||||
|
||||
// Check the merkle root.
|
||||
@@ -5105,7 +5270,14 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
|
||||
|
||||
assert(pindexPrev);
|
||||
|
||||
int daaForkHeight = GetArg("-daaforkheight", 450000);
|
||||
// For HUSH3, nBits validation starts above the original DAA fork height (450000).
|
||||
// For DragonX, nBits was never validated before the standalone binary, so the
|
||||
// chain contains blocks with incorrect nBits during the vulnerable window
|
||||
// (diff reset at RANDOMX_VALIDATION height through the attack at ~2879907).
|
||||
// Set daaForkHeight past that window so fresh sync accepts historical blocks.
|
||||
bool isdragonx = strncmp(SMART_CHAIN_SYMBOL, "DRAGONX", 7) == 0;
|
||||
int defaultDaaForkHeight = isdragonx ? ASSETCHAINS_RANDOMX_VALIDATION + 62000 : 450000;
|
||||
int daaForkHeight = GetArg("-daaforkheight", defaultDaaForkHeight);
|
||||
int nHeight = pindexPrev->GetHeight()+1;
|
||||
bool ishush3 = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? true : false;
|
||||
// Check Proof-of-Work difficulty
|
||||
@@ -5138,6 +5310,26 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
|
||||
}
|
||||
}
|
||||
|
||||
// Check Proof-of-Work difficulty for smart chains (HACs)
|
||||
// Without this check, an attacker can submit blocks with arbitrary nBits
|
||||
// (e.g., powLimit / diff=1) and they will be accepted, allowing the chain
|
||||
// to be flooded with minimum-difficulty blocks.
|
||||
// Only enforce above daaForkHeight to avoid consensus mismatch with early
|
||||
// chain blocks that were mined by a different binary version.
|
||||
if (!ishush3 && SMART_CHAIN_SYMBOL[0] != 0 && nHeight > daaForkHeight) {
|
||||
unsigned int nNextWork = GetNextWorkRequired(pindexPrev, &block, consensusParams);
|
||||
if (fDebug) {
|
||||
LogPrintf("%s: HAC nbits height=%d expected=%lu actual=%lu\n",
|
||||
__func__, nHeight, (unsigned long)nNextWork, (unsigned long)block.nBits);
|
||||
}
|
||||
if (block.nBits != nNextWork) {
|
||||
return state.DoS(100,
|
||||
error("%s: Incorrect diffbits for %s at height %d: expected %lu got %lu",
|
||||
__func__, SMART_CHAIN_SYMBOL, nHeight, (unsigned long)nNextWork, (unsigned long)block.nBits),
|
||||
REJECT_INVALID, "bad-diffbits");
|
||||
}
|
||||
}
|
||||
|
||||
// Check timestamp against prev
|
||||
if (ASSETCHAINS_ADAPTIVEPOW <= 0 || nHeight < 30) {
|
||||
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast() )
|
||||
@@ -5265,7 +5457,15 @@ bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidat
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!CheckBlockHeader(futureblockp,*ppindex!=0?(*ppindex)->GetHeight():0,*ppindex, block, state,0)) {
|
||||
// Header-accept does NOT verify RandomX PoW (fCheckPOW=0). The RandomX key for a header is derived
|
||||
// from the block at keyHeight on the header's OWN branch, which is not reliably resolvable at
|
||||
// header-accept time (reorg / side-branch / catch-up headers are not on the active chain), so a
|
||||
// header-time RandomX check repeatedly false-rejected valid reorg headers and hard-banned honest
|
||||
// peers (see audit notes; reverted b9fdc7981/7e9b2c661/defer). The full RandomX + target check runs
|
||||
// at block-connect with the correct branch key. Fake low-work chains are gated from SELECTION by
|
||||
// nMinimumChainWork; the per-peer IBD header cap bounds flood memory. Do NOT re-enable a header-time
|
||||
// RandomX check without first deriving the key from the header's own ancestry (pindexPrev->GetAncestor).
|
||||
if (!CheckBlockHeader(futureblockp,*ppindex!=0?(*ppindex)->GetHeight():0,*ppindex, block, state, 0)) {
|
||||
if ( *futureblockp == 0 ) {
|
||||
LogPrintf("%s: CheckBlockHeader futureblock=0\n", __func__);
|
||||
return false;
|
||||
@@ -5815,8 +6015,8 @@ bool static LoadBlockIndexDB()
|
||||
} else {
|
||||
pindex->nChainSproutValue = boost::none;
|
||||
}
|
||||
if (pindex->pprev->nChainSaplingValue) {
|
||||
pindex->nChainSaplingValue = *pindex->pprev->nChainSaplingValue + pindex->nSaplingValue;
|
||||
if (pindex->pprev->nChainSaplingValue && pindex->nSaplingValue) {
|
||||
pindex->nChainSaplingValue = *pindex->pprev->nChainSaplingValue + *pindex->nSaplingValue;
|
||||
} else {
|
||||
pindex->nChainSaplingValue = boost::none;
|
||||
}
|
||||
@@ -6192,7 +6392,7 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches)
|
||||
pindexIter->nChainTx = 0;
|
||||
pindexIter->nSproutValue = boost::none;
|
||||
pindexIter->nChainSproutValue = boost::none;
|
||||
pindexIter->nSaplingValue = 0;
|
||||
pindexIter->nSaplingValue = boost::none;
|
||||
pindexIter->nChainSaplingValue = boost::none;
|
||||
pindexIter->nSequenceId = 0;
|
||||
|
||||
@@ -6717,6 +6917,13 @@ void static ProcessGetData(CNode* pfrom)
|
||||
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
|
||||
|
||||
vector<CInv> vNotFound;
|
||||
// Serve up to this many blocks per ProcessGetData pass. The old code broke after a SINGLE block,
|
||||
// so a 16-block getdata was dribbled out one block per message-handler tick (~100ms), throttling
|
||||
// block download for every peer fetching from us. Bound the per-pass work (cs_main is held while
|
||||
// reading blocks from disk); any remainder is served on the next pass (the message handler keeps
|
||||
// fSleep=false while vRecvGetData is non-empty, so there is no 100ms park between passes).
|
||||
const unsigned int nMaxBlocksServedPerPass = 16;
|
||||
unsigned int nBlocksServed = 0;
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
@@ -6759,7 +6966,15 @@ void static ProcessGetData(CNode* pfrom)
|
||||
CBlock block;
|
||||
if (!ReadBlockFromDisk(block, (*mi).second,1))
|
||||
{
|
||||
assert(!"cannot load block from disk");
|
||||
// A block we advertised (BLOCK_HAVE_DATA) failed to load from disk: a transient
|
||||
// I/O error or on-disk corruption. This previously asserted and crashed the whole
|
||||
// node -- any peer getdata for such a block could take us down. Log and drop this
|
||||
// peer instead; it can re-fetch from another node. (The bulk GETBLOCKSTREAM serve
|
||||
// path already fails gracefully rather than asserting.)
|
||||
LogPrintf("%s: ReadBlockFromDisk failed for block %s (peer=%i);"
|
||||
" disconnecting peer instead of asserting\n",
|
||||
__func__, inv.hash.ToString(), pfrom->GetId());
|
||||
pfrom->fDisconnect = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -6834,7 +7049,10 @@ void static ProcessGetData(CNode* pfrom)
|
||||
}
|
||||
}
|
||||
|
||||
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
|
||||
// Serve a bounded batch of blocks per pass rather than one (see nMaxBlocksServedPerPass
|
||||
// above). The send-buffer gate at the top of the loop still pauses us if the buffer fills;
|
||||
// this counter bounds the cs_main hold for a (possibly malicious) large getdata.
|
||||
if ((inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) && ++nBlocksServed >= nMaxBlocksServedPerPass)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -7377,6 +7595,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||
uint256 hashStop;
|
||||
vRecv >> locator >> hashStop;
|
||||
|
||||
// Bound the locator before FindForkInGlobalIndex() scans it linearly under cs_main. An honest
|
||||
// GetLocator() never exceeds MAX_LOCATOR_SZ, so this cannot reject a valid peer; an oversized
|
||||
// vHave (~130k hashes fit in one message) is a message-thread liveness DoS. Ban like the
|
||||
// adjacent vInv > MAX_INV_SZ path.
|
||||
if (locator.vHave.size() > MAX_LOCATOR_SZ) {
|
||||
Misbehaving(pfrom->GetId(), 20);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
// Find the last block the caller has in the main chain
|
||||
@@ -7409,6 +7636,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||
uint256 hashStop;
|
||||
vRecv >> locator >> hashStop;
|
||||
|
||||
// Bound the locator before FindForkInGlobalIndex() scans it linearly under cs_main. An honest
|
||||
// GetLocator() never exceeds MAX_LOCATOR_SZ, so this cannot reject a valid peer; an oversized
|
||||
// vHave (~130k hashes fit in one message) is a message-thread liveness DoS. Ban like the
|
||||
// adjacent vInv > MAX_INV_SZ path.
|
||||
if (locator.vHave.size() > MAX_LOCATOR_SZ) {
|
||||
Misbehaving(pfrom->GetId(), 20);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
|
||||
@@ -7637,12 +7873,38 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||
if (state.IsInvalid(nDoS) && futureblock == 0)
|
||||
{
|
||||
if (nDoS > 0 && futureblock == 0)
|
||||
Misbehaving(pfrom->GetId(), nDoS/nDoS);
|
||||
Misbehaving(pfrom->GetId(), nDoS);
|
||||
return error("invalid header received");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SECURITY (#8: IBD header-flood cap): bound how many headers a single peer can make us
|
||||
// store while in IBD. Honest headers-first sync needs at most ~chain-length headers from a
|
||||
// peer; one that floods far past 2x the known chain length is only trying to bloat
|
||||
// mapBlockIndex/leveldb (such headers are never selected -- nMinimumChainWork gates that --
|
||||
// but they still cost memory/disk). Cap per-peer and drop the peer. IBD-only: post-IBD there is
|
||||
// no header-accept PoW check (removed as bug-prone), so a post-IBD flood is bounded only by
|
||||
// nMinimumChainWork gating selection -- memory/disk growth there is accepted as low-severity.
|
||||
if (IsInitialBlockDownload()) {
|
||||
CNodeState *hstate = State(pfrom->GetId());
|
||||
if (hstate != NULL) {
|
||||
hstate->nHeadersProcessed += (int64_t)nCount;
|
||||
// Cap RELATIVE TO THE VALIDATED ACTIVE-CHAIN HEIGHT (attacker-hard -- advancing it requires
|
||||
// connecting real PoW blocks), NOT pindexBestHeader: a forward-extending header flood advances
|
||||
// pindexBestHeader in lockstep with the attacker, so a pindexBestHeader-relative cap never fires.
|
||||
// The checkpoint height is a fixed floor so honest IBD (blocks still lagging headers) is never capped.
|
||||
int knownH = std::max((int)chainActive.Height(),
|
||||
Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()));
|
||||
int64_t headerCap = 2 * (int64_t)knownH + 200000;
|
||||
if (hstate->nHeadersProcessed > headerCap) {
|
||||
Misbehaving(pfrom->GetId(), 100);
|
||||
return error("%s: peer=%d flooded %lld headers during IBD (cap %lld)", __func__,
|
||||
pfrom->id, (long long)hstate->nHeadersProcessed, (long long)headerCap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pindexLast)
|
||||
UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
|
||||
|
||||
@@ -7659,6 +7921,118 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||
}
|
||||
|
||||
CheckBlockIndex();
|
||||
} else if (strCommand == NetMsgType::GETBLOCKSTREAM) {
|
||||
// Opt-in bulk block streaming (DragonX): a peer asks us to stream a contiguous range of
|
||||
// old blocks as back-to-back BLOCK messages. We only honor it if we advertised the bit
|
||||
// (i.e. were started with -bulkblocksync) and we are not mid-import/reindex.
|
||||
if ((nLocalServices & NODE_BULKBLOCKS) == 0 || fImporting || fReindex)
|
||||
return true;
|
||||
uint256 hashStart; int32_t nStartHeight; uint16_t nCount;
|
||||
vRecv >> hashStart >> nStartHeight >> nCount;
|
||||
|
||||
// Resolve the range under cs_main (cheap, no disk I/O), then read + stream the blocks WITHOUT
|
||||
// holding the lock, so a 128-block / 8 MiB serve never holds cs_main across disk reads (the
|
||||
// analogous ProcessGetData caps per-pass work precisely because it reads under cs_main).
|
||||
std::vector<CBlockIndex*> vSend;
|
||||
int firstH = -1;
|
||||
bool refuse = false;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
if (nCount == 0 || nCount > BULK_MAX_BLOCKS_PER_REQUEST) {
|
||||
Misbehaving(pfrom->GetId(), 20); // mirrors the getdata MAX_INV_SZ penalty
|
||||
return true;
|
||||
}
|
||||
// Light flood throttle: at most one bulk serve per peer per BULK_MIN_SERVE_INTERVAL_US. On
|
||||
// throttle, send a refusal header so the requester falls back immediately (not after 90s).
|
||||
int64_t nNowServe = GetTimeMicros();
|
||||
CNodeState* sst = State(pfrom->GetId());
|
||||
if (sst != NULL && sst->nLastBulkServeTime > nNowServe - BULK_MIN_SERVE_INTERVAL_US) {
|
||||
pfrom->PushMessage(NetMsgType::BLOCKSTREAM, hashStart, (int32_t)-1, (uint16_t)0);
|
||||
return true;
|
||||
}
|
||||
if (sst != NULL) sst->nLastBulkServeTime = nNowServe;
|
||||
|
||||
BlockMap::iterator mi = mapBlockIndex.find(hashStart);
|
||||
// Don't flood old blocks while WE are still syncing (unless allowlisted); only serve blocks
|
||||
// on our active chain at the height the requester expects (nStartHeight, tamper-checked).
|
||||
if ((IsInitialBlockDownload() && !pfrom->fAllowlisted) ||
|
||||
mi == mapBlockIndex.end() || !chainActive.Contains(mi->second) ||
|
||||
mi->second->GetHeight() != nStartHeight) {
|
||||
refuse = true;
|
||||
} else {
|
||||
CBlockIndex* pindex = mi->second;
|
||||
firstH = pindex->GetHeight();
|
||||
for (uint16_t i = 0; i < nCount && pindex != NULL; i++, pindex = chainActive.Next(pindex)) {
|
||||
if ((pindex->nStatus & BLOCK_HAVE_DATA) == 0) break; // pruned/missing
|
||||
vSend.push_back(pindex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (refuse) {
|
||||
pfrom->PushMessage(NetMsgType::BLOCKSTREAM, hashStart, (int32_t)-1, (uint16_t)0);
|
||||
return true;
|
||||
}
|
||||
// Read from disk + stream OUTSIDE cs_main. CBlockIndex pointers are stable and block files are
|
||||
// append-only, so reading by pindex without the lock is safe (a concurrent reorg cannot delete
|
||||
// block data, and the requester validates every block against its own headers regardless).
|
||||
uint16_t nSent = 0;
|
||||
size_t cumBytes = 0;
|
||||
BOOST_FOREACH(CBlockIndex* pb, vSend) {
|
||||
if (pfrom->nSendSize >= SendBufferSize()) break; // send-buffer backpressure
|
||||
boost::this_thread::interruption_point();
|
||||
CBlock block;
|
||||
if (!ReadBlockFromDisk(block, pb, 1)) break; // graceful, never assert
|
||||
size_t sz = GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION);
|
||||
if (nSent > 0 && cumBytes + sz > BULK_MAX_RESPONSE_BYTES) break; // total byte cap
|
||||
cumBytes += sz;
|
||||
pfrom->PushMessage(NetMsgType::BLOCK, block);
|
||||
nSent++;
|
||||
}
|
||||
// Trailing control header carries the ACTUAL count sent (authoritative), so the requester can
|
||||
// free any undelivered tail immediately rather than waiting for the bulk response timeout.
|
||||
pfrom->PushMessage(NetMsgType::BLOCKSTREAM, hashStart, (int32_t)firstH, nSent);
|
||||
LogPrint("net", "Bulk stream serve: %u/%u blocks from height %d (%lu bytes) peer=%d\n",
|
||||
(unsigned)nSent, (unsigned)nCount, firstH, (unsigned long)cumBytes, pfrom->id);
|
||||
return true;
|
||||
} else if (strCommand == NetMsgType::BLOCKSTREAM) {
|
||||
// Opt-in bulk block streaming (DragonX): the trailing control header for a streamed range. The
|
||||
// blocks themselves arrive as ordinary BLOCK messages (handled below); this reconciles what the
|
||||
// peer actually delivered so the undelivered tail (or a refusal) falls back at once instead of
|
||||
// waiting for the bulk timeout. Service bits are unauthenticated, so we ignore anything that
|
||||
// doesn't match our exact outstanding request.
|
||||
uint256 hashStart; int32_t nFirstHeight; uint16_t nBlocks;
|
||||
vRecv >> hashStart >> nFirstHeight >> nBlocks;
|
||||
|
||||
LOCK(cs_main);
|
||||
CNodeState* state = State(pfrom->GetId());
|
||||
if (state == NULL || !state->fBulkInFlight)
|
||||
return true; // nothing outstanding
|
||||
if (hashStart != state->nBulkHashStart)
|
||||
return true; // header for a different/stale request; ignore
|
||||
if (state->fBulkHeaderSeen)
|
||||
return true; // one-shot: already reconciled this request
|
||||
state->fBulkHeaderSeen = true;
|
||||
|
||||
// nBlocks==0 (refusal) or an over-count => free our whole outstanding range and fall back.
|
||||
// 0 < nBlocks <= count => the peer commits to that many; free only the undelivered tail now.
|
||||
// FreeBulkRangeInFlight scans THIS peer's vBlocksInFlight by literal hash, so it only ever frees
|
||||
// heights still genuinely in flight to this peer (no cross-peer effect, reorg-proof).
|
||||
bool refuse = (nBlocks == 0 || nBlocks > state->nBulkRangeCount);
|
||||
int deliver = refuse ? 0 : (int)nBlocks;
|
||||
FreeBulkRangeInFlight(state, state->nBulkRangeStart + deliver,
|
||||
state->nBulkRangeStart + state->nBulkRangeCount);
|
||||
if (refuse) {
|
||||
state->fBulkInFlight = false;
|
||||
pfrom->nServices &= ~(uint64_t)NODE_BULKBLOCKS; // local hint: don't retry bulk on this peer
|
||||
LogPrint("net", "Bulk stream refused by peer=%d (nBlocks=%u), falling back\n", pfrom->id, (unsigned)nBlocks);
|
||||
} else {
|
||||
// Track only what was promised; fBulkInFlight clears once that prefix fully drains
|
||||
// (range-drain check in SendMessages) or via the timeout fallback.
|
||||
state->nBulkRangeCount = deliver;
|
||||
if (deliver == 0)
|
||||
state->fBulkInFlight = false;
|
||||
}
|
||||
return true;
|
||||
} else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
|
||||
{
|
||||
CBlock block;
|
||||
@@ -8104,24 +8478,108 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
||||
LogPrint("net", "Reducing block download timeout for peer=%d block=%s, orig=%d new=%d\n", pto->id, queuedBlock.hash.ToString(), queuedBlock.nTimeDisconnect, nTimeoutIfRequestedNow);
|
||||
queuedBlock.nTimeDisconnect = nTimeoutIfRequestedNow;
|
||||
}
|
||||
if (queuedBlock.nTimeDisconnect < nNow) {
|
||||
if (queuedBlock.nTimeDisconnect < nNow && !queuedBlock.fBulk) {
|
||||
// Bulk-stream blocks are exempt: a 128-block batch shares one request time, so the
|
||||
// front() entry could expire before the tail streams in. The bulk response timeout
|
||||
// below frees the range without disconnecting instead.
|
||||
LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id);
|
||||
pto->fDisconnect = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Opt-in bulk block streaming (DragonX): manage the outstanding bulk range, then (below)
|
||||
// possibly issue a new one. Clearing fBulkInFlight once the batch has drained below the
|
||||
// normal window re-enables the next bulk request; a never-fully-delivered batch is freed
|
||||
// after BULK_RESPONSE_TIMEOUT_US so the normal per-block path re-fetches it (no disconnect).
|
||||
if (state.fBulkInFlight) {
|
||||
int hEnd = state.nBulkRangeStart + state.nBulkRangeCount;
|
||||
if (!BulkRangeInFlight(&state, state.nBulkRangeStart, hEnd)) {
|
||||
// Whole (possibly shrunk) range received -> done. Completion is keyed on the RANGE
|
||||
// draining, NOT on the global in-flight count crossing the window, so a partially
|
||||
// delivered batch can never leave undelivered heights stuck in-flight.
|
||||
state.fBulkInFlight = false;
|
||||
} else if (state.nBulkSince > 0 && state.nBulkSince < nNow - BULK_RESPONSE_TIMEOUT_US) {
|
||||
// Promised blocks never fully arrived: free the still-in-flight remainder (the normal
|
||||
// per-block path re-fetches it), give up bulk on this unresponsive peer. No disconnect.
|
||||
FreeBulkRangeInFlight(&state, state.nBulkRangeStart, hEnd);
|
||||
state.fBulkInFlight = false;
|
||||
pto->nServices &= ~(uint64_t)NODE_BULKBLOCKS;
|
||||
LogPrint("net", "Bulk stream timeout peer=%d, freed range [%d,%d)\n",
|
||||
pto->id, state.nBulkRangeStart, hEnd);
|
||||
}
|
||||
}
|
||||
// Message: getdata (blocks)
|
||||
static uint256 zero;
|
||||
vector<CInv> vGetData;
|
||||
if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
|
||||
if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER && !state.fBulkInFlight) {
|
||||
vector<CBlockIndex*> vToDownload;
|
||||
NodeId staller = -1;
|
||||
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
|
||||
CBlockIndex *pFrontierStuck = NULL;
|
||||
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, &pFrontierStuck);
|
||||
|
||||
// Opt-in bulk block streaming (DragonX): if the first block we need is in the deep,
|
||||
// stable region (>= BULK_TIP_MARGIN below the NETWORK tip) and the peer advertised the
|
||||
// capability, request a whole contiguous range in one shot instead of per-block getdata.
|
||||
// FindNextBlocksToDownload already advanced the cursor past what we have, so
|
||||
// vToDownload.front() is the correct, cursor-managed starting point.
|
||||
bool didBulk = false;
|
||||
if (fBulkBlockSync && (pto->nServices & NODE_BULKBLOCKS) && IsInitialBlockDownload()
|
||||
&& !vToDownload.empty() && state.pindexBestKnownBlock != NULL) {
|
||||
CBlockIndex* pfirst = vToDownload.front();
|
||||
int cursorH = pfirst->GetHeight();
|
||||
int maxH = state.pindexBestKnownBlock->GetHeight() - BULK_TIP_MARGIN;
|
||||
if (cursorH <= maxH) {
|
||||
int want = std::min(maxH - cursorH + 1, (int)BULK_MAX_BLOCKS_PER_REQUEST);
|
||||
uint16_t n = 0;
|
||||
for (int i = 0; i < want; i++) {
|
||||
CBlockIndex* pb = state.pindexBestKnownBlock->GetAncestor(cursorH + i);
|
||||
if (pb == NULL || mapBlocksInFlight.count(pb->GetBlockHash())) break;
|
||||
MarkBlockAsInFlight(pto->GetId(), pb->GetBlockHash(), consensusParams, pb, true);
|
||||
n++;
|
||||
}
|
||||
if (n > 0) {
|
||||
pto->PushMessage(NetMsgType::GETBLOCKSTREAM, pfirst->GetBlockHash(), (int32_t)cursorH, n);
|
||||
state.fBulkInFlight = true;
|
||||
state.nBulkSince = nNow;
|
||||
state.nBulkRangeStart = cursorH;
|
||||
state.nBulkRangeCount = n;
|
||||
state.nBulkHashStart = pfirst->GetBlockHash(); // request identity (matched in BLOCKSTREAM)
|
||||
state.fBulkHeaderSeen = false; // arm the one-shot header reconciliation
|
||||
didBulk = true;
|
||||
LogPrint("net", "Requesting bulk stream [%d..%d] (%u blocks) peer=%d\n",
|
||||
cursorH, cursorH + n - 1, (unsigned)n, pto->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!didBulk) {
|
||||
BOOST_FOREACH(CBlockIndex *pindex, vToDownload) {
|
||||
vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash()));
|
||||
MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
|
||||
LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), pindex->GetHeight(), pto->id);
|
||||
}
|
||||
}
|
||||
// Frontier reassignment: when this peer has nothing new to fetch because the next-needed
|
||||
// (frontier) block is in flight from another, slow peer and has been stuck beyond a short
|
||||
// threshold, re-request it from THIS (responsive) peer instead of waiting out the long
|
||||
// (~72s) timeout or disconnecting the slow peer. This breaks the head-of-line stall that
|
||||
// throttles IBD when downloading from few, distant peers. Trustless: the block is still
|
||||
// fully validated on arrival - we only change which peer serves it. -blockreassigntimeout
|
||||
// = seconds (0 disables; default 5).
|
||||
static const int64_t nReassignUs = GetArg("-blockreassigntimeout", 5) * 1000000LL;
|
||||
if (nReassignUs > 0 && vToDownload.empty() && pFrontierStuck != NULL &&
|
||||
staller != -1 && staller != pto->GetId()) {
|
||||
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itF =
|
||||
mapBlocksInFlight.find(pFrontierStuck->GetBlockHash());
|
||||
if (itF != mapBlocksInFlight.end() && itF->second.first == staller &&
|
||||
itF->second.second->nTime < nNow - nReassignUs) {
|
||||
uint256 hReassign = pFrontierStuck->GetBlockHash();
|
||||
LogPrint("net", "Reassigning stalled frontier block %s (%d) from peer=%d to peer=%d\n",
|
||||
hReassign.ToString(), pFrontierStuck->GetHeight(), staller, pto->id);
|
||||
MarkBlockAsReceived(hReassign); // free from slow peer (no disconnect)
|
||||
vGetData.push_back(CInv(MSG_BLOCK, hReassign));
|
||||
MarkBlockAsInFlight(pto->GetId(), hReassign, consensusParams, pFrontierStuck); // re-request from this peer
|
||||
}
|
||||
}
|
||||
if (state.nBlocksInFlight == 0 && staller != -1) {
|
||||
if (State(staller)->nStallingSince == 0) {
|
||||
State(staller)->nStallingSince = nNow;
|
||||
|
||||
49
src/main.h
49
src/main.h
@@ -43,6 +43,7 @@
|
||||
#include "txmempool.h"
|
||||
#include "uint256.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
@@ -64,7 +65,9 @@ class CValidationState;
|
||||
class PrecomputedTransactionData;
|
||||
|
||||
struct CNodeStateStats;
|
||||
#define DEFAULT_MEMPOOL_EXPIRY 1
|
||||
#define DEFAULT_MEMPOOL_EXPIRY 72 // hours; age-based Expire is now live via LimitMempoolSize -- was 1, too aggressive
|
||||
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
|
||||
#define DEFAULT_MAX_MEMPOOL_SIZE 300
|
||||
#define _COINBASE_MATURITY 100
|
||||
|
||||
/** Default for -blockmaxsize and -blockminsize, which control the range of sizes the mining code will create **/
|
||||
@@ -95,13 +98,44 @@ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
|
||||
static const int MAX_SCRIPTCHECK_THREADS = 16;
|
||||
/** -par default (number of script-checking threads, 0 = auto) */
|
||||
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
|
||||
/** Number of blocks that can be requested at any given time from a single peer. */
|
||||
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
|
||||
/** Number of blocks that can be requested at any given time from a single peer.
|
||||
* Runtime-tunable via -maxblocksintransit. The default of 16 caps single-peer IBD
|
||||
* throughput at (window / RTT): on a high-latency peer with tiny (sub-checkpoint)
|
||||
* blocks the transfer is bandwidth-delay-product bound, so a larger window lifts the
|
||||
* ceiling at negligible bandwidth cost. */
|
||||
static const int DEFAULT_MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
|
||||
extern int MAX_BLOCKS_IN_TRANSIT_PER_PEER;
|
||||
/** Opt-in bulk block streaming (DragonX, -bulkblocksync). A single GETBLOCKSTREAM request makes a
|
||||
* peer stream a contiguous range of old blocks as back-to-back BLOCK messages, amortizing the
|
||||
* per-block round-trip over the whole range instead of the MAX_BLOCKS_IN_TRANSIT_PER_PEER window.
|
||||
* OFF by default; negotiated via NODE_BULKBLOCKS; only used during IBD for blocks more than
|
||||
* BULK_TIP_MARGIN below the active tip; never alters the default getdata path. */
|
||||
static const bool DEFAULT_BULKBLOCKSYNC = false;
|
||||
extern bool fBulkBlockSync;
|
||||
/** Only bulk-stream blocks at least this far below the active tip (near-tip uses the normal path). */
|
||||
static const int BULK_TIP_MARGIN = 5000;
|
||||
/** Hard DoS cap: max blocks a single GETBLOCKSTREAM may request/serve. */
|
||||
static const uint16_t BULK_MAX_BLOCKS_PER_REQUEST = 128;
|
||||
/** Hard DoS cap: max total bytes streamed in response to one GETBLOCKSTREAM. */
|
||||
static const size_t BULK_MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
||||
/** Requester fallback: if a promised bulk range doesn't fully arrive within this many microseconds,
|
||||
* free the in-flight range so the normal per-block path re-fetches it. */
|
||||
static const int64_t BULK_RESPONSE_TIMEOUT_US = 90 * 1000000LL;
|
||||
/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */
|
||||
static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
|
||||
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
|
||||
* less than this number, we reached its tip. Changing this value is a protocol upgrade. */
|
||||
* less than this number, we reached its tip. Changing this value is a protocol upgrade: the
|
||||
* continuation logic (main.cpp, "nCount == MAX_HEADERS_RESULTS") and the serve-side limit must
|
||||
* match across the network, so a single node raising it unilaterally would mis-detect a stock
|
||||
* peer's 160-header reply as "tip reached" and stall header sync. Raise only as a coordinated
|
||||
* network upgrade (with a protocol-version bump). */
|
||||
static const unsigned int MAX_HEADERS_RESULTS = 160;
|
||||
/** Maximum number of entries we accept in a CBlockLocator.vHave (GETBLOCKS / GETHEADERS). An honest
|
||||
* CChain::GetLocator() emits ~10 linear hashes then exponentially-spaced ones, so even a chain of
|
||||
* 2^91 blocks stays well under this bound (GetLocator reserves 32). Matches upstream Bitcoin Core's
|
||||
* MAX_LOCATOR_SZ. A larger vHave is a peer trying to make FindForkInGlobalIndex() linearly scan a
|
||||
* huge list under cs_main (message-thread liveness DoS). */
|
||||
static const unsigned int MAX_LOCATOR_SZ = 101;
|
||||
/** Size of the "block download window": how far ahead of our current height do we fetch?
|
||||
* Larger windows tolerate larger download speed differences between peer, but increase the potential
|
||||
* degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning
|
||||
@@ -155,6 +189,7 @@ extern bool fExperimentalMode;
|
||||
extern bool fImporting;
|
||||
extern bool fReindex;
|
||||
extern int nScriptCheckThreads;
|
||||
extern int nRandomXVerifyThreads;
|
||||
extern bool fTxIndex;
|
||||
extern bool fZindex;
|
||||
extern bool fIsBareMultisigStd;
|
||||
@@ -163,7 +198,7 @@ extern bool fCheckpointsEnabled;
|
||||
// TODO: remove this flag by structuring our code such that
|
||||
// it is unneeded for testing
|
||||
extern bool fCoinbaseEnforcedProtectionEnabled;
|
||||
extern size_t nCoinCacheUsage;
|
||||
extern std::atomic<size_t> nCoinCacheUsage;
|
||||
extern CFeeRate minRelayTxFee;
|
||||
extern int64_t nMaxTipAge;
|
||||
|
||||
@@ -930,6 +965,10 @@ extern CChain chainActive;
|
||||
/** Global variable that points to the active CCoinsView (protected by cs_main) */
|
||||
extern CCoinsViewCache *pcoinsTip;
|
||||
|
||||
/** Global variable that points to the coins database (chainstate/, protected by cs_main). */
|
||||
class CCoinsViewDB;
|
||||
extern CCoinsViewDB *pcoinsdbview;
|
||||
|
||||
/** Global variable that points to the active block tree (protected by cs_main) */
|
||||
extern CBlockTreeDB *pblocktree;
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@ int printMiningStatus(bool mining)
|
||||
lines++;
|
||||
} else {
|
||||
std::cout << _("You are currently not mining.") << std::endl;
|
||||
std::cout << _("To enable mining, add 'gen=1' to your HUSH3.conf and restart.") << std::endl;
|
||||
std::cout << _("To enable mining, add 'gen=1' to your DRAGONX.conf and restart.") << std::endl;
|
||||
lines += 2;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
385
src/miner.cpp
385
src/miner.cpp
@@ -23,6 +23,7 @@
|
||||
#include "pow/tromp/equi_miner.h"
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include "amount.h"
|
||||
#include "chainparams.h"
|
||||
#include "consensus/consensus.h"
|
||||
@@ -51,6 +52,7 @@
|
||||
#include "transaction_builder.h"
|
||||
#include "sodium.h"
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/thread/shared_mutex.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#ifdef ENABLE_MINING
|
||||
#include <functional>
|
||||
@@ -1011,8 +1013,213 @@ enum RandomXSolverCancelCheck
|
||||
Reason2
|
||||
};
|
||||
|
||||
int GetRandomXInterval() { return GetArg("-ac_randomx_interval",1024); }
|
||||
int GetRandomXBlockLag() { return GetArg("-ac_randomx_lag", 64); }
|
||||
int GetRandomXInterval();
|
||||
int GetRandomXBlockLag();
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static void LogProcessMemory(const char* label) {
|
||||
// Use K32GetProcessMemoryInfo from kernel32.dll (available on Win7+)
|
||||
// to avoid linking psapi.lib
|
||||
typedef struct {
|
||||
DWORD cb;
|
||||
DWORD PageFaultCount;
|
||||
SIZE_T PeakWorkingSetSize;
|
||||
SIZE_T WorkingSetSize;
|
||||
SIZE_T QuotaPeakPagedPoolUsage;
|
||||
SIZE_T QuotaPagedPoolUsage;
|
||||
SIZE_T QuotaPeakNonPagedPoolUsage;
|
||||
SIZE_T QuotaNonPagedPoolUsage;
|
||||
SIZE_T PagefileUsage;
|
||||
SIZE_T PeakPagefileUsage;
|
||||
SIZE_T PrivateUsage;
|
||||
} PMC_EX;
|
||||
typedef BOOL (WINAPI *PFN)(HANDLE, PMC_EX*, DWORD);
|
||||
static PFN pfn = (PFN)GetProcAddress(GetModuleHandleA("kernel32.dll"), "K32GetProcessMemoryInfo");
|
||||
if (pfn) {
|
||||
PMC_EX pmc = {};
|
||||
pmc.cb = sizeof(pmc);
|
||||
if (pfn(GetCurrentProcess(), &pmc, sizeof(pmc))) {
|
||||
LogPrintf("MemDiag [%s]: WorkingSet=%.1fMB, PrivateUsage=%.1fMB, PagefileUsage=%.1fMB\n",
|
||||
label,
|
||||
pmc.WorkingSetSize / (1024.0 * 1024.0),
|
||||
pmc.PrivateUsage / (1024.0 * 1024.0),
|
||||
pmc.PagefileUsage / (1024.0 * 1024.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void LogProcessMemory(const char* label) {
|
||||
// Linux: read /proc/self/status
|
||||
FILE *f = fopen("/proc/self/status", "r");
|
||||
if (f) {
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
if (strncmp(line, "VmRSS:", 6) == 0 || strncmp(line, "VmSize:", 7) == 0) {
|
||||
// Remove newline
|
||||
line[strlen(line)-1] = '\0';
|
||||
LogPrintf("MemDiag [%s]: %s\n", label, line);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Shared RandomX dataset manager — all miner threads share a single ~2GB dataset
|
||||
// instead of each allocating their own. The dataset is read-only after initialization
|
||||
// and RandomX explicitly supports multiple VMs sharing one dataset.
|
||||
struct RandomXDatasetManager {
|
||||
randomx_flags flags;
|
||||
randomx_cache *cache;
|
||||
randomx_dataset *dataset;
|
||||
unsigned long datasetItemCount;
|
||||
std::string currentKey;
|
||||
std::mutex mtx; // protects Init/Shutdown/CreateVM
|
||||
boost::shared_mutex datasetMtx; // readers-writer lock: shared for hashing, exclusive for rebuild
|
||||
bool initialized;
|
||||
|
||||
RandomXDatasetManager() : flags(randomx_get_flags()), cache(nullptr), dataset(nullptr),
|
||||
datasetItemCount(0), initialized(false) {}
|
||||
|
||||
bool Init() {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
if (initialized) return true;
|
||||
|
||||
flags |= RANDOMX_FLAG_FULL_MEM;
|
||||
LogPrintf("RandomXDatasetManager: flags=0x%x (JIT=%d, HARD_AES=%d, FULL_MEM=%d, LARGE_PAGES=%d)\n",
|
||||
(int)flags,
|
||||
!!(flags & RANDOMX_FLAG_JIT), !!(flags & RANDOMX_FLAG_HARD_AES),
|
||||
!!(flags & RANDOMX_FLAG_FULL_MEM), !!(flags & RANDOMX_FLAG_LARGE_PAGES));
|
||||
|
||||
LogProcessMemory("before cache alloc");
|
||||
|
||||
cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES | RANDOMX_FLAG_SECURE);
|
||||
if (cache == nullptr) {
|
||||
LogPrintf("RandomXDatasetManager: cache alloc failed with large pages, trying without...\n");
|
||||
cache = randomx_alloc_cache(flags | RANDOMX_FLAG_SECURE);
|
||||
if (cache == nullptr) {
|
||||
LogPrintf("RandomXDatasetManager: cache alloc failed with secure, trying basic...\n");
|
||||
cache = randomx_alloc_cache(flags);
|
||||
if (cache == nullptr) {
|
||||
LogPrintf("RandomXDatasetManager: cannot allocate cache!\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
LogProcessMemory("after cache alloc");
|
||||
|
||||
// Try to allocate dataset with large pages first for better performance
|
||||
dataset = randomx_alloc_dataset(flags | RANDOMX_FLAG_LARGE_PAGES);
|
||||
if (dataset == nullptr) {
|
||||
LogPrintf("RandomXDatasetManager: dataset alloc failed with large pages, trying without...\n");
|
||||
dataset = randomx_alloc_dataset(flags);
|
||||
if (dataset == nullptr) {
|
||||
LogPrintf("RandomXDatasetManager: cannot allocate dataset!\n");
|
||||
randomx_release_cache(cache);
|
||||
cache = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
datasetItemCount = randomx_dataset_item_count();
|
||||
initialized = true;
|
||||
LogProcessMemory("after dataset alloc");
|
||||
// Log the actual memory addresses to help diagnose sharing issues
|
||||
uint8_t *datasetMemory = (uint8_t*)randomx_get_dataset_memory(dataset);
|
||||
size_t datasetSize = datasetItemCount * RANDOMX_DATASET_ITEM_SIZE;
|
||||
LogPrintf("RandomXDatasetManager: allocated shared dataset:\n");
|
||||
LogPrintf(" - Dataset struct at: %p\n", (void*)dataset);
|
||||
LogPrintf(" - Dataset memory at: %p (size: %.2f GB)\n", (void*)datasetMemory, datasetSize / (1024.0 * 1024.0 * 1024.0));
|
||||
LogPrintf(" - Items: %lu, Item size: %d bytes\n", datasetItemCount, RANDOMX_DATASET_ITEM_SIZE);
|
||||
LogPrintf(" - Expected total process memory: ~%.2f GB + ~2MB per mining thread\n", datasetSize / (1024.0 * 1024.0 * 1024.0));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize cache with a key and rebuild the dataset.
|
||||
// Thread-safe: acquires exclusive lock so all hashing threads must finish first.
|
||||
void UpdateKey(const void *key, size_t keySize) {
|
||||
std::string newKey((const char*)key, keySize);
|
||||
|
||||
// Fast check with shared lock — skip if key hasn't changed
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> readLock(datasetMtx);
|
||||
if (newKey == currentKey) return; // already up to date
|
||||
}
|
||||
|
||||
// Acquire exclusive lock — blocks until all hashing threads release their shared locks
|
||||
boost::unique_lock<boost::shared_mutex> writeLock(datasetMtx);
|
||||
// Double-check after acquiring exclusive lock (another thread may have rebuilt first)
|
||||
if (newKey == currentKey) return;
|
||||
|
||||
LogPrintf("RandomXDatasetManager: updating key (size=%lu)\n", keySize);
|
||||
randomx_init_cache(cache, key, keySize);
|
||||
currentKey = newKey;
|
||||
|
||||
// Rebuild dataset using all available CPU threads
|
||||
const int initThreadCount = std::thread::hardware_concurrency();
|
||||
if (initThreadCount > 1) {
|
||||
std::vector<std::thread> threads;
|
||||
uint32_t startItem = 0;
|
||||
const auto perThread = datasetItemCount / initThreadCount;
|
||||
const auto remainder = datasetItemCount % initThreadCount;
|
||||
for (int i = 0; i < initThreadCount; ++i) {
|
||||
const auto count = perThread + (i == initThreadCount - 1 ? remainder : 0);
|
||||
threads.push_back(std::thread(&randomx_init_dataset, dataset, cache, startItem, count));
|
||||
startItem += count;
|
||||
}
|
||||
for (unsigned i = 0; i < threads.size(); ++i) {
|
||||
threads[i].join();
|
||||
}
|
||||
} else {
|
||||
randomx_init_dataset(dataset, cache, 0, datasetItemCount);
|
||||
}
|
||||
LogPrintf("RandomXDatasetManager: dataset rebuilt\n");
|
||||
LogProcessMemory("after dataset init");
|
||||
}
|
||||
|
||||
// Creates a per-thread VM using the shared dataset.
|
||||
// Caller must hold a shared lock on datasetMtx.
|
||||
// The VM itself is small (~2MB scratchpad + ~84KB JIT code) — the ~2GB dataset is shared via pointer.
|
||||
// VMs should be created ONCE per thread and reused across blocks to avoid
|
||||
// heap fragmentation on Windows (repeated 2MB alloc/free causes address-space bloat).
|
||||
randomx_vm *CreateVM() {
|
||||
static std::atomic<int> vmCount{0};
|
||||
LogProcessMemory("before CreateVM");
|
||||
randomx_vm *vm = randomx_create_vm(flags, nullptr, dataset);
|
||||
if (vm != nullptr) {
|
||||
int id = ++vmCount;
|
||||
uint8_t *datasetMemory = (uint8_t*)randomx_get_dataset_memory(dataset);
|
||||
LogPrintf("RandomXDatasetManager: VM #%d created — VM at %p, shared dataset at %p\n",
|
||||
id, (void*)vm, (void*)datasetMemory);
|
||||
LogPrintf(" Per-thread overhead: ~2MB scratchpad + ~84KB JIT (dataset NOT copied)\n");
|
||||
LogProcessMemory("after CreateVM");
|
||||
}
|
||||
return vm;
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
if (dataset != nullptr) {
|
||||
randomx_release_dataset(dataset);
|
||||
dataset = nullptr;
|
||||
}
|
||||
if (cache != nullptr) {
|
||||
randomx_release_cache(cache);
|
||||
cache = nullptr;
|
||||
}
|
||||
initialized = false;
|
||||
currentKey.clear();
|
||||
LogPrintf("RandomXDatasetManager: shutdown complete\n");
|
||||
}
|
||||
|
||||
~RandomXDatasetManager() {
|
||||
Shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
// Global shared dataset manager, created by GenerateBitcoins before spawning miner threads
|
||||
static RandomXDatasetManager *g_rxDatasetManager = nullptr;
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
void static RandomXMiner(CWallet *pwallet)
|
||||
@@ -1050,33 +1257,12 @@ void static RandomXMiner()
|
||||
);
|
||||
miningTimer.start();
|
||||
|
||||
randomx_flags flags = randomx_get_flags();
|
||||
flags |= RANDOMX_FLAG_FULL_MEM;
|
||||
randomx_cache *randomxCache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES | RANDOMX_FLAG_SECURE );
|
||||
if (randomxCache == NULL) {
|
||||
LogPrintf("RandomX cache is null, trying without large pages...\n");
|
||||
randomxCache = randomx_alloc_cache(flags | RANDOMX_FLAG_SECURE);
|
||||
if (randomxCache == NULL) {
|
||||
LogPrintf("RandomX cache is null, trying without secure...\n");
|
||||
}
|
||||
randomxCache = randomx_alloc_cache(flags);
|
||||
if (randomxCache == NULL) {
|
||||
LogPrintf("RandomX cache is null, cannot mine!\n");
|
||||
}
|
||||
}
|
||||
|
||||
rxdebug("%s: created randomx flags + cache\n");
|
||||
randomx_dataset *randomxDataset = randomx_alloc_dataset(flags);
|
||||
rxdebug("%s: created dataset\n");
|
||||
|
||||
if( randomxDataset == nullptr) {
|
||||
LogPrintf("%s: allocating randomx dataset failed!\n", __func__);
|
||||
// Use the shared dataset manager — no per-thread dataset allocation
|
||||
if (g_rxDatasetManager == nullptr || !g_rxDatasetManager->initialized) {
|
||||
LogPrintf("HushRandomXMiner: shared dataset manager not initialized, aborting!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto datasetItemCount = randomx_dataset_item_count();
|
||||
rxdebug("%s: dataset items=%lu\n", datasetItemCount);
|
||||
|
||||
char randomxHash[RANDOMX_HASH_SIZE];
|
||||
rxdebug("%s: created randomxHash of size %d\n", RANDOMX_HASH_SIZE);
|
||||
char randomxKey[82]; // randomx spec says keysize of >60 bytes is implementation-specific
|
||||
@@ -1147,48 +1333,37 @@ void static RandomXMiner()
|
||||
|
||||
// fprintf(stderr,"RandomXMiner: using initial key with interval=%d and lag=%d\n", randomxInterval, randomxBlockLag);
|
||||
rxdebug("%s: using initial key, interval=%d, lag=%d, Mining_height=%u\n", randomxInterval, randomxBlockLag, Mining_height);
|
||||
// Use the initial key at the start of the chain, until the first key block
|
||||
// Update the shared dataset key — only one thread will actually rebuild,
|
||||
// others will see the key is already current and skip.
|
||||
if( (Mining_height) < randomxInterval + randomxBlockLag) {
|
||||
randomx_init_cache(randomxCache, &randomxKey, sizeof randomxKey);
|
||||
rxdebug("%s: initialized cache with initial key\n");
|
||||
g_rxDatasetManager->UpdateKey(randomxKey, strlen(randomxKey));
|
||||
rxdebug("%s: updated shared dataset with initial key\n");
|
||||
} else {
|
||||
rxdebug("%s: calculating keyHeight with randomxInterval=%d\n", randomxInterval);
|
||||
// At heights between intervals, we use the same block key and wait randomxBlockLag blocks until changing
|
||||
const int keyHeight = ((Mining_height - randomxBlockLag) / randomxInterval) * randomxInterval;
|
||||
uint256 randomxBlockKey = chainActive[keyHeight]->GetBlockHash();
|
||||
|
||||
randomx_init_cache(randomxCache, &randomxBlockKey, sizeof randomxBlockKey);
|
||||
rxdebug("%s: initialized cache with keyHeight=%d, randomxBlockKey=%s\n", keyHeight, randomxBlockKey.ToString().c_str());
|
||||
g_rxDatasetManager->UpdateKey(&randomxBlockKey, sizeof randomxBlockKey);
|
||||
rxdebug("%s: updated shared dataset with keyHeight=%d, randomxBlockKey=%s\n", keyHeight, randomxBlockKey.ToString().c_str());
|
||||
}
|
||||
|
||||
const int initThreadCount = std::thread::hardware_concurrency();
|
||||
if(initThreadCount > 1) {
|
||||
rxdebug("%s: initializing dataset with %d threads\n", initThreadCount);
|
||||
std::vector<std::thread> threads;
|
||||
uint32_t startItem = 0;
|
||||
const auto perThread = datasetItemCount / initThreadCount;
|
||||
const auto remainder = datasetItemCount % initThreadCount;
|
||||
for (int i = 0; i < initThreadCount; ++i) {
|
||||
const auto count = perThread + (i == initThreadCount - 1 ? remainder : 0);
|
||||
threads.push_back(std::thread(&randomx_init_dataset, randomxDataset, randomxCache, startItem, count));
|
||||
startItem += count;
|
||||
// Create a per-thread VM once and reuse across blocks.
|
||||
// The VM just stores a pointer to the shared dataset — the pointer
|
||||
// remains valid across key changes since UpdateKey rebuilds the dataset
|
||||
// contents in-place without reallocating. Reusing the VM avoids
|
||||
// repeated 2MB scratchpad + 84KB JIT alloc/free churn that causes
|
||||
// Windows heap fragmentation and apparent memory growth per thread.
|
||||
if (myVM == nullptr) {
|
||||
// First iteration: acquire shared lock briefly to create VM
|
||||
boost::shared_lock<boost::shared_mutex> initLock(g_rxDatasetManager->datasetMtx);
|
||||
myVM = g_rxDatasetManager->CreateVM();
|
||||
if (myVM == nullptr) {
|
||||
LogPrintf("RandomXMiner: Cannot create RandomX VM, aborting!\n");
|
||||
return;
|
||||
}
|
||||
for (unsigned i = 0; i < threads.size(); ++i) {
|
||||
threads[i].join();
|
||||
}
|
||||
threads.clear();
|
||||
} else {
|
||||
rxdebug("%s: initializing dataset with 1 thread\n");
|
||||
randomx_init_dataset(randomxDataset, randomxCache, 0, datasetItemCount);
|
||||
}
|
||||
|
||||
rxdebug("%s: dataset initialized\n");
|
||||
|
||||
myVM = randomx_create_vm(flags, nullptr, randomxDataset);
|
||||
if(myVM == NULL) {
|
||||
LogPrintf("RandomXMiner: Cannot create RandomX VM, aborting!\n");
|
||||
return;
|
||||
}
|
||||
// Acquire shared lock to prevent dataset rebuild while we're hashing
|
||||
boost::shared_lock<boost::shared_mutex> datasetLock(g_rxDatasetManager->datasetMtx);
|
||||
//fprintf(stderr,"RandomXMiner: Mining_start=%u\n", Mining_start);
|
||||
#ifdef ENABLE_WALLET
|
||||
CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, pindexPrev->GetHeight()+1, gpucount, 0);
|
||||
@@ -1268,16 +1443,17 @@ void static RandomXMiner()
|
||||
arith_uint256 hashTarget;
|
||||
hashTarget = HASHTarget;
|
||||
|
||||
CRandomXInput rxInput(pblocktemplate->block);
|
||||
CDataStream randomxInput(SER_NETWORK, PROTOCOL_VERSION);
|
||||
// Use the current block as randomx input
|
||||
randomxInput << pblocktemplate->block;
|
||||
// Serialize block header without nSolution but with nNonce for deterministic RandomX input
|
||||
randomxInput << rxInput;
|
||||
|
||||
// std::cerr << "RandomXMiner: randomxInput=" << HexStr(randomxInput) << "\n";
|
||||
// fprintf(stderr,"RandomXMiner: created randomxKey=%s , randomxInput.size=%lu\n", randomxKey, randomxInput.size() ); //randomxInput);
|
||||
rxdebug("%s: randomxKey=%s randomxInput=%s\n", randomxKey, HexStr(randomxInput).c_str());
|
||||
|
||||
rxdebug("%s: calculating randomx hash\n");
|
||||
randomx_calculate_hash(myVM, &randomxInput, sizeof randomxInput, randomxHash);
|
||||
randomx_calculate_hash(myVM, &randomxInput[0], randomxInput.size(), randomxHash);
|
||||
rxdebug("%s: calculated randomx hash\n");
|
||||
|
||||
rxdebug("%s: randomxHash=");
|
||||
@@ -1324,14 +1500,28 @@ void static RandomXMiner()
|
||||
|
||||
CValidationState state;
|
||||
//{ LOCK(cs_main);
|
||||
if ( !TestBlockValidity(state,B, chainActive.LastTip(), true, false))
|
||||
// Skip RandomX re-validation during TestBlockValidity — we already
|
||||
// computed the correct hash, and re-verifying allocates ~256MB which
|
||||
// can trigger the OOM killer on memory-constrained systems.
|
||||
SetSkipRandomXValidation(true);
|
||||
bool fValid = TestBlockValidity(state,B, chainActive.LastTip(), true, false);
|
||||
SetSkipRandomXValidation(false);
|
||||
if ( !fValid )
|
||||
{
|
||||
h = UintToArith256(B.GetHash());
|
||||
fprintf(stderr,"RandomXMiner: Invalid randomx block mined, try again ");
|
||||
fprintf(stderr,"RandomXMiner: TestBlockValidity FAILED at ht.%d nNonce=%s hash=",
|
||||
Mining_height, pblock->nNonce.ToString().c_str());
|
||||
for (z=31; z>=0; z--)
|
||||
fprintf(stderr,"%02x",((uint8_t *)&h)[z]);
|
||||
gotinvalid = 1;
|
||||
fprintf(stderr," nSolution.size=%lu\n", B.nSolution.size());
|
||||
// Dump nSolution hex for comparison with validator
|
||||
fprintf(stderr,"RandomXMiner: nSolution=");
|
||||
for (unsigned i = 0; i < B.nSolution.size(); i++)
|
||||
fprintf(stderr,"%02x", B.nSolution[i]);
|
||||
fprintf(stderr,"\n");
|
||||
LogPrintf("RandomXMiner: TestBlockValidity FAILED at ht.%d, gotinvalid=1, state=%s\n",
|
||||
Mining_height, state.GetRejectReason());
|
||||
gotinvalid = 1;
|
||||
return(false);
|
||||
}
|
||||
//}
|
||||
@@ -1399,21 +1589,24 @@ void static RandomXMiner()
|
||||
pblock->nBits = savebits;
|
||||
}
|
||||
|
||||
rxdebug("%s: going to destroy rx VM\n");
|
||||
randomx_destroy_vm(myVM);
|
||||
rxdebug("%s: destroyed VM\n");
|
||||
// Release shared lock so UpdateKey can acquire exclusive lock for dataset rebuild
|
||||
// VM is kept alive — its dataset pointer remains valid across rebuilds
|
||||
datasetLock.unlock();
|
||||
}
|
||||
|
||||
} catch (const boost::thread_interrupted&) {
|
||||
miningTimer.stop();
|
||||
c.disconnect();
|
||||
|
||||
randomx_destroy_vm(myVM);
|
||||
LogPrintf("%s: destroyed vm via thread interrupt\n", __func__);
|
||||
randomx_release_dataset(randomxDataset);
|
||||
rxdebug("%s: released dataset via thread interrupt\n");
|
||||
randomx_release_cache(randomxCache);
|
||||
rxdebug("%s: released cache via thread interrupt\n");
|
||||
if (myVM != nullptr) {
|
||||
randomx_destroy_vm(myVM);
|
||||
myVM = nullptr;
|
||||
LogPrintf("%s: destroyed vm via thread interrupt\n", __func__);
|
||||
} else {
|
||||
LogPrintf("%s: WARNING myVM already null in thread interrupt handler, skipping destroy (would double-free)\n", __func__);
|
||||
fprintf(stderr, "%s: WARNING myVM already null in thread interrupt, would have double-freed!\n", __func__);
|
||||
}
|
||||
// Dataset and cache are owned by g_rxDatasetManager — do NOT release here
|
||||
|
||||
LogPrintf("HushRandomXMiner terminated\n");
|
||||
throw;
|
||||
@@ -1422,20 +1615,21 @@ void static RandomXMiner()
|
||||
c.disconnect();
|
||||
fprintf(stderr,"RandomXMiner: runtime error: %s\n", e.what());
|
||||
|
||||
randomx_destroy_vm(myVM);
|
||||
LogPrintf("%s: destroyed vm because of error\n", __func__);
|
||||
randomx_release_dataset(randomxDataset);
|
||||
rxdebug("%s: released dataset because of error\n");
|
||||
randomx_release_cache(randomxCache);
|
||||
rxdebug("%s: released cache because of error\n");
|
||||
if (myVM != nullptr) {
|
||||
randomx_destroy_vm(myVM);
|
||||
myVM = nullptr;
|
||||
LogPrintf("%s: destroyed vm because of error\n", __func__);
|
||||
}
|
||||
// Dataset and cache are owned by g_rxDatasetManager — do NOT release here
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
randomx_release_dataset(randomxDataset);
|
||||
rxdebug("%s: released dataset in normal exit\n");
|
||||
randomx_release_cache(randomxCache);
|
||||
rxdebug("%s: released cache in normal exit\n");
|
||||
// Only destroy per-thread VM, dataset/cache are shared
|
||||
if (myVM != nullptr) {
|
||||
randomx_destroy_vm(myVM);
|
||||
myVM = nullptr;
|
||||
}
|
||||
miningTimer.stop();
|
||||
c.disconnect();
|
||||
}
|
||||
@@ -1879,8 +2073,18 @@ void static BitcoinMiner()
|
||||
if (minerThreads != NULL)
|
||||
{
|
||||
minerThreads->interrupt_all();
|
||||
// Wait for all miner threads to fully terminate before destroying shared resources
|
||||
minerThreads->join_all();
|
||||
delete minerThreads;
|
||||
minerThreads = NULL;
|
||||
|
||||
// Shutdown shared RandomX dataset manager after all threads are done
|
||||
if (g_rxDatasetManager != nullptr) {
|
||||
g_rxDatasetManager->Shutdown();
|
||||
delete g_rxDatasetManager;
|
||||
g_rxDatasetManager = nullptr;
|
||||
LogPrintf("%s: destroyed shared RandomX dataset manager\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
if(fDebug)
|
||||
@@ -1895,6 +2099,21 @@ void static BitcoinMiner()
|
||||
|
||||
minerThreads = new boost::thread_group();
|
||||
|
||||
// Initialize shared RandomX dataset manager before spawning miner threads
|
||||
if (ASSETCHAINS_ALGO == ASSETCHAINS_RANDOMX) {
|
||||
g_rxDatasetManager = new RandomXDatasetManager();
|
||||
if (!g_rxDatasetManager->Init()) {
|
||||
LogPrintf("%s: FATAL - Failed to initialize shared RandomX dataset manager\n", __func__);
|
||||
fprintf(stderr, "%s: FATAL - Failed to initialize shared RandomX dataset manager\n", __func__);
|
||||
delete g_rxDatasetManager;
|
||||
g_rxDatasetManager = nullptr;
|
||||
delete minerThreads;
|
||||
minerThreads = NULL;
|
||||
return;
|
||||
}
|
||||
LogPrintf("%s: shared RandomX dataset manager initialized\n", __func__);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nThreads; i++) {
|
||||
#ifdef ENABLE_WALLET
|
||||
if ( ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH ) {
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include "crypto/common.h"
|
||||
#include "hush/utiltls.h"
|
||||
#include <random.h>
|
||||
#include <random>
|
||||
#include <limits>
|
||||
#ifdef _WIN32
|
||||
#include <string.h>
|
||||
#else
|
||||
@@ -2004,7 +2006,7 @@ void ThreadMessageHandler()
|
||||
// Randomize the order in which we process messages from/to our peers.
|
||||
// This prevents attacks in which an attacker exploits having multiple
|
||||
// consecutive connections in the vNodes list.
|
||||
random_shuffle(vNodesCopy.begin(), vNodesCopy.end(), GetRandInt);
|
||||
std::shuffle(vNodesCopy.begin(), vNodesCopy.end(), std::mt19937(GetRand(std::numeric_limits<uint32_t>::max())));
|
||||
|
||||
BOOST_FOREACH(CNode* pnode, vNodesCopy)
|
||||
{
|
||||
@@ -2516,7 +2518,7 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss)
|
||||
// We always round down, except when we have only 1 connection
|
||||
auto newSize = (vNodes.size() / 2) == 0 ? 1 : (vNodes.size() / 2);
|
||||
|
||||
random_shuffle( vRelayNodes.begin(), vRelayNodes.end(), GetRandInt );
|
||||
std::shuffle( vRelayNodes.begin(), vRelayNodes.end(), std::mt19937(GetRand(std::numeric_limits<uint32_t>::max())) );
|
||||
|
||||
vRelayNodes.resize(newSize);
|
||||
if (HUSH_TESTNODE==1 && vNodes.size() == 0) {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <tuple>
|
||||
|
||||
constexpr uint64_t CNetAddr::V1_SERIALIZATION_SIZE;
|
||||
constexpr uint64_t CNetAddr::MAX_ADDRV2_SIZE;
|
||||
|
||||
/** check whether a given address is in a network we can probably connect to */
|
||||
bool CNetAddr::IsReachableNetwork() {
|
||||
|
||||
306
src/pow.cpp
306
src/pow.cpp
@@ -18,6 +18,7 @@
|
||||
* *
|
||||
******************************************************************************/
|
||||
#include "pow.h"
|
||||
#include "checkpoints.h"
|
||||
#include "consensus/upgrades.h"
|
||||
#include "arith_uint256.h"
|
||||
#include "chain.h"
|
||||
@@ -28,6 +29,10 @@
|
||||
#include "uint256.h"
|
||||
#include "util.h"
|
||||
#include "sodium.h"
|
||||
#include "RandomX/src/randomx.h"
|
||||
#include <mutex>
|
||||
#include <boost/thread/shared_mutex.hpp>
|
||||
#include <boost/thread/locks.hpp>
|
||||
|
||||
#ifdef ENABLE_RUST
|
||||
#include "librustzcash.h"
|
||||
@@ -313,6 +318,16 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
|
||||
if (pindexLast == NULL )
|
||||
return nProofOfWorkLimit;
|
||||
|
||||
// DragonX difficulty reset at the RANDOMX_VALIDATION activation height.
|
||||
// The chain transitioned to a new binary at this height and difficulty was
|
||||
// reset to minimum (powLimit). Without this, fresh-syncing nodes compute
|
||||
// a different nBits from GetNextWorkRequired (based on pre-reset blocks)
|
||||
// and reject the on-chain min-diff block, banning all seed nodes.
|
||||
if (ASSETCHAINS_RANDOMX_VALIDATION > 0 && pindexLast->GetHeight() + 1 == ASSETCHAINS_RANDOMX_VALIDATION) {
|
||||
LogPrintf("%s: difficulty reset to powLimit at height %d\n", __func__, ASSETCHAINS_RANDOMX_VALIDATION);
|
||||
return nProofOfWorkLimit;
|
||||
}
|
||||
|
||||
//{
|
||||
// Comparing to pindexLast->nHeight with >= because this function
|
||||
// returns the work required for the block after pindexLast.
|
||||
@@ -337,6 +352,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
|
||||
memset(zflags,0,sizeof(zflags));
|
||||
if ( pindexLast != 0 )
|
||||
height = (int32_t)pindexLast->GetHeight() + 1;
|
||||
|
||||
if ( ASSETCHAINS_ADAPTIVEPOW > 0 && pindexFirst != 0 && pblock != 0 && height >= (int32_t)(sizeof(ct)/sizeof(*ct)) )
|
||||
{
|
||||
tipdiff = (pblock->nTime - pindexFirst->nTime);
|
||||
@@ -683,6 +699,281 @@ bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& param
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetRandomXInterval() { return GetArg("-ac_randomx_interval", 1024); }
|
||||
int GetRandomXBlockLag() { return GetArg("-ac_randomx_lag", 64); }
|
||||
|
||||
// Cached RandomX validation state — reused across calls, protected by mutex
|
||||
static std::mutex cs_randomx_validator;
|
||||
static randomx_cache *s_rxCache = nullptr;
|
||||
static randomx_vm *s_rxVM = nullptr;
|
||||
static std::string s_rxCurrentKey; // tracks current key to avoid re-init
|
||||
static int64_t nTimeRandomX = 0; // cumulative RandomX validation time (us), reported under -debug=bench
|
||||
|
||||
// Thread-local flag: skip CheckRandomXSolution when the miner is validating its own block
|
||||
// The miner already computed the correct RandomX hash — re-verifying with a separate
|
||||
// cache+VM would allocate ~256MB extra memory and can trigger the OOM killer.
|
||||
thread_local bool fSkipRandomXValidation = false;
|
||||
|
||||
void SetSkipRandomXValidation(bool skip) { fSkipRandomXValidation = skip; }
|
||||
bool GetSkipRandomXValidation() { return fSkipRandomXValidation; }
|
||||
|
||||
CBlockIndex *hush_chainactive(int32_t height);
|
||||
|
||||
// Centralized predicate: does a block at this height actually require a RandomX hash check?
|
||||
// Shared by CheckRandomXSolution (inline path) and the parallel pre-verify pool so the two can
|
||||
// never drift. Returns false when the recompute is unnecessary:
|
||||
// - non-RandomX chain, or RandomX validation disabled (activation height < 0)
|
||||
// - below the RandomX activation height (those blocks used Equihash, validated elsewhere)
|
||||
// - during initial on-disk block loading / reindex (HUSH_LOADINGBLOCKS)
|
||||
// - below the last hardcoded checkpoint (chain pinned by checkpoint hash + linkage + work)
|
||||
// Deliberately does NOT consider the thread-local fSkipRandomXValidation (miner self-check) — that
|
||||
// is a property of the calling thread, handled only in the inline CheckRandomXSolution below.
|
||||
bool RandomXValidationRequired(int32_t height)
|
||||
{
|
||||
if (ASSETCHAINS_ALGO != ASSETCHAINS_RANDOMX)
|
||||
return false;
|
||||
if (ASSETCHAINS_RANDOMX_VALIDATION < 0)
|
||||
return false;
|
||||
if (height < ASSETCHAINS_RANDOMX_VALIDATION)
|
||||
return false;
|
||||
extern int32_t HUSH_LOADINGBLOCKS;
|
||||
if (HUSH_LOADINGBLOCKS != 0)
|
||||
return false;
|
||||
extern bool fCheckpointsEnabled;
|
||||
// Gate the RandomX skip on the last checkpoint actually LOCKED INTO this node's block index
|
||||
// (GetLastCheckpoint), NOT the static top checkpoint (GetTotalBlocksEstimate). The fork-rejection
|
||||
// guard uses this same in-index boundary, so a block below it is provably on the checkpoint-pinned
|
||||
// chain and cannot be a forged fork. Using the static boundary would, once checkpoints extend above
|
||||
// the RandomX activation height, leave a gap (in-index checkpoint .. static top) during IBD/eclipse
|
||||
// where a no-hashpower peer could get SHA256-grinded, RandomX-forged blocks accepted. Safe to walk
|
||||
// mapBlockIndex here: called only under cs_main (ActivateBestChainStep + inline CheckRandomXSolution).
|
||||
if (fCheckpointsEnabled) {
|
||||
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(Params().Checkpoints());
|
||||
if (pcheckpoint != NULL && height < pcheckpoint->GetHeight())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Serialize the RandomX hash input: the block header without nSolution (but with nNonce). Used by
|
||||
// both the inline CheckRandomXSolution and the parallel pre-verify pool, so the bytes are identical.
|
||||
std::vector<unsigned char> GetRandomXInput(const CBlockHeader& block)
|
||||
{
|
||||
CRandomXInput rxInput(block);
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << rxInput;
|
||||
return std::vector<unsigned char>(ss.begin(), ss.end());
|
||||
}
|
||||
|
||||
// Derive the RandomX key string for a block at `height`. Below interval+lag it is the chain-params
|
||||
// initial key; otherwise the block hash at the key-rotation height. MUST be called under cs_main
|
||||
// (reads chainActive via hush_chainactive). Returns empty if the key-height block is unavailable.
|
||||
std::string GetRandomXKey(int32_t height)
|
||||
{
|
||||
static int randomxInterval = GetRandomXInterval();
|
||||
static int randomxBlockLag = GetRandomXBlockLag();
|
||||
if (height < randomxInterval + randomxBlockLag) {
|
||||
char initialKey[82];
|
||||
snprintf(initialKey, 81, "%08x%s%08x", ASSETCHAINS_MAGIC, SMART_CHAIN_SYMBOL, ASSETCHAINS_RPCPORT);
|
||||
return std::string(initialKey, strlen(initialKey));
|
||||
}
|
||||
int keyHeight = ((height - randomxBlockLag) / randomxInterval) * randomxInterval;
|
||||
CBlockIndex *pKeyIndex = hush_chainactive(keyHeight);
|
||||
if (pKeyIndex == nullptr)
|
||||
return std::string();
|
||||
uint256 blockKey = pKeyIndex->GetBlockHash();
|
||||
return std::string((const char*)&blockKey, sizeof(blockKey));
|
||||
}
|
||||
|
||||
bool CheckRandomXSolution(const CBlockHeader *pblock, int32_t height)
|
||||
{
|
||||
// Centralized height gate (shared with the parallel pre-verify pool, Stage 0).
|
||||
if (!RandomXValidationRequired(height))
|
||||
return true;
|
||||
|
||||
// Skip when the miner is validating its own freshly-mined block via TestBlockValidity
|
||||
// (thread-local; never set on the connect thread or the pre-verify worker threads).
|
||||
if (fSkipRandomXValidation)
|
||||
return true;
|
||||
|
||||
// nSolution must be exactly RANDOMX_HASH_SIZE (32) bytes
|
||||
if (pblock->nSolution.size() != RANDOMX_HASH_SIZE) {
|
||||
return error("CheckRandomXSolution(): nSolution size %u != expected %d at height %d",
|
||||
pblock->nSolution.size(), RANDOMX_HASH_SIZE, height);
|
||||
}
|
||||
|
||||
// Derive the key (shared helper) and serialize the input (identical bytes to the pool path).
|
||||
std::string rxKey = GetRandomXKey(height);
|
||||
if (rxKey.empty())
|
||||
return error("CheckRandomXSolution(): cannot derive RandomX key for height %d", height);
|
||||
std::vector<unsigned char> ssInput = GetRandomXInput(*pblock);
|
||||
|
||||
char computedHash[RANDOMX_HASH_SIZE];
|
||||
|
||||
// Measurement (Track 1): isolate RandomX verification cost during IBD. The
|
||||
// expensive parts are the per-key cache (re)init (~every GetRandomXInterval()
|
||||
// blocks) and the hash computation itself; both happen under the lock below.
|
||||
int64_t nTimeRxStart = GetTimeMicros();
|
||||
bool fKeyInit = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cs_randomx_validator);
|
||||
|
||||
// Initialize cache + VM if needed, or re-init if key changed
|
||||
if (s_rxCache == nullptr) {
|
||||
randomx_flags flags = randomx_get_flags();
|
||||
// Try large pages for the 256MB validator cache: fewer TLB misses → ~15-30% faster
|
||||
// light-mode validation where the OS has hugepages configured. Falls back transparently
|
||||
// when unavailable, exactly as the miner does (miner.cpp:1097). Page size does not affect
|
||||
// the computed hash, so this is consensus-neutral.
|
||||
bool fLargePages = true;
|
||||
s_rxCache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
|
||||
if (s_rxCache == nullptr) {
|
||||
fLargePages = false;
|
||||
s_rxCache = randomx_alloc_cache(flags);
|
||||
}
|
||||
if (s_rxCache == nullptr) {
|
||||
return error("CheckRandomXSolution(): failed to allocate RandomX cache");
|
||||
}
|
||||
// Confirm the fast paths are active (JIT off would be ~9x slower; see randomx-benchmark).
|
||||
LogPrint("bench", "CheckRandomXSolution: RandomX flags=0x%x JIT=%d HARD_AES=%d largePages=%d\n",
|
||||
(unsigned int)flags, !!(flags & RANDOMX_FLAG_JIT), !!(flags & RANDOMX_FLAG_HARD_AES), (int)fLargePages);
|
||||
randomx_init_cache(s_rxCache, rxKey.data(), rxKey.size());
|
||||
s_rxCurrentKey = rxKey;
|
||||
fKeyInit = true;
|
||||
s_rxVM = randomx_create_vm(flags, s_rxCache, nullptr);
|
||||
if (s_rxVM == nullptr) {
|
||||
randomx_release_cache(s_rxCache);
|
||||
s_rxCache = nullptr;
|
||||
return error("CheckRandomXSolution(): failed to create RandomX VM");
|
||||
}
|
||||
} else if (s_rxCurrentKey != rxKey) {
|
||||
randomx_init_cache(s_rxCache, rxKey.data(), rxKey.size());
|
||||
s_rxCurrentKey = rxKey;
|
||||
fKeyInit = true;
|
||||
randomx_vm_set_cache(s_rxVM, s_rxCache);
|
||||
}
|
||||
|
||||
randomx_calculate_hash(s_rxVM, ssInput.data(), ssInput.size(), computedHash);
|
||||
}
|
||||
int64_t nTimeRxEnd = GetTimeMicros();
|
||||
nTimeRandomX += nTimeRxEnd - nTimeRxStart;
|
||||
LogPrint("bench", " - RandomX verify ht=%d: %.2fms%s [%.2fs]\n",
|
||||
height, (nTimeRxEnd - nTimeRxStart) * 0.001,
|
||||
fKeyInit ? " (key-init)" : "", nTimeRandomX * 0.000001);
|
||||
|
||||
// Compare computed hash against nSolution
|
||||
if (memcmp(computedHash, pblock->nSolution.data(), RANDOMX_HASH_SIZE) != 0) {
|
||||
// Debug: dump both hashes for diagnosis
|
||||
std::string computedHex, solutionHex;
|
||||
for (int i = 0; i < RANDOMX_HASH_SIZE; i++) {
|
||||
char buf[4];
|
||||
snprintf(buf, sizeof(buf), "%02x", (uint8_t)computedHash[i]);
|
||||
computedHex += buf;
|
||||
snprintf(buf, sizeof(buf), "%02x", pblock->nSolution[i]);
|
||||
solutionHex += buf;
|
||||
}
|
||||
fprintf(stderr, "CheckRandomXSolution(): HASH MISMATCH at height %d\n", height);
|
||||
fprintf(stderr, " computed : %s\n", computedHex.c_str());
|
||||
fprintf(stderr, " nSolution: %s\n", solutionHex.c_str());
|
||||
fprintf(stderr, " rxKey size=%lu, input size=%lu, nNonce=%s\n",
|
||||
rxKey.size(), ssInput.size(), pblock->nNonce.ToString().c_str());
|
||||
fprintf(stderr, " nSolution.size()=%lu, RANDOMX_HASH_SIZE=%d\n",
|
||||
pblock->nSolution.size(), RANDOMX_HASH_SIZE);
|
||||
// Also log to debug.log
|
||||
LogPrintf("CheckRandomXSolution(): HASH MISMATCH at height %d\n", height);
|
||||
LogPrintf(" computed : %s\n", computedHex);
|
||||
LogPrintf(" nSolution: %s\n", solutionHex);
|
||||
LogPrintf(" rxKey size=%lu, input size=%lu, nNonce=%s\n",
|
||||
rxKey.size(), ssInput.size(), pblock->nNonce.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
LogPrint("randomx", "CheckRandomXSolution(): valid at height %d\n", height);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================================
|
||||
// Parallel RandomX pre-verification pool (Stage 2).
|
||||
// One shared light-mode cache (holding a single key at a time) + per-thread VMs, mirroring the
|
||||
// miner's RandomXDatasetManager pattern (miner.cpp). The connect thread (ActivateBestChainStep)
|
||||
// loads the cache key for a same-key group of about-to-be-connected blocks, dispatches them to
|
||||
// this pool, and barrier-waits; each worker hashes on its own VM (sharing the read-only cache)
|
||||
// and, on a match, sets the block's transient fRandomXVerified flag so the inline check in
|
||||
// CheckBlockHeader can be skipped. The inline path remains the consensus authority for anything
|
||||
// not pre-verified, so the pool can only ever flip false->true on a real hash match.
|
||||
static boost::shared_mutex g_rxvMutex; // shared = hashing; exclusive = cache (re)init
|
||||
static randomx_cache* g_rxvCache = nullptr; // shared, read-only during hashing
|
||||
static std::string g_rxvKey; // key currently loaded into g_rxvCache
|
||||
static randomx_flags g_rxvFlags;
|
||||
static thread_local randomx_vm* tls_rxvVM = nullptr;
|
||||
static thread_local std::string tls_rxvVMKey;
|
||||
|
||||
CCheckQueue<CRandomXCheck> rxCheckQueue(1); // batch size 1: each item is ~tens of ms
|
||||
|
||||
bool RandomXValidatorPrepareKey(const std::string& rxKey)
|
||||
{
|
||||
boost::unique_lock<boost::shared_mutex> lock(g_rxvMutex);
|
||||
if (g_rxvCache == nullptr) {
|
||||
g_rxvFlags = randomx_get_flags();
|
||||
g_rxvCache = randomx_alloc_cache(g_rxvFlags | RANDOMX_FLAG_LARGE_PAGES);
|
||||
if (g_rxvCache == nullptr)
|
||||
g_rxvCache = randomx_alloc_cache(g_rxvFlags);
|
||||
if (g_rxvCache == nullptr) {
|
||||
LogPrintf("RandomXValidatorPrepareKey: cache alloc failed; parallel pre-verify disabled\n");
|
||||
return false;
|
||||
}
|
||||
randomx_init_cache(g_rxvCache, rxKey.data(), rxKey.size());
|
||||
g_rxvKey = rxKey;
|
||||
return true;
|
||||
}
|
||||
if (g_rxvKey != rxKey) {
|
||||
randomx_init_cache(g_rxvCache, rxKey.data(), rxKey.size());
|
||||
g_rxvKey = rxKey;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRandomXCheck::operator()()
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(g_rxvMutex);
|
||||
// The connect thread set the shared cache to one key before dispatching this group. If this
|
||||
// item's key doesn't match (e.g. a key-rotation straggler) or the cache is unavailable, skip it
|
||||
// and leave *presult false — the inline CheckRandomXSolution will verify it.
|
||||
if (g_rxvCache == nullptr || g_rxvKey != rxKey)
|
||||
return true;
|
||||
if (tls_rxvVM == nullptr) {
|
||||
tls_rxvVM = randomx_create_vm(g_rxvFlags, g_rxvCache, nullptr);
|
||||
if (tls_rxvVM == nullptr)
|
||||
return true; // cannot verify here -> inline fallback
|
||||
tls_rxvVMKey = g_rxvKey;
|
||||
} else if (tls_rxvVMKey != g_rxvKey) {
|
||||
// Cache was re-initialized to a new key since this VM last ran; rebind.
|
||||
randomx_vm_set_cache(tls_rxvVM, g_rxvCache);
|
||||
tls_rxvVMKey = g_rxvKey;
|
||||
}
|
||||
unsigned char h[RANDOMX_HASH_SIZE];
|
||||
randomx_calculate_hash(tls_rxvVM, input.data(), input.size(), h);
|
||||
if (memcmp(h, expected, RANDOMX_HASH_SIZE) == 0 && presult != nullptr)
|
||||
*presult = true;
|
||||
return true; // ALWAYS true: never short-circuit the queue; per-block result is in *presult
|
||||
}
|
||||
|
||||
void ThreadRandomXVerify()
|
||||
{
|
||||
RenameThread("hush-rxverify");
|
||||
rxCheckQueue.Thread();
|
||||
}
|
||||
|
||||
void RandomXValidatorShutdown()
|
||||
{
|
||||
boost::unique_lock<boost::shared_mutex> lock(g_rxvMutex);
|
||||
// Per-thread VMs are intentionally leaked (process exiting); release the shared cache.
|
||||
if (g_rxvCache != nullptr) {
|
||||
randomx_release_cache(g_rxvCache);
|
||||
g_rxvCache = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t hush_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp);
|
||||
int32_t hush_currentheight();
|
||||
void hush_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height);
|
||||
@@ -726,9 +1017,19 @@ bool CheckProofOfWork(const CBlockHeader &blkHeader, uint8_t *pubkey33, int32_t
|
||||
// Check proof of work matches claimed amount
|
||||
if ( UintToArith256(hash = blkHeader.GetHash()) > bnTarget )
|
||||
{
|
||||
if ( HUSH_LOADINGBLOCKS != 0 )
|
||||
return true;
|
||||
// During initial block loading/sync, skip PoW validation for blocks
|
||||
// before RandomX validation height. After activation, always validate
|
||||
// to prevent injection of blocks with fake PoW.
|
||||
if ( HUSH_LOADINGBLOCKS != 0 ) {
|
||||
if (ASSETCHAINS_ALGO == ASSETCHAINS_RANDOMX && ASSETCHAINS_RANDOMX_VALIDATION > 0 && height >= ASSETCHAINS_RANDOMX_VALIDATION) {
|
||||
// Fall through to reject the block — do NOT skip validation after activation
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( SMART_CHAIN_SYMBOL[0] != 0 || height > 792000 )
|
||||
{
|
||||
if ( Params().NetworkIDString() != "regtest" )
|
||||
{
|
||||
for (i=31; i>=0; i--)
|
||||
@@ -745,6 +1046,7 @@ bool CheckProofOfWork(const CBlockHeader &blkHeader, uint8_t *pubkey33, int32_t
|
||||
fprintf(stderr," <- origpubkey\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/*for (i=31; i>=0; i--)
|
||||
fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
|
||||
|
||||
67
src/pow.h
67
src/pow.h
@@ -21,8 +21,13 @@
|
||||
#define HUSH_POW_H
|
||||
|
||||
#include "chain.h"
|
||||
#include "checkqueue.h"
|
||||
#include "consensus/params.h"
|
||||
#include <stdint.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class CBlockHeader;
|
||||
class CBlockIndex;
|
||||
@@ -38,6 +43,68 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
|
||||
/** Check whether the Equihash solution in a block header is valid */
|
||||
bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams&);
|
||||
|
||||
/** Check whether a block header contains a valid RandomX solution */
|
||||
bool CheckRandomXSolution(const CBlockHeader *pblock, int32_t height);
|
||||
|
||||
/** Whether a block at this height requires a RandomX hash check (shared gate used by both the
|
||||
* inline CheckRandomXSolution and the parallel pre-verification pool). */
|
||||
bool RandomXValidationRequired(int32_t height);
|
||||
|
||||
/** Serialize the RandomX hash input (block header without nSolution) — identical bytes to the
|
||||
* inline CheckRandomXSolution path, so the parallel pool computes the same hash. */
|
||||
std::vector<unsigned char> GetRandomXInput(const CBlockHeader& block);
|
||||
|
||||
/** Derive the RandomX key string for a block at `height`. MUST be called under cs_main (reads
|
||||
* chainActive). Returns empty string if the key-height block is unavailable. */
|
||||
std::string GetRandomXKey(int32_t height);
|
||||
|
||||
/** A single RandomX pre-verification work item for the parallel validator pool. Pure value type
|
||||
* (no chainstate pointers) so workers need no cs_main. On a hash match it sets *presult=true; on
|
||||
* any failure it leaves *presult untouched — the inline CheckRandomXSolution remains the
|
||||
* consensus authority and re-verifies anything not pre-verified. operator() ALWAYS returns true,
|
||||
* so one block's failure never short-circuits the rest of the CCheckQueue batch. */
|
||||
class CRandomXCheck
|
||||
{
|
||||
private:
|
||||
std::string rxKey; // RandomX key for this block's height
|
||||
std::vector<unsigned char> input; // serialized CRandomXInput(header)
|
||||
unsigned char expected[32]; // block.nSolution (claimed RandomX hash)
|
||||
bool* presult; // -> pindex->fRandomXVerified (set true only on a hash match)
|
||||
public:
|
||||
CRandomXCheck() : presult(nullptr) { memset(expected, 0, sizeof(expected)); }
|
||||
CRandomXCheck(const std::string& keyIn, std::vector<unsigned char> inputIn,
|
||||
const unsigned char* expectedIn, bool* presultIn)
|
||||
: rxKey(keyIn), input(std::move(inputIn)), presult(presultIn)
|
||||
{ memcpy(expected, expectedIn, sizeof(expected)); }
|
||||
bool operator()();
|
||||
void swap(CRandomXCheck& c) {
|
||||
rxKey.swap(c.rxKey);
|
||||
input.swap(c.input);
|
||||
std::swap(presult, c.presult);
|
||||
for (int i = 0; i < 32; i++) std::swap(expected[i], c.expected[i]);
|
||||
}
|
||||
};
|
||||
|
||||
/** The RandomX pre-verification check queue (parallel pool). */
|
||||
extern CCheckQueue<CRandomXCheck> rxCheckQueue;
|
||||
/** Worker entry point (spawn N at startup, mirrors ThreadScriptCheck). */
|
||||
void ThreadRandomXVerify();
|
||||
/** Load `rxKey` into the shared validator cache (alloc on first use); call before dispatching a
|
||||
* same-key group of checks. Returns false on allocation failure. */
|
||||
bool RandomXValidatorPrepareKey(const std::string& rxKey);
|
||||
/** Release the shared validator cache at shutdown. */
|
||||
void RandomXValidatorShutdown();
|
||||
|
||||
/** Set thread-local flag to skip RandomX validation (used by miner during TestBlockValidity) */
|
||||
void SetSkipRandomXValidation(bool skip);
|
||||
bool GetSkipRandomXValidation();
|
||||
|
||||
/** Return the RandomX key rotation interval in blocks */
|
||||
int GetRandomXInterval();
|
||||
|
||||
/** Return the RandomX key change lag in blocks */
|
||||
int GetRandomXBlockLag();
|
||||
|
||||
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
|
||||
bool CheckProofOfWork(const CBlockHeader &blkHeader, uint8_t *pubkey33, int32_t height, const Consensus::Params& params);
|
||||
CChainPower GetBlockProof(const CBlockIndex& block);
|
||||
|
||||
@@ -237,6 +237,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom serializer for CBlockHeader that includes nNonce but omits nSolution,
|
||||
* for use as deterministic input to RandomX hashing.
|
||||
*/
|
||||
class CRandomXInput : private CBlockHeader
|
||||
{
|
||||
public:
|
||||
CRandomXInput(const CBlockHeader &header)
|
||||
{
|
||||
CBlockHeader::SetNull();
|
||||
*((CBlockHeader*)this) = header;
|
||||
}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(this->nVersion);
|
||||
READWRITE(hashPrevBlock);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashFinalSaplingRoot);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
READWRITE(nNonce);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Describes a place in the block chain to another node such that if the
|
||||
* other node doesn't have the same branch, it can find a recent common trunk.
|
||||
|
||||
@@ -75,6 +75,8 @@ const char *GETNSPV="getnSPV"; //used
|
||||
const char *NSPV="nSPV"; //used
|
||||
const char *ALERT="alert"; //used
|
||||
const char *REJECT="reject"; //used
|
||||
const char *GETBLOCKSTREAM="getblockstrm"; // 12 chars (COMMAND_SIZE max); "getblockstream" would truncate
|
||||
const char *BLOCKSTREAM="blockstream";
|
||||
} // namespace NetMsgType
|
||||
|
||||
/** All known message types. Keep this in the same order as the list of
|
||||
@@ -119,6 +121,8 @@ const static std::string allNetMessageTypes[] = {
|
||||
NetMsgType::NSPV,
|
||||
NetMsgType::ALERT,
|
||||
NetMsgType::REJECT,
|
||||
NetMsgType::GETBLOCKSTREAM,
|
||||
NetMsgType::BLOCKSTREAM,
|
||||
};
|
||||
|
||||
CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn)
|
||||
|
||||
@@ -285,6 +285,10 @@ extern const char* GETNSPV;
|
||||
extern const char* NSPV;
|
||||
extern const char* ALERT;
|
||||
extern const char* REJECT;
|
||||
/** Opt-in bulk block streaming (DragonX): request a contiguous range of old blocks. */
|
||||
extern const char* GETBLOCKSTREAM;
|
||||
/** Opt-in bulk block streaming (DragonX): control header preceding a streamed block range. */
|
||||
extern const char* BLOCKSTREAM;
|
||||
}; // namespace NetMsgType
|
||||
|
||||
/* Get a vector of all valid message types (see above) */
|
||||
@@ -304,6 +308,9 @@ enum ServiceFlags : uint64_t {
|
||||
NODE_NSPV = (1 << 30),
|
||||
NODE_ADDRINDEX = (1 << 29),
|
||||
NODE_SPENTINDEX = (1 << 28),
|
||||
// Opt-in bulk block streaming (DragonX). Unauthenticated advertisement; serve/request
|
||||
// handlers validate every block regardless, so robustness against false advertisement holds.
|
||||
NODE_BULKBLOCKS = (1 << 27),
|
||||
|
||||
// Bits 24-31 are reserved for temporary experiments. Just pick a bit that
|
||||
// isn't getting used, or one not being used much, and notify the
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
#include "rpc/server.h"
|
||||
#include "streams.h"
|
||||
#include "sync.h"
|
||||
#include "txdb.h"
|
||||
#include "util.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "script/script.h"
|
||||
#include "script/script_error.h"
|
||||
#include "script/sign.h"
|
||||
@@ -322,6 +324,15 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
|
||||
result.push_back(Pair("anchor", blockindex->hashFinalSproutRoot.GetHex()));
|
||||
result.push_back(Pair("blocktype", "mined"));
|
||||
|
||||
// Report block subsidy and fees separately so explorers don't have to
|
||||
// reimplement the reward schedule to display them.
|
||||
CAmount nSubsidy = GetBlockSubsidy(blockindex->GetHeight(), Params().GetConsensus());
|
||||
CAmount nCoinbase = block.vtx[0].GetValueOut();
|
||||
CAmount nFees = nCoinbase - nSubsidy;
|
||||
if (nFees < 0) nFees = 0; // block 1 has premine, avoid negative
|
||||
result.push_back(Pair("subsidy", ValueFromAmount(nSubsidy)));
|
||||
result.push_back(Pair("fees", ValueFromAmount(nFees)));
|
||||
|
||||
UniValue valuePools(UniValue::VARR);
|
||||
valuePools.push_back(ValuePoolDesc("sapling", blockindex->nChainSaplingValue, blockindex->nSaplingValue));
|
||||
result.push_back(Pair("valuePools", valuePools));
|
||||
@@ -851,6 +862,7 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp, const CPubKey& mypk
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
UniValue getblockmerkletree(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
{
|
||||
if (fHelp || params.size() != 1 )
|
||||
@@ -1642,7 +1654,7 @@ UniValue getchaintxstats(const UniValue& params, bool fHelp, const CPubKey& mypk
|
||||
ret.pushKV("deshielding_payments", (int64_t)pindex->nChainDeshieldingPayments);
|
||||
ret.pushKV("shielding_payments", (int64_t)pindex->nChainShieldingPayments);
|
||||
|
||||
int64_t nullifierCount = pwalletMain->NullifierCount();
|
||||
int64_t nullifierCount = pwalletMain ? pwalletMain->NullifierCount() : 0; // null under -disablewallet
|
||||
//TODO: this is unreliable, is only a cache or subset of total nullifiers
|
||||
ret.pushKV("nullifiers", (int64_t)nullifierCount);
|
||||
ret.pushKV("shielded_pool_size", (int64_t)(pindex->nChainShieldedOutputs - pindex->nChainShieldedSpends));
|
||||
|
||||
@@ -169,7 +169,7 @@ UniValue getgenerate(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
throw runtime_error(
|
||||
"getgenerate\n"
|
||||
"\nReturn if the server is set to mine coins or not. The default is false.\n"
|
||||
"It is set with the command line argument -gen (or HUSH3.conf setting gen).\n"
|
||||
"It is set with the command line argument -gen (or DRAGONX.conf setting gen).\n"
|
||||
"It can also be set with the setgenerate call.\n"
|
||||
"\nResult\n"
|
||||
"{\n"
|
||||
|
||||
@@ -560,7 +560,7 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp, const CPubKey& my
|
||||
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL);
|
||||
#else
|
||||
LOCK(cs_main);
|
||||
#endif
|
||||
|
||||
@@ -133,7 +133,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
" \"pingtime\": n, (numeric) ping time\n"
|
||||
" \"pingwait\": n, (numeric) ping wait\n"
|
||||
" \"version\": v, (numeric) The peer version, such as 170002\n"
|
||||
" \"subver\": \"/GoldenSandtrout:x.y.z[-v]/\", (string) The string version\n"
|
||||
" \"subver\": \"/DragonX:x.y.z[-v]/\", (string) The string version\n"
|
||||
" \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n"
|
||||
" \"startingheight\": n, (numeric) The starting height (block) of the peer\n"
|
||||
" \"banscore\": n, (numeric) The ban score\n"
|
||||
@@ -354,7 +354,7 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp, const CPubKey& myp
|
||||
" \"connected\" : true|false, (boolean) If connected\n"
|
||||
" \"addresses\" : [\n"
|
||||
" {\n"
|
||||
" \"address\" : \"192.168.0.201:18030\", (string) The Hush server host and port\n"
|
||||
" \"address\" : \"192.168.0.201:21768\", (string) The DragonX server host and port\n"
|
||||
" \"connected\" : \"outbound\" (string) connection, inbound or outbound\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
@@ -505,7 +505,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"version\": xxxxx, (numeric) the server version\n"
|
||||
" \"subversion\": \"/GoldenSandtrout:x.y.z[-v]/\", (string) the server subversion string\n"
|
||||
" \"subversion\": \"/DragonX:x.y.z[-v]/\", (string) the server subversion string\n"
|
||||
" \"protocolversion\": xxxxx, (numeric) the protocol version\n"
|
||||
" \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n"
|
||||
" \"timeoffset\": xxxxx, (numeric) the time offset (deprecated, always 0)\n"
|
||||
|
||||
@@ -474,6 +474,7 @@ static const CRPCCommand vRPCCommands[] =
|
||||
{ "wallet", "z_listaddresses", &z_listaddresses, true },
|
||||
{ "wallet", "z_listnullifiers", &z_listnullifiers, true },
|
||||
{ "wallet", "z_exportkey", &z_exportkey, true },
|
||||
{ "wallet", "z_exportmnemonic", &z_exportmnemonic, true },
|
||||
{ "wallet", "z_importkey", &z_importkey, true },
|
||||
{ "wallet", "z_exportviewingkey", &z_exportviewingkey, true },
|
||||
{ "wallet", "z_importviewingkey", &z_importviewingkey, true },
|
||||
|
||||
@@ -353,6 +353,7 @@ extern UniValue nspv_listccmoduleunspent(const UniValue& params, bool fHelp, con
|
||||
extern UniValue getblocksubsidy(const UniValue& params, bool fHelp, const CPubKey& mypk);
|
||||
|
||||
extern UniValue z_exportkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp
|
||||
extern UniValue z_exportmnemonic(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp
|
||||
extern UniValue z_importkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp
|
||||
extern UniValue z_exportviewingkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp
|
||||
extern UniValue z_importviewingkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"ac_perc": "11111111",
|
||||
"ac_eras": "3",
|
||||
"ac_script": "76a9145eb10cf64f2bab1b457f1f25e658526155928fac88ac",
|
||||
"clientname": "GoldenSandtrout",
|
||||
"clientname": "DragonX",
|
||||
"addnode": [
|
||||
"1.1.1.1"
|
||||
]
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <boost/optional/optional_io.hpp>
|
||||
#include <librustzcash.h>
|
||||
#include "zcash/Note.hpp"
|
||||
#include <random>
|
||||
#include <limits>
|
||||
extern bool fZDebug;
|
||||
|
||||
SpendDescriptionInfo::SpendDescriptionInfo(
|
||||
@@ -66,7 +68,7 @@ void TransactionBuilder::AddSaplingOutput(
|
||||
void TransactionBuilder::ShuffleOutputs()
|
||||
{
|
||||
LogPrintf("%s: Shuffling %d zouts\n", __func__, outputs.size() );
|
||||
random_shuffle( outputs.begin(), outputs.end(), GetRandInt );
|
||||
std::shuffle( outputs.begin(), outputs.end(), std::mt19937(GetRand(std::numeric_limits<uint32_t>::max())) );
|
||||
}
|
||||
|
||||
void TransactionBuilder::AddTransparentInput(COutPoint utxo, CScript scriptPubKey, CAmount value, uint32_t _nSequence)
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
|
||||
#include "txdb.h"
|
||||
#include "chainparams.h"
|
||||
#include "clientversion.h"
|
||||
#include "hash.h"
|
||||
#include "main.h"
|
||||
#include "pow.h"
|
||||
#include "streams.h"
|
||||
#include "uint256.h"
|
||||
#include "core_io.h"
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -52,7 +52,7 @@ class uint256;
|
||||
//! -dbcache default (MiB)
|
||||
static const int64_t nDefaultDbCache = 512;
|
||||
//! max. -dbcache (MiB)
|
||||
static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024;
|
||||
static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 65536 : 1024; // 64 GiB ceiling on 64-bit so adaptive dbcache can use most of RAM on large hosts (was 16384)
|
||||
//! min. -dbcache in (MiB)
|
||||
static const int64_t nMinDbCache = 4;
|
||||
|
||||
|
||||
@@ -469,10 +469,17 @@ extern char SMART_CHAIN_SYMBOL[];
|
||||
|
||||
std::vector<uint256> CTxMemPool::removeExpired(unsigned int nBlockHeight)
|
||||
{
|
||||
CBlockIndex *tipindex;
|
||||
// Remove expired txs from the mempool
|
||||
// Remove expired txs from the mempool. (Regression fix: the scan that populates
|
||||
// transactionsToRemove had been dropped, making this a no-op, so expired txs -- which
|
||||
// can never be mined -- were never evicted and accumulated without bound.)
|
||||
LOCK(cs);
|
||||
list<CTransaction> transactionsToRemove;
|
||||
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||
const CTransaction& tx = it->GetTx();
|
||||
if (IsExpiredTx(tx, nBlockHeight)) {
|
||||
transactionsToRemove.push_back(tx);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint256> ids;
|
||||
for (const CTransaction& tx : transactionsToRemove) {
|
||||
@@ -484,6 +491,45 @@ std::vector<uint256> CTxMemPool::removeExpired(unsigned int nBlockHeight)
|
||||
return ids;
|
||||
}
|
||||
|
||||
// Age-based eviction: remove txs whose entry time is older than `time`. Distinct from
|
||||
// removeExpired() (which drops txs past their consensus nExpiryHeight); this is the wall-clock
|
||||
// sweep LimitMempoolSize wants. Collect-then-remove to avoid iterating mapTx while mutating it.
|
||||
int CTxMemPool::Expire(int64_t time)
|
||||
{
|
||||
LOCK(cs);
|
||||
std::list<CTransaction> toRemove;
|
||||
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||
if (it->GetTime() < time)
|
||||
toRemove.push_back(it->GetTx());
|
||||
}
|
||||
for (const CTransaction& tx : toRemove) {
|
||||
std::list<CTransaction> removed;
|
||||
remove(tx, removed, true);
|
||||
}
|
||||
return (int)toRemove.size();
|
||||
}
|
||||
|
||||
// Fee-ordered eviction: drop the lowest-feerate txs (and their in-mempool descendants, via the
|
||||
// recursive remove) until DynamicMemoryUsage() is at or below sizelimit. Uses the per-tx feerate
|
||||
// index (mapTx index 1, sorted feerate DESCENDING, so the worst tx is the tail). NOTE: this is a
|
||||
// per-tx feerate, not a descendant-aggregate score, so a low-fee parent funded by a high-fee child
|
||||
// (CPFP) can be evicted -- an accepted simplification (no descendant tracking in this fork). The
|
||||
// admission cap in AcceptToMemoryPool bounds growth between block connects (no rollingMinFee here).
|
||||
void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRemaining)
|
||||
{
|
||||
LOCK(cs);
|
||||
while (DynamicMemoryUsage() > sizelimit && !mapTx.empty()) {
|
||||
// Re-derive the tail each iteration: remove() invalidates iterators.
|
||||
CTransaction tx = std::prev(mapTx.get<1>().end())->GetTx();
|
||||
std::list<CTransaction> removed;
|
||||
remove(tx, removed, true);
|
||||
if (pvNoSpendsRemaining) {
|
||||
for (const CTransaction& r : removed)
|
||||
pvNoSpendsRemaining->push_back(r.GetHash());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a block is connected. Removes from mempool and updates the miner fee estimator.
|
||||
void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
|
||||
std::list<CTransaction>& conflicts, bool fCurrentEstimate)
|
||||
|
||||
@@ -219,6 +219,8 @@ public:
|
||||
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
||||
void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
|
||||
std::vector<uint256> removeExpired(unsigned int nBlockHeight);
|
||||
int Expire(int64_t time);
|
||||
void TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRemaining = NULL);
|
||||
void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
|
||||
std::list<CTransaction>& conflicts, bool fCurrentEstimate = true);
|
||||
void removeWithoutBranchId(uint32_t nMemPoolBranchId);
|
||||
|
||||
12
src/util.cpp
12
src/util.cpp
@@ -710,7 +710,7 @@ boost::filesystem::path GetConfigFile()
|
||||
if ( SMART_CHAIN_SYMBOL[0] != 0 ) {
|
||||
sprintf(confname,"%s.conf",SMART_CHAIN_SYMBOL);
|
||||
} else {
|
||||
strcpy(confname,"HUSH3.conf");
|
||||
strcpy(confname,"DRAGONX.conf");
|
||||
}
|
||||
boost::filesystem::path pathConfigFile(GetArg("-conf",confname));
|
||||
if (!pathConfigFile.is_complete())
|
||||
@@ -731,7 +731,7 @@ void ReadConfigFile(map<string, string>& mapSettingsRet,
|
||||
|
||||
for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it)
|
||||
{
|
||||
// Don't overwrite existing settings so command line settings override HUSH3.conf
|
||||
// Don't overwrite existing settings so command line settings override DRAGONX.conf
|
||||
string strKey = string("-") + it->string_key;
|
||||
if (mapSettingsRet.count(strKey) == 0)
|
||||
{
|
||||
@@ -1029,14 +1029,16 @@ void SetThreadPriority(int nPriority)
|
||||
std::string PrivacyInfo()
|
||||
{
|
||||
return "\n" +
|
||||
FormatParagraph(strprintf(_("In order to ensure you are adequately protecting your privacy when using Hush, please see <%s>."),
|
||||
"https://hush.is/security/")) + "\n";
|
||||
FormatParagraph(strprintf(_("In order to ensure you are adequately protecting your privacy when using DragonX, please see <%s>."),
|
||||
"https://dragonx.is/security/")) + "\n";
|
||||
}
|
||||
|
||||
std::string LicenseInfo()
|
||||
{
|
||||
return "\n" +
|
||||
FormatParagraph(strprintf(_("Copyright (C) 2016-%i Duke Leto and The Hush Developers"), COPYRIGHT_YEAR)) + "\n" +
|
||||
FormatParagraph(strprintf(_("Copyright (C) 2024-%i The DragonX Developers"), COPYRIGHT_YEAR)) + "\n" +
|
||||
"\n" +
|
||||
FormatParagraph(strprintf(_("Copyright (C) 2016-2024 Duke Leto and The Hush Developers"))) + "\n" +
|
||||
"\n" +
|
||||
FormatParagraph(strprintf(_("Copyright (C) 2016-2020 jl777 and SuperNET developers"))) + "\n" +
|
||||
"\n" +
|
||||
|
||||
@@ -156,10 +156,17 @@ void ThreadNotifyWallets(CBlockIndex *pindexLastTip)
|
||||
assert(pcoinsTip->GetSaplingAnchorAt(SaplingMerkleTree::empty_root(), oldSaplingTree));
|
||||
}
|
||||
|
||||
// Use operator[] (empty-list default), NOT .at(): a block's conflict entry can be
|
||||
// absent if it was drained in an earlier cycle whose connect loop hit the IBD
|
||||
// ReadBlockFromDisk retry below (that break leaves pindexLastTip behind, so the block
|
||||
// gets rebuilt here but its conflicts were already cleared and are never re-inserted).
|
||||
// .at() would throw std::out_of_range which, uncaught under cs_main in this boost
|
||||
// thread, aborts the node (and crash-loops since pindexLastTip cannot advance). A
|
||||
// missing entry simply means no conflict notifications for this block (best-effort).
|
||||
blockStack.emplace_back(
|
||||
pindex,
|
||||
std::make_pair(oldSproutTree, oldSaplingTree),
|
||||
recentlyConflicted.first.at(pindex));
|
||||
recentlyConflicted.first[pindex]);
|
||||
|
||||
pindex = pindex->pprev;
|
||||
}
|
||||
@@ -174,11 +181,23 @@ void ThreadNotifyWallets(CBlockIndex *pindexLastTip)
|
||||
// network message processing thread.
|
||||
//
|
||||
|
||||
// Notify block disconnects
|
||||
// Notify block disconnects. If an IBD read fails mid-disconnect we must NOT fall through
|
||||
// to the connect loop (which advances pindexLastTip to the new tip and permanently abandons
|
||||
// the remaining disconnects -> wallet witness/anchor desync); skip connects this cycle and
|
||||
// retry the whole disconnect next cycle, exactly as the connect-side break retries.
|
||||
bool fDisconnectIncomplete = false;
|
||||
while (pindexLastTip && pindexLastTip != pindexFork) {
|
||||
// Read block from disk.
|
||||
CBlock block;
|
||||
if (!ReadBlockFromDisk(block, pindexLastTip,1)) {
|
||||
if (IsInitialBlockDownload()) {
|
||||
// During IBD, block data may not be flushed to disk yet.
|
||||
// Sleep briefly and retry on the next cycle instead of crashing.
|
||||
LogPrintf("%s: block at height %d not yet readable, will retry\n",
|
||||
__func__, pindexLastTip->GetHeight());
|
||||
fDisconnectIncomplete = true;
|
||||
break;
|
||||
}
|
||||
LogPrintf("*** %s\n", "Failed to read block while notifying wallets of block disconnects");
|
||||
uiInterface.ThreadSafeMessageBox(
|
||||
_("Error: A fatal internal error occurred, see debug.log for details"),
|
||||
@@ -198,14 +217,23 @@ void ThreadNotifyWallets(CBlockIndex *pindexLastTip)
|
||||
pindexLastTip = pindexLastTip->pprev;
|
||||
}
|
||||
|
||||
// Notify block connections
|
||||
while (!blockStack.empty()) {
|
||||
// Notify block connections (skipped this cycle if the disconnect loop broke to retry an
|
||||
// unreadable block, so pindexLastTip is not advanced past the un-disconnected blocks).
|
||||
while (!fDisconnectIncomplete && !blockStack.empty()) {
|
||||
auto blockData = blockStack.back();
|
||||
blockStack.pop_back();
|
||||
|
||||
// Read block from disk.
|
||||
CBlock block;
|
||||
if (!ReadBlockFromDisk(block, blockData.pindex, 1)) {
|
||||
if (IsInitialBlockDownload()) {
|
||||
// During IBD, block data may not be flushed to disk yet.
|
||||
// Push unprocessed blocks back and retry on the next cycle.
|
||||
LogPrintf("%s: block at height %d not yet readable, will retry\n",
|
||||
__func__, blockData.pindex->GetHeight());
|
||||
blockStack.push_back(blockData);
|
||||
break;
|
||||
}
|
||||
LogPrintf("*** %s\n", "Failed to read block while notifying wallets of block connects");
|
||||
uiInterface.ThreadSafeMessageBox(
|
||||
_("Error: A fatal internal error occurred, see debug.log for details"),
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
#define HUSH_VERSION_H
|
||||
|
||||
// network protocol versioning
|
||||
static const int PROTOCOL_VERSION = 1987429;
|
||||
// DragonX 1.0.0 - bumped to separate from old HUSH/DragonX nodes with RandomX bug
|
||||
static const int PROTOCOL_VERSION = 2000000;
|
||||
//! initial proto version, to be increased after version/verack negotiation
|
||||
static const int INIT_PROTO_VERSION = 209;
|
||||
//! In this version, 'getheaders' was introduced.
|
||||
@@ -30,8 +31,9 @@ static const int GETHEADERS_VERSION = 31800;
|
||||
//! disconnect from peers older than this proto version (HUSH mainnet)
|
||||
static const int MIN_HUSH_PEER_PROTO_VERSION = 1987426;
|
||||
|
||||
//! disconnect from peers older than this proto version (HACs)
|
||||
static const int MIN_PEER_PROTO_VERSION = 1987420;
|
||||
//! disconnect from peers older than this proto version (DragonX/HACs)
|
||||
//! Set to 2000000 to reject nodes without RandomX validation fix
|
||||
static const int MIN_PEER_PROTO_VERSION = 2000000;
|
||||
|
||||
//! nTime field added to CAddress, starting with this version;
|
||||
//! if possible, avoid requesting addresses nodes older than this
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
char SMART_CHAIN_SYMBOL[HUSH_SMART_CHAIN_MAXLEN];
|
||||
int64_t MAX_MONEY = 200000000 * 100000000LL;
|
||||
uint64_t ASSETCHAINS_SUPPLY;
|
||||
uint16_t BITCOIND_RPCPORT = 18031;
|
||||
uint16_t BITCOIND_RPCPORT = 21769;
|
||||
uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT;
|
||||
uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC;
|
||||
uint32_t ASSETCHAINS_MAGIC = 2387029918;
|
||||
|
||||
@@ -312,7 +312,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
|
||||
// recoverable, while keeping it logically separate from the ZIP 32
|
||||
// Sapling key hierarchy, which the user might not be using.
|
||||
HDSeed seed;
|
||||
if (!pwalletMain->GetHDSeed(seed)) {
|
||||
if (!pwalletMain->GetHDSeedForDerivation(seed)) {
|
||||
throw JSONRPCError(
|
||||
RPC_WALLET_ERROR,
|
||||
"AsyncRPCOperation_sendmany: HD seed not found");
|
||||
|
||||
@@ -377,7 +377,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
// recoverable, while keeping it logically separate from the ZIP 32
|
||||
// Sapling key hierarchy, which the user might not be using.
|
||||
HDSeed seed;
|
||||
if (!pwalletMain->GetHDSeed(seed)) {
|
||||
if (!pwalletMain->GetHDSeedForDerivation(seed)) {
|
||||
throw JSONRPCError(
|
||||
RPC_WALLET_ERROR,
|
||||
"AsyncRPCOperation_sendmany::main_impl(): HD seed not found");
|
||||
|
||||
@@ -197,7 +197,7 @@ bool ShieldToAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) c
|
||||
// recoverable, while keeping it logically separate from the ZIP 32
|
||||
// Sapling key hierarchy, which the user might not be using.
|
||||
HDSeed seed;
|
||||
if (!pwalletMain->GetHDSeed(seed)) {
|
||||
if (!pwalletMain->GetHDSeedForDerivation(seed)) {
|
||||
throw JSONRPCError(
|
||||
RPC_WALLET_ERROR,
|
||||
"CWallet::GenerateNewSaplingZKey(): HD seed not found");
|
||||
|
||||
99
src/wallet/mnemonic.cpp
Normal file
99
src/wallet/mnemonic.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2016-2024 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
#include "wallet/mnemonic.h"
|
||||
|
||||
#include "random.h"
|
||||
#include "support/cleanse.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" {
|
||||
#include "crypto/bip39/bip39.h"
|
||||
}
|
||||
|
||||
// The vendored BIP39 library references random_buffer() (used by its
|
||||
// mnemonic_generate()). We do not compile trezor's insecure rand.c; instead we
|
||||
// route it to the node CSPRNG so any BIP39 randomness is cryptographically
|
||||
// sound. random_buffer is declared weak in rand.c, so this strong definition
|
||||
// is the one that links.
|
||||
extern "C" void random_buffer(uint8_t* buf, size_t len)
|
||||
{
|
||||
GetRandBytes(buf, (int)len);
|
||||
}
|
||||
|
||||
// mnemonic_from_data()/mnemonic_to_seed() use process-static scratch buffers,
|
||||
// so serialize all access behind one lock and copy results out immediately.
|
||||
static std::mutex cs_bip39;
|
||||
|
||||
bool MnemonicIsValid(const std::string& phrase)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cs_bip39);
|
||||
return mnemonic_check(phrase.c_str()) != 0;
|
||||
}
|
||||
|
||||
bool MnemonicToEntropy(const std::string& phrase, RawHDSeed& entropyOut)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cs_bip39);
|
||||
// Reject bad checksum / unknown words first.
|
||||
if (mnemonic_check(phrase.c_str()) == 0) {
|
||||
return false;
|
||||
}
|
||||
// mnemonic_to_entropy() writes 33 bytes (entropy || 1 checksum byte) and
|
||||
// returns the total bit count (words * 11).
|
||||
uint8_t buf[33];
|
||||
int totalBits = mnemonic_to_entropy(phrase.c_str(), buf);
|
||||
if (totalBits <= 0) {
|
||||
return false;
|
||||
}
|
||||
int words = totalBits / 11;
|
||||
if (words != 12 && words != 18 && words != 24) {
|
||||
memory_cleanse(buf, sizeof(buf));
|
||||
return false;
|
||||
}
|
||||
int entropyBytes = words * 4 / 3; // 12->16, 18->24, 24->32
|
||||
entropyOut.assign(buf, buf + entropyBytes);
|
||||
memory_cleanse(buf, sizeof(buf));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EntropyToMnemonic(const RawHDSeed& entropy, std::string& phraseOut)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cs_bip39);
|
||||
const char* phrase = mnemonic_from_data(entropy.data(), (int)entropy.size());
|
||||
if (phrase == nullptr) {
|
||||
return false;
|
||||
}
|
||||
phraseOut.assign(phrase);
|
||||
mnemonic_clear(); // wipe the static buffer
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Bip39SeedFromEntropy(const RawHDSeed& entropy, RawHDSeed& seed64Out)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cs_bip39);
|
||||
// Regenerate the canonical phrase from entropy (matches SDXLite's
|
||||
// Mnemonic::from_entropy(entropy).phrase()), then PBKDF2 with an EMPTY
|
||||
// passphrase to get the standard 64-byte BIP39 seed.
|
||||
const char* phrase = mnemonic_from_data(entropy.data(), (int)entropy.size());
|
||||
if (phrase == nullptr) {
|
||||
return false;
|
||||
}
|
||||
uint8_t seed[64];
|
||||
mnemonic_to_seed(phrase, "", seed, nullptr);
|
||||
mnemonic_clear();
|
||||
seed64Out.assign(seed, seed + 64);
|
||||
memory_cleanse(seed, sizeof(seed));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GenerateMnemonicEntropy(int bits, RawHDSeed& entropyOut)
|
||||
{
|
||||
if (bits != 128 && bits != 160 && bits != 192 && bits != 224 && bits != 256) {
|
||||
return false;
|
||||
}
|
||||
entropyOut.resize(bits / 8);
|
||||
GetRandBytes(entropyOut.data(), (int)entropyOut.size());
|
||||
return true;
|
||||
}
|
||||
39
src/wallet/mnemonic.h
Normal file
39
src/wallet/mnemonic.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2016-2024 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
#ifndef HUSH_WALLET_MNEMONIC_H
|
||||
#define HUSH_WALLET_MNEMONIC_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "zcash/zip32.h" // RawHDSeed
|
||||
|
||||
// Thin, thread-safe C++ wrapper over the vendored BIP39 (trezor-crypto) library.
|
||||
// It reproduces SilentDragonXLite's tiny-bip39 0.6.2 conventions EXACTLY so the
|
||||
// same 24 words yield the same addresses in both wallets:
|
||||
// - English wordlist only (byte-identical to tiny-bip39's english.txt)
|
||||
// - empty BIP39 passphrase (no "25th word")
|
||||
// - PBKDF2-HMAC-SHA512, 2048 rounds, 64-byte seed
|
||||
// - the seed is derived from the CANONICAL phrase regenerated from entropy,
|
||||
// matching SDXLite's Mnemonic::from_entropy(entropy).phrase() round-trip.
|
||||
|
||||
//! True if `phrase` is a valid BIP39 mnemonic (word list + checksum).
|
||||
bool MnemonicIsValid(const std::string& phrase);
|
||||
|
||||
//! Parse `phrase` into its BIP39 entropy (16/20/24/28/32 bytes). Validates the
|
||||
//! checksum first. Returns false on any invalid input.
|
||||
bool MnemonicToEntropy(const std::string& phrase, RawHDSeed& entropyOut);
|
||||
|
||||
//! Regenerate the canonical English mnemonic phrase from `entropy`.
|
||||
bool EntropyToMnemonic(const RawHDSeed& entropy, std::string& phraseOut);
|
||||
|
||||
//! Derive the 64-byte BIP39 seed used for HD derivation from `entropy`, exactly
|
||||
//! as SilentDragonXLite does: canonical phrase from entropy, then PBKDF2 with an
|
||||
//! empty passphrase.
|
||||
bool Bip39SeedFromEntropy(const RawHDSeed& entropy, RawHDSeed& seed64Out);
|
||||
|
||||
//! Generate fresh BIP39 entropy of `bits` (128/160/192/224/256) from the node
|
||||
//! CSPRNG, for creating a new mnemonic wallet.
|
||||
bool GenerateMnemonicEntropy(int bits, RawHDSeed& entropyOut);
|
||||
|
||||
#endif // HUSH_WALLET_MNEMONIC_H
|
||||
@@ -742,7 +742,9 @@ UniValue dumpwallet_impl(const UniValue& params, bool fHelp, bool fDumpZKeys)
|
||||
file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
|
||||
{
|
||||
HDSeed hdSeed;
|
||||
pwalletMain->GetHDSeed(hdSeed);
|
||||
// Dump the 64-byte derivation seed (for mnemonic wallets this is the
|
||||
// expanded BIP39 seed), so re-importing the hex reproduces the same keys.
|
||||
pwalletMain->GetHDSeedForDerivation(hdSeed);
|
||||
auto rawSeed = hdSeed.RawSeed();
|
||||
file << strprintf("# HDSeed=%s fingerprint=%s", HexStr(rawSeed.begin(), rawSeed.end()), hdSeed.Fingerprint().GetHex());
|
||||
file << "\n";
|
||||
@@ -1026,6 +1028,50 @@ UniValue z_exportkey(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
return EncodeSpendingKey(sk.get());
|
||||
}
|
||||
|
||||
UniValue z_exportmnemonic(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"z_exportmnemonic\n"
|
||||
"\nReveal the wallet's BIP39 seed phrase (24 words).\n"
|
||||
"The phrase is byte-compatible with SilentDragonXLite: the same words\n"
|
||||
"restore the same transparent and shielded addresses in either wallet.\n"
|
||||
"Only works for wallets created or restored from a mnemonic (see the\n"
|
||||
"-mnemonic and -usemnemonic options). Requires the wallet be unlocked.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"mnemonic\" : \"word1 ... word24\", (string) the BIP39 seed phrase\n"
|
||||
" \"seedfp\" : \"hex\" (string) the HD seed fingerprint\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("z_exportmnemonic", "")
|
||||
+ HelpExampleRpc("z_exportmnemonic", "")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
EnsureWalletIsUnlocked();
|
||||
|
||||
if (!pwalletMain->IsMnemonicSeed()) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR,
|
||||
"This wallet's seed was not derived from a mnemonic, so no seed phrase is available. "
|
||||
"Use z_exportwallet to back up the raw HD seed instead.");
|
||||
}
|
||||
|
||||
std::string phrase;
|
||||
if (!pwalletMain->GetMnemonicPhrase(phrase)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Could not retrieve the seed phrase (is the wallet unlocked?)");
|
||||
}
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.push_back(Pair("mnemonic", phrase));
|
||||
ret.push_back(Pair("seedfp", pwalletMain->GetHDChain().seedFp.GetHex()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue z_exportviewingkey(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
|
||||
@@ -305,7 +305,7 @@ void zsTxSendsToJSON(const CWalletTx& wtx, UniValue& sends, CAmount& totalSends,
|
||||
//Decrypt sapling outgoing t to z transaction using HDseed
|
||||
if (wtx.vShieldedSpend.size()==0) {
|
||||
HDSeed seed;
|
||||
if (pwalletMain->GetHDSeed(seed)) {
|
||||
if (pwalletMain->GetHDSeedForDerivation(seed)) {
|
||||
auto opt = libzcash::SaplingOutgoingPlaintext::decrypt(
|
||||
outputDesc.outCiphertext,ovkForShieldingFromTaddr(seed),outputDesc.cv,outputDesc.cm,outputDesc.ephemeralKey);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user