Compare commits
13 Commits
2232868d9f
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d282db216 | |||
| c5fde12485 | |||
| e2e10f6ef8 | |||
| af7d9e2300 | |||
| 4cc7e0491a | |||
| 5e0a706839 | |||
| 60d66022f6 | |||
| 3b2aa866aa | |||
| 2d6359ea74 | |||
| fa16e740b6 | |||
| 5f40c8ede0 | |||
| b3e81f1eda | |||
| d05302d450 |
@@ -27,6 +27,17 @@ RUN rm -rf /build/depends/built /build/depends/work \
|
|||||||
&& rm -rf /build/src/cc/*.o /build/src/cc/*.a \
|
&& rm -rf /build/src/cc/*.o /build/src/cc/*.a \
|
||||||
&& rm -f /build/config.status /build/config.log
|
&& rm -f /build/config.status /build/config.log
|
||||||
|
|
||||||
|
# The build context excludes .git (see .dockerignore), so genbuild.sh cannot derive
|
||||||
|
# a version and would stamp the binaries "-unk". build.sh computes the real one
|
||||||
|
# on the host and passes it in here.
|
||||||
|
ARG BUILD_DESC=
|
||||||
|
ENV DRAGONX_BUILD_DESC=${BUILD_DESC}
|
||||||
|
|
||||||
|
RUN if [ -z "$DRAGONX_BUILD_DESC" ]; then \
|
||||||
|
echo "WARNING: no BUILD_DESC build-arg -- binaries will be stamped -unk." >&2; \
|
||||||
|
echo " Prefer ./build.sh --linux-compat, or pass --build-arg BUILD_DESC=..." >&2; \
|
||||||
|
fi
|
||||||
|
|
||||||
RUN cd /build && ./util/build.sh --disable-tests -j$(nproc)
|
RUN cd /build && ./util/build.sh --disable-tests -j$(nproc)
|
||||||
|
|
||||||
# Strip binaries inside the container so extracted files are already small
|
# Strip binaries inside the container so extracted files are already small
|
||||||
|
|||||||
29
build.sh
29
build.sh
@@ -164,7 +164,34 @@ if [ $BUILD_LINUX_COMPAT -eq 1 ] || [ $BUILD_LINUX_RELEASE -eq 1 ] || [ $BUILD_W
|
|||||||
COMPAT_RELEASE_DIR="$RELEASE_DIR/dragonx-$VERSION-$COMPAT_PLATFORM"
|
COMPAT_RELEASE_DIR="$RELEASE_DIR/dragonx-$VERSION-$COMPAT_PLATFORM"
|
||||||
|
|
||||||
echo "Building Docker image (Ubuntu 20.04 base)..."
|
echo "Building Docker image (Ubuntu 20.04 base)..."
|
||||||
$DOCKER_CMD build -f Dockerfile.compat -t "$DOCKER_IMAGE" .
|
# .dockerignore excludes .git, so genbuild.sh inside the container cannot
|
||||||
|
# derive the version and would stamp the binaries "-unk". Compute it on the
|
||||||
|
# host, mirroring util/genbuild.sh exactly, and pass it in via --build-arg.
|
||||||
|
# NB: build.sh runs under `set -eu -o pipefail`, so every git call here must be
|
||||||
|
# non-fatal -- a source tarball, a machine without git, or a branch whose only
|
||||||
|
# reachable tags are lightweight (v1.0.1-v1.0.3) would otherwise abort the build.
|
||||||
|
git diff >/dev/null 2>&1 || true # refresh index: touched-but-unmodified are not dirty
|
||||||
|
COMPAT_BUILD_DESC=""
|
||||||
|
COMPAT_RAWDESC=$(git describe --abbrev=0 2>/dev/null || true)
|
||||||
|
if [ -n "$COMPAT_RAWDESC" ] \
|
||||||
|
&& [ "$(git rev-parse HEAD 2>/dev/null)" = "$(git rev-list -1 "$COMPAT_RAWDESC" 2>/dev/null)" ] \
|
||||||
|
&& git diff-index --quiet HEAD -- 2>/dev/null; then
|
||||||
|
COMPAT_BUILD_DESC="$COMPAT_RAWDESC"
|
||||||
|
else
|
||||||
|
COMPAT_SUFFIX=$(git rev-parse --short HEAD 2>/dev/null || true)
|
||||||
|
if [ -n "$COMPAT_SUFFIX" ]; then
|
||||||
|
git diff-index --quiet HEAD -- 2>/dev/null || COMPAT_SUFFIX="$COMPAT_SUFFIX-dirty"
|
||||||
|
COMPAT_BUILD_DESC="v$VERSION-$COMPAT_SUFFIX"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -n "$COMPAT_BUILD_DESC" ]; then
|
||||||
|
echo "Stamping container build as: $COMPAT_BUILD_DESC"
|
||||||
|
else
|
||||||
|
echo "Warning: no usable git metadata; container binaries will be stamped -unk"
|
||||||
|
fi
|
||||||
|
$DOCKER_CMD build -f Dockerfile.compat \
|
||||||
|
--build-arg BUILD_DESC="$COMPAT_BUILD_DESC" \
|
||||||
|
-t "$DOCKER_IMAGE" .
|
||||||
|
|
||||||
echo "Extracting binaries from Docker image..."
|
echo "Extracting binaries from Docker image..."
|
||||||
CONTAINER_ID=$($DOCKER_CMD create "$DOCKER_IMAGE")
|
CONTAINER_ID=$($DOCKER_CMD create "$DOCKER_IMAGE")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
|
|||||||
AC_PREREQ([2.60])
|
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, 2)
|
define(_CLIENT_VERSION_MINOR, 3)
|
||||||
define(_CLIENT_VERSION_REVISION, 0)
|
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)))
|
||||||
|
|||||||
@@ -1,3 +1,31 @@
|
|||||||
|
dragonx (1.3.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* RandomX stratum mining support: the daemon can serve stratum clients
|
||||||
|
directly, with a reference miner behind the stratummine RPC for testing.
|
||||||
|
* Stratum fixes found by audit: a malformed 63-character job_id no longer
|
||||||
|
aborts the daemon; each block is paid to the miner that actually found it
|
||||||
|
rather than to whichever client asked for work first; and a low-difficulty
|
||||||
|
share is now rejected before it costs a RandomX hash.
|
||||||
|
* Fix -connect never dialing its targets, so a node pinned to specific peers
|
||||||
|
reaches them instead of silently falling through to peer discovery.
|
||||||
|
* Remove roughly 7,100 lines of dead code, including the CBOPRET price
|
||||||
|
validation in the coinbase check, whose guard could not be true on any
|
||||||
|
chain, and adaptive-PoW difficulty logic that DragonX does not enable.
|
||||||
|
* Honor a command-line -rpcpassword across restarts, and fix a misspelled
|
||||||
|
-rpcusername key that silently discarded the configured RPC user.
|
||||||
|
* Default -checkpoints off on regtest so an isolated node leaves initial
|
||||||
|
block download, instead of staying in IBD forever and disabling every
|
||||||
|
operation gated on it.
|
||||||
|
* Release cs_main and the mempool lock on the miner's isStake error paths;
|
||||||
|
the leak presented as a permanent stall rather than a slow response.
|
||||||
|
* Windows cross-build: the mingw target links and finds librustzcash, and a
|
||||||
|
fs::path::c_str() regression in init no longer breaks the build.
|
||||||
|
* Stamp container builds with the real version instead of "-unk".
|
||||||
|
* Repair the qa/rpc-tests harness far enough to start a DragonX node and
|
||||||
|
build the shared test chain; it previously started mainnet nodes.
|
||||||
|
|
||||||
|
-- DragonX Developers <dev@dragonx.is> Mon, 31 Aug 2026 03:46:19 +0000
|
||||||
|
|
||||||
dragonx (1.2.0) stable; urgency=medium
|
dragonx (1.2.0) stable; urgency=medium
|
||||||
|
|
||||||
* Auto-shield matured coinbase into a wallet-owned Sapling address on a block
|
* Auto-shield matured coinbase into a wallet-owned Sapling address on a block
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||||
.TH DRAGONX-CLI "1" "August 2026" "dragonx-cli v1.2.0" "User Commands"
|
.TH DRAGONX-CLI "1" "August 2026" "dragonx-cli v1.3.0" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
dragonx-cli \- manual page for dragonx-cli v1.2.0
|
dragonx-cli \- manual page for dragonx-cli v1.3.0
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
DragonX RPC client version v1.2.0
|
DragonX RPC client version v1.3.0\-af7d9e230
|
||||||
.PP
|
.PP
|
||||||
In order to ensure you are adequately protecting your privacy when using
|
In order to ensure you are adequately protecting your privacy when using
|
||||||
DragonX, please see <https://dragonx.is/security/>.
|
DragonX, please see <https://dragonx.is/security/>.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||||
.TH DRAGONX-TX "1" "August 2026" "dragonx-tx v1.2.0" "User Commands"
|
.TH DRAGONX-TX "1" "August 2026" "dragonx-tx v1.3.0" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
dragonx-tx \- manual page for dragonx-tx v1.2.0
|
dragonx-tx \- manual page for dragonx-tx v1.3.0
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
hush\-tx utility version v1.2.0
|
hush\-tx utility version v1.3.0\-af7d9e230
|
||||||
.SS "Usage:"
|
.SS "Usage:"
|
||||||
.TP
|
.TP
|
||||||
hush\-tx [options] <hex\-tx> [commands]
|
hush\-tx [options] <hex\-tx> [commands]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||||
.TH DRAGONXD "1" "August 2026" "dragonxd v1.2.0" "User Commands"
|
.TH DRAGONXD "1" "August 2026" "dragonxd v1.3.0" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
dragonxd \- manual page for dragonxd v1.2.0
|
dragonxd \- manual page for dragonxd v1.3.0
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
DragonX Daemon version v1.2.0
|
DragonX Daemon version v1.3.0\-af7d9e230
|
||||||
.PP
|
.PP
|
||||||
In order to ensure you are adequately protecting your privacy when using
|
In order to ensure you are adequately protecting your privacy when using
|
||||||
DragonX, please see <https://dragonx.is/security/>.
|
DragonX, please see <https://dragonx.is/security/>.
|
||||||
@@ -452,6 +452,13 @@ or create a wallet z\-address). Must be spendable by this wallet.
|
|||||||
Fee in puposhis for automatic coinbase\-shielding transactions (default:
|
Fee in puposhis for automatic coinbase\-shielding transactions (default:
|
||||||
10000)
|
10000)
|
||||||
.HP
|
.HP
|
||||||
|
\fB\-sietch\-min\-zouts=\fR<n>
|
||||||
|
.IP
|
||||||
|
Minimum number of shielded (Sapling) outputs Sietch adds to each
|
||||||
|
z_sendmany transaction as decoys, strengthening
|
||||||
|
amount/linkability privacy. Higher values add privacy at the cost
|
||||||
|
of larger transactions (default: 7, clamped to the range 3\-50)
|
||||||
|
.HP
|
||||||
\fB\-autoshieldminutxos\fR
|
\fB\-autoshieldminutxos\fR
|
||||||
.IP
|
.IP
|
||||||
Only auto\-shield once at least this many matured coinbase UTXOs exist
|
Only auto\-shield once at least this many matured coinbase UTXOs exist
|
||||||
@@ -710,6 +717,11 @@ Stratum server options:
|
|||||||
.IP
|
.IP
|
||||||
Enable stratum server (default: off)
|
Enable stratum server (default: off)
|
||||||
.HP
|
.HP
|
||||||
|
\fB\-stratumtarget=\fR<hex>
|
||||||
|
.IP
|
||||||
|
Pool share target (64\-hex, big\-endian; larger = easier). Default is the
|
||||||
|
diff\-1 target. Useful for solo/low\-difficulty mining.
|
||||||
|
.HP
|
||||||
\fB\-stratumaddress=\fR<address>
|
\fB\-stratumaddress=\fR<address>
|
||||||
.IP
|
.IP
|
||||||
Mining address to use when special address of 'x' is sent by miner
|
Mining address to use when special address of 'x' is sent by miner
|
||||||
|
|||||||
@@ -8,32 +8,32 @@ It is best to keep doc/relnotes/README.md up to date as changes and bug fixes ar
|
|||||||
|
|
||||||
## Branch model
|
## Branch model
|
||||||
|
|
||||||
Development happens on the `dev` branch. Releases are cut on the default branch, `dragonx`. There is no `master` branch. Code changes should land on `dev` first and undergo testing before being merged into `dragonx`.
|
Development happens on the `dev` branch. Releases are cut on the default branch, `master`. Code changes should land on `dev` first and undergo testing before being merged into `master`.
|
||||||
|
|
||||||
## Check for changes on dragonx that should be on dev
|
## Check for changes on master that should be on dev
|
||||||
|
|
||||||
Occasionally trivial changes are made directly on the `dragonx` branch, such as documentation changes. In theory, no code changes should happen on `dragonx` without being on `dev` first, but it's better to be safe than sorry. We want the `dev` branch which undergoes testing to be as close as possible to what the `dragonx` branch will become, so we don't want to merge `dev` into `dragonx` and just assume everything works. So it's best to merge the `dragonx` branch into `dev` just before merging the `dev` branch into `dragonx`.
|
Occasionally trivial changes are made directly on the `master` branch, such as documentation changes. In theory, no code changes should happen on `master` without being on `dev` first, but it's better to be safe than sorry. We want the `dev` branch which undergoes testing to be as close as possible to what the `master` branch will become, so we don't want to merge `dev` into `master` and just assume everything works. So it's best to merge the `master` branch into `dev` just before merging the `dev` branch into `master`.
|
||||||
|
|
||||||
To check if the `dragonx` branch has any changes that the `dev` branch does not:
|
To check if the `master` branch has any changes that the `dev` branch does not:
|
||||||
|
|
||||||
```
|
```
|
||||||
# this assumes you are working with https://git.dragonx.is/DragonX/dragonx as your remote
|
# this assumes you are working with https://git.dragonx.is/DragonX/dragonx as your remote
|
||||||
git checkout dev
|
git checkout dev
|
||||||
git pull # make sure dev is up to date
|
git pull # make sure dev is up to date
|
||||||
git checkout dragonx
|
git checkout master
|
||||||
git pull # make sure dragonx is up to date
|
git pull # make sure master is up to date
|
||||||
git diff dev...dragonx # look at the set of changes which exist in dragonx but not dev
|
git diff dev...master # look at the set of changes which exist in master but not dev
|
||||||
```
|
```
|
||||||
|
|
||||||
If the last command has no output, congrats, there is nothing to do. If the last command has output, then you should merge `dragonx` into `dev`:
|
If the last command has no output, congrats, there is nothing to do. If the last command has output, then you should merge `master` into `dev`:
|
||||||
|
|
||||||
```
|
```
|
||||||
git checkout dev
|
git checkout dev
|
||||||
git merge dragonx
|
git merge master
|
||||||
git push origin dev
|
git push origin dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Use the `--no-ff` flag when merging `dev` into `dragonx` for a release (see below). The `--no-ff` flag makes sure to make a merge commit, no matter what, even if a "fast forward" could be done. For those in the future looking back, it's much better to see evidence of when branches were merged.
|
Use the `--no-ff` flag when merging `dev` into `master` for a release (see below). The `--no-ff` flag makes sure to make a merge commit, no matter what, even if a "fast forward" could be done. For those in the future looking back, it's much better to see evidence of when branches were merged.
|
||||||
|
|
||||||
|
|
||||||
### Git Issues
|
### Git Issues
|
||||||
@@ -66,6 +66,7 @@ Install deps on Linux:
|
|||||||
- Run "make seeds"
|
- Run "make seeds"
|
||||||
- Commit the result
|
- Commit the result
|
||||||
- Update version in configure.ac and src/clientversion.h to update the dragonxd version
|
- Update version in configure.ac and src/clientversion.h to update the dragonxd version
|
||||||
|
- **The new version MUST be higher than every version already tagged**, including tags that were never built or published. Check with `git tag -l --sort=-v:refname | head`. Two trees stamped with the same `CLIENT_VERSION` are indistinguishable on the wire, in `getnetworkinfo`, and to the wallet's in-app updater — and a published archive that does not match its tag destroys the only provenance check users have.
|
||||||
- In src/clientversion.h you update `CLIENT_VERSION_*` variables. Usually you will just update `CLIENT_VERSION_REVISION`
|
- In src/clientversion.h you update `CLIENT_VERSION_*` variables. Usually you will just update `CLIENT_VERSION_REVISION`
|
||||||
- If there is a consensus change, it may be a good idea to update `CLIENT_VERSION_MINOR` or `CLIENT_VERSION_MAJOR`
|
- If there is a consensus change, it may be a good idea to update `CLIENT_VERSION_MINOR` or `CLIENT_VERSION_MAJOR`
|
||||||
- To make a pre-release "beta" you can modify `CLIENT_VERSION_BUILD` but that is rarely done.
|
- To make a pre-release "beta" you can modify `CLIENT_VERSION_BUILD` but that is rarely done.
|
||||||
@@ -97,17 +98,27 @@ Install deps on Linux:
|
|||||||
- Try to generate checkpoints as close to the release as possible, so you can have a recent block height be protected.
|
- Try to generate checkpoints as close to the release as possible, so you can have a recent block height be protected.
|
||||||
- For instance, don't update checkpoints and then do a release a month later. You can always update checkpoint data again or multiple times
|
- For instance, don't update checkpoints and then do a release a month later. You can always update checkpoint data again or multiple times
|
||||||
- Update doc/relnotes/README.md
|
- Update doc/relnotes/README.md
|
||||||
- To get the stats of file changes: `git diff --stat dragonx...dev`
|
- To get the stats of file changes: `git diff --stat master...dev`
|
||||||
- Do a fresh clone and fresh sync with new checkpoints
|
- Do a fresh clone and fresh sync with new checkpoints
|
||||||
- Stop node, wait 20 minutes, and then do a partial sync with new checkpoints
|
- Stop node, wait 20 minutes, and then do a partial sync with new checkpoints
|
||||||
- Merge dev into dragonx: `git checkout dev && git pull && git checkout dragonx && git pull && git merge --no-ff dev && git push`
|
- Merge dev into master: `git checkout dev && git pull && git checkout master && git pull && git merge --no-ff dev && git push`
|
||||||
- The above command makes sure that your local dev branch is up to date before doing anything
|
- The above command makes sure that your local dev branch is up to date before doing anything
|
||||||
- The above command will not merge if "git pull" creates a merge conflict
|
- The above command will not merge if "git pull" creates a merge conflict
|
||||||
- The above command will not push if there is a problem with merging dev
|
- The above command will not push if there is a problem with merging dev
|
||||||
- Make Gitea release with git tag from the dragonx branch (make sure to merge dev in first)
|
- Make Gitea release with git tag from the master branch (make sure to merge dev in first)
|
||||||
- Make sure git tag starts with a `v` such as `v1.0.3`
|
- Make sure git tag starts with a `v` such as `v1.0.3`
|
||||||
- Use util/gen-linux-binary-release.sh to make a Linux release binary
|
- **The tag MUST be annotated** (`git tag -a v1.3.0 -m 'DragonX v1.3.0'`), not lightweight. `util/genbuild.sh` calls `git describe` *without* `--tags`, which only ever sees annotated tags — a lightweight tag makes the build stamp itself `v<older-tag>-<sha>` instead of the release version. v1.0.0 through v1.0.3 are lightweight, which is why their builds are labelled that way.
|
||||||
- Upload Linux binary to Gitea release and add SHA256 sum
|
- Verify before building: `git describe` must print exactly the tag, with no `-<n>-g<sha>` suffix.
|
||||||
|
- Use `./build.sh` (container-based, see doc/build-containers.md) or util/gen-linux-binary-release.sh to make a Linux release binary
|
||||||
|
- **Sign every archive and publish the signatures.** This step is mandatory and was missing from this document until v1.3.0 — its absence is why v1.1.0 and v1.2.0 were tagged but never became installable releases.
|
||||||
|
- The wallet's in-app daemon updater pins an ed25519 public key in `ObsidianDragon/src/util/daemon_updater.h` and sets `kDaemonRequireSignature = true`. **An update is refused outright unless a valid `<archive>.sig` is published beside the archive.** No signature means every existing user silently stays on their old daemon.
|
||||||
|
- Sign with `ObsidianDragon/scripts/sign-daemon-release.sh`:
|
||||||
|
- `scripts/sign-daemon-release.sh sign <secret.key> <archive>...` produces `<archive>.sig` (base64 of a detached 64-byte ed25519 signature over the exact archive bytes)
|
||||||
|
- or `scripts/sign-daemon-release.sh release <secret.key> <version>` to zip, sign, and print the checksum table in one step
|
||||||
|
- Keep the secret key offline, mode 600. The matching base64 public key must already be pinned in `kDaemonSignaturePublicKeyBase64`.
|
||||||
|
- Upload each Linux binary archive **and its `.sig`** to the Gitea release
|
||||||
|
- **Paste the SHA-256 checksum table into the release body** as markdown rows of the form `| <archive>.zip | `<sha256hex>` |`. The updater parses this table and will not install an archive that is absent from it.
|
||||||
|
- Confirm the release is actually consumable before announcing it: the updater looks for an asset whose name contains `"-" + platformToken + ".zip"` (`linux-amd64`, `macos`, `win64`). An archive named for a distro variant instead of the platform token is invisible to it.
|
||||||
- Create an x86 Debian package for the release:
|
- Create an x86 Debian package for the release:
|
||||||
- Edit contrib/debian/changelog to add information about the new release
|
- Edit contrib/debian/changelog to add information about the new release
|
||||||
- Use `util/build-debian-package.sh` to make an x86 Debian package for the release
|
- Use `util/build-debian-package.sh` to make an x86 Debian package for the release
|
||||||
@@ -115,7 +126,7 @@ Install deps on Linux:
|
|||||||
- `lintian` is an optional dependency, it's not needed to build the .deb
|
- `lintian` is an optional dependency, it's not needed to build the .deb
|
||||||
- Upload .deb to Gitea release
|
- Upload .deb to Gitea release
|
||||||
- Add SHA256 checksum of .deb to release
|
- Add SHA256 checksum of .deb to release
|
||||||
- Use util/build-debian-package-ARM.sh (does this still work?) to make an ARM Debian package for the release
|
- ARM Debian package: `util/build-debian-package-ARM.sh` is referenced here historically but **is not present in the tree**. Skip, or restore the script first.
|
||||||
- Upload the debian packages to the Gitea release page, with SHA256 sums
|
- Upload the debian packages to the Gitea release page, with SHA256 sums
|
||||||
|
|
||||||
## Platform-specific notes
|
## Platform-specific notes
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2016 The Zcash developers
|
# Copyright (c) 2016 The Zcash developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2014 The Bitcoin Core developers
|
# Copyright (c) 2014 The Bitcoin Core developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -52,7 +52,7 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
print "Warning: this test will take about 70 seconds in the best case. Be patient."
|
print("Warning: this test will take about 70 seconds in the best case. Be patient.")
|
||||||
self.nodes[0].generate(10)
|
self.nodes[0].generate(10)
|
||||||
templat = self.nodes[0].getblocktemplate()
|
templat = self.nodes[0].getblocktemplate()
|
||||||
longpollid = templat['longpollid']
|
longpollid = templat['longpollid']
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2014 The Bitcoin Core developers
|
# Copyright (c) 2014 The Bitcoin Core developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.authproxy import JSONRPCException
|
from test_framework.authproxy import JSONRPCException
|
||||||
|
from test_framework.util import initialize_chain_clean, start_node
|
||||||
|
|
||||||
from binascii import a2b_hex, b2a_hex
|
from binascii import a2b_hex, b2a_hex
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
@@ -69,14 +70,43 @@ def genmrklroot(leaflist):
|
|||||||
cur = n
|
cur = n
|
||||||
return cur[0]
|
return cur[0]
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Sapling v4 transaction layout.
|
||||||
|
#
|
||||||
|
# This test was written against the pre-Overwinter serialization, where a tx
|
||||||
|
# began with a 4-byte nVersion immediately followed by the vin count, so the
|
||||||
|
# first input's prevout hash lived at offset 4+1. DragonX transactions are
|
||||||
|
# Sapling v4: 4-byte header (nVersion | fOverwintered) + 4-byte nVersionGroupId
|
||||||
|
# + vin count, so the prevout hash starts 4 bytes further in. Poking the old
|
||||||
|
# offset corrupts nVersionGroupId and every proposal below just comes back
|
||||||
|
# "Block decode failed" instead of exercising any consensus rule.
|
||||||
|
CB_PREVOUT_OFF = 4+4+1
|
||||||
|
# Likewise the tx no longer ends at nLockTime: nExpiryHeight (4), valueBalance
|
||||||
|
# (8) and the empty vShieldedSpend/vShieldedOutput/vJoinSplit counts (1 each)
|
||||||
|
# trail it, so nLockTime is the 4 bytes at [-19:-15].
|
||||||
|
TX_TAIL_AFTER_LOCKTIME = 4+8+1+1+1
|
||||||
|
|
||||||
|
def tx_seq_off(tx):
|
||||||
|
"""Offset of the first input's nSequence in a Sapling v4 tx."""
|
||||||
|
scriptlen_off = CB_PREVOUT_OFF + 32 + 4 # after prevout hash + prevout.n
|
||||||
|
return scriptlen_off + 1 + tx[scriptlen_off]
|
||||||
|
|
||||||
|
def tx_vout0_value_off(tx):
|
||||||
|
"""Offset of the first output's 8-byte value in a Sapling v4 tx."""
|
||||||
|
return tx_seq_off(tx) + 4 + 1 # after nSequence + vout count
|
||||||
|
|
||||||
def template_to_bytes(tmpl, txlist):
|
def template_to_bytes(tmpl, txlist):
|
||||||
blkver = pack('<L', tmpl['version'])
|
blkver = pack('<L', tmpl['version'])
|
||||||
mrklroot = genmrklroot(list(dblsha(a) for a in txlist))
|
mrklroot = genmrklroot(list(dblsha(a) for a in txlist))
|
||||||
reserved = b'\0'*32
|
# hashFinalSaplingRoot. The all-zeroes placeholder this test used predates
|
||||||
|
# Sapling; a header carrying the wrong root is rejected with
|
||||||
|
# 'bad-sapling-root-in-block', which would sink even Test 11 (valid block).
|
||||||
|
reserved = a2b_hex(tmpl['finalsaplingroothash'])[::-1]
|
||||||
timestamp = pack('<L', tmpl['curtime'])
|
timestamp = pack('<L', tmpl['curtime'])
|
||||||
nonce = b'\0'*32
|
nonce = b'\0'*32
|
||||||
soln = b'\0'
|
soln = b'\0'
|
||||||
blk = blkver + a2b_hex(tmpl['previousblockhash'])[::-1] + mrklroot + reserved + timestamp + a2b_hex(tmpl['bits'])[::-1] + nonce + soln
|
# bytearray, not bytes: Test 9 mutates one byte of the result in place.
|
||||||
|
blk = bytearray(blkver + a2b_hex(tmpl['previousblockhash'])[::-1] + mrklroot + reserved + timestamp + a2b_hex(tmpl['bits'])[::-1] + nonce + soln)
|
||||||
blk += varlenEncode(len(txlist))
|
blk += varlenEncode(len(txlist))
|
||||||
for tx in txlist:
|
for tx in txlist:
|
||||||
blk += tx
|
blk += tx
|
||||||
@@ -95,6 +125,20 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
|
|||||||
Test block proposals with getblocktemplate.
|
Test block proposals with getblocktemplate.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
def setup_chain(self):
|
||||||
|
print("Initializing test directory "+self.options.tmpdir)
|
||||||
|
initialize_chain_clean(self.options.tmpdir, 1)
|
||||||
|
|
||||||
|
def setup_network(self, split=False):
|
||||||
|
# -daaforkheight=0: ContextualCheckBlockHeader only enforces nBits for a
|
||||||
|
# smart chain above daaForkHeight, which defaults to
|
||||||
|
# ASSETCHAINS_RANDOMX_VALIDATION+62000 (millions of blocks) so that a
|
||||||
|
# fresh sync accepts DragonX's historical bad-nBits window. Without this
|
||||||
|
# flag Test 8 (bad bits) is unreachable at regtest heights and the
|
||||||
|
# daemon happily accepts a block with arbitrary nBits.
|
||||||
|
self.nodes = [ start_node(0, self.options.tmpdir, ['-daaforkheight=0']) ]
|
||||||
|
self.is_network_split = False
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
node = self.nodes[0]
|
node = self.nodes[0]
|
||||||
node.generate(1) # Mine a block to leave initial block download
|
node.generate(1) # Mine a block to leave initial block download
|
||||||
@@ -117,9 +161,9 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
|
|||||||
#txlist[0][4+1+36+1+1] -= 1
|
#txlist[0][4+1+36+1+1] -= 1
|
||||||
|
|
||||||
# Test 2: Bad input hash for gen tx
|
# Test 2: Bad input hash for gen tx
|
||||||
txlist[0][4+1] += 1
|
txlist[0][CB_PREVOUT_OFF] += 1
|
||||||
assert_template(node, tmpl, txlist, 'bad-cb-missing')
|
assert_template(node, tmpl, txlist, 'bad-cb-missing')
|
||||||
txlist[0][4+1] -= 1
|
txlist[0][CB_PREVOUT_OFF] -= 1
|
||||||
|
|
||||||
# Test 3: Truncated final tx
|
# Test 3: Truncated final tx
|
||||||
lastbyte = txlist[-1].pop()
|
lastbyte = txlist[-1].pop()
|
||||||
@@ -136,14 +180,33 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
# Test 5: Add an invalid tx to the end (non-duplicate)
|
# Test 5: Add an invalid tx to the end (non-duplicate)
|
||||||
txlist.append(bytearray(txlist[0]))
|
txlist.append(bytearray(txlist[0]))
|
||||||
txlist[-1][4+1] = b'\xff'
|
txlist[-1][CB_PREVOUT_OFF] = 0xff
|
||||||
|
# DragonX is a fully private chain (ASSETCHAINS_PRIVATE): a non-coinbase
|
||||||
|
# tx paying a positive amount to a t-addr is killed in CheckTransaction
|
||||||
|
# with 'bad-txns-acprivacy-chain' before ConnectBlock ever looks up its
|
||||||
|
# inputs. Zero the output value -- CheckTransaction exempts zero-value
|
||||||
|
# vouts -- so the tx survives to the missing-input check this case is
|
||||||
|
# actually about.
|
||||||
|
_val_off = tx_vout0_value_off(txlist[-1])
|
||||||
|
txlist[-1][_val_off:_val_off+8] = b'\0'*8
|
||||||
assert_template(node, tmpl, txlist, 'bad-txns-inputs-missingorspent')
|
assert_template(node, tmpl, txlist, 'bad-txns-inputs-missingorspent')
|
||||||
txlist.pop()
|
txlist.pop()
|
||||||
|
|
||||||
# Test 6: Future tx lock time
|
# Test 6: Future tx lock time
|
||||||
txlist[0][-4:] = b'\xff\xff\xff\xff'
|
# The server-supplied coinbase carries nSequence=0xffffffff, and
|
||||||
|
# IsFinalTx() short-circuits on all-final inputs, so a future nLockTime
|
||||||
|
# alone leaves the tx final and the block valid. (DragonX's IsFinalTx
|
||||||
|
# also whitelists 0xfffffffe below the Hush hardfork height, which
|
||||||
|
# regtest is.) Make the input genuinely non-final so the future
|
||||||
|
# nLockTime is the thing under test.
|
||||||
|
seq_off = tx_seq_off(txlist[0])
|
||||||
|
realseq = txlist[0][seq_off:seq_off+4]
|
||||||
|
txlist[0][seq_off:seq_off+4] = b'\0\0\0\0'
|
||||||
|
reallocktime = txlist[0][-TX_TAIL_AFTER_LOCKTIME-4:-TX_TAIL_AFTER_LOCKTIME]
|
||||||
|
txlist[0][-TX_TAIL_AFTER_LOCKTIME-4:-TX_TAIL_AFTER_LOCKTIME] = b'\xff\xff\xff\xff'
|
||||||
assert_template(node, tmpl, txlist, 'bad-txns-nonfinal')
|
assert_template(node, tmpl, txlist, 'bad-txns-nonfinal')
|
||||||
txlist[0][-4:] = b'\0\0\0\0'
|
txlist[0][-TX_TAIL_AFTER_LOCKTIME-4:-TX_TAIL_AFTER_LOCKTIME] = reallocktime
|
||||||
|
txlist[0][seq_off:seq_off+4] = realseq
|
||||||
|
|
||||||
# Test 7: Bad tx count
|
# Test 7: Bad tx count
|
||||||
txlist.append(b'')
|
txlist.append(b'')
|
||||||
@@ -171,7 +234,9 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
|
|||||||
tmpl['curtime'] = 0x7fffffff
|
tmpl['curtime'] = 0x7fffffff
|
||||||
assert_template(node, tmpl, txlist, 'time-too-new')
|
assert_template(node, tmpl, txlist, 'time-too-new')
|
||||||
tmpl['curtime'] = 0
|
tmpl['curtime'] = 0
|
||||||
assert_template(node, tmpl, txlist, 'time-too-old')
|
# DragonX split Bitcoin's 'time-too-old' into 'time-too-old-median'
|
||||||
|
# (block time <= prev MedianTimePast) and 'time-too-old-prevblock'.
|
||||||
|
assert_template(node, tmpl, txlist, 'time-too-old-median')
|
||||||
tmpl['curtime'] = realtime
|
tmpl['curtime'] = realtime
|
||||||
|
|
||||||
# Test 11: Valid block
|
# Test 11: Valid block
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2014 The Bitcoin Core developers
|
# Copyright (c) 2014 The Bitcoin Core developers
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Released under the GPLv3
|
# Released under the GPLv3
|
||||||
@@ -28,7 +28,7 @@ class ReindexTest(BitcoinTestFramework):
|
|||||||
wait_bitcoinds()
|
wait_bitcoinds()
|
||||||
self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug", "-reindex", "-checkblockindex=1"])
|
self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug", "-reindex", "-checkblockindex=1"])
|
||||||
assert_equal(self.nodes[0].getblockcount(), 3)
|
assert_equal(self.nodes[0].getblockcount(), 3)
|
||||||
print "Success"
|
print("Success")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
ReindexTest().main()
|
ReindexTest().main()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
# and for constructing a getheaders message
|
# and for constructing a getheaders message
|
||||||
#
|
#
|
||||||
|
|
||||||
from mininode import CBlock, CBlockHeader, CBlockLocator, CTransaction, msg_block, msg_headers, msg_tx
|
from .mininode import CBlock, CBlockHeader, CBlockLocator, CTransaction, msg_block, msg_headers, msg_tx
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import cStringIO
|
import cStringIO
|
||||||
|
|||||||
@@ -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
|
||||||
# blocktools.py - utilities for manipulating blocks and transactions
|
# blocktools.py - utilities for manipulating blocks and transactions
|
||||||
from mininode import CBlock, CTransaction, CTxIn, CTxOut, COutPoint
|
from .mininode import CBlock, CTransaction, CTxIn, CTxOut, COutPoint
|
||||||
from script import CScript, OP_0, OP_EQUAL, OP_HASH160
|
from .script import CScript, OP_0, OP_EQUAL, OP_HASH160
|
||||||
|
|
||||||
# Create a block (with regtest difficulty)
|
# Create a block (with regtest difficulty)
|
||||||
def create_block(hashprev, coinbase, nTime=None, nBits=None):
|
def create_block(hashprev, coinbase, nTime=None, nBits=None):
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
# 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
|
||||||
|
|
||||||
from mininode import CBlock, CTransaction, CInv, NodeConn, NodeConnCB, \
|
from .mininode import CBlock, CTransaction, CInv, NodeConn, NodeConnCB, \
|
||||||
msg_inv, msg_getheaders, msg_ping, msg_mempool, mininode_lock, MAX_INV_SZ
|
msg_inv, msg_getheaders, msg_ping, msg_mempool, mininode_lock, MAX_INV_SZ
|
||||||
from blockstore import BlockStore, TxStore
|
from .blockstore import BlockStore, TxStore
|
||||||
from util import p2p_port
|
from .util import p2p_port
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import shutil
|
|||||||
import tempfile
|
import tempfile
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from authproxy import JSONRPCException
|
from .authproxy import JSONRPCException
|
||||||
from util import assert_equal, check_json_precision, \
|
from .util import assert_equal, check_json_precision, \
|
||||||
initialize_chain, initialize_chain_clean, \
|
initialize_chain, initialize_chain_clean, \
|
||||||
start_nodes, connect_nodes_bi, stop_nodes, \
|
start_nodes, connect_nodes_bi, stop_nodes, \
|
||||||
sync_blocks, sync_mempools, wait_bitcoinds
|
sync_blocks, sync_mempools, wait_bitcoinds
|
||||||
@@ -91,7 +91,7 @@ class BitcoinTestFramework(object):
|
|||||||
parser.add_option("--noshutdown", dest="noshutdown", default=False, action="store_true",
|
parser.add_option("--noshutdown", dest="noshutdown", default=False, action="store_true",
|
||||||
help="Don't stop nodes after the test execution")
|
help="Don't stop nodes after the test execution")
|
||||||
parser.add_option("--srcdir", dest="srcdir", default="../../src",
|
parser.add_option("--srcdir", dest="srcdir", default="../../src",
|
||||||
help="Source directory containing hushd/hush-cli (default: %default)")
|
help="Source directory containing dragonxd/dragonx-cli (default: %default)")
|
||||||
parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
|
parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
|
||||||
help="Root directory for datadirs")
|
help="Root directory for datadirs")
|
||||||
parser.add_option("--tracerpc", dest="trace_rpc", default=False, action="store_true",
|
parser.add_option("--tracerpc", dest="trace_rpc", default=False, action="store_true",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import subprocess
|
|||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from authproxy import AuthServiceProxy
|
from .authproxy import AuthServiceProxy
|
||||||
|
|
||||||
def p2p_port(n):
|
def p2p_port(n):
|
||||||
return 11000 + n + os.getpid()%999
|
return 11000 + n + os.getpid()%999
|
||||||
@@ -97,8 +97,8 @@ def initialize_datadir(dirname, n):
|
|||||||
print("Creating dirs %s" % datadir)
|
print("Creating dirs %s" % datadir)
|
||||||
os.makedirs(datadir)
|
os.makedirs(datadir)
|
||||||
|
|
||||||
print("Writing to " + os.path.join(datadir,"ZZZ.conf"))
|
print("Writing to " + os.path.join(datadir,"DRAGONX.conf"))
|
||||||
with open(os.path.join(datadir, "ZZZ.conf"), 'w') as f:
|
with open(os.path.join(datadir, "DRAGONX.conf"), 'w') as f:
|
||||||
f.write("regtest=1\n");
|
f.write("regtest=1\n");
|
||||||
f.write("txindex=1\n");
|
f.write("txindex=1\n");
|
||||||
#f.write("testnode=1\n");
|
#f.write("testnode=1\n");
|
||||||
@@ -116,7 +116,19 @@ def initialize_datadir(dirname, n):
|
|||||||
f.write("spentindex=1\n");
|
f.write("spentindex=1\n");
|
||||||
f.write("timestampindex=1\n");
|
f.write("timestampindex=1\n");
|
||||||
#f.write("zindex=1\n");
|
#f.write("zindex=1\n");
|
||||||
print("Done writing to %s" % os.path.join(datadir,"ZZZ.conf") )
|
print("Done writing to %s" % os.path.join(datadir,"DRAGONX.conf") )
|
||||||
|
|
||||||
|
# dragonxd refuses to start without an asmap file ("Could not find any asmap file!"),
|
||||||
|
# so every regtest datadir needs one. Link the tree's copy rather than duplicating it.
|
||||||
|
for src in ("../../../asmap.dat", "../../../src/asmap.dat",
|
||||||
|
os.path.expanduser("~/.hush/DRAGONX/asmap.dat")):
|
||||||
|
cand = src if os.path.isabs(src) else os.path.join(os.path.dirname(os.path.abspath(__file__)), src)
|
||||||
|
if os.path.exists(cand):
|
||||||
|
dst = os.path.join(datadir, "asmap.dat")
|
||||||
|
if not os.path.exists(dst):
|
||||||
|
try: os.symlink(os.path.realpath(cand), dst)
|
||||||
|
except OSError: shutil.copyfile(cand, dst)
|
||||||
|
break
|
||||||
|
|
||||||
return datadir
|
return datadir
|
||||||
|
|
||||||
@@ -133,11 +145,23 @@ def initialize_chain(test_dir):
|
|||||||
# Create cache directories, run hushds:
|
# Create cache directories, run hushds:
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
datadir=initialize_datadir("cache", i)
|
datadir=initialize_datadir("cache", i)
|
||||||
args = [ os.getenv("BITCOIND", "hushd"), "-keypool=1", "-datadir="+datadir, "-discover=0" ]
|
# Same two requirements as start_node(): -regtest must be a command-line flag (the
|
||||||
|
# conf key is ignored, and without it this cache node runs on MAINNET), and -asmap
|
||||||
|
# must be absolute or dragonxd refuses to start.
|
||||||
|
# -connect=<anything> makes init.cpp soft-set -listen=0 ("parameter interaction: -connect
|
||||||
|
# set -> setting -listen=0"), so cache node0 never opened its p2p port and nodes 1-3 could
|
||||||
|
# never sync to it -- initialize_chain() then hung forever in sync_blocks(). Pass -listen
|
||||||
|
# and -bind explicitly (an explicit arg beats SoftSetBoolArg) and keep the loopback bind so
|
||||||
|
# the cache nodes stay off the public network.
|
||||||
|
args = [ os.getenv("BITCOIND", "src/dragonxd"), "-regtest", "-connect=0", "-keypool=1", "-datadir="+datadir, "-discover=0",
|
||||||
|
"-listen=1", "-bind=127.0.0.1", "-dnsseed=0" ]
|
||||||
|
_am = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../asmap.dat")
|
||||||
|
if os.path.exists(_am):
|
||||||
|
args.append("-asmap=" + os.path.realpath(_am))
|
||||||
if i > 0:
|
if i > 0:
|
||||||
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
|
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
|
||||||
bitcoind_processes[i] = subprocess.Popen(args)
|
bitcoind_processes[i] = subprocess.Popen(args)
|
||||||
cmd = os.getenv("BITCOINCLI", "hush-cli")
|
cmd = os.getenv("BITCOINCLI", "src/dragonx-cli")
|
||||||
cmd_args = cmd + " -datadir="+datadir + " -rpcwait getblockcount"
|
cmd_args = cmd + " -datadir="+datadir + " -rpcwait getblockcount"
|
||||||
if os.getenv("PYTHON_DEBUG", ""):
|
if os.getenv("PYTHON_DEBUG", ""):
|
||||||
print("initialize_chain: hushd started, calling: " + cmd_args)
|
print("initialize_chain: hushd started, calling: " + cmd_args)
|
||||||
@@ -180,10 +204,17 @@ def initialize_chain(test_dir):
|
|||||||
wait_bitcoinds()
|
wait_bitcoinds()
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
print("Cleaning up cache dir files")
|
print("Cleaning up cache dir files")
|
||||||
os.remove(log_filename("cache", i, "debug.log"))
|
# log_filename() points at <cache>/node<i>/regtest, but that IS the -datadir we passed;
|
||||||
os.remove(log_filename("cache", i, "db.log"))
|
# dragonxd writes its logs/peers.dat one level deeper, into the net-specific
|
||||||
os.remove(log_filename("cache", i, "peers.dat"))
|
# <datadir>/regtest subdir (same datadir-vs-netdir split that forced -asmap to be
|
||||||
os.remove(log_filename("cache", i, "fee_estimates.dat"))
|
# absolute in start_node). Try both, and tolerate files a node never created.
|
||||||
|
for name in ("debug.log", "db.log", "peers.dat", "fee_estimates.dat"):
|
||||||
|
for cand in (log_filename("cache", i, os.path.join("regtest", name)),
|
||||||
|
log_filename("cache", i, name)):
|
||||||
|
try:
|
||||||
|
os.remove(cand)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
from_dir = os.path.join("cache", "node"+str(i))
|
from_dir = os.path.join("cache", "node"+str(i))
|
||||||
@@ -227,9 +258,10 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
|
|||||||
"""
|
"""
|
||||||
print("Starting node " + str(i) + " in dir " + dirname)
|
print("Starting node " + str(i) + " in dir " + dirname)
|
||||||
datadir = os.path.join(dirname, "node"+str(i), "regtest")
|
datadir = os.path.join(dirname, "node"+str(i), "regtest")
|
||||||
|
if extra_args is None: extra_args = []
|
||||||
# creating special config
|
# creating special config
|
||||||
if len(extra_args) > 0 and extra_args[0] == '-ac_name=ZZZ':
|
if len(extra_args) > 0 and extra_args[0] == '-ac_name=ZZZ':
|
||||||
configpath = datadir + "/ZZZ.conf"
|
configpath = datadir + "/DRAGONX.conf"
|
||||||
with open(configpath, "w+") as config:
|
with open(configpath, "w+") as config:
|
||||||
config.write("rpcuser=hush\n")
|
config.write("rpcuser=hush\n")
|
||||||
config.write("rpcpassword=puppy\n")
|
config.write("rpcpassword=puppy\n")
|
||||||
@@ -247,16 +279,30 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
|
|||||||
print("Done writing to %s" % configpath)
|
print("Done writing to %s" % configpath)
|
||||||
|
|
||||||
if binary is None:
|
if binary is None:
|
||||||
binary = os.getenv("BITCOIND", "src/hushd")
|
binary = os.getenv("BITCOIND", "src/dragonxd")
|
||||||
args = [ binary, "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ]
|
# -regtest MUST be a command-line flag. DragonX ignores "regtest=1" in the conf file, so
|
||||||
|
# without this the node silently runs on MAINNET: it loads the real genesis, dials the real
|
||||||
|
# seeds and starts syncing the live chain into the test datadir (observed: 196k blocks and
|
||||||
|
# 679MB before a test timed out). -connect=0 keeps the regtest node off the public network.
|
||||||
|
args = [ binary, "-regtest", "-connect=0", "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ]
|
||||||
|
# -asmap relative paths are resolved against the NET-SPECIFIC datadir (init.cpp), which for
|
||||||
|
# regtest is <datadir>/regtest -- so a copy sitting in <datadir> is never found. Pass an
|
||||||
|
# absolute path; without it dragonxd exits with "Could not find any asmap file!".
|
||||||
|
_asmap = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../asmap.dat")
|
||||||
|
if os.path.exists(_asmap):
|
||||||
|
args.append("-asmap=" + os.path.realpath(_asmap))
|
||||||
if extra_args is not None: args.extend(extra_args)
|
if extra_args is not None: args.extend(extra_args)
|
||||||
print("args=" + ' '.join(args))
|
print("args=" + ' '.join(args))
|
||||||
bitcoind_processes[i] = subprocess.Popen(args)
|
bitcoind_processes[i] = subprocess.Popen(args)
|
||||||
devnull = open("/dev/null", "w+")
|
devnull = open("/dev/null", "w+")
|
||||||
|
|
||||||
cmd = os.getenv("BITCOINCLI", "src/hush-cli")
|
cmd = os.getenv("BITCOINCLI", "src/dragonx-cli")
|
||||||
print("cmd=" + cmd)
|
print("cmd=" + cmd)
|
||||||
args = [ extra_args[0], "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ]
|
# The CLI only needs the datadir: initialize_datadir() already wrote DRAGONX.conf there
|
||||||
|
# with the right rpcport/user/password. The old form passed extra_args[0] as argv[0] and
|
||||||
|
# replayed daemon-only flags at the CLI, which only worked for the -ac_name=ZZZ assetchain
|
||||||
|
# tests and broke every test that passes no extra_args.
|
||||||
|
args = [ "-regtest", "-datadir="+datadir ]
|
||||||
cmd_args = ' '.join(args) + " -rpcwait getblockcount "
|
cmd_args = ' '.join(args) + " -rpcwait getblockcount "
|
||||||
if os.getenv("PYTHON_DEBUG", ""):
|
if os.getenv("PYTHON_DEBUG", ""):
|
||||||
print("start_node: hushd started, calling : " + cmd + " " + cmd_args)
|
print("start_node: hushd started, calling : " + cmd + " " + cmd_args)
|
||||||
@@ -266,18 +312,22 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
|
|||||||
import time
|
import time
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
subprocess.check_call(strcmd, shell=True);
|
subprocess.check_call(strcmd, shell=True);
|
||||||
#subprocess.check_call([ os.getenv("BITCOINCLI", "hush-cli"), "-datadir="+datadir] +
|
#subprocess.check_call([ os.getenv("BITCOINCLI", "dragonx-cli"), "-datadir="+datadir] +
|
||||||
# _rpchost_to_args(rpchost) +
|
# _rpchost_to_args(rpchost) +
|
||||||
# ["-rpcwait", "-rpcport=6438", "getblockcount"], stdout=devnull)
|
# ["-rpcwait", "-rpcport=6438", "getblockcount"], stdout=devnull)
|
||||||
if os.getenv("PYTHON_DEBUG", ""):
|
if os.getenv("PYTHON_DEBUG", ""):
|
||||||
print("start_node: calling hush-cli -rpcwait getblockcount returned")
|
print("start_node: calling hush-cli -rpcwait getblockcount returned")
|
||||||
devnull.close()
|
devnull.close()
|
||||||
port = extra_args[3]
|
# Port comes from the same helper initialize_datadir() used, except for the assetchain
|
||||||
#port = rpc_port(i)
|
# tests which pass it positionally as extra_args[3] == "-rpcport=NNNN".
|
||||||
|
if len(extra_args) > 3 and str(extra_args[0]) == '-ac_name=ZZZ':
|
||||||
|
port = extra_args[3][9:]
|
||||||
|
else:
|
||||||
|
port = str(rpc_port(i))
|
||||||
#print("port=%s" % port)
|
#print("port=%s" % port)
|
||||||
username = rpc_username()
|
username = rpc_username()
|
||||||
password = rpc_password()
|
password = rpc_password()
|
||||||
url = "http://%s:%s@%s:%s" % (username, password, rpchost or '127.0.0.1', port[9:])
|
url = "http://%s:%s@%s:%s" % (username, password, rpchost or '127.0.0.1', port)
|
||||||
print("connecting to " + url)
|
print("connecting to " + url)
|
||||||
if timewait is not None:
|
if timewait is not None:
|
||||||
proxy = AuthServiceProxy(url, timeout=timewait)
|
proxy = AuthServiceProxy(url, timeout=timewait)
|
||||||
@@ -315,7 +365,7 @@ def stop_nodes(nodes):
|
|||||||
del nodes[:] # Emptying array closes connections as a side effect
|
del nodes[:] # Emptying array closes connections as a side effect
|
||||||
|
|
||||||
def set_node_times(nodes, t):
|
def set_node_times(nodes, t):
|
||||||
print("Setting nodes time to " + t)
|
print("Setting nodes time to " + str(t))
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
node.setmocktime(t)
|
node.setmocktime(t)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2014 The Bitcoin Core developers
|
# Copyright (c) 2014 The Bitcoin Core developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -20,8 +20,20 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
print("Initializing test directory "+self.options.tmpdir)
|
print("Initializing test directory "+self.options.tmpdir)
|
||||||
initialize_chain_clean(self.options.tmpdir, 4)
|
initialize_chain_clean(self.options.tmpdir, 4)
|
||||||
|
|
||||||
|
# PORT NOTE (DragonX/regtest, not a change of test intent):
|
||||||
|
# test_framework.start_node() hardcodes "-connect=0", and init.cpp turns that into
|
||||||
|
# "-connect set -> setting -listen=0". With listening off the nodes never bind their
|
||||||
|
# p2p port, so connect_nodes_bi() connects nothing at all (its version==0 poll loop
|
||||||
|
# exits immediately because there are no local peers) and the first sync_all() hangs
|
||||||
|
# forever. Passing -bind forces -listen back to 1. -dnsseed=0 keeps these regtest
|
||||||
|
# nodes from dialing the live DragonX network.
|
||||||
|
NET_ARGS = ["-listen=1", "-bind=127.0.0.1", "-dnsseed=0"]
|
||||||
|
|
||||||
|
def net_args(self, n, extra=None):
|
||||||
|
return [list(self.NET_ARGS) + list(extra or []) for _ in range(n)]
|
||||||
|
|
||||||
def setup_network(self, split=False):
|
def setup_network(self, split=False):
|
||||||
self.nodes = start_nodes(3, self.options.tmpdir)
|
self.nodes = start_nodes(3, self.options.tmpdir, self.net_args(3))
|
||||||
connect_nodes_bi(self.nodes,0,1)
|
connect_nodes_bi(self.nodes,0,1)
|
||||||
connect_nodes_bi(self.nodes,1,2)
|
connect_nodes_bi(self.nodes,1,2)
|
||||||
connect_nodes_bi(self.nodes,0,2)
|
connect_nodes_bi(self.nodes,0,2)
|
||||||
@@ -29,7 +41,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
def run_test (self):
|
def run_test (self):
|
||||||
print "Mining blocks..."
|
print("Mining blocks...")
|
||||||
|
|
||||||
self.nodes[0].generate(4)
|
self.nodes[0].generate(4)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
@@ -106,7 +118,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
signed_tx = self.nodes[2].signrawtransaction(raw_tx)
|
signed_tx = self.nodes[2].signrawtransaction(raw_tx)
|
||||||
try:
|
try:
|
||||||
self.nodes[2].sendrawtransaction(signed_tx["hex"])
|
self.nodes[2].sendrawtransaction(signed_tx["hex"])
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert("absurdly high fees" in errorString)
|
assert("absurdly high fees" in errorString)
|
||||||
assert("900000000 > 190000" in errorString)
|
assert("900000000 > 190000" in errorString)
|
||||||
@@ -186,7 +198,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
|
txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
|
||||||
sync_mempools(self.nodes)
|
sync_mempools(self.nodes)
|
||||||
|
|
||||||
self.nodes.append(start_node(3, self.options.tmpdir))
|
self.nodes.append(start_node(3, self.options.tmpdir, list(self.NET_ARGS)))
|
||||||
connect_nodes_bi(self.nodes, 0, 3)
|
connect_nodes_bi(self.nodes, 0, 3)
|
||||||
sync_blocks(self.nodes)
|
sync_blocks(self.nodes)
|
||||||
|
|
||||||
@@ -227,7 +239,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
#do some -walletbroadcast tests
|
#do some -walletbroadcast tests
|
||||||
stop_nodes(self.nodes)
|
stop_nodes(self.nodes)
|
||||||
wait_bitcoinds()
|
wait_bitcoinds()
|
||||||
self.nodes = start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"],["-walletbroadcast=0"],["-walletbroadcast=0"]])
|
self.nodes = start_nodes(3, self.options.tmpdir, self.net_args(3, ["-walletbroadcast=0"]))
|
||||||
connect_nodes_bi(self.nodes,0,1)
|
connect_nodes_bi(self.nodes,0,1)
|
||||||
connect_nodes_bi(self.nodes,1,2)
|
connect_nodes_bi(self.nodes,1,2)
|
||||||
connect_nodes_bi(self.nodes,0,2)
|
connect_nodes_bi(self.nodes,0,2)
|
||||||
@@ -256,7 +268,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
#restart the nodes with -walletbroadcast=1
|
#restart the nodes with -walletbroadcast=1
|
||||||
stop_nodes(self.nodes)
|
stop_nodes(self.nodes)
|
||||||
wait_bitcoinds()
|
wait_bitcoinds()
|
||||||
self.nodes = start_nodes(3, self.options.tmpdir)
|
self.nodes = start_nodes(3, self.options.tmpdir, self.net_args(3))
|
||||||
connect_nodes_bi(self.nodes,0,1)
|
connect_nodes_bi(self.nodes,0,1)
|
||||||
connect_nodes_bi(self.nodes,1,2)
|
connect_nodes_bi(self.nodes,1,2)
|
||||||
connect_nodes_bi(self.nodes,0,2)
|
connect_nodes_bi(self.nodes,0,2)
|
||||||
@@ -290,7 +302,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
num_t_recipients = 3000
|
num_t_recipients = 3000
|
||||||
amount_per_recipient = Decimal('0.00000001')
|
amount_per_recipient = Decimal('0.00000001')
|
||||||
errorString = ''
|
errorString = ''
|
||||||
for i in xrange(0,num_t_recipients):
|
for i in range(0,num_t_recipients):
|
||||||
newtaddr = self.nodes[2].getnewaddress()
|
newtaddr = self.nodes[2].getnewaddress()
|
||||||
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
||||||
|
|
||||||
@@ -305,7 +317,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_sendmany(myzaddr, recipients)
|
self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert("Too many outputs, size of raw transaction" in errorString)
|
assert("Too many outputs, size of raw transaction" in errorString)
|
||||||
|
|
||||||
@@ -314,10 +326,10 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
num_z_recipients = 50
|
num_z_recipients = 50
|
||||||
amount_per_recipient = Decimal('0.00000001')
|
amount_per_recipient = Decimal('0.00000001')
|
||||||
errorString = ''
|
errorString = ''
|
||||||
for i in xrange(0,num_t_recipients):
|
for i in range(0,num_t_recipients):
|
||||||
newtaddr = self.nodes[2].getnewaddress()
|
newtaddr = self.nodes[2].getnewaddress()
|
||||||
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
||||||
for i in xrange(0,num_z_recipients):
|
for i in range(0,num_z_recipients):
|
||||||
newzaddr = self.nodes[2].z_getnewaddress()
|
newzaddr = self.nodes[2].z_getnewaddress()
|
||||||
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
||||||
|
|
||||||
@@ -327,7 +339,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_sendmany(myzaddr, recipients)
|
self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert("size of raw transaction would be larger than limit" in errorString)
|
assert("size of raw transaction would be larger than limit" in errorString)
|
||||||
|
|
||||||
@@ -335,12 +347,12 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
num_z_recipients = 100
|
num_z_recipients = 100
|
||||||
amount_per_recipient = Decimal('0.00000001')
|
amount_per_recipient = Decimal('0.00000001')
|
||||||
errorString = ''
|
errorString = ''
|
||||||
for i in xrange(0,num_z_recipients):
|
for i in range(0,num_z_recipients):
|
||||||
newzaddr = self.nodes[2].z_getnewaddress()
|
newzaddr = self.nodes[2].z_getnewaddress()
|
||||||
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_sendmany(myzaddr, recipients)
|
self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert("Invalid parameter, too many zaddr outputs" in errorString)
|
assert("Invalid parameter, too many zaddr outputs" in errorString)
|
||||||
|
|
||||||
@@ -426,7 +438,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
errorString = ""
|
errorString = ""
|
||||||
try:
|
try:
|
||||||
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1f-4")
|
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1f-4")
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
|
|
||||||
assert_equal("Invalid amount" in errorString, True)
|
assert_equal("Invalid amount" in errorString, True)
|
||||||
@@ -434,7 +446,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
errorString = ""
|
errorString = ""
|
||||||
try:
|
try:
|
||||||
self.nodes[0].generate("2") #use a string to as block amount parameter must fail because it's not interpreted as amount
|
self.nodes[0].generate("2") #use a string to as block amount parameter must fail because it's not interpreted as amount
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
|
|
||||||
assert_equal("not an integer" in errorString, True)
|
assert_equal("not an integer" in errorString, True)
|
||||||
@@ -448,9 +460,9 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
|
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||||
assert(myopid)
|
assert(myopid)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
print errorString
|
print(errorString)
|
||||||
assert(False)
|
assert(False)
|
||||||
|
|
||||||
# This fee is larger than the default fee and since amount=0
|
# This fee is larger than the default fee and since amount=0
|
||||||
@@ -462,7 +474,7 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
|
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert('Small transaction amount' in errorString)
|
assert('Small transaction amount' in errorString)
|
||||||
|
|
||||||
@@ -475,9 +487,9 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
|
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
|
||||||
assert(myopid)
|
assert(myopid)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
print errorString
|
print(errorString)
|
||||||
assert(False)
|
assert(False)
|
||||||
|
|
||||||
# Make sure amount=0, fee=0 transaction are valid to add to mempool
|
# Make sure amount=0, fee=0 transaction are valid to add to mempool
|
||||||
@@ -490,9 +502,9 @@ class WalletTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
|
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
|
||||||
assert(myopid)
|
assert(myopid)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
print errorString
|
print(errorString)
|
||||||
assert(False)
|
assert(False)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2018 The Zcash developers
|
# Copyright (c) 2018 The Zcash developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2017 The Zcash developers
|
# Copyright (c) 2017 The Zcash developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -32,7 +32,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
def run_test (self):
|
def run_test (self):
|
||||||
print "Mining blocks..."
|
print("Mining blocks...")
|
||||||
|
|
||||||
self.nodes[0].generate(1)
|
self.nodes[0].generate(1)
|
||||||
do_not_shield_taddr = self.nodes[0].getnewaddress()
|
do_not_shield_taddr = self.nodes[0].getnewaddress()
|
||||||
@@ -81,7 +81,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
self.nodes[0].z_mergetoaddress("*", myzaddr)
|
self.nodes[0].z_mergetoaddress("*", myzaddr)
|
||||||
assert(False)
|
assert(False)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("JSON value is not an array as expected" in errorString, True)
|
assert_equal("JSON value is not an array as expected" in errorString, True)
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
self.nodes[2].z_mergetoaddress([mytaddr], myzaddr)
|
self.nodes[2].z_mergetoaddress([mytaddr], myzaddr)
|
||||||
assert(False)
|
assert(False)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Could not find any funds to merge" in errorString, True)
|
assert_equal("Could not find any funds to merge" in errorString, True)
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, -1)
|
self.nodes[0].z_mergetoaddress(["*"], myzaddr, -1)
|
||||||
assert(False)
|
assert(False)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Amount out of range" in errorString, True)
|
assert_equal("Amount out of range" in errorString, True)
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('21000000.00000001'))
|
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('21000000.00000001'))
|
||||||
assert(False)
|
assert(False)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Amount out of range" in errorString, True)
|
assert_equal("Amount out of range" in errorString, True)
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, 999)
|
self.nodes[0].z_mergetoaddress(["*"], myzaddr, 999)
|
||||||
assert(False)
|
assert(False)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Insufficient funds" in errorString, True)
|
assert_equal("Insufficient funds" in errorString, True)
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), -1)
|
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), -1)
|
||||||
assert(False)
|
assert(False)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Limit on maximum number of UTXOs cannot be negative" in errorString, True)
|
assert_equal("Limit on maximum number of UTXOs cannot be negative" in errorString, True)
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 99999999999999)
|
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 99999999999999)
|
||||||
assert(False)
|
assert(False)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("JSON integer out of range" in errorString, True)
|
assert_equal("JSON integer out of range" in errorString, True)
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, -1)
|
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, -1)
|
||||||
assert(False)
|
assert(False)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Limit on maximum number of notes cannot be negative" in errorString, True)
|
assert_equal("Limit on maximum number of notes cannot be negative" in errorString, True)
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, 99999999999999)
|
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, 99999999999999)
|
||||||
assert(False)
|
assert(False)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("JSON integer out of range" in errorString, True)
|
assert_equal("JSON integer out of range" in errorString, True)
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
|||||||
try:
|
try:
|
||||||
self.nodes[0].z_mergetoaddress([mytaddr], mytaddr)
|
self.nodes[0].z_mergetoaddress([mytaddr], mytaddr)
|
||||||
assert(False)
|
assert(False)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Destination address is also the only source address, and all its funds are already merged" in errorString, True)
|
assert_equal("Destination address is also the only source address, and all its funds are already merged" in errorString, True)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2016 The Zcash developers
|
# Copyright (c) 2016 The Zcash developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -7,15 +7,71 @@
|
|||||||
|
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import assert_equal, assert_true, bitcoind_processes, \
|
from test_framework.util import assert_equal, assert_true, bitcoind_processes, \
|
||||||
connect_nodes_bi, start_node, start_nodes, wait_and_assert_operationid_status
|
connect_nodes_bi, initialize_chain_clean, p2p_port, start_node, start_nodes, \
|
||||||
|
sync_blocks, wait_and_assert_operationid_status
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
class WalletNullifiersTest (BitcoinTestFramework):
|
class WalletNullifiersTest (BitcoinTestFramework):
|
||||||
|
|
||||||
|
# The framework default setup_chain() calls initialize_chain(), which pre-builds a
|
||||||
|
# 200-block chain in a *relative* "cache/" directory shared by every test process
|
||||||
|
# running out of this tree. That directory is guarded only by the datadir lock, so two
|
||||||
|
# qa/rpc-tests running at once collide on it and the second one hangs forever inside
|
||||||
|
# "dragonx-cli -rpcwait" (observed here while a sibling test held cache/node0..3).
|
||||||
|
# Build the identical pre-condition -- 4 nodes, two rounds of 25 blocks each, i.e. 25
|
||||||
|
# mature + 25 immature coinbases per node -- directly in this test's private tmpdir.
|
||||||
|
# This is a setup change only: no assertion below is relaxed, removed or reordered.
|
||||||
|
def setup_chain(self):
|
||||||
|
print("Initializing test directory "+self.options.tmpdir)
|
||||||
|
initialize_chain_clean(self.options.tmpdir, 4)
|
||||||
|
|
||||||
|
# Three networking facts about this daemon force extra flags here. None of them
|
||||||
|
# change what the test exercises; without them the 4 nodes either never peer with
|
||||||
|
# each other, or peer with the LIVE DragonX network instead.
|
||||||
|
#
|
||||||
|
# 1. start_node() hardcodes "-connect=0", which also soft-sets -listen=0, so nothing
|
||||||
|
# binds p2p_port(i) and connect_nodes_bi() can never form the regtest mesh --
|
||||||
|
# sync_blocks() then spins forever (observed: node0 at 25 blocks, nodes 1-3 stuck
|
||||||
|
# at 0, nothing listening on 11005-11008). -listen=1 -bind=127.0.0.1 restores the
|
||||||
|
# mesh and keeps it on loopback.
|
||||||
|
# 2. hush_args() appends node1..node10.dragonx.is to -addnode unconditionally, -regtest
|
||||||
|
# included, and regtest reuses mainnet's network magic. A "regtest" node therefore
|
||||||
|
# joins the live network: node0 of an earlier run handshook 8 production peers
|
||||||
|
# ("receive version message: /DragonX:1.0.3/ ... blocks=3254266") and ingested their
|
||||||
|
# headers. -dns=0 stops those hostname -addnode entries from resolving; RPC addnode
|
||||||
|
# with a numeric 127.0.0.1:port is unaffected.
|
||||||
|
# 3. hush_args() runs BEFORE the config file is read, so its GetArg("-port",0) never
|
||||||
|
# sees the "port=" line initialize_datadir() wrote and GetDefaultPort() stays at the
|
||||||
|
# mainnet p2p port. "-connect=0" is then parsed as the address 0.0.0.0:<mainnet
|
||||||
|
# port>, i.e. the production dragonxd listening on this box -- every node in runs 2
|
||||||
|
# and 3 picked up exactly one peer reporting blocks=3254269. Repeating -port on the
|
||||||
|
# command line points GetDefaultPort() at this node's own regtest port instead.
|
||||||
|
#
|
||||||
|
# -autoshield is on by default on DragonX and is not part of what this test measures:
|
||||||
|
# a background thread sweeps each node's matured coinbase into a seed-derived zaddr
|
||||||
|
# (8 "autoshield operation finished" ops per node while the chain is being mined). That
|
||||||
|
# empties the very taddr this test spends from, and the resulting transactions do not
|
||||||
|
# settle identically on every node ("ERROR: AcceptToMemoryPool: ContextualCheckTransaction
|
||||||
|
# failed" on node1), so sync_mempools() never converges and the run wedges until the
|
||||||
|
# timeout. Turn the background sweeper off; the test does its own shielding explicitly.
|
||||||
|
def net_args(self, i):
|
||||||
|
return ['-listen=1', '-bind=127.0.0.1', '-dns=0', '-autoshield=0',
|
||||||
|
'-port=%d' % p2p_port(i)]
|
||||||
|
|
||||||
def setup_nodes(self):
|
def setup_nodes(self):
|
||||||
return start_nodes(4, self.options.tmpdir,
|
return start_nodes(4, self.options.tmpdir,
|
||||||
extra_args=[['-experimentalfeatures', '-developerencryptwallet']] * 4)
|
extra_args=[['-experimentalfeatures', '-developerencryptwallet']
|
||||||
|
+ self.net_args(i) for i in range(4)])
|
||||||
|
|
||||||
|
def setup_network(self, split = False):
|
||||||
|
super().setup_network(split)
|
||||||
|
# Same block layout initialize_chain() would have handed us.
|
||||||
|
for _ in range(2):
|
||||||
|
for peer in range(4):
|
||||||
|
self.nodes[peer].generate(25)
|
||||||
|
sync_blocks(self.nodes)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
def run_test (self):
|
def run_test (self):
|
||||||
# add zaddr to node 0
|
# add zaddr to node 0
|
||||||
@@ -25,7 +81,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
|
|||||||
mytaddr = self.nodes[0].getnewaddress()
|
mytaddr = self.nodes[0].getnewaddress()
|
||||||
recipients = []
|
recipients = []
|
||||||
recipients.append({"address":myzaddr0, "amount":Decimal('10.0')-Decimal('0.0001')}) # utxo amount less fee
|
recipients.append({"address":myzaddr0, "amount":Decimal('10.0')-Decimal('0.0001')}) # utxo amount less fee
|
||||||
|
|
||||||
wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(mytaddr, recipients), timeout=120)
|
wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(mytaddr, recipients), timeout=120)
|
||||||
|
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
@@ -44,7 +100,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
|
|||||||
bitcoind_processes[1].wait()
|
bitcoind_processes[1].wait()
|
||||||
|
|
||||||
# restart node 1
|
# restart node 1
|
||||||
self.nodes[1] = start_node(1, self.options.tmpdir)
|
self.nodes[1] = start_node(1, self.options.tmpdir, self.net_args(1))
|
||||||
connect_nodes_bi(self.nodes, 0, 1)
|
connect_nodes_bi(self.nodes, 0, 1)
|
||||||
connect_nodes_bi(self.nodes, 1, 2)
|
connect_nodes_bi(self.nodes, 1, 2)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
@@ -52,7 +108,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
|
|||||||
# send node 0 zaddr to note 2 zaddr
|
# send node 0 zaddr to note 2 zaddr
|
||||||
recipients = []
|
recipients = []
|
||||||
recipients.append({"address":myzaddr, "amount":7.0})
|
recipients.append({"address":myzaddr, "amount":7.0})
|
||||||
|
|
||||||
wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(myzaddr0, recipients), timeout=120)
|
wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(myzaddr0, recipients), timeout=120)
|
||||||
|
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
@@ -97,7 +153,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
|
|||||||
mytaddr1 = self.nodes[1].getnewaddress()
|
mytaddr1 = self.nodes[1].getnewaddress()
|
||||||
recipients = []
|
recipients = []
|
||||||
recipients.append({"address":mytaddr1, "amount":1.0})
|
recipients.append({"address":mytaddr1, "amount":1.0})
|
||||||
|
|
||||||
wait_and_assert_operationid_status(self.nodes[1], self.nodes[1].z_sendmany(myzaddr, recipients), timeout=120)
|
wait_and_assert_operationid_status(self.nodes[1], self.nodes[1].z_sendmany(myzaddr, recipients), timeout=120)
|
||||||
|
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2018 The Zcash developers
|
# Copyright (c) 2018 The Zcash developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -13,6 +13,29 @@ from test_framework.util import (
|
|||||||
)
|
)
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
def get_value_pool(pools, pool_id):
|
||||||
|
"""
|
||||||
|
Return the valuePools entry with the given id, or None if this chain does
|
||||||
|
not have that pool. DragonX's getblockchaininfo only reports the Sapling
|
||||||
|
pool (no Sprout history exists on this chain), so the pools list can no
|
||||||
|
longer be indexed positionally the way the upstream test did.
|
||||||
|
"""
|
||||||
|
for pool in pools:
|
||||||
|
if pool['id'] == pool_id:
|
||||||
|
return pool
|
||||||
|
return None
|
||||||
|
|
||||||
|
def assert_pool_values(pools, sprout_value, sapling_value):
|
||||||
|
sprout = get_value_pool(pools, 'sprout')
|
||||||
|
if sprout is not None:
|
||||||
|
assert_equal(sprout['chainValue'], sprout_value)
|
||||||
|
else:
|
||||||
|
# No Sprout pool at all is the same statement as "the Sprout pool holds nothing"
|
||||||
|
assert_equal(sprout_value, Decimal('0'))
|
||||||
|
sapling = get_value_pool(pools, 'sapling')
|
||||||
|
assert_true(sapling is not None, "Sapling value pool missing from getblockchaininfo")
|
||||||
|
assert_equal(sapling['chainValue'], sapling_value)
|
||||||
|
|
||||||
class WalletPersistenceTest (BitcoinTestFramework):
|
class WalletPersistenceTest (BitcoinTestFramework):
|
||||||
|
|
||||||
def setup_chain(self):
|
def setup_chain(self):
|
||||||
@@ -20,8 +43,20 @@ class WalletPersistenceTest (BitcoinTestFramework):
|
|||||||
initialize_chain_clean(self.options.tmpdir, 3)
|
initialize_chain_clean(self.options.tmpdir, 3)
|
||||||
|
|
||||||
def setup_network(self, split=False):
|
def setup_network(self, split=False):
|
||||||
|
# -listen=1/-bind: the framework's start_node() passes -connect=0, and DragonX (like
|
||||||
|
# Bitcoin) reacts to -connect by soft-setting -listen=0. A non-listening node can never
|
||||||
|
# accept the "addnode 127.0.0.1:<port>" that connect_nodes_bi() issues, so without this
|
||||||
|
# the three nodes stay isolated and sync_all() spins forever. -bind keeps the listener on
|
||||||
|
# loopback so a regtest node never becomes reachable from the public internet.
|
||||||
self.nodes = start_nodes(3, self.options.tmpdir,
|
self.nodes = start_nodes(3, self.options.tmpdir,
|
||||||
extra_args=[[
|
extra_args=[[
|
||||||
|
'-listen=1',
|
||||||
|
'-bind=127.0.0.1',
|
||||||
|
# -dns=0: DragonX appends node1..node10.dragonx.is to -addnode for every chain
|
||||||
|
# named DRAGONX (hush_utils.h), and -connect=0 does not suppress -addnode. Without
|
||||||
|
# this a regtest node dials the LIVE DragonX network and is fed mainnet headers.
|
||||||
|
# The addnode calls connect_nodes_bi() makes use literal IPs, so they still work.
|
||||||
|
'-dns=0',
|
||||||
'-nuparams=5ba81b19:100', # Overwinter
|
'-nuparams=5ba81b19:100', # Overwinter
|
||||||
'-nuparams=76b809bb:201', # Sapling
|
'-nuparams=76b809bb:201', # Sapling
|
||||||
]] * 3)
|
]] * 3)
|
||||||
@@ -69,12 +104,11 @@ class WalletPersistenceTest (BitcoinTestFramework):
|
|||||||
|
|
||||||
# Verify shielded balance
|
# Verify shielded balance
|
||||||
assert_equal(self.nodes[0].z_getbalance(sapling_addr), Decimal('20'))
|
assert_equal(self.nodes[0].z_getbalance(sapling_addr), Decimal('20'))
|
||||||
|
|
||||||
# Verify size of shielded pools
|
# Verify size of shielded pools
|
||||||
pools = self.nodes[0].getblockchaininfo()['valuePools']
|
pools = self.nodes[0].getblockchaininfo()['valuePools']
|
||||||
assert_equal(pools[0]['chainValue'], Decimal('0')) # Sprout
|
assert_pool_values(pools, Decimal('0'), Decimal('20'))
|
||||||
assert_equal(pools[1]['chainValue'], Decimal('20')) # Sapling
|
|
||||||
|
|
||||||
# Restart the nodes
|
# Restart the nodes
|
||||||
stop_nodes(self.nodes)
|
stop_nodes(self.nodes)
|
||||||
wait_bitcoinds()
|
wait_bitcoinds()
|
||||||
@@ -82,8 +116,7 @@ class WalletPersistenceTest (BitcoinTestFramework):
|
|||||||
|
|
||||||
# Verify size of shielded pools
|
# Verify size of shielded pools
|
||||||
pools = self.nodes[0].getblockchaininfo()['valuePools']
|
pools = self.nodes[0].getblockchaininfo()['valuePools']
|
||||||
assert_equal(pools[0]['chainValue'], Decimal('0')) # Sprout
|
assert_pool_values(pools, Decimal('0'), Decimal('20'))
|
||||||
assert_equal(pools[1]['chainValue'], Decimal('20')) # Sapling
|
|
||||||
|
|
||||||
# Node 0 sends some shielded funds to Node 1
|
# Node 0 sends some shielded funds to Node 1
|
||||||
dest_addr = self.nodes[1].z_getnewaddress('sapling')
|
dest_addr = self.nodes[1].z_getnewaddress('sapling')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2016 The Zcash developers
|
# Copyright (c) 2016 The Zcash developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.authproxy import JSONRPCException
|
from test_framework.authproxy import JSONRPCException
|
||||||
from test_framework.mininode import COIN
|
|
||||||
from test_framework.util import assert_equal, initialize_chain_clean, \
|
from test_framework.util import assert_equal, initialize_chain_clean, \
|
||||||
start_nodes, connect_nodes_bi, wait_and_assert_operationid_status
|
start_nodes, connect_nodes_bi, wait_and_assert_operationid_status
|
||||||
|
|
||||||
@@ -14,6 +13,20 @@ import sys
|
|||||||
import timeit
|
import timeit
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
# Upstream imported this from test_framework.mininode, which is still python2 and
|
||||||
|
# fails to even parse under python3 (0x100000000L literals). mininode is a p2p
|
||||||
|
# serialisation module this test does not otherwise use, so rather than drag a
|
||||||
|
# large unrelated port into test_framework/ the one constant needed is inlined.
|
||||||
|
# Same value as test_framework/mininode.py:52.
|
||||||
|
COIN = 100000000 # 1 DRGX in puposhis
|
||||||
|
|
||||||
|
# DragonX has no Sprout pool: getblockchaininfo/getblock only ever emit a
|
||||||
|
# "sapling" entry in valuePools (see rpc/blockchain.cpp), and z_getnewaddress
|
||||||
|
# only makes Sapling addresses. The shielded value this test moves therefore
|
||||||
|
# lands in the Sapling pool, so every check that upstream made against 'sprout'
|
||||||
|
# is made against 'sapling' here. The assertion itself is unchanged.
|
||||||
|
SHIELDED_POOL = 'sapling'
|
||||||
|
|
||||||
def check_value_pool(node, name, total):
|
def check_value_pool(node, name, total):
|
||||||
value_pools = node.getblockchaininfo()['valuePools']
|
value_pools = node.getblockchaininfo()['valuePools']
|
||||||
found = False
|
found = False
|
||||||
@@ -42,7 +55,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
def run_test (self):
|
def run_test (self):
|
||||||
print "Mining blocks..."
|
print("Mining blocks...")
|
||||||
|
|
||||||
self.nodes[0].generate(4)
|
self.nodes[0].generate(4)
|
||||||
|
|
||||||
@@ -59,17 +72,17 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
assert_equal(self.nodes[2].getbalance(), 0)
|
assert_equal(self.nodes[2].getbalance(), 0)
|
||||||
assert_equal(self.nodes[3].getbalance(), 0)
|
assert_equal(self.nodes[3].getbalance(), 0)
|
||||||
|
|
||||||
check_value_pool(self.nodes[0], 'sprout', 0)
|
check_value_pool(self.nodes[0], SHIELDED_POOL, 0)
|
||||||
check_value_pool(self.nodes[1], 'sprout', 0)
|
check_value_pool(self.nodes[1], SHIELDED_POOL, 0)
|
||||||
check_value_pool(self.nodes[2], 'sprout', 0)
|
check_value_pool(self.nodes[2], SHIELDED_POOL, 0)
|
||||||
check_value_pool(self.nodes[3], 'sprout', 0)
|
check_value_pool(self.nodes[3], SHIELDED_POOL, 0)
|
||||||
|
|
||||||
# Send will fail because we are enforcing the consensus rule that
|
# Send will fail because we are enforcing the consensus rule that
|
||||||
# coinbase utxos can only be sent to a zaddr.
|
# coinbase utxos can only be sent to a zaddr.
|
||||||
errorString = ""
|
errorString = ""
|
||||||
try:
|
try:
|
||||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1)
|
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Coinbase funds can only be sent to a zaddr" in errorString, True)
|
assert_equal("Coinbase funds can only be sent to a zaddr" in errorString, True)
|
||||||
|
|
||||||
@@ -88,18 +101,18 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
# as it's currently not possible to specify a change address in z_sendmany.
|
# as it's currently not possible to specify a change address in z_sendmany.
|
||||||
recipients = []
|
recipients = []
|
||||||
recipients.append({"address":myzaddr, "amount":Decimal('1.23456789')})
|
recipients.append({"address":myzaddr, "amount":Decimal('1.23456789')})
|
||||||
|
|
||||||
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
|
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
|
||||||
error_result = wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "wallet does not allow any change", 10)
|
error_result = wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "wallet does not allow any change", 10)
|
||||||
|
|
||||||
# Test that the returned status object contains a params field with the operation's input parameters
|
# Test that the returned status object contains a params field with the operation's input parameters
|
||||||
assert_equal(error_result["method"], "z_sendmany")
|
assert_equal(error_result["method"], "z_sendmany")
|
||||||
params = error_result["params"]
|
params = error_result["params"]
|
||||||
assert_equal(params["fee"], Decimal('0.0001')) # default
|
assert_equal(Decimal(params["fee"]), Decimal('0.0001')) # default
|
||||||
assert_equal(params["minconf"], Decimal('1')) # default
|
assert_equal(Decimal(params["minconf"]), Decimal('1')) # default
|
||||||
assert_equal(params["fromaddress"], mytaddr)
|
assert_equal(params["fromaddress"], mytaddr)
|
||||||
assert_equal(params["amounts"][0]["address"], myzaddr)
|
assert_equal(params["amounts"][0]["address"], myzaddr)
|
||||||
assert_equal(params["amounts"][0]["amount"], Decimal('1.23456789'))
|
assert_equal(Decimal(params["amounts"][0]["amount"]), Decimal('1.23456789'))
|
||||||
|
|
||||||
# Add viewing key for myzaddr to Node 3
|
# Add viewing key for myzaddr to Node 3
|
||||||
myviewingkey = self.nodes[0].z_exportviewingkey(myzaddr)
|
myviewingkey = self.nodes[0].z_exportviewingkey(myzaddr)
|
||||||
@@ -169,14 +182,17 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
|
assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
|
||||||
assert_equal(Decimal(resp["total"]), Decimal('39.9999'))
|
assert_equal(Decimal(resp["total"]), Decimal('39.9999'))
|
||||||
|
|
||||||
# The Sprout value pool should reflect the send
|
# The shielded value pool should reflect the send
|
||||||
sproutvalue = shieldvalue
|
shieldedvalue = shieldvalue
|
||||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
|
||||||
|
|
||||||
# A custom fee of 0 is okay. Here the node will send the note value back to itself.
|
# A custom fee of 0 is okay. Here the node will send the note value back to itself.
|
||||||
recipients = []
|
recipients = []
|
||||||
recipients.append({"address":myzaddr, "amount": Decimal('19.9999')})
|
recipients.append({"address":myzaddr, "amount": Decimal('19.9999')})
|
||||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('0.0'))
|
# NB: the fee is passed as a JSON number, not a Decimal. authproxy serialises
|
||||||
|
# Decimal as a JSON *string* and z_sendmany reads the fee with params[3].get_real(),
|
||||||
|
# which only accepts VNUM -- see port notes. The value is unchanged.
|
||||||
|
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, 0.0)
|
||||||
mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
|
mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
self.nodes[1].generate(1)
|
self.nodes[1].generate(1)
|
||||||
@@ -186,8 +202,8 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
|
assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
|
||||||
assert_equal(Decimal(resp["total"]), Decimal('39.9999'))
|
assert_equal(Decimal(resp["total"]), Decimal('39.9999'))
|
||||||
|
|
||||||
# The Sprout value pool should be unchanged
|
# The shielded value pool should be unchanged
|
||||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
|
||||||
|
|
||||||
# convert note to transparent funds
|
# convert note to transparent funds
|
||||||
unshieldvalue = Decimal('10.0')
|
unshieldvalue = Decimal('10.0')
|
||||||
@@ -206,12 +222,12 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
# check balances
|
# check balances
|
||||||
sproutvalue -= unshieldvalue + Decimal('0.0001')
|
shieldedvalue -= unshieldvalue + Decimal('0.0001')
|
||||||
resp = self.nodes[0].z_gettotalbalance()
|
resp = self.nodes[0].z_gettotalbalance()
|
||||||
assert_equal(Decimal(resp["transparent"]), Decimal('30.0'))
|
assert_equal(Decimal(resp["transparent"]), Decimal('30.0'))
|
||||||
assert_equal(Decimal(resp["private"]), Decimal('9.9998'))
|
assert_equal(Decimal(resp["private"]), Decimal('9.9998'))
|
||||||
assert_equal(Decimal(resp["total"]), Decimal('39.9998'))
|
assert_equal(Decimal(resp["total"]), Decimal('39.9998'))
|
||||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
|
||||||
|
|
||||||
# z_sendmany will return an error if there is transparent change output considered dust.
|
# z_sendmany will return an error if there is transparent change output considered dust.
|
||||||
# UTXO selection in z_sendmany sorts in ascending order, so smallest utxos are consumed first.
|
# UTXO selection in z_sendmany sorts in ascending order, so smallest utxos are consumed first.
|
||||||
@@ -226,7 +242,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
errorString = ""
|
errorString = ""
|
||||||
try:
|
try:
|
||||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 99999)
|
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 99999)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Insufficient funds" in errorString, True)
|
assert_equal("Insufficient funds" in errorString, True)
|
||||||
|
|
||||||
@@ -241,7 +257,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
# Send will fail because of insufficient funds unless sender uses coinbase utxos
|
# Send will fail because of insufficient funds unless sender uses coinbase utxos
|
||||||
try:
|
try:
|
||||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 21)
|
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 21)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Insufficient funds, coinbase funds can only be spent after they have been sent to a zaddr" in errorString, True)
|
assert_equal("Insufficient funds, coinbase funds can only be spent after they have been sent to a zaddr" in errorString, True)
|
||||||
|
|
||||||
@@ -256,7 +272,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
# Note that regtest chainparams does not require standard tx, so setting the amount to be
|
# Note that regtest chainparams does not require standard tx, so setting the amount to be
|
||||||
# less than the dust threshold, e.g. 0.00000001 will not result in mempool rejection.
|
# less than the dust threshold, e.g. 0.00000001 will not result in mempool rejection.
|
||||||
start_time = timeit.default_timer()
|
start_time = timeit.default_timer()
|
||||||
for i in xrange(0,num_t_recipients):
|
for i in range(0,num_t_recipients):
|
||||||
newtaddr = self.nodes[2].getnewaddress()
|
newtaddr = self.nodes[2].getnewaddress()
|
||||||
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
||||||
elapsed = timeit.default_timer() - start_time
|
elapsed = timeit.default_timer() - start_time
|
||||||
@@ -287,28 +303,30 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
|
|
||||||
# check balance
|
# check balance
|
||||||
node2balance = amount_per_recipient * num_t_recipients
|
node2balance = amount_per_recipient * num_t_recipients
|
||||||
sproutvalue -= node2balance + Decimal('0.0001')
|
shieldedvalue -= node2balance + Decimal('0.0001')
|
||||||
assert_equal(self.nodes[2].getbalance(), node2balance)
|
assert_equal(self.nodes[2].getbalance(), node2balance)
|
||||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
|
||||||
|
|
||||||
# Send will fail because fee is negative
|
# Send will fail because fee is negative
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_sendmany(myzaddr, recipients, 1, -1)
|
self.nodes[0].z_sendmany(myzaddr, recipients, 1, -1)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Amount out of range" in errorString, True)
|
assert_equal("Amount out of range" in errorString, True)
|
||||||
|
|
||||||
# Send will fail because fee is larger than MAX_MONEY
|
# Send will fail because fee is larger than MAX_MONEY
|
||||||
|
errorString = ""
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('21000000.00000001'))
|
self.nodes[0].z_sendmany(myzaddr, recipients, 1, float(Decimal('21000000.00000001')))
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Amount out of range" in errorString, True)
|
assert_equal("Amount out of range" in errorString, True)
|
||||||
|
|
||||||
# Send will fail because fee is larger than sum of outputs
|
# Send will fail because fee is larger than sum of outputs
|
||||||
|
errorString = ""
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_sendmany(myzaddr, recipients, 1, (amount_per_recipient * num_t_recipients) + Decimal('0.00000001'))
|
self.nodes[0].z_sendmany(myzaddr, recipients, 1, float((amount_per_recipient * num_t_recipients) + Decimal('0.00000001')))
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("is greater than the sum of outputs" in errorString, True)
|
assert_equal("is greater than the sum of outputs" in errorString, True)
|
||||||
|
|
||||||
@@ -334,10 +352,10 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
send_amount = num_recipients * amount_per_recipient
|
send_amount = num_recipients * amount_per_recipient
|
||||||
custom_fee = Decimal('0.00012345')
|
custom_fee = Decimal('0.00012345')
|
||||||
zbalance = self.nodes[0].z_getbalance(myzaddr)
|
zbalance = self.nodes[0].z_getbalance(myzaddr)
|
||||||
for i in xrange(0,num_recipients):
|
for i in range(0,num_recipients):
|
||||||
newzaddr = self.nodes[2].z_getnewaddress()
|
newzaddr = self.nodes[2].z_getnewaddress()
|
||||||
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
||||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, custom_fee)
|
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, float(custom_fee))
|
||||||
wait_and_assert_operationid_status(self.nodes[0], myopid)
|
wait_and_assert_operationid_status(self.nodes[0], myopid)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
self.nodes[1].generate(1)
|
self.nodes[1].generate(1)
|
||||||
@@ -353,8 +371,8 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||||||
|
|
||||||
resp = self.nodes[0].z_getbalance(myzaddr)
|
resp = self.nodes[0].z_getbalance(myzaddr)
|
||||||
assert_equal(Decimal(resp), zbalance - custom_fee - send_amount)
|
assert_equal(Decimal(resp), zbalance - custom_fee - send_amount)
|
||||||
sproutvalue -= custom_fee
|
shieldedvalue -= custom_fee
|
||||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
|
||||||
|
|
||||||
notes = self.nodes[0].z_listunspent(1, 99999, False, [myzaddr])
|
notes = self.nodes[0].z_listunspent(1, 99999, False, [myzaddr])
|
||||||
sum_of_notes = sum([note["amount"] for note in notes])
|
sum_of_notes = sum([note["amount"] for note in notes])
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2018 The Zcash developers
|
# Copyright (c) 2018 The Zcash developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -8,21 +8,99 @@ from test_framework.test_framework import BitcoinTestFramework
|
|||||||
from test_framework.authproxy import JSONRPCException
|
from test_framework.authproxy import JSONRPCException
|
||||||
from test_framework.util import (
|
from test_framework.util import (
|
||||||
assert_equal,
|
assert_equal,
|
||||||
start_nodes,
|
initialize_chain_clean,
|
||||||
|
p2p_port,
|
||||||
|
set_node_times,
|
||||||
|
start_node,
|
||||||
|
sync_blocks,
|
||||||
wait_and_assert_operationid_status,
|
wait_and_assert_operationid_status,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import os
|
||||||
|
import stat
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
# Test wallet behaviour with Sapling addresses
|
# Test wallet behaviour with Sapling addresses
|
||||||
class WalletSaplingTest(BitcoinTestFramework):
|
class WalletSaplingTest(BitcoinTestFramework):
|
||||||
|
|
||||||
|
def setup_chain(self):
|
||||||
|
# The shared initialize_chain() cache builder in test_framework/util.py has not been
|
||||||
|
# repaired for DragonX (it spawns the cache nodes by bare name off PATH, drives the
|
||||||
|
# CLI without -regtest so it dials the assetchain RPC port, and deletes debug.log
|
||||||
|
# from the non-net-specific datadir). Build the same starting state here instead --
|
||||||
|
# see _generate_starting_chain() -- so this test does not depend on it.
|
||||||
|
print("Initializing test directory " + self.options.tmpdir)
|
||||||
|
initialize_chain_clean(self.options.tmpdir, 4)
|
||||||
|
|
||||||
|
# !!! test_framework/util.py:start_node() hardcodes "-connect=0" into every regtest
|
||||||
|
# node's argv. Modern Bitcoin Core special-cases that value to mean "make no automatic
|
||||||
|
# connections", but THIS codebase does not (net.cpp ThreadOpenConnections just iterates
|
||||||
|
# mapMultiArgs["-connect"]), so "0" is dialled as a hostname: it resolves to 0.0.0.0,
|
||||||
|
# which on Linux connects to localhost on Params().GetDefaultPort() -- 21768, the live
|
||||||
|
# DRAGONX p2p port. Observed directly: a regtest node started by the unmodified
|
||||||
|
# framework peered with the production dragonxd on this host and with seven public
|
||||||
|
# mainnet nodes (heights ~3.25M) and began ingesting mainnet headers. "-connect" also
|
||||||
|
# soft-sets "-listen=0", so the framework's own connect_nodes_bi() can never establish
|
||||||
|
# the local links a multi-node test needs.
|
||||||
|
#
|
||||||
|
# Both problems are in the shared framework, which this port is not allowed to touch, so
|
||||||
|
# they are worked around per-node here: the daemon is launched through a tiny wrapper
|
||||||
|
# that strips the "-connect=0" argument, and the real topology/listening flags are passed
|
||||||
|
# as extra_args (which start_node appends after its own).
|
||||||
|
def _daemon_wrapper(self):
|
||||||
|
srcdir = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)), "..", "..", "src", "dragonxd")
|
||||||
|
path = os.path.join(self.options.tmpdir, "dragonxd-no-connect0")
|
||||||
|
with open(path, "w") as f:
|
||||||
|
f.write("#!/usr/bin/env bash\n")
|
||||||
|
f.write("args=()\n")
|
||||||
|
f.write('for a in "$@"; do\n')
|
||||||
|
f.write(' if [ "$a" = "-connect=0" ]; then continue; fi\n')
|
||||||
|
f.write(' args+=("$a")\n')
|
||||||
|
f.write("done\n")
|
||||||
|
f.write('exec %s "${args[@]}"\n' % os.path.realpath(srcdir))
|
||||||
|
os.chmod(path, os.stat(path).st_mode | stat.S_IXUSR)
|
||||||
|
return path
|
||||||
|
|
||||||
def setup_nodes(self):
|
def setup_nodes(self):
|
||||||
return start_nodes(4, self.options.tmpdir, [[
|
binary = self._daemon_wrapper()
|
||||||
#'-nuparams=5ba81b19:201', # Overwinter
|
nodes = []
|
||||||
#'-nuparams=76b809bb:203', # Sapling
|
for i in range(4):
|
||||||
#'-experimentalfeatures', '-zmergetoaddress',
|
extra_args = [
|
||||||
]] * 4)
|
#'-nuparams=5ba81b19:201', # Overwinter
|
||||||
|
#'-nuparams=76b809bb:203', # Sapling
|
||||||
|
#'-experimentalfeatures', '-zmergetoaddress',
|
||||||
|
# Listen on this test's PID-keyed port so the nodes can actually peer with
|
||||||
|
# each other, and only ever dial each other -- never the public network.
|
||||||
|
'-listen=1',
|
||||||
|
'-bind=127.0.0.1',
|
||||||
|
'-port=%d' % p2p_port(i),
|
||||||
|
] + ['-connect=127.0.0.1:%d' % p2p_port(j) for j in range(4) if j != i]
|
||||||
|
nodes.append(start_node(i, self.options.tmpdir, extra_args, binary=binary))
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
def setup_network(self, split=False):
|
||||||
|
super(WalletSaplingTest, self).setup_network(split)
|
||||||
|
self._generate_starting_chain()
|
||||||
|
|
||||||
|
def _generate_starting_chain(self):
|
||||||
|
# Equivalent of test_framework.util.initialize_chain(): a 200-block chain where each
|
||||||
|
# of the 4 nodes mined 25 blocks twice, so every node holds 25 mature and 25 immature
|
||||||
|
# coinbases. Block timestamps are 10 minutes apart starting 1 Jan 2014, as there.
|
||||||
|
block_time = 1388534400
|
||||||
|
for _round in range(2):
|
||||||
|
for peer in range(4):
|
||||||
|
for _j in range(25):
|
||||||
|
set_node_times(self.nodes, block_time)
|
||||||
|
self.nodes[peer].generate(1)
|
||||||
|
block_time += 10 * 75
|
||||||
|
# Must sync before next peer starts generating blocks
|
||||||
|
sync_blocks(self.nodes)
|
||||||
|
# Drop back to wall-clock time: initialize_chain() stops the cache nodes and the test
|
||||||
|
# then runs against freshly started nodes that have no mocktime set.
|
||||||
|
set_node_times(self.nodes, 0)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
# Sanity-check the test harness
|
# Sanity-check the test harness
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2017 The Zcash developers
|
# Copyright (c) 2017 The Zcash developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -22,8 +22,24 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
|
|||||||
initialize_chain_clean(self.options.tmpdir, 4)
|
initialize_chain_clean(self.options.tmpdir, 4)
|
||||||
|
|
||||||
def setup_network(self, split=False):
|
def setup_network(self, split=False):
|
||||||
args = ['-regtestprotectcoinbase', '-debug=zrpcunsafe']
|
# DragonX-specific environment flags. None of these change what the test
|
||||||
args2 = ['-regtestprotectcoinbase', '-debug=zrpcunsafe', "-mempooltxinputlimit=7"]
|
# asserts; without them the test cannot run at all on this daemon:
|
||||||
|
# -listen=1/-bind=127.0.0.1: the framework's start_node() always passes
|
||||||
|
# -connect=0, and AppInit2 soft-sets -listen=0 whenever -connect is
|
||||||
|
# present, so the nodes never listen and connect_nodes_bi() silently
|
||||||
|
# builds an empty topology (sync_all() then spins forever). An explicit
|
||||||
|
# -listen=1 beats the SoftSetBoolArg.
|
||||||
|
# -dnsseed=0: chainparams_commandline() keeps DRAGONX's DNS seeds and
|
||||||
|
# overwrites pchMessageStart with the DRAGONX chain magic on *every*
|
||||||
|
# network including regtest, so a regtest node otherwise dials and
|
||||||
|
# handshakes with live mainnet peers (observed: 8 mainnet peers,
|
||||||
|
# blocks=3254237, feeding mainnet headers into the regtest node).
|
||||||
|
# -autoshield=0: DragonX auto-shields matured coinbase every 25 blocks
|
||||||
|
# by default, which would race the manual z_shieldcoinbase calls under
|
||||||
|
# test and move the balances this test checks.
|
||||||
|
isolate = ['-listen=1', '-bind=127.0.0.1', '-dnsseed=0', '-autoshield=0']
|
||||||
|
args = ['-regtestprotectcoinbase', '-debug=zrpcunsafe'] + isolate
|
||||||
|
args2 = ['-regtestprotectcoinbase', '-debug=zrpcunsafe', "-mempooltxinputlimit=7"] + isolate
|
||||||
if self.addr_type != 'sprout':
|
if self.addr_type != 'sprout':
|
||||||
nu = [
|
nu = [
|
||||||
'-nuparams=5ba81b19:0', # Overwinter
|
'-nuparams=5ba81b19:0', # Overwinter
|
||||||
@@ -42,7 +58,7 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
|
|||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
def run_test (self):
|
def run_test (self):
|
||||||
print "Mining blocks..."
|
print("Mining blocks...")
|
||||||
|
|
||||||
self.nodes[0].generate(1)
|
self.nodes[0].generate(1)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
@@ -73,42 +89,42 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
|
|||||||
self.nodes[2].importaddress(mytaddr)
|
self.nodes[2].importaddress(mytaddr)
|
||||||
try:
|
try:
|
||||||
self.nodes[2].z_shieldcoinbase(mytaddr, myzaddr)
|
self.nodes[2].z_shieldcoinbase(mytaddr, myzaddr)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Could not find any coinbase funds to shield" in errorString, True)
|
assert_equal("Could not find any coinbase funds to shield" in errorString, True)
|
||||||
|
|
||||||
# Shielding will fail because fee is negative
|
# Shielding will fail because fee is negative
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_shieldcoinbase("*", myzaddr, -1)
|
self.nodes[0].z_shieldcoinbase("*", myzaddr, -1)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Amount out of range" in errorString, True)
|
assert_equal("Amount out of range" in errorString, True)
|
||||||
|
|
||||||
# Shielding will fail because fee is larger than MAX_MONEY
|
# Shielding will fail because fee is larger than MAX_MONEY
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('21000000.00000001'))
|
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('21000000.00000001'))
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Amount out of range" in errorString, True)
|
assert_equal("Amount out of range" in errorString, True)
|
||||||
|
|
||||||
# Shielding will fail because fee is larger than sum of utxos
|
# Shielding will fail because fee is larger than sum of utxos
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_shieldcoinbase("*", myzaddr, 999)
|
self.nodes[0].z_shieldcoinbase("*", myzaddr, 999)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Insufficient coinbase funds" in errorString, True)
|
assert_equal("Insufficient coinbase funds" in errorString, True)
|
||||||
|
|
||||||
# Shielding will fail because limit parameter must be at least 0
|
# Shielding will fail because limit parameter must be at least 0
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), -1)
|
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), -1)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("Limit on maximum number of utxos cannot be negative" in errorString, True)
|
assert_equal("Limit on maximum number of utxos cannot be negative" in errorString, True)
|
||||||
|
|
||||||
# Shielding will fail because limit parameter is absurdly large
|
# Shielding will fail because limit parameter is absurdly large
|
||||||
try:
|
try:
|
||||||
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), 99999999999999)
|
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), 99999999999999)
|
||||||
except JSONRPCException,e:
|
except JSONRPCException as e:
|
||||||
errorString = e.error['message']
|
errorString = e.error['message']
|
||||||
assert_equal("JSON integer out of range" in errorString, True)
|
assert_equal("JSON integer out of range" in errorString, True)
|
||||||
|
|
||||||
@@ -214,3 +230,13 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
|
|||||||
sync_mempools(self.nodes[:2])
|
sync_mempools(self.nodes[:2])
|
||||||
self.nodes[1].generate(1)
|
self.nodes[1].generate(1)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Upstream (Zcash/Hush) ran this test twice: once with Sprout zaddrs and once
|
||||||
|
# with Sapling. DragonX has no Sprout support at all -- z_getnewaddress only
|
||||||
|
# accepts "sapling" or "amnesia" (src/wallet/rpcwallet.cpp z_getnewaddress),
|
||||||
|
# so WalletShieldCoinbaseTest('sprout') cannot even allocate its target
|
||||||
|
# address. The sprout-only branches inside run_test() are kept intact for
|
||||||
|
# reference but only the sapling variant is executed.
|
||||||
|
print("Running for sapling...")
|
||||||
|
WalletShieldCoinbaseTest('sapling').main()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2016 The Zcash developers
|
# Copyright (c) 2016 The Zcash developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -20,7 +20,16 @@ class WalletTreeStateTest (BitcoinTestFramework):
|
|||||||
|
|
||||||
# Start nodes with -regtestprotectcoinbase to set fCoinbaseMustBeProtected to true.
|
# Start nodes with -regtestprotectcoinbase to set fCoinbaseMustBeProtected to true.
|
||||||
def setup_network(self, split=False):
|
def setup_network(self, split=False):
|
||||||
self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[['-regtestprotectcoinbase','-debug=zrpc']] * 3 )
|
# -listen=1 and -dns=0 are DragonX-specific harness requirements, not part of the
|
||||||
|
# original test: start_node() passes -connect=0, which trips the "-connect set ->
|
||||||
|
# setting -listen=0" parameter interaction, so without -listen=1 the three nodes
|
||||||
|
# cannot open the p2p links connect_nodes_bi() asks for (node1/node2 stay at height
|
||||||
|
# 0 forever and sync_all() can never converge). -dns=0 blocks the unconditional
|
||||||
|
# node1..node10.dragonx.is -addnode injection in hush_args(), which otherwise dials
|
||||||
|
# the real DragonX seed nodes from regtest and floods these nodes with mainnet headers.
|
||||||
|
self.nodes = start_nodes(3, self.options.tmpdir,
|
||||||
|
extra_args=[['-regtestprotectcoinbase','-debug=zrpc',
|
||||||
|
'-listen=1','-dns=0']] * 3 )
|
||||||
connect_nodes_bi(self.nodes,0,1)
|
connect_nodes_bi(self.nodes,0,1)
|
||||||
connect_nodes_bi(self.nodes,1,2)
|
connect_nodes_bi(self.nodes,1,2)
|
||||||
connect_nodes_bi(self.nodes,0,2)
|
connect_nodes_bi(self.nodes,0,2)
|
||||||
@@ -28,7 +37,7 @@ class WalletTreeStateTest (BitcoinTestFramework):
|
|||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
def run_test (self):
|
def run_test (self):
|
||||||
print "Mining blocks..."
|
print("Mining blocks...")
|
||||||
|
|
||||||
self.nodes[0].generate(100)
|
self.nodes[0].generate(100)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
@@ -79,7 +88,7 @@ class WalletTreeStateTest (BitcoinTestFramework):
|
|||||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
|
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||||
|
|
||||||
# Wait for Tx 2 to begin executing...
|
# Wait for Tx 2 to begin executing...
|
||||||
for x in xrange(1, 60):
|
for x in range(1, 60):
|
||||||
results = self.nodes[0].z_getoperationstatus([myopid])
|
results = self.nodes[0].z_getoperationstatus([myopid])
|
||||||
status = results[0]["status"]
|
status = results[0]["status"]
|
||||||
if status == "executing":
|
if status == "executing":
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2016-2024 The Hush developers
|
# Copyright (c) 2016-2024 The Hush developers
|
||||||
# Copyright (c) 2014 The Bitcoin Core developers
|
# Copyright (c) 2014 The Bitcoin Core developers
|
||||||
# Distributed under the GPLv3 software license, see the accompanying
|
# Distributed under the GPLv3 software license, see the accompanying
|
||||||
@@ -37,8 +37,8 @@ and confirm again balances are correct.
|
|||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.authproxy import JSONRPCException
|
from test_framework.authproxy import JSONRPCException
|
||||||
from test_framework.util import assert_equal, initialize_chain_clean, \
|
from test_framework.util import assert_equal, initialize_chain_clean, \
|
||||||
start_nodes, start_node, connect_nodes, stop_node, \
|
start_nodes, start_node, connect_nodes, \
|
||||||
sync_blocks, sync_mempools
|
sync_blocks, sync_mempools, bitcoind_processes
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
@@ -48,6 +48,46 @@ import logging
|
|||||||
|
|
||||||
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
|
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
|
||||||
|
|
||||||
|
# Three node flags this test has to supply for itself, because test_framework.util.start_node()
|
||||||
|
# cannot give it a usable isolated regtest network on DragonX:
|
||||||
|
#
|
||||||
|
# -listen=1: start_node() passes "-connect=0" to every node. In DragonX (as in Bitcoin) the
|
||||||
|
# presence of a -connect argument SoftSetBoolArg()s -listen to false, so no test node ever
|
||||||
|
# binds its p2p port and connect_nodes() cannot build the loopback topology this test needs.
|
||||||
|
# Verified on a live run: only the RPC ports were listening and getpeerinfo showed zero
|
||||||
|
# 127.0.0.1 peers on all four nodes. SoftSetBoolArg does not override an explicit value.
|
||||||
|
#
|
||||||
|
# -dns=0: hush_args() unconditionally appends node1..node10.dragonx.is to -addnode whenever the
|
||||||
|
# chain name is DRAGONX, regardless of network, and -connect=0 does not suppress it. On a live
|
||||||
|
# run every "regtest" node ended up with 7-8 established connections to production mainnet
|
||||||
|
# nodes on port 21768, was flooded with mainnet headers ("AcceptBlockHeader: hashPrevBlock ...
|
||||||
|
# not found"), and one of them aborted on CheckBlockIndex(). -dns=0 stops the hostnames from
|
||||||
|
# resolving while leaving the literal 127.0.0.1:PORT addnodes connect_nodes() uses intact.
|
||||||
|
#
|
||||||
|
# -allowlist=127.0.0.1: "-connect=0" does not mean "no connections". The daemon resolves the
|
||||||
|
# literal "0" to 0.0.0.0 and dials it on the chain's default p2p port, i.e. 127.0.0.1:21768 --
|
||||||
|
# which on a machine that also runs a real node is the PRODUCTION daemon (observed:
|
||||||
|
# ESTAB 127.0.0.1:41282 -> 127.0.0.1:21768 from every test node, peer subver /DragonX:1.0.3/,
|
||||||
|
# startingheight 3254254). That peer is outbound, so it is the node's only preferred-download
|
||||||
|
# peer; main.cpp:8398 then computes fFetch=false for every inbound peer, and node3 -- which in
|
||||||
|
# this test's topology is dialed by everyone and dials no one -- never downloads an announced
|
||||||
|
# block, so sync_blocks() hangs forever. Allowlisting loopback makes inbound test peers
|
||||||
|
# preferred-download too (main.cpp:381), which restores block propagation.
|
||||||
|
LISTEN = "-listen=1"
|
||||||
|
NODNS = "-dns=0"
|
||||||
|
ALLOWLIST = "-allowlist=127.0.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
def stop_node_and_reap(node, i):
|
||||||
|
# Equivalent to test_framework.util.stop_node(), which cannot be called: it does
|
||||||
|
# print("Stopping node " + i) with the int index that every caller passes, which raises
|
||||||
|
# TypeError. Reimplemented here rather than editing shared framework code other tests use.
|
||||||
|
print("Stopping node %d" % i)
|
||||||
|
node.stop()
|
||||||
|
bitcoind_processes[i].wait()
|
||||||
|
del bitcoind_processes[i]
|
||||||
|
|
||||||
|
|
||||||
class WalletBackupTest(BitcoinTestFramework):
|
class WalletBackupTest(BitcoinTestFramework):
|
||||||
|
|
||||||
def setup_chain(self):
|
def setup_chain(self):
|
||||||
@@ -62,7 +102,10 @@ class WalletBackupTest(BitcoinTestFramework):
|
|||||||
ed2 = "-exportdir=" + self.options.tmpdir + "/node2"
|
ed2 = "-exportdir=" + self.options.tmpdir + "/node2"
|
||||||
|
|
||||||
# nodes 1, 2,3 are spenders, let's give them a keypool=100
|
# nodes 1, 2,3 are spenders, let's give them a keypool=100
|
||||||
extra_args = [["-keypool=100", ed0], ["-keypool=100", ed1], ["-keypool=100", ed2], []]
|
extra_args = [["-keypool=100", ed0, LISTEN, NODNS, ALLOWLIST],
|
||||||
|
["-keypool=100", ed1, LISTEN, NODNS, ALLOWLIST],
|
||||||
|
["-keypool=100", ed2, LISTEN, NODNS, ALLOWLIST],
|
||||||
|
[LISTEN, NODNS, ALLOWLIST]]
|
||||||
self.nodes = start_nodes(4, self.options.tmpdir, extra_args)
|
self.nodes = start_nodes(4, self.options.tmpdir, extra_args)
|
||||||
connect_nodes(self.nodes[0], 3)
|
connect_nodes(self.nodes[0], 3)
|
||||||
connect_nodes(self.nodes[1], 3)
|
connect_nodes(self.nodes[1], 3)
|
||||||
@@ -95,18 +138,18 @@ class WalletBackupTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
# As above, this mirrors the original bash test.
|
# As above, this mirrors the original bash test.
|
||||||
def start_three(self):
|
def start_three(self):
|
||||||
self.nodes[0] = start_node(0, self.options.tmpdir)
|
self.nodes[0] = start_node(0, self.options.tmpdir, [LISTEN, NODNS, ALLOWLIST])
|
||||||
self.nodes[1] = start_node(1, self.options.tmpdir)
|
self.nodes[1] = start_node(1, self.options.tmpdir, [LISTEN, NODNS, ALLOWLIST])
|
||||||
self.nodes[2] = start_node(2, self.options.tmpdir)
|
self.nodes[2] = start_node(2, self.options.tmpdir, [LISTEN, NODNS, ALLOWLIST])
|
||||||
connect_nodes(self.nodes[0], 3)
|
connect_nodes(self.nodes[0], 3)
|
||||||
connect_nodes(self.nodes[1], 3)
|
connect_nodes(self.nodes[1], 3)
|
||||||
connect_nodes(self.nodes[2], 3)
|
connect_nodes(self.nodes[2], 3)
|
||||||
connect_nodes(self.nodes[2], 0)
|
connect_nodes(self.nodes[2], 0)
|
||||||
|
|
||||||
def stop_three(self):
|
def stop_three(self):
|
||||||
stop_node(self.nodes[0], 0)
|
stop_node_and_reap(self.nodes[0], 0)
|
||||||
stop_node(self.nodes[1], 1)
|
stop_node_and_reap(self.nodes[1], 1)
|
||||||
stop_node(self.nodes[2], 2)
|
stop_node_and_reap(self.nodes[2], 2)
|
||||||
|
|
||||||
def erase_three(self):
|
def erase_three(self):
|
||||||
os.remove(self.options.tmpdir + "/node0/regtest/wallet.dat")
|
os.remove(self.options.tmpdir + "/node0/regtest/wallet.dat")
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ hush_gtest_SOURCES = \
|
|||||||
gtest/utils.cpp \
|
gtest/utils.cpp \
|
||||||
gtest/test_randomx_preverify.cpp \
|
gtest/test_randomx_preverify.cpp \
|
||||||
gtest/test_hdtransparent.cpp \
|
gtest/test_hdtransparent.cpp \
|
||||||
gtest/test_mnemonic_compat.cpp
|
gtest/test_mnemonic_compat.cpp \
|
||||||
|
gtest/test_stratum_jobid.cpp
|
||||||
|
|
||||||
hush_gtest_CPPFLAGS = $(AM_CPPFLAGS) -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC $(BITCOIN_INCLUDES)
|
hush_gtest_CPPFLAGS = $(AM_CPPFLAGS) -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC $(BITCOIN_INCLUDES)
|
||||||
hush_gtest_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
hush_gtest_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||||
|
|||||||
29
src/chain.h
29
src/chain.h
@@ -35,7 +35,7 @@ extern bool fZindex;
|
|||||||
// These version thresholds control whether nSproutValue/nSaplingValue are
|
// These version thresholds control whether nSproutValue/nSaplingValue are
|
||||||
// serialized in the block index. They must be <= CLIENT_VERSION or the
|
// serialized in the block index. They must be <= CLIENT_VERSION or the
|
||||||
// values will never be persisted, causing nChainSaplingValue to reset
|
// values will never be persisted, causing nChainSaplingValue to reset
|
||||||
// to 0 after node restart. DragonX CLIENT_VERSION is 1010050 (v1.1.0.50).
|
// to 0 after node restart. DragonX CLIENT_VERSION is 1030050 (v1.3.0.50).
|
||||||
static const int SPROUT_VALUE_VERSION = 1000000;
|
static const int SPROUT_VALUE_VERSION = 1000000;
|
||||||
static const int SAPLING_VALUE_VERSION = 1000000;
|
static const int SAPLING_VALUE_VERSION = 1000000;
|
||||||
// Block-index records written at >= this version store nSaplingValue as a boost::optional
|
// Block-index records written at >= this version store nSaplingValue as a boost::optional
|
||||||
@@ -113,10 +113,9 @@ enum BlockStatus: uint32_t {
|
|||||||
//! Scripts & signatures ok. Implies all parents are also at least SCRIPTS.
|
//! Scripts & signatures ok. Implies all parents are also at least SCRIPTS.
|
||||||
BLOCK_VALID_SCRIPTS = 5,
|
BLOCK_VALID_SCRIPTS = 5,
|
||||||
|
|
||||||
// flag to check if contextual check block has passed in Accept block, if it has not check at connect block.
|
|
||||||
BLOCK_VALID_CONTEXT = 6,
|
|
||||||
|
|
||||||
//! All validity bits.
|
//! All validity bits.
|
||||||
|
//! NOTE: the levels above are sequential VALUES occupying this 3-bit field, not independent
|
||||||
|
//! bits, so any flag stored in nStatus must live entirely outside this mask.
|
||||||
BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS |
|
BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS |
|
||||||
BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS,
|
BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS,
|
||||||
|
|
||||||
@@ -129,9 +128,29 @@ enum BlockStatus: uint32_t {
|
|||||||
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
|
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
|
||||||
|
|
||||||
BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade
|
BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade
|
||||||
BLOCK_IN_TMPFILE = 256
|
BLOCK_IN_TMPFILE = 256,
|
||||||
|
|
||||||
|
//! ContextualCheckBlock already passed in AcceptBlock, so ConnectBlock may skip re-running it.
|
||||||
|
//! Was 6 until v1.3.0, which put it INSIDE BLOCK_VALID_MASK (1|2|3|4|5 == 7): `nStatus |=
|
||||||
|
//! BLOCK_VALID_CONTEXT` then overwrote the validity level rather than setting a flag, so a
|
||||||
|
//! block that was only written to disk read back as BLOCK_VALID_SCRIPTS and every later
|
||||||
|
//! RaiseValidity() silently no-opped. Detected by CheckBlockIndex's "CHAIN valid implies all
|
||||||
|
//! parents are CHAIN valid" assert, which aborts any node doing out-of-order block download
|
||||||
|
//! (regtest only, where fDefaultConsistencyChecks is true). Validity was only ever inflated,
|
||||||
|
//! never deflated, so ConnectBlock's full validation was never skipped -- see git history.
|
||||||
|
//! Legacy block indexes still carry the polluted low bits; they resolve on reindex, and until
|
||||||
|
//! then simply re-run the contextual check they used to skip.
|
||||||
|
BLOCK_VALID_CONTEXT = 512
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! The validity level is a small integer packed into BLOCK_VALID_MASK, so every other nStatus flag
|
||||||
|
//! must be disjoint from it. Enforced here so this class of bug cannot be reintroduced silently.
|
||||||
|
static_assert((BLOCK_VALID_CONTEXT & BLOCK_VALID_MASK) == 0, "BLOCK_VALID_CONTEXT overlaps the validity-level field");
|
||||||
|
static_assert((BLOCK_HAVE_MASK & BLOCK_VALID_MASK) == 0, "BLOCK_HAVE_MASK overlaps the validity-level field");
|
||||||
|
static_assert((BLOCK_FAILED_MASK & BLOCK_VALID_MASK) == 0, "BLOCK_FAILED_MASK overlaps the validity-level field");
|
||||||
|
static_assert((BLOCK_ACTIVATES_UPGRADE & BLOCK_VALID_MASK) == 0, "BLOCK_ACTIVATES_UPGRADE overlaps the validity-level field");
|
||||||
|
static_assert((BLOCK_IN_TMPFILE & BLOCK_VALID_MASK) == 0, "BLOCK_IN_TMPFILE overlaps the validity-level field");
|
||||||
|
|
||||||
//! Short-hand for the highest consensus validity we implement.
|
//! Short-hand for the highest consensus validity we implement.
|
||||||
//! Blocks with this validity are assumed to satisfy all consensus rules.
|
//! Blocks with this validity are assumed to satisfy all consensus rules.
|
||||||
static const BlockStatus BLOCK_VALID_CONSENSUS = BLOCK_VALID_SCRIPTS;
|
static const BlockStatus BLOCK_VALID_CONSENSUS = BLOCK_VALID_SCRIPTS;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
|
//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
|
||||||
// Must be kept in sync with configure.ac , ugh!
|
// Must be kept in sync with configure.ac , ugh!
|
||||||
#define CLIENT_VERSION_MAJOR 1
|
#define CLIENT_VERSION_MAJOR 1
|
||||||
#define CLIENT_VERSION_MINOR 2
|
#define CLIENT_VERSION_MINOR 3
|
||||||
#define CLIENT_VERSION_REVISION 0
|
#define CLIENT_VERSION_REVISION 0
|
||||||
#define CLIENT_VERSION_BUILD 50
|
#define CLIENT_VERSION_BUILD 50
|
||||||
|
|
||||||
|
|||||||
98
src/gtest/test_stratum_jobid.cpp
Normal file
98
src/gtest/test_stratum_jobid.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// Copyright (c) 2016-2026 The Hush developers
|
||||||
|
// Distributed under the GPLv3 software license, see the accompanying
|
||||||
|
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||||
|
//
|
||||||
|
// Regression coverage for the b3e81f1ed fix:
|
||||||
|
// "stratum: do not abort the daemon on a malformed 63-character job_id"
|
||||||
|
//
|
||||||
|
// The EWBF "31 bytes job_id" path in stratum_mining_submit() completes a
|
||||||
|
// 63-character job_id with each hex digit in turn and feeds the result to
|
||||||
|
// uint256(). ParseHex() stops at the first non-hex character and returns a
|
||||||
|
// SHORT vector without signalling an error, and base_blob's vector ctor
|
||||||
|
// asserts vch.size() == 32. asserts are live in release builds here, and the
|
||||||
|
// job_id arrives from an unauthenticated client -- so a single mining.submit
|
||||||
|
// whose 63-character job_id contains any non-hex byte (63 spaces will do)
|
||||||
|
// used to abort the node.
|
||||||
|
//
|
||||||
|
// The fix size-checks each candidate before constructing uint256. These tests
|
||||||
|
// pin the exact invariant that guard relies on, using the real ParseHex and
|
||||||
|
// uint256 primitives, without ever constructing a uint256 from a short vector
|
||||||
|
// (which would still abort under the guard we are protecting).
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "uint256.h"
|
||||||
|
#include "utilstrencodings.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const std::string HEXDIGITS = "0123456789abcdef";
|
||||||
|
|
||||||
|
// Mirrors the guarded completion loop in stratum_mining_submit(): try every
|
||||||
|
// single-hex-digit completion and report whether ANY of them parses to a
|
||||||
|
// whole 32-byte value. Only then is uint256() construction reached.
|
||||||
|
bool AnyCompletionParsesTo32(const std::string& jobid) {
|
||||||
|
for (char d : HEXDIGITS) {
|
||||||
|
std::vector<unsigned char> vch = ParseHex(jobid + d);
|
||||||
|
if (vch.size() == 32) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// The exact example from the fix commit: a 63-character job_id of spaces.
|
||||||
|
// No completion may reach a 32-byte vector, so the daemon never constructs
|
||||||
|
// uint256() and never aborts.
|
||||||
|
TEST(StratumJobId, AllSpacesNeverYields32Bytes) {
|
||||||
|
std::string spaces(63, ' ');
|
||||||
|
ASSERT_EQ(spaces.size(), 63u);
|
||||||
|
for (char d : HEXDIGITS) {
|
||||||
|
EXPECT_NE(ParseHex(spaces + d).size(), 32u)
|
||||||
|
<< "completion '" << d << "' unexpectedly produced 32 bytes";
|
||||||
|
}
|
||||||
|
EXPECT_FALSE(AnyCompletionParsesTo32(spaces));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A single non-hex byte embedded in an otherwise-hex 63-char job_id is enough:
|
||||||
|
// ParseHex stops at it, so every completion is short.
|
||||||
|
TEST(StratumJobId, SingleNonHexByteInMiddleIsRejected) {
|
||||||
|
std::string jobid(63, 'a');
|
||||||
|
jobid[30] = 'g'; // 'g' is not a hex digit
|
||||||
|
ASSERT_EQ(jobid.size(), 63u);
|
||||||
|
EXPECT_FALSE(AnyCompletionParsesTo32(jobid));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A non-hex byte at the very end (position 62) is likewise rejected: the last
|
||||||
|
// hex pair can never complete to a whole byte.
|
||||||
|
TEST(StratumJobId, NonHexByteAtEndIsRejected) {
|
||||||
|
std::string jobid(62, 'a');
|
||||||
|
jobid.push_back('z'); // length 63, last char non-hex
|
||||||
|
ASSERT_EQ(jobid.size(), 63u);
|
||||||
|
EXPECT_FALSE(AnyCompletionParsesTo32(jobid));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A genuine truncated EWBF job_id -- 63 real hex characters -- must complete
|
||||||
|
// to exactly 32 bytes for every digit, so uint256() construction is safe.
|
||||||
|
TEST(StratumJobId, ValidSixtyThreeHexCompletesToExactly32Bytes) {
|
||||||
|
std::string jobid(63, 'a');
|
||||||
|
ASSERT_EQ(jobid.size(), 63u);
|
||||||
|
for (char d : HEXDIGITS) {
|
||||||
|
std::vector<unsigned char> vch = ParseHex(jobid + d);
|
||||||
|
ASSERT_EQ(vch.size(), 32u) << "completion '" << d << "'";
|
||||||
|
// 32-byte vector: construction must not trip the size assertion.
|
||||||
|
uint256 h(vch);
|
||||||
|
EXPECT_EQ(h.size(), 32u);
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(AnyCompletionParsesTo32(jobid));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity anchors for ParseHex's odd/even handling that the loop depends on:
|
||||||
|
// an odd hex length drops the trailing nibble (63 hex -> 31 bytes), and one
|
||||||
|
// more hex char fills the 32nd byte (64 hex -> 32 bytes).
|
||||||
|
TEST(StratumJobId, ParseHexOddLengthDropsTrailingNibble) {
|
||||||
|
EXPECT_EQ(ParseHex(std::string(63, 'a')).size(), 31u);
|
||||||
|
EXPECT_EQ(ParseHex(std::string(64, 'a')).size(), 32u);
|
||||||
|
}
|
||||||
@@ -1383,8 +1383,14 @@ void hush_configfile(char *symbol,uint16_t rpcport)
|
|||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
_hush_userpass(myusername,mypassword,fp);
|
_hush_userpass(myusername,mypassword,fp);
|
||||||
mapArgs["-rpcpassword"] = mypassword;
|
// Feed the credentials read by InitRPCAuthentication (httprpc.cpp) and the
|
||||||
mapArgs["-rpcusername"] = myusername;
|
// CLI (bitcoin-cli.cpp) -- both read "-rpcuser"/"-rpcpassword". Use SoftSetArg
|
||||||
|
// so a value passed on the command line (or an explicit -rpcuser/-rpcpassword)
|
||||||
|
// still wins: the old direct assignment silently overwrote a command-line
|
||||||
|
// -rpcpassword on every restart once this conf existed, and the username was
|
||||||
|
// written to a misspelled "-rpcusername" key that nothing ever reads.
|
||||||
|
SoftSetArg("-rpcpassword", mypassword);
|
||||||
|
SoftSetArg("-rpcuser", myusername);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1432,7 +1432,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||||||
mempool.setSanityCheck(1.0 / ratio);
|
mempool.setSanityCheck(1.0 / ratio);
|
||||||
}
|
}
|
||||||
fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
|
fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
|
||||||
fCheckpointsEnabled = GetBoolArg("-checkpoints", true);
|
// Regtest inherits the DRAGONX *mainnet* checkpoint set (chainparams_commandline applies it
|
||||||
|
// for every SMART_CHAIN_SYMBOL=="DRAGONX" network, regardless of -regtest), whose top height
|
||||||
|
// ~3.2M would otherwise pin an isolated regtest chain in IsInitialBlockDownload() forever --
|
||||||
|
// disabling the ChainTip auto-ops (autoshield/sweep/consolidation) and the below-checkpoint
|
||||||
|
// script-check skip. Default checkpoints OFF on regtest so a fresh regtest node leaves IBD
|
||||||
|
// normally; still overridable with -checkpoints=1.
|
||||||
|
fCheckpointsEnabled = GetBoolArg("-checkpoints", chainparams.NetworkIDString() != "regtest");
|
||||||
|
|
||||||
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
|
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
|
||||||
nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
|
nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
|
||||||
|
|||||||
@@ -656,9 +656,18 @@ void CustomizeWork(const StratumClient& client, const StratumWork& current_work,
|
|||||||
LogPrint("stratum", "%s\n", msg);
|
LogPrint("stratum", "%s\n", msg);
|
||||||
throw std::runtime_error(msg);
|
throw std::runtime_error(msg);
|
||||||
}
|
}
|
||||||
if (cb.vout[0].scriptPubKey == (CScript() << OP_FALSE)) {
|
// Unconditional. This used to be guarded on the coinbase still carrying the OP_FALSE
|
||||||
cb.vout[0].scriptPubKey = GetScriptForDestination(addr.Get());
|
// placeholder, which made it a no-op for every client after the first once a customized
|
||||||
|
// coinbase had been written back into the shared template -- so those miners silently
|
||||||
|
// mined the first miner's payout address. The template is now left pristine (see
|
||||||
|
// GetWorkUnit), and stamping unconditionally means a coinbase that somehow arrives
|
||||||
|
// already customized can never be inherited by a different miner.
|
||||||
|
if (!addr.IsValid()) {
|
||||||
|
const std::string msg = strprintf("%s: no valid payout address for this client; unable to customize work", __func__);
|
||||||
|
LogPrint("stratum", "%s\n", msg);
|
||||||
|
throw std::runtime_error(msg);
|
||||||
}
|
}
|
||||||
|
cb.vout[0].scriptPubKey = GetScriptForDestination(addr.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// cb_branch = current_work.m_cb_branch;
|
// cb_branch = current_work.m_cb_branch;
|
||||||
@@ -864,20 +873,24 @@ std::string GetWorkUnit(StratumClient& client)
|
|||||||
static const std::vector<unsigned char> dummy(32-extranonce1.size(), 0x00); // extranonce2
|
static const std::vector<unsigned char> dummy(32-extranonce1.size(), 0x00); // extranonce2
|
||||||
CustomizeWork(client, current_work, client.m_addr, extranonce1, dummy, cb, bf, cb_branch);
|
CustomizeWork(client, current_work, client.m_addr, extranonce1, dummy, cb, bf, cb_branch);
|
||||||
|
|
||||||
// without 2 lines below equihash solutinon on SubmitWork will be incorrect, bcz we should
|
|
||||||
// change vtx[0] in current work and re-calc hashMerkleRoot
|
|
||||||
// TODO: refactor all of these ... may be change this in current_work directly is bad idea,
|
|
||||||
// and we should do all checks and hashMerkleRoot at SubmitBlock(...)
|
|
||||||
|
|
||||||
current_work.GetBlock().vtx[0] = cb;
|
|
||||||
current_work.GetBlock().hashMerkleRoot = current_work.GetBlock().BuildMerkleTree();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CBlockHeader blkhdr;
|
CBlockHeader blkhdr;
|
||||||
// Setup native proof-of-work
|
// Setup native proof-of-work
|
||||||
|
|
||||||
blkhdr = current_work.GetBlock().GetBlockHeader(); // copy entire blockheader created with CreateNewBlock to blkhdr
|
// The shared template MUST keep its pristine OP_FALSE coinbase. This previously did
|
||||||
|
// current_work.GetBlock().vtx[0] = cb;
|
||||||
|
// current_work.GetBlock().hashMerkleRoot = current_work.GetBlock().BuildMerkleTree();
|
||||||
|
// which published one client's coinbase to every other client on the same job: the merkle
|
||||||
|
// root they were told to mine, and the block they eventually submitted, both committed to
|
||||||
|
// the first client's payout address. Derive this client's header from a local copy instead,
|
||||||
|
// which is what the TODO that used to sit here was asking for.
|
||||||
|
{
|
||||||
|
CBlock tmp(current_work.GetBlock());
|
||||||
|
tmp.vtx[0] = cb;
|
||||||
|
blkhdr = tmp.GetBlockHeader();
|
||||||
|
blkhdr.hashMerkleRoot = tmp.BuildMerkleTree();
|
||||||
|
}
|
||||||
// CDataStream ds(SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
|
// CDataStream ds(SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
|
||||||
CDataStream ds(SER_GETHASH, PROTOCOL_VERSION);
|
CDataStream ds(SER_GETHASH, PROTOCOL_VERSION);
|
||||||
ds << cb;
|
ds << cb;
|
||||||
@@ -1012,9 +1025,32 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork
|
|||||||
: std::vector<unsigned char>(sol.begin() + 3, sol.end());
|
: std::vector<unsigned char>(sol.begin() + 3, sol.end());
|
||||||
|
|
||||||
blkhdr.hashFinalSaplingRoot = current_work.GetBlock().hashFinalSaplingRoot;
|
blkhdr.hashFinalSaplingRoot = current_work.GetBlock().hashFinalSaplingRoot;
|
||||||
blkhdr.hashMerkleRoot = current_work.GetBlock().hashMerkleRoot;
|
// Recompute from the coinbase CustomizeWork() just derived for THIS client. Reading the
|
||||||
|
// shared template's root would be wrong now that the template is left pristine, and was
|
||||||
|
// wrong before too -- it carried whichever client happened to request work first.
|
||||||
|
{
|
||||||
|
CBlock tmp(current_work.GetBlock());
|
||||||
|
tmp.vtx[0] = cb;
|
||||||
|
blkhdr.hashMerkleRoot = tmp.BuildMerkleTree();
|
||||||
|
}
|
||||||
blkhdr.nNonce = (uint256) nonce;
|
blkhdr.nNonce = (uint256) nonce;
|
||||||
|
|
||||||
|
// Cheap SHA256d filter first. This test used to sit *below* the RandomX verify, so 32
|
||||||
|
// arbitrary bytes from any peer bought a full ~65ms randomx_calculate_hash before anything
|
||||||
|
// rejected them -- on the shared HTTP/RPC libevent thread, and holding the global
|
||||||
|
// cs_randomx_validator that block validation also takes. GetHash() is SerializeHash over the
|
||||||
|
// header including nSolution, so passing this costs real grinding. Semantics are unchanged:
|
||||||
|
// an empty local_diff still parses to zero and still rejects, exactly as before.
|
||||||
|
if (!instance_of_cstratumparams.fAllowLowDiffShares &&
|
||||||
|
UintToArith256(blkhdr.GetHash()) > arith_uint256(current_work.local_diff)) {
|
||||||
|
CBlockIndex diff_index;
|
||||||
|
diff_index.nBits = UintToArith256(blkhdr.GetHash()).GetCompact();
|
||||||
|
const double share_diff = GetDifficulty(&diff_index);
|
||||||
|
diff_index.nBits = arith_uint256(current_work.local_diff).GetCompact();
|
||||||
|
const double target_diff = GetDifficulty(&diff_index);
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Low diff share (diff %g, local %g)", share_diff, target_diff));
|
||||||
|
}
|
||||||
|
|
||||||
// block is constructed, now it's time to VerifyEH
|
// block is constructed, now it's time to VerifyEH
|
||||||
|
|
||||||
if (StratumIsRandomX()) {
|
if (StratumIsRandomX()) {
|
||||||
@@ -1072,10 +1108,8 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork
|
|||||||
std::chrono::duration<double, std::milli> elapsed;
|
std::chrono::duration<double, std::milli> elapsed;
|
||||||
uint64_t shares_accepted_since_last;
|
uint64_t shares_accepted_since_last;
|
||||||
|
|
||||||
// TODO: we need to check hash > local port diff, and if it's true -> throw an exception -> diff too low (!)
|
// (the low-diff share check moved above the RandomX verify -- see SubmitBlock's cheap
|
||||||
if (!instance_of_cstratumparams.fAllowLowDiffShares)
|
// SHA256d filter -- so that attacker-controlled bytes cannot buy a RandomX hash)
|
||||||
if (UintToArith256(blkhdr.GetHash()) > arith_uint256(current_work.local_diff))
|
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Low diff share (diff %g, local %g)", hush_real_diff, hush_local_diff));
|
|
||||||
|
|
||||||
if (finish > start)
|
if (finish > start)
|
||||||
{
|
{
|
||||||
@@ -1323,6 +1357,18 @@ UniValue stratum_mining_submit(StratumClient& client, const UniValue& params)
|
|||||||
|
|
||||||
const std::string method("mining.submit");
|
const std::string method("mining.submit");
|
||||||
BoundParams(method, params, 5,5);
|
BoundParams(method, params, 5,5);
|
||||||
|
|
||||||
|
// Parity with every other handler (GetWorkUnit, mining.aux.*, mining.extranonce.*), which all
|
||||||
|
// refuse an unauthorized client. NOTE this is not authentication: mining.authorize validates no
|
||||||
|
// credential, so it only costs an attacker one extra line. It is here so the submit path cannot
|
||||||
|
// be reached without at least completing the handshake; the cheap-target check below is what
|
||||||
|
// actually bounds the work an unknown peer can force.
|
||||||
|
if (!client.m_authorized && client.m_aux_addr.empty()) {
|
||||||
|
const std::string msg = strprintf("%s: share submitted by an unauthorized client", __func__);
|
||||||
|
LogPrint("stratum", "%s\n", msg);
|
||||||
|
throw JSONRPCError(RPC_INVALID_REQUEST, "Stratum client not authorized. Use mining.authorize first, with a DragonX R.. address as the username or 'x' to mine to the default address.");
|
||||||
|
}
|
||||||
|
|
||||||
// First parameter is the client username, which is ignored.
|
// First parameter is the client username, which is ignored.
|
||||||
|
|
||||||
/* EWBF 31 bytes job_id fix */
|
/* EWBF 31 bytes job_id fix */
|
||||||
@@ -1334,7 +1380,16 @@ UniValue stratum_mining_submit(StratumClient& client, const UniValue& params)
|
|||||||
if (job_id_str.length() == 63) {
|
if (job_id_str.length() == 63) {
|
||||||
fEWBFJobIDFixNeeded = true;
|
fEWBFJobIDFixNeeded = true;
|
||||||
for(const auto& hexDigit : hexDigits) {
|
for(const auto& hexDigit : hexDigits) {
|
||||||
ret = uint256(ParseHex(job_id_str + hexDigit));
|
// ParseHex() stops at the first non-hex character and returns a SHORT vector
|
||||||
|
// without signalling an error, and base_blob(const std::vector<unsigned char>&)
|
||||||
|
// asserts vch.size() == 32. Constructing without checking therefore lets any
|
||||||
|
// 63-character job_id containing a non-hex byte abort the daemon -- from an
|
||||||
|
// unauthenticated client, before any other validation. Skip bad candidates
|
||||||
|
// instead; if none of the 16 completions parse, ret stays null, misses
|
||||||
|
// work_templates below, and the handler returns false cleanly.
|
||||||
|
std::vector<unsigned char> vch = ParseHex(job_id_str + hexDigit);
|
||||||
|
if (vch.size() != 32) continue;
|
||||||
|
ret = uint256(vch);
|
||||||
if (work_templates.count(ret)) break;
|
if (work_templates.count(ret)) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,12 @@ fi
|
|||||||
|
|
||||||
DESC=""
|
DESC=""
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
if [ -e "$(which git 2>/dev/null)" -a "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ]; then
|
# Allow the build system to supply the version when git metadata is unavailable:
|
||||||
|
# container builds exclude .git, and a linked worktree's .git file points outside
|
||||||
|
# the build context. Without this such builds are stamped "-unk".
|
||||||
|
if [ -n "${DRAGONX_BUILD_DESC:-}" ]; then
|
||||||
|
DESC="$DRAGONX_BUILD_DESC"
|
||||||
|
elif [ -e "$(which git 2>/dev/null)" -a "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ]; then
|
||||||
# clean 'dirty' status of touched files that haven't been modified
|
# clean 'dirty' status of touched files that haven't been modified
|
||||||
git diff >/dev/null 2>/dev/null
|
git diff >/dev/null 2>/dev/null
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user