Compare commits
1 Commits
v1.0.2
...
4cb5a86056
| Author | SHA1 | Date | |
|---|---|---|---|
| 4cb5a86056 |
@@ -1,27 +0,0 @@
|
|||||||
.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
|
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -171,8 +171,4 @@ release-linux/
|
|||||||
release/
|
release/
|
||||||
src/dragonxd
|
src/dragonxd
|
||||||
src/dragonx-cli
|
src/dragonx-cli
|
||||||
src/dragonx-tx
|
src/dragonx-tx
|
||||||
src/dragonxd.exe
|
|
||||||
src/dragonx-cli.exe
|
|
||||||
src/dragonx-tx.exe
|
|
||||||
doc/relnotes/
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
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"]
|
|
||||||
25
README.md
25
README.md
@@ -106,32 +106,19 @@ apt-get install -y gcc-7 g++-7 && \
|
|||||||
|
|
||||||
# Build on Mac
|
# Build on Mac
|
||||||
|
|
||||||
Install Xcode Command Line Tools and [Homebrew](https://brew.sh/), then install dependencies:
|
```
|
||||||
|
sudo port update
|
||||||
```sh
|
sudo port upgrade outdated
|
||||||
xcode-select --install
|
sudo port install qt5
|
||||||
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
|
# clone git repo
|
||||||
git clone https://git.hush.is/hush/hush3
|
git clone https://git.hush.is/hush/hush3
|
||||||
cd hush3
|
cd hush3
|
||||||
# Build (uses 3 build processes, you need 2GB of RAM for each)
|
# Build
|
||||||
# Make sure libtool gnubin and cargo are on PATH
|
# This uses 3 build processes, you need 2GB of RAM for each.
|
||||||
export PATH="$HOME/.cargo/bin:/usr/local/opt/libtool/libexec/gnubin:$PATH"
|
|
||||||
./build.sh -j3
|
./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
|
# Installing Hush binaries
|
||||||
|
|
||||||
1. [Download the release](https://git.hush.is/hush/hush3/releases) with a .deb file extension.
|
1. [Download the release](https://git.hush.is/hush/hush3/releases) with a .deb file extension.
|
||||||
|
|||||||
79
build.sh
79
build.sh
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
set -eu -o pipefail
|
set -eu -o pipefail
|
||||||
|
|
||||||
VERSION="1.0.2"
|
VERSION="1.0.0"
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
RELEASE_DIR="$SCRIPT_DIR/release"
|
RELEASE_DIR="$SCRIPT_DIR/release"
|
||||||
|
|
||||||
@@ -14,7 +14,6 @@ RELEASE_DIR="$SCRIPT_DIR/release"
|
|||||||
BUILD_LINUX_RELEASE=0
|
BUILD_LINUX_RELEASE=0
|
||||||
BUILD_WIN_RELEASE=0
|
BUILD_WIN_RELEASE=0
|
||||||
BUILD_MAC_RELEASE=0
|
BUILD_MAC_RELEASE=0
|
||||||
BUILD_LINUX_COMPAT=0
|
|
||||||
REMAINING_ARGS=()
|
REMAINING_ARGS=()
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
@@ -22,9 +21,6 @@ for arg in "$@"; do
|
|||||||
--linux-release)
|
--linux-release)
|
||||||
BUILD_LINUX_RELEASE=1
|
BUILD_LINUX_RELEASE=1
|
||||||
;;
|
;;
|
||||||
--linux-compat)
|
|
||||||
BUILD_LINUX_COMPAT=1
|
|
||||||
;;
|
|
||||||
--win-release)
|
--win-release)
|
||||||
BUILD_WIN_RELEASE=1
|
BUILD_WIN_RELEASE=1
|
||||||
;;
|
;;
|
||||||
@@ -80,12 +76,8 @@ package_release() {
|
|||||||
echo "Packaging release for $platform..."
|
echo "Packaging release for $platform..."
|
||||||
mkdir -p "$release_subdir"
|
mkdir -p "$release_subdir"
|
||||||
|
|
||||||
# Copy bootstrap script (platform-appropriate)
|
# Copy bootstrap script
|
||||||
if [ "$platform" = "win64" ]; then
|
cp "$SCRIPT_DIR/util/bootstrap-dragonx.sh" "$release_subdir/"
|
||||||
cp "$SCRIPT_DIR/util/bootstrap-dragonx.bat" "$release_subdir/"
|
|
||||||
else
|
|
||||||
cp "$SCRIPT_DIR/util/bootstrap-dragonx.sh" "$release_subdir/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy common files
|
# Copy common files
|
||||||
cp "$SCRIPT_DIR/contrib/asmap/asmap.dat" "$release_subdir/" 2>/dev/null || true
|
cp "$SCRIPT_DIR/contrib/asmap/asmap.dat" "$release_subdir/" 2>/dev/null || true
|
||||||
@@ -118,78 +110,27 @@ package_release() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Handle release builds
|
# 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
|
if [ $BUILD_LINUX_RELEASE -eq 1 ] || [ $BUILD_WIN_RELEASE -eq 1 ] || [ $BUILD_MAC_RELEASE -eq 1 ]; then
|
||||||
mkdir -p "$RELEASE_DIR"
|
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
|
if [ $BUILD_LINUX_RELEASE -eq 1 ]; then
|
||||||
echo "=== Building Linux release ==="
|
echo "=== Building Linux release ==="
|
||||||
clean_for_platform linux
|
clean_for_platform linux
|
||||||
./util/build.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
./util/build.sh --disable-tests "${REMAINING_ARGS[@]}"
|
||||||
package_release linux-amd64
|
package_release linux-amd64
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $BUILD_WIN_RELEASE -eq 1 ]; then
|
if [ $BUILD_WIN_RELEASE -eq 1 ]; then
|
||||||
echo "=== Building Windows release ==="
|
echo "=== Building Windows release ==="
|
||||||
clean_for_platform windows
|
clean_for_platform windows
|
||||||
./util/build-win.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
./util/build-win.sh --disable-tests "${REMAINING_ARGS[@]}"
|
||||||
package_release win64
|
package_release win64
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $BUILD_MAC_RELEASE -eq 1 ]; then
|
if [ $BUILD_MAC_RELEASE -eq 1 ]; then
|
||||||
echo "=== Building macOS release ==="
|
echo "=== Building macOS release ==="
|
||||||
clean_for_platform macos
|
clean_for_platform macos
|
||||||
./util/build-mac.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
./util/build-mac.sh --disable-tests "${REMAINING_ARGS[@]}"
|
||||||
package_release macos
|
package_release macos
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -201,11 +142,11 @@ fi
|
|||||||
|
|
||||||
# Standard build (auto-detect OS)
|
# Standard build (auto-detect OS)
|
||||||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||||
./util/build.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
./util/build.sh --disable-tests "${REMAINING_ARGS[@]}"
|
||||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
./util/build-mac.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
./util/build-mac.sh --disable-tests "${REMAINING_ARGS[@]}"
|
||||||
elif [[ "$OSTYPE" == "msys"* ]]; then
|
elif [[ "$OSTYPE" == "msys"* ]]; then
|
||||||
./util/build-win.sh --disable-tests ${REMAINING_ARGS[@]+"${REMAINING_ARGS[@]}"}
|
./util/build-win.sh --disable-tests "${REMAINING_ARGS[@]}"
|
||||||
else
|
else
|
||||||
echo "Unable to detect your OS. What are you using?"
|
echo "Unable to detect your OS. What are you using?"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ AC_PREREQ([2.60])
|
|||||||
define(_CLIENT_VERSION_MAJOR, 1)
|
define(_CLIENT_VERSION_MAJOR, 1)
|
||||||
dnl Must be kept in sync with src/clientversion.h , ugh!
|
dnl Must be kept in sync with src/clientversion.h , ugh!
|
||||||
define(_CLIENT_VERSION_MINOR, 0)
|
define(_CLIENT_VERSION_MINOR, 0)
|
||||||
define(_CLIENT_VERSION_REVISION, 2)
|
define(_CLIENT_VERSION_REVISION, 0)
|
||||||
define(_CLIENT_VERSION_BUILD, 50)
|
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(_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_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)))
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
build_darwin_CC = gcc-15
|
build_darwin_CC = gcc-8
|
||||||
build_darwin_CXX = g++-15
|
build_darwin_CXX = g++-8
|
||||||
build_darwin_AR: = $(shell xcrun -f ar)
|
build_darwin_AR: = $(shell xcrun -f ar)
|
||||||
build_darwin_RANLIB: = $(shell xcrun -f ranlib)
|
build_darwin_RANLIB: = $(shell xcrun -f ranlib)
|
||||||
build_darwin_STRIP: = $(shell xcrun -f strip)
|
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
|
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 host on darwin builder. overrides darwin host preferences.
|
||||||
darwin_CC= gcc-15
|
darwin_CC= gcc-8
|
||||||
darwin_CXX= g++-15
|
darwin_CXX= g++-8
|
||||||
darwin_AR:=$(shell xcrun -f ar)
|
darwin_AR:=$(shell xcrun -f ar)
|
||||||
darwin_RANLIB:=$(shell xcrun -f ranlib)
|
darwin_RANLIB:=$(shell xcrun -f ranlib)
|
||||||
darwin_STRIP:=$(shell xcrun -f strip)
|
darwin_STRIP:=$(shell xcrun -f strip)
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ OSX_MIN_VERSION=10.12
|
|||||||
OSX_SDK_VERSION=10.12
|
OSX_SDK_VERSION=10.12
|
||||||
OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk
|
OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk
|
||||||
LD64_VERSION=253.9
|
LD64_VERSION=253.9
|
||||||
darwin_CC=gcc-15 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION)
|
darwin_CC=gcc-8 -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_CXX=g++-8 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION)
|
||||||
|
|
||||||
darwin_CFLAGS=-pipe
|
darwin_CFLAGS=-pipe
|
||||||
darwin_CXXFLAGS=$(darwin_CFLAGS)
|
darwin_CXXFLAGS=$(darwin_CFLAGS)
|
||||||
|
|||||||
@@ -40,15 +40,9 @@ define $(package)_preprocess_cmds
|
|||||||
cat $($(package)_patch_dir)/cargo.config | sed 's|CRATE_REGISTRY|$(host_prefix)/$(CRATE_REGISTRY)|' > .cargo/config
|
cat $($(package)_patch_dir)/cargo.config | sed 's|CRATE_REGISTRY|$(host_prefix)/$(CRATE_REGISTRY)|' > .cargo/config
|
||||||
endef
|
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
|
define $(package)_build_cmds
|
||||||
$(host_prefix)/native/bin/cargo build --package librustzcash $($(package)_build_opts)
|
$(host_prefix)/native/bin/cargo build --package librustzcash $($(package)_build_opts)
|
||||||
endef
|
endef
|
||||||
endif
|
|
||||||
|
|
||||||
define $(package)_stage_cmds
|
define $(package)_stage_cmds
|
||||||
mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \
|
mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/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
|
|
||||||
@@ -594,7 +594,7 @@ libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBOOST_SPIRIT_THREADSAFE -DHAV
|
|||||||
#libzcash_a_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
#libzcash_a_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||||
#libzcash_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMONTGOMERY_OUTPUT
|
#libzcash_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMONTGOMERY_OUTPUT
|
||||||
|
|
||||||
libzcash_a_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing -std=gnu++17
|
libzcash_a_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing -std=gnu17
|
||||||
libzcash_a_LDFLAGS = $(SAN_LDFLAGS) $(HARDENED_LDFLAGS)
|
libzcash_a_LDFLAGS = $(SAN_LDFLAGS) $(HARDENED_LDFLAGS)
|
||||||
libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT
|
libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT
|
||||||
|
|
||||||
@@ -636,7 +636,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_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=gnu++17
|
libhush_a_CXXFLAGS = $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing -std=gnu17
|
||||||
|
|
||||||
libhush_a_LDFLAGS = $(HARDENED_LDFLAGS)
|
libhush_a_LDFLAGS = $(HARDENED_LDFLAGS)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
SHELL = /bin/sh
|
SHELL = /bin/sh
|
||||||
CC_DARWIN = g++-15
|
CC_DARWIN = g++-8
|
||||||
CC_WIN = x86_64-w64-mingw32-gcc-posix
|
CC_WIN = x86_64-w64-mingw32-gcc-posix
|
||||||
CC_AARCH64 = aarch64-linux-gnu-g++
|
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
|
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
|
||||||
|
|||||||
Binary file not shown.
@@ -32,12 +32,8 @@ class CChainPower;
|
|||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
extern bool fZindex;
|
extern bool fZindex;
|
||||||
// These version thresholds control whether nSproutValue/nSaplingValue are
|
static const int SPROUT_VALUE_VERSION = 1001400;
|
||||||
// serialized in the block index. They must be <= CLIENT_VERSION or the
|
static const int SAPLING_VALUE_VERSION = 1010100;
|
||||||
// values will never be persisted, causing nChainSaplingValue to reset
|
|
||||||
// to 0 after node restart. DragonX CLIENT_VERSION is 1000250 (v1.0.2.50).
|
|
||||||
static const int SPROUT_VALUE_VERSION = 1000000;
|
|
||||||
static const int SAPLING_VALUE_VERSION = 1000000;
|
|
||||||
extern int32_t ASSETCHAINS_LWMAPOS;
|
extern int32_t ASSETCHAINS_LWMAPOS;
|
||||||
extern char SMART_CHAIN_SYMBOL[65];
|
extern char SMART_CHAIN_SYMBOL[65];
|
||||||
extern uint64_t ASSETCHAINS_NOTARY_PAY[];
|
extern uint64_t ASSETCHAINS_NOTARY_PAY[];
|
||||||
|
|||||||
@@ -5638,9 +5638,18 @@ void *chainparams_commandline() {
|
|||||||
(2836000, uint256S("0x00000000004f1a5b9b0fad39c6751db29b99bfcb045181b6077d791ee0cf91f2"))
|
(2836000, uint256S("0x00000000004f1a5b9b0fad39c6751db29b99bfcb045181b6077d791ee0cf91f2"))
|
||||||
(2837000, uint256S("0x000000000027c61ed8745c18d6b00edec9414e30dd880d92d598a6a0ce0fc238"))
|
(2837000, uint256S("0x000000000027c61ed8745c18d6b00edec9414e30dd880d92d598a6a0ce0fc238"))
|
||||||
(2838000, uint256S("0x00000000010947813b04f02da1166a07ba213369ec83695f4d8a6270c57f1141"))
|
(2838000, uint256S("0x00000000010947813b04f02da1166a07ba213369ec83695f4d8a6270c57f1141"))
|
||||||
,(int64_t) 1770622731, // time of last checkpointed block
|
(2839000, uint256S("0x01aa99bfd837b9795b2f067a253792e32c5b24e5beeac52d7dc8e5772e346ec2"))
|
||||||
(int64_t) 2940000, // total txs
|
(2840000, uint256S("0x00000f097b50c4d50cf046ccc3cc3e34f189b61a1a564685cfd713fc2ffd52b6"))
|
||||||
(double) 4576 // txs in the last day before block 2838000
|
(2841000, uint256S("0x00028a1d142a6cd7db7f6d6b18dd7c0ec1084bb09b03d4eda2476efc77f5d58c"))
|
||||||
|
(2842000, uint256S("0x0005cd49b00a8afa60ce1b88d9964dae60024f2e65a071e5ca1ea1f25770014d"))
|
||||||
|
(2843000, uint256S("0x0003bff7b5424419a8eeece89b8ea9b55f7169f28890f1b70641da3ea6fd14f9"))
|
||||||
|
(2844000, uint256S("0x00001813233d048530ca6bb8f07ce51f5d77dd0f68caaab74982e6655c931315"))
|
||||||
|
(2845000, uint256S("0x000070bd390b117f5c4675a5658a58a4853c687b77553742c89bddff67565da9"))
|
||||||
|
(2846000, uint256S("0x0000c92668956d600a532e8039ac5a8c25d916ec7d66221f89813a75c4eedc41"))
|
||||||
|
(2847000, uint256S("0x0001480bacbd427672a16552928a384362742c4454e97baabe1c5a7c9e15b745"))
|
||||||
|
,(int64_t) 1772014532, // time of last checkpointed block
|
||||||
|
(int64_t) 2952051, // total txs
|
||||||
|
(double) 4576 // txs in the last day before block 2847871
|
||||||
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
// Must be kept in sync with configure.ac , ugh!
|
// Must be kept in sync with configure.ac , ugh!
|
||||||
#define CLIENT_VERSION_MAJOR 1
|
#define CLIENT_VERSION_MAJOR 1
|
||||||
#define CLIENT_VERSION_MINOR 0
|
#define CLIENT_VERSION_MINOR 0
|
||||||
#define CLIENT_VERSION_REVISION 2
|
#define CLIENT_VERSION_REVISION 0
|
||||||
#define CLIENT_VERSION_BUILD 50
|
#define CLIENT_VERSION_BUILD 50
|
||||||
|
|
||||||
//! Set to true for release, false for prerelease or test build
|
//! Set to true for release, false for prerelease or test build
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ bool sanity_test_range_fmt()
|
|||||||
{
|
{
|
||||||
std::string test;
|
std::string test;
|
||||||
try {
|
try {
|
||||||
(void)test.at(1);
|
test.at(1);
|
||||||
} catch (const std::out_of_range&) {
|
} catch (const std::out_of_range&) {
|
||||||
return true;
|
return true;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
|||||||
@@ -51,9 +51,8 @@ namespace port {
|
|||||||
|
|
||||||
// Mac OS
|
// Mac OS
|
||||||
#elif defined(OS_MACOSX)
|
#elif defined(OS_MACOSX)
|
||||||
#include <atomic>
|
|
||||||
inline void MemoryBarrier() {
|
inline void MemoryBarrier() {
|
||||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
OSMemoryBarrier();
|
||||||
}
|
}
|
||||||
#define LEVELDB_HAVE_MEMORY_BARRIER
|
#define LEVELDB_HAVE_MEMORY_BARRIER
|
||||||
|
|
||||||
|
|||||||
BIN
src/libcc.dylib
BIN
src/libcc.dylib
Binary file not shown.
29
src/main.cpp
29
src/main.cpp
@@ -5107,14 +5107,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
|
|||||||
|
|
||||||
assert(pindexPrev);
|
assert(pindexPrev);
|
||||||
|
|
||||||
// For HUSH3, nBits validation starts above the original DAA fork height (450000).
|
int daaForkHeight = GetArg("-daaforkheight", 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;
|
int nHeight = pindexPrev->GetHeight()+1;
|
||||||
bool ishush3 = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? true : false;
|
bool ishush3 = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? true : false;
|
||||||
// Check Proof-of-Work difficulty
|
// Check Proof-of-Work difficulty
|
||||||
@@ -5147,26 +5140,6 @@ 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
|
// Check timestamp against prev
|
||||||
if (ASSETCHAINS_ADAPTIVEPOW <= 0 || nHeight < 30) {
|
if (ASSETCHAINS_ADAPTIVEPOW <= 0 || nHeight < 30) {
|
||||||
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast() )
|
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast() )
|
||||||
|
|||||||
@@ -33,8 +33,6 @@
|
|||||||
#include "crypto/common.h"
|
#include "crypto/common.h"
|
||||||
#include "hush/utiltls.h"
|
#include "hush/utiltls.h"
|
||||||
#include <random.h>
|
#include <random.h>
|
||||||
#include <random>
|
|
||||||
#include <limits>
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#else
|
#else
|
||||||
@@ -2006,7 +2004,7 @@ void ThreadMessageHandler()
|
|||||||
// Randomize the order in which we process messages from/to our peers.
|
// Randomize the order in which we process messages from/to our peers.
|
||||||
// This prevents attacks in which an attacker exploits having multiple
|
// This prevents attacks in which an attacker exploits having multiple
|
||||||
// consecutive connections in the vNodes list.
|
// consecutive connections in the vNodes list.
|
||||||
std::shuffle(vNodesCopy.begin(), vNodesCopy.end(), std::mt19937(GetRand(std::numeric_limits<uint32_t>::max())));
|
random_shuffle(vNodesCopy.begin(), vNodesCopy.end(), GetRandInt);
|
||||||
|
|
||||||
BOOST_FOREACH(CNode* pnode, vNodesCopy)
|
BOOST_FOREACH(CNode* pnode, vNodesCopy)
|
||||||
{
|
{
|
||||||
@@ -2518,7 +2516,7 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss)
|
|||||||
// We always round down, except when we have only 1 connection
|
// We always round down, except when we have only 1 connection
|
||||||
auto newSize = (vNodes.size() / 2) == 0 ? 1 : (vNodes.size() / 2);
|
auto newSize = (vNodes.size() / 2) == 0 ? 1 : (vNodes.size() / 2);
|
||||||
|
|
||||||
std::shuffle( vRelayNodes.begin(), vRelayNodes.end(), std::mt19937(GetRand(std::numeric_limits<uint32_t>::max())) );
|
random_shuffle( vRelayNodes.begin(), vRelayNodes.end(), GetRandInt );
|
||||||
|
|
||||||
vRelayNodes.resize(newSize);
|
vRelayNodes.resize(newSize);
|
||||||
if (HUSH_TESTNODE==1 && vNodes.size() == 0) {
|
if (HUSH_TESTNODE==1 && vNodes.size() == 0) {
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
constexpr uint64_t CNetAddr::V1_SERIALIZATION_SIZE;
|
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 */
|
/** check whether a given address is in a network we can probably connect to */
|
||||||
bool CNetAddr::IsReachableNetwork() {
|
bool CNetAddr::IsReachableNetwork() {
|
||||||
|
|||||||
24
src/pow.cpp
24
src/pow.cpp
@@ -315,16 +315,6 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
|
|||||||
if (pindexLast == NULL )
|
if (pindexLast == NULL )
|
||||||
return nProofOfWorkLimit;
|
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
|
// Comparing to pindexLast->nHeight with >= because this function
|
||||||
// returns the work required for the block after pindexLast.
|
// returns the work required for the block after pindexLast.
|
||||||
@@ -873,17 +863,10 @@ bool CheckProofOfWork(const CBlockHeader &blkHeader, uint8_t *pubkey33, int32_t
|
|||||||
// Check proof of work matches claimed amount
|
// Check proof of work matches claimed amount
|
||||||
if ( UintToArith256(hash = blkHeader.GetHash()) > bnTarget )
|
if ( UintToArith256(hash = blkHeader.GetHash()) > bnTarget )
|
||||||
{
|
{
|
||||||
// During initial block loading/sync, skip PoW validation for blocks
|
if ( HUSH_LOADINGBLOCKS != 0 )
|
||||||
// before RandomX validation height. After activation, always validate
|
return true;
|
||||||
// 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 ( SMART_CHAIN_SYMBOL[0] != 0 || height > 792000 )
|
||||||
{
|
{
|
||||||
if ( Params().NetworkIDString() != "regtest" )
|
if ( Params().NetworkIDString() != "regtest" )
|
||||||
@@ -903,6 +886,7 @@ bool CheckProofOfWork(const CBlockHeader &blkHeader, uint8_t *pubkey33, int32_t
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
/*for (i=31; i>=0; i--)
|
/*for (i=31; i>=0; i--)
|
||||||
fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
|
fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
|
||||||
|
|||||||
@@ -322,15 +322,6 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
|
|||||||
result.push_back(Pair("anchor", blockindex->hashFinalSproutRoot.GetHex()));
|
result.push_back(Pair("anchor", blockindex->hashFinalSproutRoot.GetHex()));
|
||||||
result.push_back(Pair("blocktype", "mined"));
|
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);
|
UniValue valuePools(UniValue::VARR);
|
||||||
valuePools.push_back(ValuePoolDesc("sapling", blockindex->nChainSaplingValue, blockindex->nSaplingValue));
|
valuePools.push_back(ValuePoolDesc("sapling", blockindex->nChainSaplingValue, blockindex->nSaplingValue));
|
||||||
result.push_back(Pair("valuePools", valuePools));
|
result.push_back(Pair("valuePools", valuePools));
|
||||||
|
|||||||
@@ -11,8 +11,6 @@
|
|||||||
#include <boost/optional/optional_io.hpp>
|
#include <boost/optional/optional_io.hpp>
|
||||||
#include <librustzcash.h>
|
#include <librustzcash.h>
|
||||||
#include "zcash/Note.hpp"
|
#include "zcash/Note.hpp"
|
||||||
#include <random>
|
|
||||||
#include <limits>
|
|
||||||
extern bool fZDebug;
|
extern bool fZDebug;
|
||||||
|
|
||||||
SpendDescriptionInfo::SpendDescriptionInfo(
|
SpendDescriptionInfo::SpendDescriptionInfo(
|
||||||
@@ -68,7 +66,7 @@ void TransactionBuilder::AddSaplingOutput(
|
|||||||
void TransactionBuilder::ShuffleOutputs()
|
void TransactionBuilder::ShuffleOutputs()
|
||||||
{
|
{
|
||||||
LogPrintf("%s: Shuffling %d zouts\n", __func__, outputs.size() );
|
LogPrintf("%s: Shuffling %d zouts\n", __func__, outputs.size() );
|
||||||
std::shuffle( outputs.begin(), outputs.end(), std::mt19937(GetRand(std::numeric_limits<uint32_t>::max())) );
|
random_shuffle( outputs.begin(), outputs.end(), GetRandInt );
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionBuilder::AddTransparentInput(COutPoint utxo, CScript scriptPubKey, CAmount value, uint32_t _nSequence)
|
void TransactionBuilder::AddTransparentInput(COutPoint utxo, CScript scriptPubKey, CAmount value, uint32_t _nSequence)
|
||||||
|
|||||||
@@ -39,8 +39,6 @@
|
|||||||
#include "coins.h"
|
#include "coins.h"
|
||||||
#include "wallet/asyncrpcoperation_saplingconsolidation.h"
|
#include "wallet/asyncrpcoperation_saplingconsolidation.h"
|
||||||
#include "wallet/asyncrpcoperation_sweep.h"
|
#include "wallet/asyncrpcoperation_sweep.h"
|
||||||
#include <random>
|
|
||||||
#include <limits>
|
|
||||||
#include "zcash/zip32.h"
|
#include "zcash/zip32.h"
|
||||||
#include "cc/CCinclude.h"
|
#include "cc/CCinclude.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -3418,7 +3416,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
|
|||||||
vector<pair<CAmount, pair<const CWalletTx*,unsigned int> > > vValue;
|
vector<pair<CAmount, pair<const CWalletTx*,unsigned int> > > vValue;
|
||||||
CAmount nTotalLower = 0;
|
CAmount nTotalLower = 0;
|
||||||
|
|
||||||
std::shuffle(vCoins.begin(), vCoins.end(), std::mt19937(GetRand(std::numeric_limits<uint32_t>::max())));
|
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
|
||||||
|
|
||||||
BOOST_FOREACH(const COutput &output, vCoins)
|
BOOST_FOREACH(const COutput &output, vCoins)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
DragonX RandomX Block Time Calculator
|
|
||||||
|
|
||||||
Estimates how long it will take to find a block given your hashrate
|
|
||||||
and the current network difficulty.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
python3 block_time_calculator.py <hashrate_h/s> [--difficulty <diff>]
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
python3 block_time_calculator.py 1000 # 1000 H/s, auto-fetch difficulty
|
|
||||||
python3 block_time_calculator.py 5K # 5 KH/s
|
|
||||||
python3 block_time_calculator.py 1.2M # 1.2 MH/s
|
|
||||||
python3 block_time_calculator.py 500 --difficulty 1234.56
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# DragonX chain constants
|
|
||||||
BLOCK_TIME = 36 # seconds
|
|
||||||
# powLimit = 0x0f0f0f0f... (32 bytes of 0x0f) = (2^256 - 1) / 17
|
|
||||||
# The multiplier 2^256 / powLimit ≈ 17
|
|
||||||
POW_LIMIT_HEX = "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"
|
|
||||||
POW_LIMIT = int(POW_LIMIT_HEX, 16)
|
|
||||||
TWO_256 = 2 ** 256
|
|
||||||
|
|
||||||
|
|
||||||
def parse_hashrate(value):
|
|
||||||
"""Parse hashrate string with optional K/M/G/T suffix."""
|
|
||||||
suffixes = {"K": 1e3, "M": 1e6, "G": 1e9, "T": 1e12}
|
|
||||||
value = value.strip().upper()
|
|
||||||
if value and value[-1] in suffixes:
|
|
||||||
return float(value[:-1]) * suffixes[value[-1]]
|
|
||||||
return float(value)
|
|
||||||
|
|
||||||
|
|
||||||
def get_difficulty_from_node():
|
|
||||||
"""Try to fetch current difficulty from a running DragonX node."""
|
|
||||||
try:
|
|
||||||
result = subprocess.run(
|
|
||||||
["dragonx-cli", "getmininginfo"],
|
|
||||||
capture_output=True, text=True, timeout=10
|
|
||||||
)
|
|
||||||
if result.returncode == 0:
|
|
||||||
info = json.loads(result.stdout)
|
|
||||||
return float(info["difficulty"])
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
except (subprocess.TimeoutExpired, json.JSONDecodeError, KeyError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Try with src/ path relative to script location
|
|
||||||
try:
|
|
||||||
import os
|
|
||||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
cli_path = os.path.join(script_dir, "src", "dragonx-cli")
|
|
||||||
result = subprocess.run(
|
|
||||||
[cli_path, "getmininginfo"],
|
|
||||||
capture_output=True, text=True, timeout=10
|
|
||||||
)
|
|
||||||
if result.returncode == 0:
|
|
||||||
info = json.loads(result.stdout)
|
|
||||||
return float(info["difficulty"])
|
|
||||||
except (FileNotFoundError, subprocess.TimeoutExpired, json.JSONDecodeError, KeyError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def format_duration(seconds):
|
|
||||||
"""Format seconds into a human-readable duration string."""
|
|
||||||
days = seconds / 86400
|
|
||||||
if days >= 365:
|
|
||||||
years = days / 365.25
|
|
||||||
return f"{years:.2f} years ({days:.1f} days)"
|
|
||||||
if days >= 1:
|
|
||||||
hours = (seconds % 86400) / 3600
|
|
||||||
return f"{days:.2f} days ({days * 24:.1f} hours)"
|
|
||||||
hours = seconds / 3600
|
|
||||||
if hours >= 1:
|
|
||||||
return f"{hours:.2f} hours"
|
|
||||||
minutes = seconds / 60
|
|
||||||
return f"{minutes:.1f} minutes"
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="DragonX RandomX Block Time Calculator"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"hashrate",
|
|
||||||
help="Your hashrate in H/s (supports K/M/G/T suffixes, e.g. 5K, 1.2M)"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--difficulty", "-d", type=float, default=None,
|
|
||||||
help="Network difficulty (auto-fetched from local node if omitted)"
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
try:
|
|
||||||
hashrate = parse_hashrate(args.hashrate)
|
|
||||||
except ValueError:
|
|
||||||
print(f"Error: Invalid hashrate '{args.hashrate}'", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if hashrate <= 0:
|
|
||||||
print("Error: Hashrate must be positive", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
difficulty = args.difficulty
|
|
||||||
if difficulty is None:
|
|
||||||
print("Querying local DragonX node for current difficulty...")
|
|
||||||
difficulty = get_difficulty_from_node()
|
|
||||||
if difficulty is None:
|
|
||||||
print(
|
|
||||||
"Error: Could not connect to DragonX node.\n"
|
|
||||||
"Make sure dragonxd is running, or pass --difficulty manually.",
|
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if difficulty <= 0:
|
|
||||||
print("Error: Difficulty must be positive", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Expected hashes to find a block = 2^256 / current_target
|
|
||||||
# Since difficulty = powLimit / current_target:
|
|
||||||
# current_target = powLimit / difficulty
|
|
||||||
# expected_hashes = 2^256 / (powLimit / difficulty) = difficulty * 2^256 / powLimit
|
|
||||||
expected_hashes = difficulty * TWO_256 / POW_LIMIT
|
|
||||||
time_seconds = expected_hashes / hashrate
|
|
||||||
time_days = time_seconds / 86400
|
|
||||||
|
|
||||||
# Estimate network hashrate from difficulty and block time
|
|
||||||
network_hashrate = expected_hashes / BLOCK_TIME
|
|
||||||
|
|
||||||
print()
|
|
||||||
print("=" * 50)
|
|
||||||
print(" DragonX Block Time Estimator (RandomX)")
|
|
||||||
print("=" * 50)
|
|
||||||
print(f" Network difficulty : {difficulty:,.4f}")
|
|
||||||
print(f" Your hashrate : {hashrate:,.0f} H/s")
|
|
||||||
print(f" Est. network hash : {network_hashrate:,.0f} H/s")
|
|
||||||
print(f" Block time target : {BLOCK_TIME}s")
|
|
||||||
print(f" Block reward : 3 DRGX")
|
|
||||||
print("-" * 50)
|
|
||||||
print(f" Expected time to find a block:")
|
|
||||||
print(f" {format_duration(time_seconds)}")
|
|
||||||
print(f" ({time_days:.4f} days)")
|
|
||||||
print("-" * 50)
|
|
||||||
print(f" Est. blocks/day : {86400 / time_seconds:.6f}")
|
|
||||||
print(f" Est. DRGX/day : {86400 / time_seconds * 3:.6f}")
|
|
||||||
print("=" * 50)
|
|
||||||
print()
|
|
||||||
print("Note: This is a statistical estimate. Actual time varies due to randomness.")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
@echo off
|
|
||||||
REM Copyright 2024 The Hush Developers
|
|
||||||
REM Copyright 2024 The DragonX Developers
|
|
||||||
REM Released under the GPLv3
|
|
||||||
REM
|
|
||||||
REM Download and apply a DRAGONX blockchain bootstrap on Windows.
|
|
||||||
REM Safely preserves wallet.dat and configuration files.
|
|
||||||
|
|
||||||
setlocal enabledelayedexpansion
|
|
||||||
|
|
||||||
set "BOOTSTRAP_BASE_URL=https://bootstrap.dragonx.is"
|
|
||||||
set "BOOTSTRAP_FALLBACK_URL=https://bootstrap2.dragonx.is"
|
|
||||||
set "BOOTSTRAP_FILE=DRAGONX.zip"
|
|
||||||
set "CHAIN_NAME=DRAGONX"
|
|
||||||
|
|
||||||
REM Data directory on Windows
|
|
||||||
set "DATADIR=%APPDATA%\Hush\%CHAIN_NAME%"
|
|
||||||
|
|
||||||
REM Find dragonx-cli relative to this script
|
|
||||||
set "CLI="
|
|
||||||
set "SCRIPT_DIR=%~dp0"
|
|
||||||
if exist "%SCRIPT_DIR%dragonx-cli.exe" (
|
|
||||||
set "CLI=%SCRIPT_DIR%dragonx-cli.exe"
|
|
||||||
)
|
|
||||||
|
|
||||||
echo ============================================
|
|
||||||
echo DragonX Bootstrap Installer
|
|
||||||
echo ============================================
|
|
||||||
echo.
|
|
||||||
echo [INFO] Data directory: %DATADIR%
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Step 1: Stop daemon if running
|
|
||||||
call :stop_daemon
|
|
||||||
if errorlevel 1 goto :error_exit
|
|
||||||
|
|
||||||
REM Step 2: Clean old chain data
|
|
||||||
call :clean_chain_data
|
|
||||||
if errorlevel 1 goto :error_exit
|
|
||||||
|
|
||||||
REM Step 3: Download bootstrap
|
|
||||||
call :download_bootstrap
|
|
||||||
if errorlevel 1 goto :error_exit
|
|
||||||
|
|
||||||
REM Step 4: Extract bootstrap
|
|
||||||
call :extract_bootstrap
|
|
||||||
if errorlevel 1 goto :error_exit
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo [INFO] Bootstrap installation complete!
|
|
||||||
echo [INFO] You can now start DragonX with: dragonxd.exe
|
|
||||||
echo.
|
|
||||||
goto :EOF
|
|
||||||
|
|
||||||
REM ============================================
|
|
||||||
REM Stop daemon if running
|
|
||||||
REM ============================================
|
|
||||||
:stop_daemon
|
|
||||||
if "%CLI%"=="" (
|
|
||||||
echo [WARN] dragonx-cli.exe not found next to this script.
|
|
||||||
echo [WARN] Please make sure the DragonX daemon is stopped before continuing.
|
|
||||||
set /p "ANSWER=Is the DragonX daemon stopped? (y/N): "
|
|
||||||
if /i not "!ANSWER!"=="y" (
|
|
||||||
echo [ERROR] Please stop the daemon first and run this script again.
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
exit /b 0
|
|
||||||
)
|
|
||||||
|
|
||||||
"%CLI%" getinfo >nul 2>&1
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo [INFO] Daemon is not running.
|
|
||||||
exit /b 0
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [INFO] Stopping DragonX daemon...
|
|
||||||
"%CLI%" stop >nul 2>&1
|
|
||||||
|
|
||||||
set "TRIES=0"
|
|
||||||
:wait_loop
|
|
||||||
"%CLI%" getinfo >nul 2>&1
|
|
||||||
if errorlevel 1 goto :daemon_stopped
|
|
||||||
timeout /t 2 /nobreak >nul
|
|
||||||
set /a TRIES+=1
|
|
||||||
if %TRIES% geq 60 (
|
|
||||||
echo [ERROR] Daemon did not stop after 120 seconds. Please stop it manually and retry.
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
goto :wait_loop
|
|
||||||
|
|
||||||
:daemon_stopped
|
|
||||||
echo [INFO] Daemon stopped.
|
|
||||||
exit /b 0
|
|
||||||
|
|
||||||
REM ============================================
|
|
||||||
REM Clean blockchain data, preserving wallet and config
|
|
||||||
REM ============================================
|
|
||||||
:clean_chain_data
|
|
||||||
if not exist "%DATADIR%" (
|
|
||||||
echo [INFO] Data directory does not exist yet, creating it.
|
|
||||||
mkdir "%DATADIR%"
|
|
||||||
exit /b 0
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [INFO] Cleaning blockchain data from %DATADIR% ...
|
|
||||||
|
|
||||||
REM Preserve wallet.dat and config
|
|
||||||
set "TMPDIR=%TEMP%\dragonx-bootstrap-%RANDOM%"
|
|
||||||
mkdir "%TMPDIR%" 2>nul
|
|
||||||
|
|
||||||
for %%F in (wallet.dat DRAGONX.conf peers.dat) do (
|
|
||||||
if exist "%DATADIR%\%%F" (
|
|
||||||
copy /y "%DATADIR%\%%F" "%TMPDIR%\%%F" >nul
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Remove blockchain directories and files
|
|
||||||
for %%D in (blocks chainstate notarizations komodo) do (
|
|
||||||
if exist "%DATADIR%\%%D" (
|
|
||||||
rmdir /s /q "%DATADIR%\%%D" 2>nul
|
|
||||||
)
|
|
||||||
)
|
|
||||||
for %%F in (db.log debug.log fee_estimates.dat banlist.dat) do (
|
|
||||||
if exist "%DATADIR%\%%F" (
|
|
||||||
del /f /q "%DATADIR%\%%F" 2>nul
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Restore preserved files
|
|
||||||
for %%F in (wallet.dat DRAGONX.conf peers.dat) do (
|
|
||||||
if exist "%TMPDIR%\%%F" (
|
|
||||||
copy /y "%TMPDIR%\%%F" "%DATADIR%\%%F" >nul
|
|
||||||
)
|
|
||||||
)
|
|
||||||
rmdir /s /q "%TMPDIR%" 2>nul
|
|
||||||
|
|
||||||
echo [INFO] Blockchain data cleaned.
|
|
||||||
exit /b 0
|
|
||||||
|
|
||||||
REM ============================================
|
|
||||||
REM Download bootstrap (with fallback)
|
|
||||||
REM ============================================
|
|
||||||
:download_bootstrap
|
|
||||||
echo [INFO] Downloading bootstrap from %BOOTSTRAP_BASE_URL% ...
|
|
||||||
echo [INFO] This may take a while depending on your connection speed.
|
|
||||||
|
|
||||||
REM Try primary URL
|
|
||||||
call :do_download "%BOOTSTRAP_BASE_URL%"
|
|
||||||
if not errorlevel 1 goto :download_verify
|
|
||||||
|
|
||||||
echo [WARN] Primary download failed, trying fallback %BOOTSTRAP_FALLBACK_URL% ...
|
|
||||||
call :do_download "%BOOTSTRAP_FALLBACK_URL%"
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo [ERROR] Download failed from both primary and fallback servers.
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
:download_verify
|
|
||||||
echo [INFO] Bootstrap download complete.
|
|
||||||
|
|
||||||
REM Verify SHA256 checksum
|
|
||||||
echo [INFO] Verifying checksum...
|
|
||||||
pushd "%DATADIR%"
|
|
||||||
|
|
||||||
REM Read expected hash from the .sha256 file (format: "hash filename" or "hash *filename")
|
|
||||||
set "EXPECTED_HASH="
|
|
||||||
for /f "tokens=1" %%A in (%BOOTSTRAP_FILE%.sha256) do (
|
|
||||||
set "EXPECTED_HASH=%%A"
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%EXPECTED_HASH%"=="" (
|
|
||||||
echo [WARN] Could not read expected checksum, skipping verification.
|
|
||||||
goto :verify_done
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Use certutil to compute SHA256
|
|
||||||
certutil -hashfile "%BOOTSTRAP_FILE%" SHA256 > "%TEMP%\dragonx_hash.tmp" 2>nul
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo [WARN] certutil not available, skipping checksum verification.
|
|
||||||
goto :verify_done
|
|
||||||
)
|
|
||||||
|
|
||||||
REM certutil outputs hash on the second line
|
|
||||||
set "ACTUAL_HASH="
|
|
||||||
set "LINE_NUM=0"
|
|
||||||
for /f "skip=1 tokens=*" %%H in (%TEMP%\dragonx_hash.tmp) do (
|
|
||||||
if not defined ACTUAL_HASH (
|
|
||||||
set "ACTUAL_HASH=%%H"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
REM Remove spaces from certutil output
|
|
||||||
set "ACTUAL_HASH=!ACTUAL_HASH: =!"
|
|
||||||
del /f /q "%TEMP%\dragonx_hash.tmp" 2>nul
|
|
||||||
|
|
||||||
if /i "!ACTUAL_HASH!"=="!EXPECTED_HASH!" (
|
|
||||||
echo [INFO] SHA256 checksum verified.
|
|
||||||
) else (
|
|
||||||
echo [ERROR] SHA256 checksum verification failed! The download may be corrupted.
|
|
||||||
echo [ERROR] Expected: !EXPECTED_HASH!
|
|
||||||
echo [ERROR] Got: !ACTUAL_HASH!
|
|
||||||
popd
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
:verify_done
|
|
||||||
REM Clean up checksum files
|
|
||||||
del /f /q "%BOOTSTRAP_FILE%.md5" 2>nul
|
|
||||||
del /f /q "%BOOTSTRAP_FILE%.sha256" 2>nul
|
|
||||||
popd
|
|
||||||
exit /b 0
|
|
||||||
|
|
||||||
REM ============================================
|
|
||||||
REM Download files from a given base URL
|
|
||||||
REM Usage: call :do_download "base_url"
|
|
||||||
REM ============================================
|
|
||||||
:do_download
|
|
||||||
set "BASE=%~1"
|
|
||||||
|
|
||||||
REM Use PowerShell to download (available on all modern Windows)
|
|
||||||
echo [INFO] Downloading %BASE%/%BOOTSTRAP_FILE% ...
|
|
||||||
powershell -NoProfile -Command ^
|
|
||||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; try { (New-Object Net.WebClient).DownloadFile('%BASE%/%BOOTSTRAP_FILE%', '%DATADIR%\%BOOTSTRAP_FILE%') } catch { exit 1 }"
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
|
|
||||||
echo [INFO] Downloading checksums...
|
|
||||||
powershell -NoProfile -Command ^
|
|
||||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; try { (New-Object Net.WebClient).DownloadFile('%BASE%/%BOOTSTRAP_FILE%.md5', '%DATADIR%\%BOOTSTRAP_FILE%.md5') } catch { exit 1 }"
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
|
|
||||||
powershell -NoProfile -Command ^
|
|
||||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; try { (New-Object Net.WebClient).DownloadFile('%BASE%/%BOOTSTRAP_FILE%.sha256', '%DATADIR%\%BOOTSTRAP_FILE%.sha256') } catch { exit 1 }"
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
|
|
||||||
exit /b 0
|
|
||||||
|
|
||||||
REM ============================================
|
|
||||||
REM Extract bootstrap zip
|
|
||||||
REM ============================================
|
|
||||||
:extract_bootstrap
|
|
||||||
echo [INFO] Extracting bootstrap...
|
|
||||||
pushd "%DATADIR%"
|
|
||||||
|
|
||||||
REM Use PowerShell to extract zip, excluding wallet.dat and .conf files
|
|
||||||
powershell -NoProfile -Command ^
|
|
||||||
"Add-Type -AssemblyName System.IO.Compression.FileSystem; $zip = [System.IO.Compression.ZipFile]::OpenRead('%DATADIR%\%BOOTSTRAP_FILE%'); foreach ($entry in $zip.Entries) { if ($entry.Name -eq 'wallet.dat' -or $entry.Name -like '*.conf') { continue } $dest = Join-Path '%DATADIR%' $entry.FullName; if ($entry.FullName.EndsWith('/')) { New-Item -ItemType Directory -Force -Path $dest | Out-Null } else { $parent = Split-Path $dest -Parent; if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Force -Path $parent | Out-Null } [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $dest, $true) } }; $zip.Dispose()"
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo [ERROR] Extraction failed.
|
|
||||||
popd
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [INFO] Bootstrap extracted successfully.
|
|
||||||
|
|
||||||
REM Clean up archive
|
|
||||||
del /f /q "%BOOTSTRAP_FILE%" 2>nul
|
|
||||||
echo [INFO] Removed downloaded archive to save disk space.
|
|
||||||
|
|
||||||
popd
|
|
||||||
exit /b 0
|
|
||||||
|
|
||||||
:error_exit
|
|
||||||
echo.
|
|
||||||
echo [ERROR] Bootstrap installation failed.
|
|
||||||
exit /b 1
|
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
BOOTSTRAP_BASE_URL="https://bootstrap.dragonx.is"
|
BOOTSTRAP_BASE_URL="https://bootstrap.dragonx.is"
|
||||||
BOOTSTRAP_FALLBACK_URL="https://bootstrap2.dragonx.is"
|
|
||||||
BOOTSTRAP_FILE="DRAGONX.zip"
|
BOOTSTRAP_FILE="DRAGONX.zip"
|
||||||
CHAIN_NAME="DRAGONX"
|
CHAIN_NAME="DRAGONX"
|
||||||
|
|
||||||
@@ -119,49 +118,35 @@ clean_chain_data() {
|
|||||||
info "Blockchain data cleaned."
|
info "Blockchain data cleaned."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Download a file via wget or curl (returns non-zero on failure)
|
# Download a file via wget or curl
|
||||||
download_file() {
|
download_file() {
|
||||||
local url="$1"
|
local url="$1"
|
||||||
local outfile="$2"
|
local outfile="$2"
|
||||||
|
|
||||||
if command -v wget &>/dev/null; then
|
if command -v wget &>/dev/null; then
|
||||||
wget --progress=bar:force -O "$outfile" "$url"
|
wget --progress=bar:force -O "$outfile" "$url" || error "Download failed: $url"
|
||||||
elif command -v curl &>/dev/null; then
|
elif command -v curl &>/dev/null; then
|
||||||
curl -L --progress-bar -o "$outfile" "$url"
|
curl -L --progress-bar -o "$outfile" "$url" || error "Download failed: $url"
|
||||||
else
|
else
|
||||||
error "Neither wget nor curl found. Please install one and retry."
|
error "Neither wget nor curl found. Please install one and retry."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Try downloading from a given base URL; returns non-zero on failure
|
|
||||||
download_from() {
|
|
||||||
local base_url="$1"
|
|
||||||
local outfile="$DATADIR/$BOOTSTRAP_FILE"
|
|
||||||
local md5file="$DATADIR/${BOOTSTRAP_FILE}.md5"
|
|
||||||
local sha256file="$DATADIR/${BOOTSTRAP_FILE}.sha256"
|
|
||||||
|
|
||||||
info "Downloading bootstrap from $base_url ..."
|
|
||||||
info "This may take a while depending on your connection speed."
|
|
||||||
|
|
||||||
download_file "$base_url/$BOOTSTRAP_FILE" "$outfile" || return 1
|
|
||||||
info "Bootstrap download complete."
|
|
||||||
|
|
||||||
info "Downloading checksums..."
|
|
||||||
download_file "$base_url/${BOOTSTRAP_FILE}.md5" "$md5file" || return 1
|
|
||||||
download_file "$base_url/${BOOTSTRAP_FILE}.sha256" "$sha256file" || return 1
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Download the bootstrap and verify checksums
|
# Download the bootstrap and verify checksums
|
||||||
download_bootstrap() {
|
download_bootstrap() {
|
||||||
local outfile="$DATADIR/$BOOTSTRAP_FILE"
|
local outfile="$DATADIR/$BOOTSTRAP_FILE"
|
||||||
local md5file="$DATADIR/${BOOTSTRAP_FILE}.md5"
|
local md5file="$DATADIR/${BOOTSTRAP_FILE}.md5"
|
||||||
local sha256file="$DATADIR/${BOOTSTRAP_FILE}.sha256"
|
local sha256file="$DATADIR/${BOOTSTRAP_FILE}.sha256"
|
||||||
|
|
||||||
if ! download_from "$BOOTSTRAP_BASE_URL"; then
|
info "Downloading bootstrap from $BOOTSTRAP_BASE_URL ..."
|
||||||
warn "Primary download failed, trying fallback $BOOTSTRAP_FALLBACK_URL ..."
|
info "This may take a while depending on your connection speed."
|
||||||
download_from "$BOOTSTRAP_FALLBACK_URL" || error "Download failed from both primary and fallback servers."
|
|
||||||
fi
|
download_file "$BOOTSTRAP_BASE_URL/$BOOTSTRAP_FILE" "$outfile"
|
||||||
|
info "Bootstrap download complete."
|
||||||
|
|
||||||
|
info "Downloading checksums..."
|
||||||
|
download_file "$BOOTSTRAP_BASE_URL/${BOOTSTRAP_FILE}.md5" "$md5file"
|
||||||
|
download_file "$BOOTSTRAP_BASE_URL/${BOOTSTRAP_FILE}.sha256" "$sha256file"
|
||||||
|
|
||||||
# Verify checksums
|
# Verify checksums
|
||||||
info "Verifying checksums..."
|
info "Verifying checksums..."
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||||
|
|
||||||
export CC=gcc-15
|
export CC=gcc-8
|
||||||
export CXX=g++-15
|
export CXX=g++-8
|
||||||
export LIBTOOL=libtool
|
export LIBTOOL=libtool
|
||||||
export AR=ar
|
export AR=ar
|
||||||
export RANLIB=ranlib
|
export RANLIB=ranlib
|
||||||
@@ -34,24 +34,16 @@ fi
|
|||||||
# If --enable-lcov is the first argument, enable lcov coverage support:
|
# If --enable-lcov is the first argument, enable lcov coverage support:
|
||||||
LCOV_ARG=''
|
LCOV_ARG=''
|
||||||
HARDENING_ARG='--disable-hardening'
|
HARDENING_ARG='--disable-hardening'
|
||||||
TEST_ARG=''
|
|
||||||
if [ "x${1:-}" = 'x--enable-lcov' ]
|
if [ "x${1:-}" = 'x--enable-lcov' ]
|
||||||
then
|
then
|
||||||
LCOV_ARG='--enable-lcov'
|
LCOV_ARG='--enable-lcov'
|
||||||
HARDENING_ARG='--disable-hardening'
|
HARDENING_ARG='--disable-hardening'
|
||||||
shift
|
shift
|
||||||
elif [ "x${1:-}" = 'x--disable-tests' ]
|
|
||||||
then
|
|
||||||
TEST_ARG='--enable-tests=no'
|
|
||||||
shift
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TRIPLET=`./depends/config.guess`
|
TRIPLET=`./depends/config.guess`
|
||||||
PREFIX="$(pwd)/depends/$TRIPLET"
|
PREFIX="$(pwd)/depends/$TRIPLET"
|
||||||
|
|
||||||
# Ensure system Rust is in PATH for modern macOS compatibility
|
|
||||||
export PATH="$HOME/.cargo/bin:$PATH"
|
|
||||||
|
|
||||||
make "$@" -C ./depends/ V=1 NO_QT=1
|
make "$@" -C ./depends/ V=1 NO_QT=1
|
||||||
|
|
||||||
#BUILD CCLIB
|
#BUILD CCLIB
|
||||||
@@ -76,7 +68,7 @@ cd $WD
|
|||||||
|
|
||||||
./autogen.sh
|
./autogen.sh
|
||||||
CPPFLAGS="-I$PREFIX/include -arch x86_64" LDFLAGS="-L$PREFIX/lib -arch x86_64 -Wl,-no_pie" \
|
CPPFLAGS="-I$PREFIX/include -arch x86_64" LDFLAGS="-L$PREFIX/lib -arch x86_64 -Wl,-no_pie" \
|
||||||
CXXFLAGS='-arch x86_64 -I/usr/local/Cellar/gcc/15.2.0_1/include/c++/15/ -I$PREFIX/include -fwrapv -fno-strict-aliasing -Wno-builtin-declaration-mismatch -Werror -Wno-error=deprecated-declarations -g -Wl,-undefined -Wl,dynamic_lookup' \
|
CXXFLAGS='-arch x86_64 -I/usr/local/Cellar/gcc\@8/8.3.0/include/c++/8.3.0/ -I$PREFIX/include -fwrapv -fno-strict-aliasing -Wno-builtin-declaration-mismatch -Werror -g -Wl,-undefined -Wl,dynamic_lookup' \
|
||||||
./configure --prefix="${PREFIX}" --with-gui=no "$HARDENING_ARG" "$LCOV_ARG" $TEST_ARG
|
./configure --prefix="${PREFIX}" --with-gui=no "$HARDENING_ARG" "$LCOV_ARG"
|
||||||
|
|
||||||
make "$@" V=1 NO_GTEST=1 STATIC=1
|
make "$@" V=1 NO_GTEST=1 STATIC=1
|
||||||
|
|||||||
@@ -1,554 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
DragonX Block Validation Test Suite
|
|
||||||
|
|
||||||
Submits tampered blocks to a running DragonX node and verifies they are all
|
|
||||||
rejected. Each test modifies a single field in a real block fetched from the
|
|
||||||
chain tip, then submits via the submitblock RPC.
|
|
||||||
|
|
||||||
Tests:
|
|
||||||
1. Bad nBits (diff=1) - ContextualCheckBlockHeader / CheckProofOfWork
|
|
||||||
2. Bad RandomX solution - CheckRandomXSolution
|
|
||||||
3. Future timestamp - CheckBlockHeader time check
|
|
||||||
4. Bad block version (version=0) - CheckBlockHeader version check
|
|
||||||
5. Bad Merkle root - CheckBlock Merkle validation
|
|
||||||
6. Bad hashPrevBlock - ContextualCheckBlockHeader / AcceptBlockHeader
|
|
||||||
7. Inflated coinbase reward - ConnectBlock subsidy check
|
|
||||||
8. Duplicate transaction - CheckBlock Merkle malleability (CVE-2012-2459)
|
|
||||||
9. Timestamp too old (MTP) - ContextualCheckBlockHeader median time check
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
python3 test_block_validation.py
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import struct
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import hashlib
|
|
||||||
import copy
|
|
||||||
|
|
||||||
CLI = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src", "dragonx-cli")
|
|
||||||
DEBUG_LOG = os.path.expanduser("~/.hush/DRAGONX/debug.log")
|
|
||||||
|
|
||||||
# ---------- RPC helpers ----------
|
|
||||||
|
|
||||||
def rpc(method, *args):
|
|
||||||
cmd = [CLI, method] + [str(a) for a in args]
|
|
||||||
try:
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
||||||
return result.stdout.strip()
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
if e.stdout and e.stdout.strip():
|
|
||||||
return e.stdout.strip()
|
|
||||||
if e.stderr and e.stderr.strip():
|
|
||||||
return e.stderr.strip()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def rpc_json(method, *args):
|
|
||||||
raw = rpc(method, *args)
|
|
||||||
return json.loads(raw)
|
|
||||||
|
|
||||||
# ---------- Serialization helpers ----------
|
|
||||||
|
|
||||||
def read_int32(data, offset):
|
|
||||||
return struct.unpack_from('<i', data, offset)[0], offset + 4
|
|
||||||
|
|
||||||
def read_uint32(data, offset):
|
|
||||||
return struct.unpack_from('<I', data, offset)[0], offset + 4
|
|
||||||
|
|
||||||
def read_int64(data, offset):
|
|
||||||
return struct.unpack_from('<q', data, offset)[0], offset + 8
|
|
||||||
|
|
||||||
def read_uint256(data, offset):
|
|
||||||
return data[offset:offset+32], offset + 32
|
|
||||||
|
|
||||||
def read_compactsize(data, offset):
|
|
||||||
val = data[offset]
|
|
||||||
if val < 253:
|
|
||||||
return val, offset + 1
|
|
||||||
elif val == 253:
|
|
||||||
return struct.unpack_from('<H', data, offset + 1)[0], offset + 3
|
|
||||||
elif val == 254:
|
|
||||||
return struct.unpack_from('<I', data, offset + 1)[0], offset + 5
|
|
||||||
else:
|
|
||||||
return struct.unpack_from('<Q', data, offset + 1)[0], offset + 9
|
|
||||||
|
|
||||||
def write_compactsize(val):
|
|
||||||
if val < 253:
|
|
||||||
return bytes([val])
|
|
||||||
elif val <= 0xFFFF:
|
|
||||||
return b'\xfd' + struct.pack('<H', val)
|
|
||||||
elif val <= 0xFFFFFFFF:
|
|
||||||
return b'\xfe' + struct.pack('<I', val)
|
|
||||||
else:
|
|
||||||
return b'\xff' + struct.pack('<Q', val)
|
|
||||||
|
|
||||||
def dsha256(data):
|
|
||||||
return hashlib.sha256(hashlib.sha256(data).digest()).digest()
|
|
||||||
|
|
||||||
# ---------- Block parsing ----------
|
|
||||||
|
|
||||||
# Header field offsets (all little-endian):
|
|
||||||
# 0: nVersion (int32, 4 bytes)
|
|
||||||
# 4: hashPrevBlock (uint256, 32 bytes)
|
|
||||||
# 36: hashMerkleRoot (uint256, 32 bytes)
|
|
||||||
# 68: hashFinalSaplingRoot (uint256, 32 bytes)
|
|
||||||
# 100: nTime (uint32, 4 bytes)
|
|
||||||
# 104: nBits (uint32, 4 bytes)
|
|
||||||
# 108: nNonce (uint256, 32 bytes)
|
|
||||||
# 140: nSolution (compactsize + data)
|
|
||||||
|
|
||||||
OFF_VERSION = 0
|
|
||||||
OFF_PREVHASH = 4
|
|
||||||
OFF_MERKLEROOT = 36
|
|
||||||
OFF_SAPLINGROOT = 68
|
|
||||||
OFF_TIME = 100
|
|
||||||
OFF_BITS = 104
|
|
||||||
OFF_NONCE = 108
|
|
||||||
HEADER_FIXED = 140 # everything before nSolution
|
|
||||||
|
|
||||||
def parse_header(data):
|
|
||||||
"""Parse block header fields. Returns dict with values and offsets."""
|
|
||||||
hdr = {}
|
|
||||||
hdr['nVersion'], _ = read_int32(data, OFF_VERSION)
|
|
||||||
hdr['hashPrevBlock'], _ = read_uint256(data, OFF_PREVHASH)
|
|
||||||
hdr['hashMerkleRoot'], _ = read_uint256(data, OFF_MERKLEROOT)
|
|
||||||
hdr['hashFinalSaplingRoot'], _ = read_uint256(data, OFF_SAPLINGROOT)
|
|
||||||
hdr['nTime'], _ = read_uint32(data, OFF_TIME)
|
|
||||||
hdr['nBits'], _ = read_uint32(data, OFF_BITS)
|
|
||||||
hdr['nNonce'], _ = read_uint256(data, OFF_NONCE)
|
|
||||||
sol_len, sol_start = read_compactsize(data, HEADER_FIXED)
|
|
||||||
hdr['nSolution'] = data[HEADER_FIXED:sol_start + sol_len]
|
|
||||||
hdr['header_end'] = sol_start + sol_len # offset where tx data begins
|
|
||||||
return hdr
|
|
||||||
|
|
||||||
def find_tx_boundaries(data, tx_start_offset):
|
|
||||||
"""Find the start offsets and raw bytes of each transaction in the block.
|
|
||||||
Returns list of (start_offset, raw_tx_bytes)."""
|
|
||||||
offset = tx_start_offset
|
|
||||||
tx_count, offset = read_compactsize(data, offset)
|
|
||||||
txs = []
|
|
||||||
for _ in range(tx_count):
|
|
||||||
tx_begin = offset
|
|
||||||
# Parse enough to skip past this transaction
|
|
||||||
offset = skip_transaction(data, offset)
|
|
||||||
txs.append((tx_begin, data[tx_begin:offset]))
|
|
||||||
return tx_count, txs, tx_start_offset
|
|
||||||
|
|
||||||
def skip_transaction(data, offset):
|
|
||||||
"""Skip over a serialized Sapling v4 transaction, returning offset after it."""
|
|
||||||
start = offset
|
|
||||||
# header (nVersion with fOverwintered flag)
|
|
||||||
header, offset = read_uint32(data, offset)
|
|
||||||
fOverwintered = (header >> 31) & 1
|
|
||||||
nVersion = header & 0x7FFFFFFF
|
|
||||||
|
|
||||||
if fOverwintered:
|
|
||||||
nVersionGroupId, offset = read_uint32(data, offset)
|
|
||||||
|
|
||||||
# vin
|
|
||||||
vin_count, offset = read_compactsize(data, offset)
|
|
||||||
for _ in range(vin_count):
|
|
||||||
offset += 32 # prevout hash
|
|
||||||
offset += 4 # prevout n
|
|
||||||
script_len, offset = read_compactsize(data, offset)
|
|
||||||
offset += script_len # scriptSig
|
|
||||||
offset += 4 # nSequence
|
|
||||||
|
|
||||||
# vout
|
|
||||||
vout_count, offset = read_compactsize(data, offset)
|
|
||||||
for _ in range(vout_count):
|
|
||||||
offset += 8 # nValue
|
|
||||||
script_len, offset = read_compactsize(data, offset)
|
|
||||||
offset += script_len # scriptPubKey
|
|
||||||
|
|
||||||
# nLockTime
|
|
||||||
offset += 4
|
|
||||||
|
|
||||||
if fOverwintered:
|
|
||||||
# nExpiryHeight
|
|
||||||
offset += 4
|
|
||||||
|
|
||||||
if nVersion >= 4 and fOverwintered:
|
|
||||||
# valueBalance
|
|
||||||
offset += 8
|
|
||||||
# vShieldedSpend
|
|
||||||
ss_count, offset = read_compactsize(data, offset)
|
|
||||||
for _ in range(ss_count):
|
|
||||||
offset += 32 # cv
|
|
||||||
offset += 32 # anchor
|
|
||||||
offset += 32 # nullifier
|
|
||||||
offset += 32 # rk
|
|
||||||
offset += 192 # zkproof (Groth16)
|
|
||||||
offset += 64 # spendAuthSig
|
|
||||||
# vShieldedOutput
|
|
||||||
so_count, offset = read_compactsize(data, offset)
|
|
||||||
for _ in range(so_count):
|
|
||||||
offset += 32 # cv
|
|
||||||
offset += 32 # cmu
|
|
||||||
offset += 32 # ephemeralKey
|
|
||||||
offset += 580 # encCiphertext
|
|
||||||
offset += 80 # outCiphertext
|
|
||||||
offset += 192 # zkproof
|
|
||||||
if ss_count > 0 or so_count > 0:
|
|
||||||
offset += 64 # bindingSig
|
|
||||||
|
|
||||||
if nVersion >= 2:
|
|
||||||
# vjoinsplit
|
|
||||||
js_count, offset = read_compactsize(data, offset)
|
|
||||||
if js_count > 0:
|
|
||||||
for _ in range(js_count):
|
|
||||||
offset += 8 # vpub_old
|
|
||||||
offset += 8 # vpub_new
|
|
||||||
offset += 32 # anchor
|
|
||||||
offset += 32 * 2 # nullifiers (2)
|
|
||||||
offset += 32 * 2 # commitments (2)
|
|
||||||
offset += 32 # ephemeralKey
|
|
||||||
offset += 32 # randomSeed
|
|
||||||
offset += 32 * 2 # macs (2)
|
|
||||||
if nVersion >= 4 and fOverwintered:
|
|
||||||
offset += 192 # Groth16 proof
|
|
||||||
else:
|
|
||||||
offset += 296 # PHGR proof
|
|
||||||
offset += 601 * 2 # encCiphertexts (2)
|
|
||||||
offset += 32 # joinSplitPubKey
|
|
||||||
offset += 64 # joinSplitSig
|
|
||||||
|
|
||||||
return offset
|
|
||||||
|
|
||||||
# ---------- Log checking ----------
|
|
||||||
|
|
||||||
def get_log_position():
|
|
||||||
if os.path.exists(DEBUG_LOG):
|
|
||||||
return os.path.getsize(DEBUG_LOG)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def get_new_log_entries(pos_before):
|
|
||||||
if not os.path.exists(DEBUG_LOG):
|
|
||||||
return []
|
|
||||||
with open(DEBUG_LOG, "r", errors="replace") as f:
|
|
||||||
f.seek(pos_before)
|
|
||||||
text = f.read()
|
|
||||||
lines = []
|
|
||||||
for line in text.splitlines():
|
|
||||||
low = line.lower()
|
|
||||||
if any(kw in low for kw in ["failed", "error", "reject", "invalid",
|
|
||||||
"high-hash", "bad-diff", "mismatch",
|
|
||||||
"checkblock", "checkproof", "randomx",
|
|
||||||
"bad-txnmrklroot", "bad-cb", "time-too",
|
|
||||||
"bad-blk", "version-too", "duplicate",
|
|
||||||
"bad-prevblk", "acceptblock"]):
|
|
||||||
lines.append(line.strip())
|
|
||||||
return lines
|
|
||||||
|
|
||||||
# ---------- Test framework ----------
|
|
||||||
|
|
||||||
class TestResult:
|
|
||||||
def __init__(self, name):
|
|
||||||
self.name = name
|
|
||||||
self.passed = False
|
|
||||||
self.rpc_result = ""
|
|
||||||
self.log_lines = []
|
|
||||||
self.detail = ""
|
|
||||||
|
|
||||||
def submit_and_check(test_name, tampered_hex, original_tip):
|
|
||||||
"""Submit a tampered block and check that it was rejected."""
|
|
||||||
res = TestResult(test_name)
|
|
||||||
log_pos = get_log_position()
|
|
||||||
|
|
||||||
# Small delay to ensure log timestamps differ
|
|
||||||
time.sleep(0.2)
|
|
||||||
|
|
||||||
res.rpc_result = rpc("submitblock", tampered_hex)
|
|
||||||
time.sleep(0.3)
|
|
||||||
|
|
||||||
res.log_lines = get_new_log_entries(log_pos)
|
|
||||||
|
|
||||||
# Check chain tip unchanged (allow natural advancement to a different block)
|
|
||||||
new_tip = rpc("getbestblockhash")
|
|
||||||
# The tip may have advanced naturally from new blocks being mined.
|
|
||||||
# That's fine — what matters is the tampered block didn't become the tip.
|
|
||||||
# We can't easily compute the tampered block's hash here, but we can check
|
|
||||||
# that the RPC/log indicate rejection.
|
|
||||||
tip_unchanged = True # assume OK unless we see evidence otherwise
|
|
||||||
|
|
||||||
# Determine if rejection occurred
|
|
||||||
rpc_rejected = res.rpc_result.lower() in ("rejected", "invalid", "") if res.rpc_result is not None else True
|
|
||||||
if res.rpc_result is None or res.rpc_result == "":
|
|
||||||
rpc_rejected = True
|
|
||||||
# "duplicate" means the node already had a block with this header hash — also a rejection
|
|
||||||
if res.rpc_result and "duplicate" in res.rpc_result.lower():
|
|
||||||
rpc_rejected = True
|
|
||||||
log_rejected = any("FAILED" in l or "MISMATCH" in l or "ERROR" in l for l in res.log_lines)
|
|
||||||
|
|
||||||
res.passed = tip_unchanged and (rpc_rejected or log_rejected)
|
|
||||||
|
|
||||||
if res.log_lines:
|
|
||||||
# Pick the most informative line
|
|
||||||
for l in res.log_lines:
|
|
||||||
if "ERROR" in l or "FAILED" in l or "MISMATCH" in l:
|
|
||||||
res.detail = l
|
|
||||||
break
|
|
||||||
if not res.detail:
|
|
||||||
res.detail = res.log_lines[-1]
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
# ---------- Individual tests ----------
|
|
||||||
|
|
||||||
def test_bad_nbits(block_data, tip_hash):
|
|
||||||
"""Test 1: Change nBits to diff=1 (powLimit)."""
|
|
||||||
tampered = bytearray(block_data)
|
|
||||||
struct.pack_into('<I', tampered, OFF_BITS, 0x200f0f0f)
|
|
||||||
return submit_and_check("Bad nBits (diff=1)", tampered.hex(), tip_hash)
|
|
||||||
|
|
||||||
def test_bad_randomx_solution(block_data, tip_hash):
|
|
||||||
"""Test 2: Corrupt the RandomX solution (flip all bytes)."""
|
|
||||||
tampered = bytearray(block_data)
|
|
||||||
sol_len, sol_data_start = read_compactsize(block_data, HEADER_FIXED)
|
|
||||||
# Flip every byte in the solution
|
|
||||||
for i in range(sol_data_start, sol_data_start + sol_len):
|
|
||||||
tampered[i] ^= 0xFF
|
|
||||||
return submit_and_check("Bad RandomX solution", tampered.hex(), tip_hash)
|
|
||||||
|
|
||||||
def test_future_timestamp(block_data, tip_hash):
|
|
||||||
"""Test 3: Set timestamp far in the future (+3600 seconds)."""
|
|
||||||
tampered = bytearray(block_data)
|
|
||||||
future_time = int(time.time()) + 3600 # 1 hour from now
|
|
||||||
struct.pack_into('<I', tampered, OFF_TIME, future_time)
|
|
||||||
return submit_and_check("Future timestamp (+1hr)", tampered.hex(), tip_hash)
|
|
||||||
|
|
||||||
def test_bad_version(block_data, tip_hash):
|
|
||||||
"""Test 4: Set block version to 0 (below MIN_BLOCK_VERSION=4)."""
|
|
||||||
tampered = bytearray(block_data)
|
|
||||||
struct.pack_into('<i', tampered, OFF_VERSION, 0)
|
|
||||||
return submit_and_check("Bad version (v=0)", tampered.hex(), tip_hash)
|
|
||||||
|
|
||||||
def test_bad_merkle_root(block_data, tip_hash):
|
|
||||||
"""Test 5: Corrupt the Merkle root hash."""
|
|
||||||
tampered = bytearray(block_data)
|
|
||||||
for i in range(OFF_MERKLEROOT, OFF_MERKLEROOT + 32):
|
|
||||||
tampered[i] ^= 0xFF
|
|
||||||
return submit_and_check("Bad Merkle root", tampered.hex(), tip_hash)
|
|
||||||
|
|
||||||
def test_bad_prevhash(block_data, tip_hash):
|
|
||||||
"""Test 6: Set hashPrevBlock to a random/nonexistent hash."""
|
|
||||||
tampered = bytearray(block_data)
|
|
||||||
# Set to all 0x42 (definitely not a real block hash)
|
|
||||||
for i in range(OFF_PREVHASH, OFF_PREVHASH + 32):
|
|
||||||
tampered[i] = 0x42
|
|
||||||
return submit_and_check("Bad hashPrevBlock", tampered.hex(), tip_hash)
|
|
||||||
|
|
||||||
def compute_merkle_root(tx_hashes):
|
|
||||||
"""Compute Merkle root from a list of transaction hashes (bytes)."""
|
|
||||||
if not tx_hashes:
|
|
||||||
return b'\x00' * 32
|
|
||||||
level = list(tx_hashes)
|
|
||||||
while len(level) > 1:
|
|
||||||
next_level = []
|
|
||||||
for i in range(0, len(level), 2):
|
|
||||||
if i + 1 < len(level):
|
|
||||||
next_level.append(dsha256(level[i] + level[i+1]))
|
|
||||||
else:
|
|
||||||
next_level.append(dsha256(level[i] + level[i]))
|
|
||||||
level = next_level
|
|
||||||
return level[0]
|
|
||||||
|
|
||||||
def rebuild_block_with_new_merkle(header_bytes, tx_data_list):
|
|
||||||
"""Rebuild a block with recomputed Merkle root from modified transactions."""
|
|
||||||
# Compute tx hashes
|
|
||||||
tx_hashes = [dsha256(tx_bytes) for tx_bytes in tx_data_list]
|
|
||||||
new_merkle = compute_merkle_root(tx_hashes)
|
|
||||||
|
|
||||||
# Rebuild header with new merkle root
|
|
||||||
tampered = bytearray(header_bytes)
|
|
||||||
tampered[OFF_MERKLEROOT:OFF_MERKLEROOT+32] = new_merkle
|
|
||||||
|
|
||||||
# Append tx count + tx data
|
|
||||||
tampered += write_compactsize(len(tx_data_list))
|
|
||||||
for tx_bytes in tx_data_list:
|
|
||||||
tampered += tx_bytes
|
|
||||||
|
|
||||||
return tampered
|
|
||||||
|
|
||||||
def test_inflated_coinbase(block_data, tip_hash):
|
|
||||||
"""Test 7: Double the coinbase output value and recompute Merkle root."""
|
|
||||||
hdr = parse_header(block_data)
|
|
||||||
tx_data_start = hdr['header_end']
|
|
||||||
header_bytes = block_data[:tx_data_start]
|
|
||||||
|
|
||||||
tx_count, txs, _ = find_tx_boundaries(block_data, tx_data_start)
|
|
||||||
|
|
||||||
if tx_count == 0:
|
|
||||||
res = TestResult("Inflated coinbase")
|
|
||||||
res.detail = "SKIP: No transactions in block"
|
|
||||||
return res
|
|
||||||
|
|
||||||
# Parse the coinbase tx to find its first output value
|
|
||||||
coinbase_raw = bytearray(txs[0][1])
|
|
||||||
offset = 0
|
|
||||||
tx_header, offset = read_uint32(coinbase_raw, offset)
|
|
||||||
fOverwintered = (tx_header >> 31) & 1
|
|
||||||
if fOverwintered:
|
|
||||||
offset += 4 # nVersionGroupId
|
|
||||||
|
|
||||||
# vin
|
|
||||||
vin_count, offset = read_compactsize(coinbase_raw, offset)
|
|
||||||
for _ in range(vin_count):
|
|
||||||
offset += 32 + 4 # prevout
|
|
||||||
script_len, offset = read_compactsize(coinbase_raw, offset)
|
|
||||||
offset += script_len + 4 # scriptSig + nSequence
|
|
||||||
|
|
||||||
# vout - find the first output's nValue
|
|
||||||
vout_count, offset = read_compactsize(coinbase_raw, offset)
|
|
||||||
if vout_count == 0:
|
|
||||||
res = TestResult("Inflated coinbase")
|
|
||||||
res.detail = "SKIP: Coinbase has no outputs"
|
|
||||||
return res
|
|
||||||
|
|
||||||
# offset now points to the first vout's nValue (int64) within the coinbase tx
|
|
||||||
original_value = struct.unpack_from('<q', coinbase_raw, offset)[0]
|
|
||||||
inflated_value = original_value * 100 # 100x the reward
|
|
||||||
struct.pack_into('<q', coinbase_raw, offset, inflated_value)
|
|
||||||
|
|
||||||
# Rebuild block with modified coinbase and recomputed Merkle root
|
|
||||||
all_txs = [bytes(coinbase_raw)] + [raw for _, raw in txs[1:]]
|
|
||||||
tampered = rebuild_block_with_new_merkle(header_bytes, all_txs)
|
|
||||||
|
|
||||||
return submit_and_check(
|
|
||||||
f"Inflated coinbase ({original_value} -> {inflated_value} sat)",
|
|
||||||
tampered.hex(), tip_hash
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_duplicate_transaction(block_data, tip_hash):
|
|
||||||
"""Test 8: Duplicate a transaction in the block (Merkle malleability)."""
|
|
||||||
hdr = parse_header(block_data)
|
|
||||||
tx_data_start = hdr['header_end']
|
|
||||||
header_bytes = block_data[:tx_data_start]
|
|
||||||
|
|
||||||
tx_count, txs, _ = find_tx_boundaries(block_data, tx_data_start)
|
|
||||||
|
|
||||||
if tx_count < 1:
|
|
||||||
res = TestResult("Duplicate transaction")
|
|
||||||
res.detail = "SKIP: No transactions in block"
|
|
||||||
return res
|
|
||||||
|
|
||||||
# Duplicate the last transaction and recompute Merkle root
|
|
||||||
all_txs = [raw for _, raw in txs] + [txs[-1][1]]
|
|
||||||
tampered = rebuild_block_with_new_merkle(header_bytes, all_txs)
|
|
||||||
|
|
||||||
return submit_and_check("Duplicate transaction (Merkle malleability)", tampered.hex(), tip_hash)
|
|
||||||
|
|
||||||
def test_timestamp_too_old(block_data, tip_hash):
|
|
||||||
"""Test 9: Set timestamp to 0 (way before median time past)."""
|
|
||||||
tampered = bytearray(block_data)
|
|
||||||
# Set nTime to 1 (basically epoch start - way before MTP)
|
|
||||||
struct.pack_into('<I', tampered, OFF_TIME, 1)
|
|
||||||
return submit_and_check("Timestamp too old (nTime=1)", tampered.hex(), tip_hash)
|
|
||||||
|
|
||||||
# ---------- Main ----------
|
|
||||||
|
|
||||||
def main():
|
|
||||||
print("=" * 70)
|
|
||||||
print(" DragonX Block Validation Test Suite")
|
|
||||||
print("=" * 70)
|
|
||||||
|
|
||||||
# Get chain state
|
|
||||||
print("\nConnecting to node...")
|
|
||||||
info = rpc_json("getblockchaininfo")
|
|
||||||
height = info["blocks"]
|
|
||||||
tip_hash = info["bestblockhash"]
|
|
||||||
print(f" Chain height : {height}")
|
|
||||||
print(f" Chain tip : {tip_hash}")
|
|
||||||
|
|
||||||
block_info = rpc_json("getblock", tip_hash)
|
|
||||||
print(f" Current nBits: 0x{int(block_info['bits'], 16):08x}")
|
|
||||||
print(f" Difficulty : {block_info['difficulty']}")
|
|
||||||
|
|
||||||
# Fetch raw block
|
|
||||||
block_hex = rpc("getblock", tip_hash, "0")
|
|
||||||
block_data = bytes.fromhex(block_hex)
|
|
||||||
print(f" Block size : {len(block_data)} bytes")
|
|
||||||
|
|
||||||
hdr = parse_header(block_data)
|
|
||||||
tx_data_start = hdr['header_end']
|
|
||||||
tx_count, txs, _ = find_tx_boundaries(block_data, tx_data_start)
|
|
||||||
print(f" Transactions : {tx_count}")
|
|
||||||
|
|
||||||
# Run all tests
|
|
||||||
tests = [
|
|
||||||
("1. Bad nBits (diff=1)", test_bad_nbits),
|
|
||||||
("2. Bad RandomX solution", test_bad_randomx_solution),
|
|
||||||
("3. Future timestamp (+1hr)", test_future_timestamp),
|
|
||||||
("4. Bad block version (v=0)", test_bad_version),
|
|
||||||
("5. Bad Merkle root", test_bad_merkle_root),
|
|
||||||
("6. Bad hashPrevBlock", test_bad_prevhash),
|
|
||||||
("7. Inflated coinbase reward", test_inflated_coinbase),
|
|
||||||
("8. Duplicate transaction", test_duplicate_transaction),
|
|
||||||
("9. Timestamp too old (MTP)", test_timestamp_too_old),
|
|
||||||
]
|
|
||||||
|
|
||||||
print(f"\nRunning {len(tests)} validation tests...\n")
|
|
||||||
print("-" * 70)
|
|
||||||
|
|
||||||
results = []
|
|
||||||
for label, test_func in tests:
|
|
||||||
# Re-fetch tip in case of a new block during testing
|
|
||||||
current_tip = rpc("getbestblockhash")
|
|
||||||
if current_tip != tip_hash:
|
|
||||||
print(f" [info] Chain tip advanced, re-fetching block...")
|
|
||||||
tip_hash = current_tip
|
|
||||||
block_hex = rpc("getblock", tip_hash, "0")
|
|
||||||
block_data = bytes.fromhex(block_hex)
|
|
||||||
|
|
||||||
sys.stdout.write(f" {label:<45}")
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
res = test_func(block_data, tip_hash)
|
|
||||||
results.append(res)
|
|
||||||
|
|
||||||
if res.passed:
|
|
||||||
print(f" PASS")
|
|
||||||
elif "SKIP" in res.detail:
|
|
||||||
print(f" SKIP")
|
|
||||||
else:
|
|
||||||
print(f" FAIL")
|
|
||||||
|
|
||||||
# Print detail on a second line
|
|
||||||
if res.detail:
|
|
||||||
# Truncate long lines for readability
|
|
||||||
detail = res.detail[:120] + "..." if len(res.detail) > 120 else res.detail
|
|
||||||
print(f" -> {detail}")
|
|
||||||
elif res.rpc_result:
|
|
||||||
print(f" -> RPC: {res.rpc_result}")
|
|
||||||
|
|
||||||
# Summary
|
|
||||||
print("\n" + "=" * 70)
|
|
||||||
passed = sum(1 for r in results if r.passed)
|
|
||||||
failed = sum(1 for r in results if not r.passed and "SKIP" not in r.detail)
|
|
||||||
skipped = sum(1 for r in results if "SKIP" in r.detail)
|
|
||||||
total = len(results)
|
|
||||||
|
|
||||||
print(f" Results: {passed}/{total} passed, {failed} failed, {skipped} skipped")
|
|
||||||
|
|
||||||
if failed == 0:
|
|
||||||
print(" ALL TESTS PASSED - Block validation is intact!")
|
|
||||||
else:
|
|
||||||
print("\n FAILED TESTS:")
|
|
||||||
for r in results:
|
|
||||||
if not r.passed and "SKIP" not in r.detail:
|
|
||||||
print(f" - {r.name}: {r.detail or r.rpc_result}")
|
|
||||||
|
|
||||||
# Verify chain tip is still intact
|
|
||||||
final_tip = rpc("getbestblockhash")
|
|
||||||
if final_tip == tip_hash or True: # tip may have advanced naturally
|
|
||||||
print(f"\n Chain integrity: OK (tip={final_tip[:16]}...)")
|
|
||||||
print("=" * 70)
|
|
||||||
|
|
||||||
return 0 if failed == 0 else 1
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Test script to verify that DragonX rejects a block with diff=1 (trivially easy nBits).
|
|
||||||
|
|
||||||
This script:
|
|
||||||
1. Connects to the local DragonX node via RPC
|
|
||||||
2. Fetches the current tip block in raw hex
|
|
||||||
3. Deserializes the block header
|
|
||||||
4. Tampers with nBits to set difficulty=1 (0x200f0f0f)
|
|
||||||
5. Reserializes and submits via submitblock
|
|
||||||
6. Verifies the node rejects it
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
python3 test_diff1_block.py
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import struct
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
CLI = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src", "dragonx-cli")
|
|
||||||
DEBUG_LOG = os.path.expanduser("~/.hush/DRAGONX/debug.log")
|
|
||||||
|
|
||||||
def rpc(method, *args):
|
|
||||||
"""Call dragonx-cli with the given RPC method and arguments."""
|
|
||||||
cmd = [CLI, method] + [str(a) for a in args]
|
|
||||||
try:
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
||||||
return result.stdout.strip()
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
# Some RPC calls return non-zero for rejection messages
|
|
||||||
if e.stdout and e.stdout.strip():
|
|
||||||
return e.stdout.strip()
|
|
||||||
if e.stderr and e.stderr.strip():
|
|
||||||
return e.stderr.strip()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def rpc_json(method, *args):
|
|
||||||
"""Call dragonx-cli and parse JSON result."""
|
|
||||||
raw = rpc(method, *args)
|
|
||||||
return json.loads(raw)
|
|
||||||
|
|
||||||
|
|
||||||
def read_uint32(data, offset):
|
|
||||||
return struct.unpack_from('<I', data, offset)[0], offset + 4
|
|
||||||
|
|
||||||
def read_int32(data, offset):
|
|
||||||
return struct.unpack_from('<i', data, offset)[0], offset + 4
|
|
||||||
|
|
||||||
def read_uint256(data, offset):
|
|
||||||
return data[offset:offset+32], offset + 32
|
|
||||||
|
|
||||||
def read_compactsize(data, offset):
|
|
||||||
val = data[offset]
|
|
||||||
if val < 253:
|
|
||||||
return val, offset + 1
|
|
||||||
elif val == 253:
|
|
||||||
return struct.unpack_from('<H', data, offset + 1)[0], offset + 3
|
|
||||||
elif val == 254:
|
|
||||||
return struct.unpack_from('<I', data, offset + 1)[0], offset + 5
|
|
||||||
else:
|
|
||||||
return struct.unpack_from('<Q', data, offset + 1)[0], offset + 9
|
|
||||||
|
|
||||||
def write_uint32(val):
|
|
||||||
return struct.pack('<I', val)
|
|
||||||
|
|
||||||
def write_int32(val):
|
|
||||||
return struct.pack('<i', val)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
print("=" * 60)
|
|
||||||
print("DragonX Diff=1 Block Rejection Test")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# Step 1: Get current chain info
|
|
||||||
print("\n[1] Fetching chain info...")
|
|
||||||
info = rpc_json("getblockchaininfo")
|
|
||||||
height = info["blocks"]
|
|
||||||
best_hash = info["bestblockhash"]
|
|
||||||
print(f" Chain height: {height}")
|
|
||||||
print(f" Best block: {best_hash}")
|
|
||||||
|
|
||||||
# Step 2: Get the tip block header details
|
|
||||||
print("\n[2] Fetching tip block details...")
|
|
||||||
block_info = rpc_json("getblock", best_hash)
|
|
||||||
current_bits = block_info["bits"]
|
|
||||||
current_difficulty = block_info["difficulty"]
|
|
||||||
print(f" Current nBits: {current_bits}")
|
|
||||||
print(f" Current difficulty: {current_difficulty}")
|
|
||||||
|
|
||||||
# Step 3: Get the raw block hex
|
|
||||||
print("\n[3] Fetching raw block hex...")
|
|
||||||
block_hex = rpc("getblock", best_hash, "0")
|
|
||||||
block_data = bytes.fromhex(block_hex)
|
|
||||||
print(f" Raw block size: {len(block_data)} bytes")
|
|
||||||
|
|
||||||
# Step 4: Parse the block header to find the nBits offset
|
|
||||||
# Header format:
|
|
||||||
# nVersion: 4 bytes (int32)
|
|
||||||
# hashPrevBlock: 32 bytes (uint256)
|
|
||||||
# hashMerkleRoot: 32 bytes (uint256)
|
|
||||||
# hashFinalSaplingRoot: 32 bytes (uint256)
|
|
||||||
# nTime: 4 bytes (uint32)
|
|
||||||
# nBits: 4 bytes (uint32) <-- this is what we tamper
|
|
||||||
# nNonce: 32 bytes (uint256)
|
|
||||||
# nSolution: compactsize + data
|
|
||||||
|
|
||||||
offset = 0
|
|
||||||
nVersion, offset = read_int32(block_data, offset)
|
|
||||||
hashPrevBlock, offset = read_uint256(block_data, offset)
|
|
||||||
hashMerkleRoot, offset = read_uint256(block_data, offset)
|
|
||||||
hashFinalSaplingRoot, offset = read_uint256(block_data, offset)
|
|
||||||
nTime, offset = read_uint32(block_data, offset)
|
|
||||||
nbits_offset = offset
|
|
||||||
nBits, offset = read_uint32(block_data, offset)
|
|
||||||
nNonce, offset = read_uint256(block_data, offset)
|
|
||||||
sol_len, offset = read_compactsize(block_data, offset)
|
|
||||||
|
|
||||||
print(f"\n[4] Parsed block header:")
|
|
||||||
print(f" nVersion: {nVersion}")
|
|
||||||
print(f" nTime: {nTime}")
|
|
||||||
print(f" nBits: 0x{nBits:08x} (offset {nbits_offset})")
|
|
||||||
print(f" nSolution: {sol_len} bytes")
|
|
||||||
|
|
||||||
# Step 5: Tamper nBits to diff=1
|
|
||||||
# 0x200f0f0f is the powLimit for DragonX (minimum difficulty / diff=1)
|
|
||||||
DIFF1_NBITS = 0x200f0f0f
|
|
||||||
print(f"\n[5] Tampering nBits from 0x{nBits:08x} -> 0x{DIFF1_NBITS:08x} (diff=1)...")
|
|
||||||
|
|
||||||
tampered_data = bytearray(block_data)
|
|
||||||
struct.pack_into('<I', tampered_data, nbits_offset, DIFF1_NBITS)
|
|
||||||
tampered_hex = tampered_data.hex()
|
|
||||||
|
|
||||||
# Verify the tamper worked
|
|
||||||
check_nbits = struct.unpack_from('<I', tampered_data, nbits_offset)[0]
|
|
||||||
assert check_nbits == DIFF1_NBITS, "nBits tamper failed!"
|
|
||||||
print(f" Verified tampered nBits: 0x{check_nbits:08x}")
|
|
||||||
|
|
||||||
# Step 6: Record log position before submitting
|
|
||||||
log_size_before = 0
|
|
||||||
if os.path.exists(DEBUG_LOG):
|
|
||||||
log_size_before = os.path.getsize(DEBUG_LOG)
|
|
||||||
|
|
||||||
# Step 7: Submit the tampered block
|
|
||||||
print(f"\n[6] Submitting tampered block via submitblock...")
|
|
||||||
submit_time = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
|
|
||||||
result = rpc("submitblock", tampered_hex)
|
|
||||||
print(f" submitblock result: {repr(result)}")
|
|
||||||
# Note: Bitcoin-derived RPC returns empty string when a block is processed,
|
|
||||||
# even if it fails internal validation. This is normal behavior.
|
|
||||||
|
|
||||||
# Step 8: Check debug.log for the actual rejection reason
|
|
||||||
print(f"\n[7] Checking debug.log for rejection details...")
|
|
||||||
log_tail = ""
|
|
||||||
if os.path.exists(DEBUG_LOG):
|
|
||||||
with open(DEBUG_LOG, "r", errors="replace") as f:
|
|
||||||
f.seek(log_size_before)
|
|
||||||
log_tail = f.read()
|
|
||||||
# Find rejection-related lines
|
|
||||||
rejection_lines = []
|
|
||||||
for line in log_tail.splitlines():
|
|
||||||
lowline = line.lower()
|
|
||||||
if any(kw in lowline for kw in ["failed", "error", "reject", "invalid",
|
|
||||||
"high-hash", "bad-diff", "mismatch",
|
|
||||||
"checkblock", "checkproof", "randomx"]):
|
|
||||||
rejection_lines.append(line.strip())
|
|
||||||
if rejection_lines:
|
|
||||||
print(" Rejection log entries:")
|
|
||||||
for line in rejection_lines[-10:]:
|
|
||||||
print(f" {line}")
|
|
||||||
else:
|
|
||||||
print(" No rejection entries found in new log output.")
|
|
||||||
else:
|
|
||||||
print(f" debug.log not found at {DEBUG_LOG}")
|
|
||||||
|
|
||||||
# Step 9: Evaluate result
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
rejected_by_rpc = result.lower() in ("rejected", "invalid") if result else False
|
|
||||||
rejected_by_log = any("FAILED" in l or "MISMATCH" in l for l in (rejection_lines if os.path.exists(DEBUG_LOG) and rejection_lines else []))
|
|
||||||
|
|
||||||
if rejected_by_rpc or rejected_by_log or result == "":
|
|
||||||
print("PASS: Block with diff=1 was correctly REJECTED!")
|
|
||||||
if result:
|
|
||||||
print(f" RPC result: {result}")
|
|
||||||
else:
|
|
||||||
print(" RPC returned empty (block processed but failed validation)")
|
|
||||||
elif "duplicate" in (result or "").lower():
|
|
||||||
print(f"NOTE: Block was seen as duplicate. Result: {result}")
|
|
||||||
else:
|
|
||||||
print(f"RESULT: {result}")
|
|
||||||
print(" Check debug.log for rejection details.")
|
|
||||||
|
|
||||||
# Step 10: Verify chain tip didn't change
|
|
||||||
print("\n[8] Verifying chain tip unchanged...")
|
|
||||||
new_hash = rpc("getbestblockhash")
|
|
||||||
if new_hash == best_hash:
|
|
||||||
print(f" Chain tip unchanged: {new_hash}")
|
|
||||||
print(" CONFIRMED: Bad block did not affect the chain.")
|
|
||||||
else:
|
|
||||||
print(f" WARNING: Chain tip changed! {best_hash} -> {new_hash}")
|
|
||||||
print(" This should NOT happen!")
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("Test complete.")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Reference in New Issue
Block a user