Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fad05d3ab4 | |||
| 5d3f7e520c | |||
| 092a608fd9 | |||
| 3dd667b127 | |||
| dde6cd810f | |||
| 499f02a905 | |||
| 4dc57e80b1 | |||
| 04ac7c1186 | |||
| 65130c3120 | |||
| 698bcf9574 | |||
| a0ccb4be1d | |||
| 358011bd54 | |||
| 7a62fc4877 | |||
| 9a8f17b2c8 | |||
| 1ec6590fb7 | |||
| 92e6c7008d | |||
| 733df964ec | |||
| d12e7dc99d | |||
| 9734402d7b | |||
| 20b2cbe830 | |||
| 2d7dd90c55 | |||
| 3caec548ae | |||
| 2a7fdcc1db | |||
| 143c33de48 | |||
| e2f88175ab | |||
| a494eabdce | |||
| dad162a89a | |||
| bb292a89cc | |||
| 660678f9bb | |||
| fbcf160478 | |||
| 7dc904c96f | |||
| 4d72e5fc30 | |||
| ca730a5d98 | |||
| 2ebbbc777c |
@@ -1,4 +1,8 @@
|
||||
FROM ubuntu:20.04
|
||||
# Base image is parameterised so one Dockerfile can produce binaries for several
|
||||
# glibc floors: docker build --build-arg BASE_IMAGE=ubuntu:18.04 ...
|
||||
# The default is unchanged, so `./build.sh --linux-compat` behaves exactly as before.
|
||||
ARG BASE_IMAGE=ubuntu:20.04
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
|
||||
26
build.sh
26
build.sh
@@ -6,10 +6,34 @@
|
||||
|
||||
set -eu -o pipefail
|
||||
|
||||
VERSION="1.0.3"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
RELEASE_DIR="$SCRIPT_DIR/release"
|
||||
|
||||
# Derive the release version from configure.ac instead of hardcoding it here.
|
||||
# A stale literal names the release directories after the wrong version while the
|
||||
# binaries inside report the real one: this said 1.0.3 while the tree was already
|
||||
# 1.1.0, so `./build.sh --all-release` would have produced
|
||||
# release/dragonx-1.0.3-<platform>/ full of binaries announcing 1.1.0.
|
||||
# Mirrors configure.ac's _CLIENT_VERSION_SUFFIX m4 exactly:
|
||||
# build < 25 -> beta(build+1) build < 50 -> rc(build-24)
|
||||
# build == 50 -> plain release build > 50 -> point release (build-50)
|
||||
_acdef() { sed -n "s/^define(_CLIENT_VERSION_$1, *\([0-9]\{1,\}\))/\1/p" "$SCRIPT_DIR/configure.ac"; }
|
||||
_V_MAJOR="$(_acdef MAJOR)"
|
||||
_V_MINOR="$(_acdef MINOR)"
|
||||
_V_REVISION="$(_acdef REVISION)"
|
||||
_V_BUILD="$(_acdef BUILD)"
|
||||
if [ -z "$_V_MAJOR" ] || [ -z "$_V_MINOR" ] || [ -z "$_V_REVISION" ] || [ -z "$_V_BUILD" ]; then
|
||||
echo "ERROR: could not read the version from $SCRIPT_DIR/configure.ac" >&2
|
||||
echo " refusing to build a release whose directory name would be wrong." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$_V_BUILD" -lt 25 ]; then _V_SUFFIX="$_V_REVISION-beta$((_V_BUILD + 1))"
|
||||
elif [ "$_V_BUILD" -lt 50 ]; then _V_SUFFIX="$_V_REVISION-rc$((_V_BUILD - 24))"
|
||||
elif [ "$_V_BUILD" -eq 50 ]; then _V_SUFFIX="$_V_REVISION"
|
||||
else _V_SUFFIX="$_V_REVISION-$((_V_BUILD - 50))"
|
||||
fi
|
||||
VERSION="$_V_MAJOR.$_V_MINOR.$_V_SUFFIX"
|
||||
|
||||
# Parse release flags
|
||||
BUILD_LINUX_RELEASE=0
|
||||
BUILD_WIN_RELEASE=0
|
||||
|
||||
@@ -2,8 +2,8 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
|
||||
AC_PREREQ([2.60])
|
||||
define(_CLIENT_VERSION_MAJOR, 1)
|
||||
dnl Must be kept in sync with src/clientversion.h , ugh!
|
||||
define(_CLIENT_VERSION_MINOR, 0)
|
||||
define(_CLIENT_VERSION_REVISION, 3)
|
||||
define(_CLIENT_VERSION_MINOR, 2)
|
||||
define(_CLIENT_VERSION_REVISION, 0)
|
||||
define(_CLIENT_VERSION_BUILD, 50)
|
||||
define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50)))
|
||||
define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1)))
|
||||
|
||||
@@ -1,3 +1,61 @@
|
||||
dragonx (1.2.0) stable; urgency=medium
|
||||
|
||||
* Auto-shield matured coinbase into a wallet-owned Sapling address on a block
|
||||
interval. The destination is derived from the HD seed at m/32'/coin'/i' and
|
||||
is the lowest index inside -mnemonicsaplinggap, so a bare seed-phrase restore
|
||||
re-derives it; auto-shielding refuses to run rather than send anywhere a
|
||||
restore would not find. Enabled only when the seed's provenance is known to be
|
||||
recoverable, so upgraded wallets stay opted out until the operator says
|
||||
otherwise.
|
||||
* Create new wallets from a BIP39 seed phrase by default, byte-compatible with
|
||||
SilentDragonXLite. z_exportmnemonic returns the phrase; -mnemonic restores
|
||||
from it.
|
||||
* New RPC z_autoshieldstatus reports whether auto-shielding is on, the resolved
|
||||
destination, the HD seed's provenance, and why it is off when it is off.
|
||||
* Bound each auto-shield round to 400 inputs and correct the transaction size
|
||||
estimate to account for all three Sapling output descriptions, and lock the
|
||||
selected coins for the duration of proof building so a concurrent
|
||||
z_shieldcoinbase or z_sendmany cannot select them too.
|
||||
* Repair, rather than reject, an hdchain record truncated by an older wallet
|
||||
build. Previously one address generated under a pre-1.1.0 binary left the
|
||||
wallet unopenable with "Wallet corrupted"; the record is now completed and
|
||||
rewritten, and the error text names the seed-phrase remedy when it genuinely
|
||||
cannot be recovered.
|
||||
* Clear a stale sweep flag that could otherwise leave sweeping, consolidation
|
||||
and auto-shielding permanently disabled together, and stop the async queue
|
||||
silently discarding operations at shutdown while reporting success.
|
||||
* Peer discovery: seed from the round-robin DNS record seed.dragonx.is rather
|
||||
than three hostnames that no longer resolve, count DNS-seeded addresses so
|
||||
the fixed-seed fallback is no longer triggered spuriously, give every entry
|
||||
in the compiled-in seed list its P2P port, and stop non-DRAGONX smart chains
|
||||
inheriting DragonX's seeds. Adds two seed nodes in new regions.
|
||||
* Derive the release version from configure.ac in build.sh instead of a
|
||||
hardcoded literal, and document container-based release builds in
|
||||
doc/build-containers.md.
|
||||
|
||||
-- DragonX Developers <dev@dragonx.is> Tue, 25 Aug 2026 19:45:00 +0000
|
||||
|
||||
dragonx (1.1.0) stable; urgency=medium
|
||||
|
||||
* Extend DRAGONX checkpoints to height 3,226,000, enabling the existing
|
||||
RandomX skip below the last in-index checkpoint. Blocks requiring a
|
||||
RandomX verify drop from ~391,000 to ~5,500; measured 91.3 blk/s below
|
||||
the checkpoint vs 4.7 above on identical hardware.
|
||||
* Verify each block's RandomX solution once per connect instead of twice,
|
||||
and only when CheckBlockHeader actually performed the verification
|
||||
(future-timestamped blocks keep their check).
|
||||
* Build RandomX with ARCH=default rather than ARCH=native, so binaries are
|
||||
not silently tuned to the build machine's CPU. A Zen4 build previously
|
||||
emitted AVX-512 into librandomx.a, which cannot run on the seed fleet or
|
||||
on older user CPUs.
|
||||
* Remove a hardcoded git commit id that made builds without git metadata
|
||||
report themselves as an unrelated 2018 commit.
|
||||
* Includes the accumulated dev-branch work since 1.0.2: parallel RandomX
|
||||
pre-verification, adaptive -dbcache, Sapling witness desync fix, BIP39
|
||||
seed phrases, chain-level Sapling turnstile, and the audit fixes.
|
||||
|
||||
-- DragonX Developers <dev@dragonx.is> Sun, 23 Aug 2026 10:41:53 -0500
|
||||
|
||||
dragonx (1.0.3) stable; urgency=medium
|
||||
|
||||
* IBD/sync speedups: parallel RandomX pre-verification, adaptive -dbcache, P2P download fixes
|
||||
@@ -8,6 +66,22 @@ dragonx (1.0.3) stable; urgency=medium
|
||||
|
||||
-- DragonX <dan-s-dev@proton.me> Tue, 07 Jul 2026 05:49:59 +0200
|
||||
|
||||
dragonx (1.0.2) stable; urgency=medium
|
||||
|
||||
* Fix Sapling pool persistence, and report the block subsidy and total fees
|
||||
in the getblock RPC
|
||||
* Fix the Windows bootstrap script and add a mirror fallback
|
||||
* Fix the Windows build
|
||||
* Fix the macOS Sequoia build with GCC 15
|
||||
|
||||
-- DragonX <dan-s-dev@proton.me> Thu, 19 Mar 2026 10:09:18 -0500
|
||||
|
||||
dragonx (1.0.1) stable; urgency=medium
|
||||
|
||||
* Fix a fresh-sync failure at the difficulty reset height 2838976
|
||||
|
||||
-- DragonX <dan-s-dev@proton.me> Thu, 12 Mar 2026 01:25:21 -0500
|
||||
|
||||
dragonx (1.0.0) stable; urgency=medium
|
||||
|
||||
* Initial release of DragonX, forked from Hush Full Node
|
||||
@@ -15,7 +89,7 @@ dragonx (1.0.0) stable; urgency=medium
|
||||
* RandomX proof-of-work, 36-second block time, fully shielded transactions
|
||||
* New binary names: dragonxd, dragonx-cli, dragonx-tx
|
||||
|
||||
-- DragonX <dan-s-dev@proton.me> Mon, 03 Mar 2026 00:00:00 +0000
|
||||
-- DragonX <dan-s-dev@proton.me> Tue, 10 Mar 2026 19:39:55 -0500
|
||||
|
||||
hush (3.10.5) stable; urgency=medium
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
DEBIAN/manpages/dragonx-cli.1
|
||||
DEBIAN/manpages/dragonx-tx.1
|
||||
DEBIAN/manpages/dragonxd.1
|
||||
doc/man/dragonxd.1
|
||||
doc/man/dragonx-cli.1
|
||||
doc/man/dragonx-tx.1
|
||||
|
||||
@@ -177,7 +177,7 @@ def main():
|
||||
g.write('\n')
|
||||
with open(os.path.join(indir,'nodes_test.txt'), 'r', encoding="utf8") as f:
|
||||
process_nodes(g, f, 'chainparams_seed_test')
|
||||
g.write('#endif // HUSH_CHAINPARAMSSEEDS_H\n')
|
||||
g.write('#endif // DRAGONX_CHAINPARAMSSEEDS_H\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
# generate-seeds.py expects <ip>:<port> (see its docstring). Without the port it
|
||||
# emits 0, and every fixed seed becomes unconnectable -- which is what shipped:
|
||||
# the whole chainparams_seed_main array carried 0x00,0x00 as the port.
|
||||
# node1.dragonx.is
|
||||
212.56.41.63
|
||||
212.56.41.63:21768
|
||||
|
||||
# node2.dragonx.is
|
||||
194.140.198.176
|
||||
194.140.198.176:21768
|
||||
|
||||
# node3.dragonx.is
|
||||
212.56.41.47
|
||||
212.56.41.47:21768
|
||||
|
||||
# node4.dragonx.is
|
||||
144.126.147.165
|
||||
144.126.147.165:21768
|
||||
|
||||
# node5.dragonx.is
|
||||
176.126.87.241
|
||||
176.126.87.241:21768
|
||||
|
||||
# node6.dragonx.is
|
||||
13.140.58.251:21768
|
||||
|
||||
# node7.dragonx.is
|
||||
5.104.83.100:21768
|
||||
|
||||
223
doc/build-containers.md
Normal file
223
doc/build-containers.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# Building release binaries in containers
|
||||
|
||||
Release binaries must be built in a container based on an **old** Linux distribution.
|
||||
This document is written to be executed, by a person or an agent, on a machine that
|
||||
has nothing set up yet.
|
||||
|
||||
---
|
||||
|
||||
## 1. Why this exists
|
||||
|
||||
glibc compatibility runs one way only. A binary linked against glibc 2.35 demands
|
||||
symbol versions that glibc 2.31 does not have, and refuses to start. A binary linked
|
||||
against glibc 2.29 runs on 2.29, 2.31 and 2.35 alike.
|
||||
|
||||
Measured on the actual fleet, 2026-08-25:
|
||||
|
||||
| binary | max GLIBC required | runs on |
|
||||
|---|---|---|
|
||||
| what all four 20.04 seeds run today (`v1.0.3-d159e7208`) | `GLIBC_2.29` | 18.04, 20.04, 22.04 |
|
||||
| anything built on seed 176 today (Ubuntu 22.04) | `GLIBC_2.34` | 22.04 only |
|
||||
|
||||
The second binary will not start on four of our own five seeds. The loader reports:
|
||||
|
||||
```
|
||||
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found
|
||||
/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.30' not found
|
||||
```
|
||||
|
||||
Nothing new is being *called*. glibc 2.34 merged libpthread and libdl into libc and
|
||||
re-versioned every `pthread_*`, `dlsym` and `dladdr` symbol; 2.33 replaced the old
|
||||
`__xstat` inlines with real `stat`/`fstat`/`lstat64`. All of those functions exist in
|
||||
2.31 under older tags. Building against older headers is the entire fix.
|
||||
|
||||
**Do not try to solve this with full static linking.** The daemon calls `getaddrinfo`,
|
||||
`gethostbyname` and `getnameinfo`, and it must resolve `node1..node5.dragonx.is`, which
|
||||
are hard-coded and injected into `-addnode` on every node. Under a fully static glibc
|
||||
binary those go through NSS, which `dlopen`s `libnss_dns.so.2` at run time — it either
|
||||
fails or silently requires the target to have the same glibc you linked against, which
|
||||
defeats the purpose.
|
||||
|
||||
---
|
||||
|
||||
## 2. What already exists in this repo
|
||||
|
||||
Do not write a new build system. Two pieces are already here:
|
||||
|
||||
- **`Dockerfile.compat`** — an Ubuntu base image that installs the toolchain, copies the
|
||||
tree, **deletes any host-built `depends/` and object files**, runs `./util/build.sh`,
|
||||
and strips the three binaries.
|
||||
- **`./build.sh --linux-compat`** — builds that image, creates a throwaway container,
|
||||
copies `dragonxd`, `dragonx-cli` and `dragonx-tx` out into
|
||||
`release/dragonx-<version>-linux-amd64-ubuntu2004/`, adds `bootstrap-dragonx.sh`,
|
||||
`asmap.dat` and the two sapling params, fixes ownership, and prints the binary's
|
||||
maximum required GLIBC version.
|
||||
|
||||
The base image is parameterised via `ARG BASE_IMAGE` (default `ubuntu:20.04`), so the
|
||||
same Dockerfile can target several glibc floors.
|
||||
|
||||
---
|
||||
|
||||
## 3. Prerequisites
|
||||
|
||||
Docker (the scripted path uses `docker` specifically; podman works for the manual path
|
||||
if you alias or substitute it).
|
||||
|
||||
```sh
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y docker.io git
|
||||
sudo usermod -aG docker "$USER" # then log out and back in, or every command needs sudo
|
||||
```
|
||||
|
||||
Budget, measured on a 4-core box:
|
||||
|
||||
| resource | needs |
|
||||
|---|---|
|
||||
| disk | ~15 GB free (the `depends/` tree alone is ~1.6 GB per target, plus image layers) |
|
||||
| RAM | 4 GB minimum, 8 GB comfortable — the link step is the peak |
|
||||
| time | **1–2 hours per base image on first build.** `depends/` builds boost, BDB, wolfssl, libevent, libsodium, libcurl and rust from source. Later builds reuse Docker layer cache unless the tree changed. |
|
||||
|
||||
`depends/` downloads and builds its own rust toolchain, so the host's rust (or absence
|
||||
of it) is irrelevant.
|
||||
|
||||
---
|
||||
|
||||
## 4. Build one target
|
||||
|
||||
```sh
|
||||
git clone https://git.dragonx.is/DragonX/dragonx
|
||||
cd dragonx
|
||||
git checkout <the tag or branch you are releasing>
|
||||
|
||||
./build.sh --linux-compat
|
||||
```
|
||||
|
||||
Output lands in `release/dragonx-<version>-linux-amd64-ubuntu2004/` and the script
|
||||
prints the max GLIBC at the end. `<version>` is read from `configure.ac`, not
|
||||
hardcoded, so it always matches what the binaries report.
|
||||
|
||||
---
|
||||
|
||||
## 5. Build several targets
|
||||
|
||||
```sh
|
||||
for BASE in ubuntu:18.04 ubuntu:20.04 ubuntu:22.04; do
|
||||
TAG="dragonx-compat-${BASE#ubuntu:}"
|
||||
TAG="${TAG//./}"
|
||||
docker build --build-arg "BASE_IMAGE=$BASE" -f Dockerfile.compat -t "$TAG" .
|
||||
|
||||
OUT="release/dragonx-$(grep -oP 'define\(_CLIENT_VERSION_MAJOR, \K[0-9]+' configure.ac).$(grep -oP 'define\(_CLIENT_VERSION_MINOR, \K[0-9]+' configure.ac).$(grep -oP 'define\(_CLIENT_VERSION_REVISION, \K[0-9]+' configure.ac)-linux-amd64-${BASE#ubuntu:}"
|
||||
mkdir -p "$OUT"
|
||||
CID=$(docker create "$TAG")
|
||||
for b in dragonxd dragonx-cli dragonx-tx; do docker cp "$CID:/build/src/$b" "$OUT/$b"; done
|
||||
docker rm "$CID" >/dev/null
|
||||
cp util/bootstrap-dragonx.sh contrib/asmap/asmap.dat sapling-output.params sapling-spend.params "$OUT/" 2>/dev/null || true
|
||||
done
|
||||
```
|
||||
|
||||
### Which base to choose
|
||||
|
||||
| base | glibc it provides | default GCC | verdict |
|
||||
|---|---|---|---|
|
||||
| `ubuntu:18.04` | 2.27 | 7 | **Verify before relying on it.** The tree is built with `-std=c++17`; GCC 7's C++17 support is incomplete and its cmake (3.10) may be too old for RandomX. Attempt only if you need to reach 18.04 users, and treat a successful build as the proof. |
|
||||
| `ubuntu:20.04` | 2.31 | 9 | **Recommended floor.** GCC 9 covers C++17 fully. Evidence it works: the binary the fleet runs today requires only `GLIBC_2.29`, i.e. the code touches nothing newer, so a 20.04 build reaches 18.04 machines anyway. |
|
||||
| `ubuntu:22.04` | 2.35 | 11 | **Do not ship this.** It is what we already have and what excludes four of our own seeds. Useful only for development. |
|
||||
|
||||
Ubuntu 20.04 left standard support in April 2025, which is precisely why it belongs in
|
||||
a container on a patched host rather than on a build box someone has to maintain.
|
||||
|
||||
---
|
||||
|
||||
## 6. Verify — this step is not optional
|
||||
|
||||
A build that silently targets the wrong glibc looks completely normal until a user
|
||||
reports that nothing starts.
|
||||
|
||||
```sh
|
||||
BIN=release/dragonx-<version>-linux-amd64-ubuntu2004/dragonxd
|
||||
|
||||
# The ceiling. Must be <= the glibc of the OLDEST system you intend to support.
|
||||
objdump -p "$BIN" | grep -oE 'GLIBC_2\.[0-9]+' | sort -t. -k2 -n | tail -1
|
||||
objdump -p "$BIN" | grep -oE 'GLIBCXX_3\.4\.[0-9]+' | sort -t. -k3 -n | tail -1
|
||||
|
||||
# If the ceiling is too high, this names the symbols responsible.
|
||||
readelf --dyn-syms --wide "$BIN" | grep -E '@GLIBC_2\.(3[2-9])'
|
||||
```
|
||||
|
||||
Expected for a 20.04 build: `GLIBC_2.29` or lower, `GLIBCXX_3.4.26` or lower.
|
||||
|
||||
Then actually run it somewhere old. A ceiling check proves the loader will resolve the
|
||||
symbols; it does not prove the binary works. `./dragonxd --version` on a real 20.04 box
|
||||
is a ten-second confirmation.
|
||||
|
||||
---
|
||||
|
||||
## 7. Traps
|
||||
|
||||
Each of these has cost real time.
|
||||
|
||||
**`ETXTBSY` when installing over a running daemon.** `cp` onto the binary fails with
|
||||
"Text file busy" *even after the process has exited* — `pgrep` returning nothing is not
|
||||
sufficient, the kernel still holds the text mapping. Stage into the same directory and
|
||||
`mv` (rename is not blocked), allow ~10 s to settle, and **sha256-verify the installed
|
||||
file before starting it**. A failed copy that goes unnoticed leaves the old binary
|
||||
running and looks like a successful deploy.
|
||||
|
||||
**Never touch `configure.ac` in a configured tree.** Even `cp`-ing back a byte-identical
|
||||
copy updates its mtime, which makes `make` regenerate `aclocal.m4` and `configure` and
|
||||
then re-run `configure`, which fails with `libdb_cxx headers missing` because the
|
||||
depends prefix is not on the command line. If it happens: confirm
|
||||
`git diff --quiet HEAD -- configure.ac`, then restore mtime order oldest-to-newest with
|
||||
one-second gaps — `configure.ac`/`Makefile.am`, then `aclocal.m4`, then
|
||||
`configure`/`Makefile.in`, then `config.status`, then `Makefile`. Inside a container
|
||||
this cannot happen, which is one more reason to build there.
|
||||
|
||||
**Never blind-`touch` a path that might not exist.** `touch src/config/hush-config.h`
|
||||
silently *creates* an empty stray file; the real header is `bitcoin-config.h`. Check
|
||||
`git status` after any timestamp surgery.
|
||||
|
||||
**RandomX must be built with `ARCH=default`.** `util/build.sh` already passes it and the
|
||||
comment there explains why: `ARCH=native` tunes to the build machine, and a build on an
|
||||
AVX-512 host emitted 746 `zmm` instructions into `librandomx.a`, which `SIGILL`s on the
|
||||
entire fleet. If you ever invoke cmake by hand, pass `-DARCH=default`.
|
||||
|
||||
**Strip before distributing.** Unstripped is ~220 MB, stripped ~16 MB. `Dockerfile.compat`
|
||||
already strips inside the container.
|
||||
|
||||
**`util/build-win.sh` discards every argument.** There is no `"$@"` handling in it, so
|
||||
`-j$(nproc)` and `--disable-tests` are dropped on the floor and the Windows build is
|
||||
single-threaded. Expect it to be far slower than you planned.
|
||||
|
||||
**Windows also needs `-Wa,-mbig-obj` and `-DARCH=default`.** Both are in
|
||||
`util/build-win.sh` today. The mingw flag was missing from `dev` for a month; without it
|
||||
the cross-compile fails at link because boost-heavy translation units exceed the
|
||||
PE/COFF section limit. Do not lose it on a re-branch.
|
||||
|
||||
**macOS cannot be containerised.** `util/build-mac.sh` is a native-Mac script, and there
|
||||
is no darwin cross-compile path in `depends/` at all: `hosts/darwin.mk` wants
|
||||
`native_cctools`, which has no package definition, and there is no SDK in the tree. It
|
||||
also hardcodes an Intel Homebrew GCC path, so it produces x86_64 only — no arm64, no
|
||||
universal binary. macOS needs a real Mac.
|
||||
|
||||
**The `contrib/gitian-descriptors/` files are not a build path.** They are unmodified
|
||||
upstream Bitcoin files (`name: "bitcoin-win-0.11"`, suite `trusty`) with zero DragonX
|
||||
content. Ignore them.
|
||||
|
||||
---
|
||||
|
||||
## 8. Handoff checklist
|
||||
|
||||
- [ ] Docker installed, user in the `docker` group, ~15 GB free
|
||||
- [ ] Correct tag or branch checked out, tree clean (`git status`)
|
||||
- [ ] Version in `configure.ac` is the one you intend to release
|
||||
- [ ] `./build.sh --linux-compat` completes
|
||||
- [ ] GLIBC ceiling is **2.31 or lower** (2.29 expected)
|
||||
- [ ] GLIBCXX ceiling is **3.4.28 or lower** (3.4.26 expected)
|
||||
- [ ] `dragonxd --version` runs on a real machine of the oldest supported distro
|
||||
- [ ] Binaries stripped, `release/` contains the bootstrap script, `asmap.dat` and both sapling params
|
||||
- [ ] sha256 recorded for each artifact
|
||||
|
||||
One more thing that is not a build step but belongs in the same conversation: the
|
||||
in-app daemon updater refuses any release without a detached signature
|
||||
(`kDaemonRequireSignature = true`). Publishing checksums alone means no existing user
|
||||
can update in place.
|
||||
@@ -1,9 +1,9 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||
.TH DRAGONX "1" "July 2026" "DragonX RPC client version v1.0.3-95aeaed0c-dirty" "User Commands"
|
||||
.TH DRAGONX-CLI "1" "August 2026" "dragonx-cli v1.2.0" "User Commands"
|
||||
.SH NAME
|
||||
DragonX \- manual page for DragonX RPC client version v1.0.3-95aeaed0c-dirty
|
||||
dragonx-cli \- manual page for dragonx-cli v1.2.0
|
||||
.SH DESCRIPTION
|
||||
DragonX RPC client version v1.0.3\-95aeaed0c\-dirty
|
||||
DragonX RPC client version v1.2.0
|
||||
.PP
|
||||
In order to ensure you are adequately protecting your privacy when using
|
||||
DragonX, please see <https://dragonx.is/security/>.
|
||||
@@ -70,25 +70,22 @@ Timeout in seconds during HTTP requests, or 0 for no timeout. (default:
|
||||
.IP
|
||||
Read extra arguments from standard input, one per line until EOF/Ctrl\-D
|
||||
(recommended for sensitive information such as passphrases)
|
||||
.PP
|
||||
.SH COPYRIGHT
|
||||
|
||||
In order to ensure you are adequately protecting your privacy when using
|
||||
DragonX, please see <https://dragonx.is/security/>.
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2024\-2026 The DragonX Developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2024 Duke Leto and The Hush Developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2020 jl777 and SuperNET developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2018 The Zcash developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2009\-2014 The Bitcoin Core developers
|
||||
.PP
|
||||
|
||||
Copyright (C) 2024-2026 The DragonX Developers
|
||||
|
||||
Copyright (C) 2016-2024 Duke Leto and The Hush Developers
|
||||
|
||||
Copyright (C) 2016-2020 jl777 and SuperNET developers
|
||||
|
||||
Copyright (C) 2016-2018 The Zcash developers
|
||||
|
||||
Copyright (C) 2009-2014 The Bitcoin Core developers
|
||||
|
||||
This is experimental Free Software! Fuck Yeah!!!!!
|
||||
.PP
|
||||
|
||||
Distributed under the GPLv3 software license, see the accompanying file COPYING
|
||||
or <https://www.gnu.org/licenses/gpl\-3.0.en.html>.
|
||||
or <https://www.gnu.org/licenses/gpl-3.0.en.html>.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||
.TH DRAGONX-TX "1" "July 2026" "dragonx-tx v1.0.3-4caf2fc68" "User Commands"
|
||||
.TH DRAGONX-TX "1" "August 2026" "dragonx-tx v1.2.0" "User Commands"
|
||||
.SH NAME
|
||||
dragonx-tx \- DragonX transaction utility
|
||||
dragonx-tx \- manual page for dragonx-tx v1.2.0
|
||||
.SH DESCRIPTION
|
||||
hush\-tx utility version v1.0.3\-4caf2fc68
|
||||
hush\-tx utility version v1.2.0
|
||||
.SS "Usage:"
|
||||
.TP
|
||||
hush\-tx [options] <hex\-tx> [commands]
|
||||
@@ -84,3 +84,22 @@ Load JSON file FILENAME into register NAME
|
||||
set=NAME:JSON\-STRING
|
||||
.IP
|
||||
Set register NAME to given JSON\-STRING
|
||||
.SH COPYRIGHT
|
||||
|
||||
In order to ensure you are adequately protecting your privacy when using
|
||||
DragonX, please see <https://dragonx.is/security/>.
|
||||
|
||||
Copyright (C) 2024-2026 The DragonX Developers
|
||||
|
||||
Copyright (C) 2016-2024 Duke Leto and The Hush Developers
|
||||
|
||||
Copyright (C) 2016-2020 jl777 and SuperNET developers
|
||||
|
||||
Copyright (C) 2016-2018 The Zcash developers
|
||||
|
||||
Copyright (C) 2009-2014 The Bitcoin Core developers
|
||||
|
||||
This is experimental Free Software! Fuck Yeah!!!!!
|
||||
|
||||
Distributed under the GPLv3 software license, see the accompanying file COPYING
|
||||
or <https://www.gnu.org/licenses/gpl-3.0.en.html>.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||
.TH DRAGONX "1" "July 2026" "DragonX Daemon version v1.0.3-4caf2fc68" "User Commands"
|
||||
.TH DRAGONXD "1" "August 2026" "dragonxd v1.2.0" "User Commands"
|
||||
.SH NAME
|
||||
DragonX \- manual page for DragonX Daemon version v1.0.3-4caf2fc68
|
||||
dragonxd \- manual page for dragonxd v1.2.0
|
||||
.SH DESCRIPTION
|
||||
DragonX Daemon version v1.0.3\-4caf2fc68
|
||||
DragonX Daemon version v1.2.0
|
||||
.PP
|
||||
In order to ensure you are adequately protecting your privacy when using
|
||||
DragonX, please see <https://dragonx.is/security/>.
|
||||
@@ -52,7 +52,7 @@ Specify directory to be used when exporting data
|
||||
.HP
|
||||
\fB\-dbcache=\fR<n>
|
||||
.IP
|
||||
Set database cache size in megabytes (4 to 16384). Default: adaptive \-
|
||||
Set database cache size in megabytes (4 to 65536). Default: adaptive \-
|
||||
uses most free RAM to speed up initial block download (far fewer
|
||||
UTXO flushes to disk) and automatically shrinks if other
|
||||
applications need memory, always leaving a reserve free. Setting
|
||||
@@ -88,8 +88,8 @@ leave that many cores free, default: 0)
|
||||
\fB\-randomxverifythreads=\fR<n>
|
||||
.IP
|
||||
Number of threads for parallel RandomX PoW pre\-verification of
|
||||
post\-checkpoint blocks during sync (0 = inline only, max 16,
|
||||
default: same as \fB\-par\fR)
|
||||
post\-checkpoint blocks during network sync; no effect on reindex
|
||||
(0 = inline only, max 16, default: same as \fB\-par\fR)
|
||||
.HP
|
||||
\fB\-pid=\fR<file>
|
||||
.IP
|
||||
@@ -361,15 +361,18 @@ exposes the seed to your shell history and process list.
|
||||
\fB\-mnemonic=\fR<words>
|
||||
.IP
|
||||
Restore/create a fresh/empty wallet from a BIP39 seed phrase, compatible
|
||||
with SilentDragonXLite (English, no passphrase). WARNING: exposes
|
||||
the phrase to your shell history and process list; prefer
|
||||
DRAGONX.conf with tight permissions.
|
||||
with SilentDragonXLite (English, no passphrase; cross\-wallet
|
||||
restore parity is mainnet\-only \fB\-\-\fR testnet/regtest derive a
|
||||
different HD coin_type). WARNING: exposes the phrase to your
|
||||
shell history and process list; prefer DRAGONX.conf with tight
|
||||
permissions.
|
||||
.HP
|
||||
\fB\-usemnemonic\fR
|
||||
.IP
|
||||
Create new wallets from a fresh BIP39 seed phrase so the 24 words can be
|
||||
exported (z_exportmnemonic) and used in SilentDragonXLite
|
||||
(default: 0)
|
||||
exported (z_exportmnemonic) and used in SilentDragonXLite. Set to
|
||||
0 for a raw random seed with no recovery phrase; existing wallets
|
||||
are never changed (default: 1)
|
||||
.HP
|
||||
\fB\-hdtransparentgaplimit=\fR<n>
|
||||
.IP
|
||||
@@ -427,6 +430,33 @@ Enable sweeping to an external wallet (default false)
|
||||
.IP
|
||||
Addresses to exclude from sweeping (default none)
|
||||
.HP
|
||||
\fB\-autoshield\fR
|
||||
.IP
|
||||
Automatically shield matured coinbase (mining rewards) into a
|
||||
seed\-derived wallet z\-address (default: true for wallets created
|
||||
or restored by this software, false when the HD seed provenance
|
||||
is unknown). No\-op when not mining or wallet is locked.
|
||||
.HP
|
||||
\fB\-autoshieldinterval\fR
|
||||
.IP
|
||||
Block interval between automatic coinbase\-shielding rounds (default: 25,
|
||||
min 5)
|
||||
.HP
|
||||
\fB\-autoshieldaddress=\fR<zaddr>
|
||||
.IP
|
||||
Destination Sapling z\-address for auto\-shielded coinbase (default: reuse
|
||||
or create a wallet z\-address). Must be spendable by this wallet.
|
||||
.HP
|
||||
\fB\-autoshieldfee\fR
|
||||
.IP
|
||||
Fee in puposhis for automatic coinbase\-shielding transactions (default:
|
||||
10000)
|
||||
.HP
|
||||
\fB\-autoshieldminutxos\fR
|
||||
.IP
|
||||
Only auto\-shield once at least this many matured coinbase UTXOs exist
|
||||
(default: 1)
|
||||
.HP
|
||||
\fB\-deletetx\fR
|
||||
.IP
|
||||
Enable Old Transaction Deletion
|
||||
@@ -446,7 +476,7 @@ Keep transactions for at least <n> blocks (default: 10000)
|
||||
.HP
|
||||
\fB\-paytxfee=\fR<amt>
|
||||
.IP
|
||||
Fee (in HUSH/kB) to add to transactions you send (default: 0.00)
|
||||
Fee (in DRAGONX/kB) to add to transactions you send (default: 0.00)
|
||||
.HP
|
||||
\fB\-keepnotewitnesscache\fR
|
||||
.IP
|
||||
@@ -485,7 +515,7 @@ mined will become invalid (default: 200)
|
||||
.HP
|
||||
\fB\-maxtxfee=\fR<amt>
|
||||
.IP
|
||||
Maximum total fees (in HUSH) to use in a single wallet transaction;
|
||||
Maximum total fees (in DRAGONX) to use in a single wallet transaction;
|
||||
setting this too low may abort large transactions (default: 0.10)
|
||||
.HP
|
||||
\fB\-upgradewallet\fR
|
||||
@@ -554,8 +584,8 @@ Prepend debug output with timestamp (default: 1)
|
||||
.HP
|
||||
\fB\-minrelaytxfee=\fR<amt>
|
||||
.IP
|
||||
Fees (in HUSH/kB) smaller than this are considered zero fee for relaying
|
||||
(default: 0.000001)
|
||||
Fees (in DRAGONX/kB) smaller than this are considered zero fee for
|
||||
relaying (default: 0.000001)
|
||||
.HP
|
||||
\fB\-printtoconsole\fR
|
||||
.IP
|
||||
@@ -804,25 +834,22 @@ Starting supply, default is 10
|
||||
\fB\-ac_txpow\fR
|
||||
.IP
|
||||
Enforce transaction\-rate limit, default 0
|
||||
.PP
|
||||
.SH COPYRIGHT
|
||||
|
||||
In order to ensure you are adequately protecting your privacy when using
|
||||
DragonX, please see <https://dragonx.is/security/>.
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2024\-2026 The DragonX Developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2024 Duke Leto and The Hush Developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2020 jl777 and SuperNET developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2016\-2018 The Zcash developers
|
||||
.PP
|
||||
.br
|
||||
Copyright \(co 2009\-2014 The Bitcoin Core developers
|
||||
.PP
|
||||
|
||||
Copyright (C) 2024-2026 The DragonX Developers
|
||||
|
||||
Copyright (C) 2016-2024 Duke Leto and The Hush Developers
|
||||
|
||||
Copyright (C) 2016-2020 jl777 and SuperNET developers
|
||||
|
||||
Copyright (C) 2016-2018 The Zcash developers
|
||||
|
||||
Copyright (C) 2009-2014 The Bitcoin Core developers
|
||||
|
||||
This is experimental Free Software! Fuck Yeah!!!!!
|
||||
.PP
|
||||
|
||||
Distributed under the GPLv3 software license, see the accompanying file COPYING
|
||||
or <https://www.gnu.org/licenses/gpl\-3.0.en.html>.
|
||||
or <https://www.gnu.org/licenses/gpl-3.0.en.html>.
|
||||
|
||||
@@ -1,296 +0,0 @@
|
||||
<!-- Creator : groff version 1.23.0 -->
|
||||
<!-- CreationDate: Mon Jan 5 14:12:33 2026 -->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta name="generator" content="groff -Thtml, see www.gnu.org">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
|
||||
<meta name="Content-Style" content="text/css">
|
||||
<style type="text/css">
|
||||
p { margin-top: 0; margin-bottom: 0; vertical-align: top }
|
||||
pre { margin-top: 0; margin-bottom: 0; vertical-align: top }
|
||||
table { margin-top: 0; margin-bottom: 0; vertical-align: top }
|
||||
h1 { text-align: center }
|
||||
</style>
|
||||
<title>HUSH-CLI</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1 align="center">HUSH-CLI</h1>
|
||||
|
||||
<a href="#NAME">NAME</a><br>
|
||||
<a href="#DESCRIPTION">DESCRIPTION</a><br>
|
||||
<a href="#Usage:">Usage:</a><br>
|
||||
<a href="#OPTIONS">OPTIONS</a><br>
|
||||
<a href="#COPYRIGHT">COPYRIGHT</a><br>
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
<h2>NAME
|
||||
<a name="NAME"></a>
|
||||
</h2>
|
||||
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">hush-cli -
|
||||
manual page for hush-cli v3.10.4</p>
|
||||
|
||||
<h2>DESCRIPTION
|
||||
<a name="DESCRIPTION"></a>
|
||||
</h2>
|
||||
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">Hush RPC client
|
||||
version v3.10.4-7e63e2f01-dirty</p>
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">In order to
|
||||
ensure you are adequately protecting your privacy when using
|
||||
Hush, please see <https://hush.is/security/>.</p>
|
||||
|
||||
<h3>Usage:
|
||||
<a name="Usage:"></a>
|
||||
</h3>
|
||||
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">hush-cli
|
||||
[options] <command> [params]</p>
|
||||
|
||||
<p style="margin-left:18%;">Send command to Hush</p>
|
||||
|
||||
<p style="margin-left:9%;">hush-cli [options] help</p>
|
||||
|
||||
<p style="margin-left:18%;">List commands</p>
|
||||
|
||||
<p style="margin-left:9%;">hush-cli [options] help
|
||||
<command></p>
|
||||
|
||||
<p style="margin-left:18%;">Get help for a command</p>
|
||||
|
||||
<h2>OPTIONS
|
||||
<a name="OPTIONS"></a>
|
||||
</h2>
|
||||
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="3%">
|
||||
|
||||
|
||||
<p style="margin-top: 1em">-?</p></td>
|
||||
<td width="88%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">This help
|
||||
message</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="15%">
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-conf=</b><file></p></td>
|
||||
<td width="76%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Specify
|
||||
configuration file (default: HUSH3.conf)</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="18%">
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-datadir=</b><dir></p></td>
|
||||
<td width="73%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Specify data
|
||||
directory (this path cannot use ’˜’)</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="10%">
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-testnet</b></p></td>
|
||||
<td width="81%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Use the test
|
||||
network</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="10%">
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-regtest</b></p></td>
|
||||
<td width="81%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Enter
|
||||
regression test mode, which uses a special chain in which
|
||||
blocks can be solved instantly. This is intended for
|
||||
regression testing tools and app development.</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="20%">
|
||||
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-rpcconnect=</b><ip></p> </td>
|
||||
<td width="71%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Send commands
|
||||
to node running on <ip> (default: 127.0.0.1)</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="19%">
|
||||
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-rpcport=</b><port></p> </td>
|
||||
<td width="72%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Connect to
|
||||
JSON-RPC on <port> (default: 18030 )</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="10%">
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-rpcwait</b></p></td>
|
||||
<td width="81%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Wait for RPC
|
||||
server to start</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="19%">
|
||||
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-rpcuser=</b><user></p> </td>
|
||||
<td width="72%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Username for
|
||||
JSON-RPC connections</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="22%">
|
||||
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-rpcpassword=</b><pw></p> </td>
|
||||
<td width="69%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Password for
|
||||
JSON-RPC connections</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="27%">
|
||||
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-rpcclienttimeout=</b><n></p> </td>
|
||||
<td width="64%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Timeout in
|
||||
seconds during HTTP requests, or 0 for no timeout. (default:
|
||||
900)</p>
|
||||
|
||||
<table width="100%" border="0" rules="none" frame="void"
|
||||
cellspacing="0" cellpadding="0">
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="8%">
|
||||
|
||||
|
||||
<p style="margin-top: 1em"><b>-stdin</b></p></td>
|
||||
<td width="83%">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%; margin-top: 1em">Read extra
|
||||
arguments from standard input, one per line until EOF/Ctrl-D
|
||||
(recommended for sensitive information such as
|
||||
passphrases)</p>
|
||||
|
||||
<h2>COPYRIGHT
|
||||
<a name="COPYRIGHT"></a>
|
||||
</h2>
|
||||
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">In order to
|
||||
ensure you are adequately protecting your privacy when using
|
||||
Hush, please see <https://hush.is/security/>.</p>
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">Copyright (C)
|
||||
2016-2025 Duke Leto and The Hush Developers</p>
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">Copyright (C)
|
||||
2016-2020 jl777 and SuperNET developers</p>
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">Copyright (C)
|
||||
2016-2018 The Zcash developers</p>
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">Copyright (C)
|
||||
2009-2014 The Bitcoin Core developers</p>
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">This is
|
||||
experimental Free Software! Fuck Yeah!!!!!</p>
|
||||
|
||||
<p style="margin-left:9%; margin-top: 1em">Distributed
|
||||
under the GPLv3 software license, see the accompanying file
|
||||
COPYING or
|
||||
<https://www.gnu.org/licenses/gpl-3.0.en.html>.</p>
|
||||
<hr>
|
||||
</body>
|
||||
</html>
|
||||
2681
doc/man/hushd.html
2681
doc/man/hushd.html
File diff suppressed because it is too large
Load Diff
@@ -243,6 +243,7 @@ BITCOIN_CORE_H = \
|
||||
wallet/asyncrpcoperation_mergetoaddress.h \
|
||||
wallet/asyncrpcoperation_saplingconsolidation.h \
|
||||
wallet/asyncrpcoperation_sweep.h \
|
||||
wallet/asyncrpcoperation_autoshieldcoinbase.h \
|
||||
wallet/asyncrpcoperation_sendmany.h \
|
||||
wallet/asyncrpcoperation_shieldcoinbase.h \
|
||||
wallet/crypter.h \
|
||||
@@ -321,6 +322,7 @@ libbitcoin_wallet_a_SOURCES = \
|
||||
wallet/asyncrpcoperation_mergetoaddress.cpp \
|
||||
wallet/asyncrpcoperation_saplingconsolidation.cpp \
|
||||
wallet/asyncrpcoperation_sweep.cpp \
|
||||
wallet/asyncrpcoperation_autoshieldcoinbase.cpp \
|
||||
wallet/asyncrpcoperation_sendmany.cpp \
|
||||
wallet/asyncrpcoperation_shieldcoinbase.cpp \
|
||||
wallet/crypter.cpp \
|
||||
|
||||
@@ -96,18 +96,21 @@ void AsyncRPCQueue::run(size_t workerId) {
|
||||
*
|
||||
* Don't use std::make_shared<AsyncRPCOperation>().
|
||||
*/
|
||||
void AsyncRPCQueue::addOperation(const std::shared_ptr<AsyncRPCOperation> &ptrOperation) {
|
||||
bool AsyncRPCQueue::addOperation(const std::shared_ptr<AsyncRPCOperation> &ptrOperation) {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
|
||||
// Don't add if queue is closed or finishing
|
||||
// Don't add if queue is closed or finishing. Report it: silently dropping the
|
||||
// operation made callers announce work that would never run.
|
||||
// (isClosed/isFinishing read atomics, so calling them under the guard is safe.)
|
||||
if (isClosed() || isFinishing()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
AsyncRPCOperationId id = ptrOperation->getId();
|
||||
operation_map_.emplace(id, ptrOperation);
|
||||
operation_id_queue_.push(id);
|
||||
this->condition_.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,7 +63,12 @@ public:
|
||||
size_t getOperationCount() const;
|
||||
std::shared_ptr<AsyncRPCOperation> getOperationForId(AsyncRPCOperationId) const;
|
||||
std::shared_ptr<AsyncRPCOperation> popOperationForId(AsyncRPCOperationId);
|
||||
void addOperation(const std::shared_ptr<AsyncRPCOperation> &ptrOperation);
|
||||
// Returns false if the queue is closed or finishing, in which case the
|
||||
// operation was NOT queued and will never run. Callers must react: a caller
|
||||
// that ignores this both reports success for work that will not happen and
|
||||
// leaves any state it set for the operation (running flags, coin locks)
|
||||
// stranded for the life of the process.
|
||||
bool addOperation(const std::shared_ptr<AsyncRPCOperation> &ptrOperation);
|
||||
std::vector<AsyncRPCOperationId> getAllOperationIds() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -35,7 +35,7 @@ extern bool fZindex;
|
||||
// These version thresholds control whether nSproutValue/nSaplingValue are
|
||||
// serialized in the block index. They must be <= CLIENT_VERSION or the
|
||||
// values will never be persisted, causing nChainSaplingValue to reset
|
||||
// to 0 after node restart. DragonX CLIENT_VERSION is 1000350 (v1.0.3.50).
|
||||
// to 0 after node restart. DragonX CLIENT_VERSION is 1010050 (v1.1.0.50).
|
||||
static const int SPROUT_VALUE_VERSION = 1000000;
|
||||
static const int SAPLING_VALUE_VERSION = 1000000;
|
||||
// Block-index records written at >= this version store nSaplingValue as a boost::optional
|
||||
|
||||
@@ -181,11 +181,29 @@ public:
|
||||
assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
|
||||
vFixedSeeds.clear();
|
||||
vSeeds.clear();
|
||||
// Hush Official DNS Seeds
|
||||
vSeeds.push_back(CDNSSeedData("node1", "seed1.hush.is"));
|
||||
vSeeds.push_back(CDNSSeedData("node2", "seed2.hush.is"));
|
||||
// Community run DNS Seeds
|
||||
vSeeds.push_back(CDNSSeedData("node3", "dns.leto.net"));
|
||||
// DragonX DNS seeds. These must be names that actually resolve.
|
||||
//
|
||||
// An assetchain INHERITS these: chainparams_commandline() sets the port, magic,
|
||||
// blocktime, upgrade heights and checkpoints, but never touches vSeeds or
|
||||
// vFixedSeeds. DRAGONX therefore ran on Hush's seeds -- seed1.hush.is,
|
||||
// seed2.hush.is and dns.leto.net -- and all three have no A records left, so DNS
|
||||
// seeding silently returned zero addresses on every start. The only bootstrap
|
||||
// path that worked was the node1..node5.dragonx.is -addnode injection in
|
||||
// hush_utils.h, which is almost certainly why that injection exists.
|
||||
//
|
||||
// seed.dragonx.is is a round-robin A record over the seed set, so ONE lookup
|
||||
// returns all of them and the set can be changed -- a node added, a node retired
|
||||
// -- with a DNS edit instead of a release. That is the point of it: the previous
|
||||
// arrangement hardcoded the seed list into the binary twice over (here and in the
|
||||
// -addnode injection in hush_utils.h), so the network's entry points could only
|
||||
// change by shipping a new version.
|
||||
//
|
||||
// node1/node5 stay as static fallbacks in case the round-robin record is ever
|
||||
// mistyped or removed. They are the same hosts, so this is insurance against a
|
||||
// DNS mistake rather than genuine redundancy.
|
||||
vSeeds.push_back(CDNSSeedData("seed", "seed.dragonx.is"));
|
||||
vSeeds.push_back(CDNSSeedData("node1", "node1.dragonx.is"));
|
||||
vSeeds.push_back(CDNSSeedData("node5", "node5.dragonx.is"));
|
||||
|
||||
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,60);
|
||||
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,85);
|
||||
@@ -520,10 +538,24 @@ void *chainparams_commandline() {
|
||||
//}
|
||||
if ( SMART_CHAIN_SYMBOL[0] != 0 )
|
||||
{
|
||||
// A smart chain inherits vSeeds/vFixedSeeds from the base network params,
|
||||
// and nothing below ever touched them. That is how DRAGONX came to run on
|
||||
// Hush's seeds -- seed1.hush.is and friends, long since removed from DNS --
|
||||
// for as long as it did. Seeds are per-chain by nature: an address that
|
||||
// serves one chain is useless to another, and dialling it is at best wasted
|
||||
// effort and at worst a peer that speaks a different protocol.
|
||||
//
|
||||
// DRAGONX keeps the seeds configured in CMainParams (which are its own).
|
||||
// Every other chain starts empty and relies on -addnode/-connect, which is
|
||||
// what an assetchain operator has to configure anyway.
|
||||
if (strcmp(SMART_CHAIN_SYMBOL,"HUSH3") == 0) {
|
||||
ASSETCHAINS_P2PPORT = 18030;
|
||||
}
|
||||
|
||||
if (strncmp(SMART_CHAIN_SYMBOL, "DRAGONX", 7) != 0) {
|
||||
pCurrentParams->ClearSeeds();
|
||||
}
|
||||
|
||||
if ( ASSETCHAINS_BLOCKTIME != 60 )
|
||||
{
|
||||
pCurrentParams->consensus.nMaxFutureBlockTime = 7 * ASSETCHAINS_BLOCKTIME; // 7 blocks
|
||||
@@ -5640,9 +5672,401 @@ void *chainparams_commandline() {
|
||||
(2836000, uint256S("0x00000000004f1a5b9b0fad39c6751db29b99bfcb045181b6077d791ee0cf91f2"))
|
||||
(2837000, uint256S("0x000000000027c61ed8745c18d6b00edec9414e30dd880d92d598a6a0ce0fc238"))
|
||||
(2838000, uint256S("0x00000000010947813b04f02da1166a07ba213369ec83695f4d8a6270c57f1141"))
|
||||
,(int64_t) 1770622731, // time of last checkpointed block
|
||||
(int64_t) 2940000, // total txs
|
||||
(double) 4576 // txs in the last day before block 2838000
|
||||
// Extended 2026-08-21: the table previously ended at 2,838,000, which is 976
|
||||
// blocks BELOW ASSETCHAINS_RANDOMX_VALIDATION (2,838,976), so the RandomX
|
||||
// skip in RandomXValidationRequired() could never fire. Verified against 5
|
||||
// independent full nodes, both directions, all on the active chain.
|
||||
(2839000, uint256S("0x036b5f8f60733ef985d422bbf2367822dd032a9440fd3524420d4ecfa21ba9f6"))
|
||||
(2840000, uint256S("0x0007994f4c5a67c3030e41980e8f28de88f8c0095a5148393e3ae11fb7116071"))
|
||||
(2841000, uint256S("0x0002d6ec728f3a1aa76ffcda9a0bd713328a1d192bc76374cf9d819af755161d"))
|
||||
(2842000, uint256S("0x00002cb9b235571f8bdfe133ac25f12a05cb33a1c7894d4fe3cab0b7a46eb30d"))
|
||||
(2843000, uint256S("0x000007ad1b64a69892768646121a5d675ea425fa5ad1147207c7b5c286ee2e0d"))
|
||||
(2844000, uint256S("0x00000347a2169ad507a954c5529fbbe52ea19c9d0953005bb8964825b4944133"))
|
||||
(2845000, uint256S("0x0000037a9cbecf55c5100c890a83b6fb8c0d4413d86ea0a45f86f0644709fb79"))
|
||||
(2846000, uint256S("0x00000011d9e2ae53be284f718e22461ad28dfd4ee6e1b9ef98412f5b2f5831b1"))
|
||||
(2847000, uint256S("0x000004a70b7f18cec9623e7c5460093264ba00a44e66ac8c0d2551a488216907"))
|
||||
(2848000, uint256S("0x000005dadf5e3f8d4837da9580ca28934d70ff7cb63bc326df5f44f91bbc22fe"))
|
||||
(2849000, uint256S("0x000002f92bdffcf58b8dca4382a66af71e06447045758dab93c39c77c1d9c91e"))
|
||||
(2850000, uint256S("0x0000003db45806f522f1cbf059bab8ddb1a428bcc9063bced294c7bf0289a3c3"))
|
||||
(2851000, uint256S("0x000005352fb09271bb1f804cd55c57cabefcfbf34dad29d0209f16c98903a836"))
|
||||
(2852000, uint256S("0x00000123edae432f080e11dcf7a0718e9f144f571746b0ca902a2e2f81eecccc"))
|
||||
(2853000, uint256S("0x000006ae53f9318417fc4df6d3672f2a88a3f67a7b3e77d9f833cc23778e023b"))
|
||||
(2854000, uint256S("0x000004715fdde7854b5b227fb5cc1c4cf006935fd80ce8390b0178e4a7c6367c"))
|
||||
(2855000, uint256S("0x000001b53c3d0689ae4df1ad2b5bbd5ae61b2a392b2abc25cb8d866bff4cafe9"))
|
||||
(2856000, uint256S("0x00000011f6bb05a702ffc936985dabbbb983a771b97a6a518798781325adc400"))
|
||||
(2857000, uint256S("0x0000034dc8e9de8530491ae750fca3d7ea5b8992165a13e16aa90a35b690fb53"))
|
||||
(2858000, uint256S("0x0000002ba3bc10f84ebaab3713c58016ce5320b0a68efafc542aaa954d6d2d05"))
|
||||
(2859000, uint256S("0x000000b2740c0b463d424bc5630ebb80fc01823c701859bd4a0b92161880f11d"))
|
||||
(2860000, uint256S("0x0000037007777311e559e889930c7590814e28f0bd3794796658b6cf588a9c19"))
|
||||
(2861000, uint256S("0x000002f1fb62662b6d62addea67d78e6dafa3ec0e50d8098d6f593434022b4e3"))
|
||||
(2862000, uint256S("0x0000056b00954317d9e2518bf462e839ba8cfbd2c522e307414b75869ec1483b"))
|
||||
(2863000, uint256S("0x00000444ea6f3ff4bdaa22f7d9fddd1001530b5d96172799560ceca2cb7c9936"))
|
||||
(2864000, uint256S("0x00000379a4d7ed4096c2715f05c154a6c18f1322eae69f76c0566f88b6372f6c"))
|
||||
(2865000, uint256S("0x000004a98a372678405acceb0fe311a10fd6a57fd512b057675546b687cb81c4"))
|
||||
(2866000, uint256S("0x000000301204227ce755e160607d720619b0f06e12664cdae92abcf2dc500ef7"))
|
||||
(2867000, uint256S("0x00000229bdbc6f916558d279de61cad703fa4842041d373cfe19e9c6f05e5d0e"))
|
||||
(2868000, uint256S("0x000001511aa190c24e92a4149741038221fd6dd794680ae8972dea6ab7d73a14"))
|
||||
(2869000, uint256S("0x000001209e63f22310be9844155012a7736a8134028ce2f1b536caa95a87455a"))
|
||||
(2870000, uint256S("0x000001d04778f2526ee1427709e733843e18decf20ee71d482271f04152737ac"))
|
||||
(2871000, uint256S("0x000000e9bcee55b40cf6b5105c707d48e2fdd852c38ab2d9994203de560e6c22"))
|
||||
(2872000, uint256S("0x0000020f46c3732afe95b72159ea059a7fbe443e2a9978a951321ab77608ecba"))
|
||||
(2873000, uint256S("0x000000be45b4ec2b826ed385e13602a4034857736278722455128e134c8ca072"))
|
||||
(2874000, uint256S("0x000002605ca5037e8a5cdc19c0c418fdb0b922c3eec713aa0357f0077ddc0173"))
|
||||
(2875000, uint256S("0x0000028d16a1dcfa320bb247e2fbd31c318df0e9f0362b177f56f0cd193b79ca"))
|
||||
(2876000, uint256S("0x0000000289fccbfdd88bb3bb1f22c5c5d4fbb16690922fd1a2ba9002c12c4df1"))
|
||||
(2877000, uint256S("0x000000a4625231d51e8c65eeb8c13352e50aba0f570d4497849fef94434f2cd6"))
|
||||
(2878000, uint256S("0x000000080fdf691ba03c1ade593abd758c9683452aa195121029e44614daeff4"))
|
||||
(2879000, uint256S("0x000000956fec52943b4577e2959dede6411997a2720965bfb615b6bdd8fbfe3a"))
|
||||
(2880000, uint256S("0x000002799452afc6905050bf9f1e0688b644b118c78051836e19e3d959a62c6e"))
|
||||
(2881000, uint256S("0x000000a55304c9b02690aa867ba4b775887384a323e71ddb9486234aab7337ae"))
|
||||
(2882000, uint256S("0x000000770d3b0e3eef93d5d652c236b7ac993f102984161bad8fff57d4b97f87"))
|
||||
(2883000, uint256S("0x000000f0cd04f12eb06e250f1f81556310d2d7607a8aa7b56dd9bc8aa0f6a7ab"))
|
||||
(2884000, uint256S("0x0000001701df19e9b84d616b6b9e38c2b0d040cf53f8b5f300d7d62a0da705d8"))
|
||||
(2885000, uint256S("0x000000d08bc242a49321452104c7797a03e1c62206a9549e801f60a120374359"))
|
||||
(2886000, uint256S("0x0000005b2559414fbd1150b6c981eb33b79b09b99cf741c2f085bb7856779e4f"))
|
||||
(2887000, uint256S("0x000000f806f6b02d5b1a9c0fbeccb8c8590b368f577f0171fa309db1927b9731"))
|
||||
(2888000, uint256S("0x0000015e75de0201216b1c0761a0193f3590d4b980d7e2685dd8ae17ce2e6b59"))
|
||||
(2889000, uint256S("0x0000017c84d2d3187ebc80680087af61ba582c4cd34fb171c174aa5f2e689632"))
|
||||
(2890000, uint256S("0x0000013c9ed9f4eaa71e4d8da5c986c6d031e0907afd134e8f1268b899141a16"))
|
||||
(2891000, uint256S("0x0000018144f06b18c18d26863a57d157ede9137876334afab3641647e13b94b8"))
|
||||
(2892000, uint256S("0x000000d6605f31785f1a1f1afe6058fc8d438b67017e88c8c13eb438808af7c8"))
|
||||
(2893000, uint256S("0x0000001fbcaf6af29a76c408290d9169a58ea696473c7379cd5c675ab80069dc"))
|
||||
(2894000, uint256S("0x000000173a49f8d72af0d6377c8e219e8f7c6f16fd193414bd2252a2b0e6b17f"))
|
||||
(2895000, uint256S("0x0000003100994bacedd566893497bcb50aa4533a2a2c8199b9390eb9535945c7"))
|
||||
(2896000, uint256S("0x000000b9b69f0dcd15d8b6f44502bef17e92c595d6564d729490d40021c82dc0"))
|
||||
(2897000, uint256S("0x000001126a35ef0fbd43ac5450efe13750de16bb86bc1d735008f2ba6ddf202f"))
|
||||
(2898000, uint256S("0x000001105d958e9dd8208bf5ddf552a3ee175e00ed64175e4260bf227d7038e3"))
|
||||
(2899000, uint256S("0x0000012bfbf4516bdf5ccbdb420d02a2abdfced13e00729bfa6504f113ace2a7"))
|
||||
(2900000, uint256S("0x000001ad94f932e03125a5cb8b97d65d8456cd5c5ac6c0d23a1e30dc66b722ab"))
|
||||
(2901000, uint256S("0x0000015029fd0001c47219ba9eb923e8c19dc3b6bbb8986797fdcb796ba5e7d6"))
|
||||
(2902000, uint256S("0x000001a9deda168129dcfd63eb8cc147e3278a4364dfcc7a55461598622bb2b9"))
|
||||
(2903000, uint256S("0x0000010c3e62bbe5e083e0412f13d01c17aadfaa3bf45744732737e123779b41"))
|
||||
(2904000, uint256S("0x00000108897e8acc0f3d0ef3e4f083768a8831669ff41505c6a9bb2e34bae7a5"))
|
||||
(2905000, uint256S("0x0000025de11376fdc16dbfd98b7cecbf07f9b134b7baba9bb5aa65f4ea88baf1"))
|
||||
(2906000, uint256S("0x000001685929800faea4c5d443eca9d8a45b441098a797f30360ad5fb6a40afc"))
|
||||
(2907000, uint256S("0x00000181dbf2036c3d3e13d251b3398ff7b4b36073babffd207b7bd7234224be"))
|
||||
(2908000, uint256S("0x0000014d9f26e7993aae49141cf86940844dfcd3315469d4a89fa925695651fa"))
|
||||
(2909000, uint256S("0x000000e1f57f68602d01fa47f8c61f02d3448158429e064dce3b693298645360"))
|
||||
(2910000, uint256S("0x0000000bff875f2e5dee11daff9357824dab610b81a38f8d24bebf4590bf7925"))
|
||||
(2911000, uint256S("0x0000016d24cfc8b99f25e66324ed9c221fe5ba67ab64d410796dc52b510f18b3"))
|
||||
(2912000, uint256S("0x00000153cd34e3d692ff6913b74bf604735b20e5b8fcf4b30fe2be08b4d9a9ad"))
|
||||
(2913000, uint256S("0x00000001b5fa024c9e0af58b0e6fe95fbd80c9ed1f0905d1db9c511f64d8de4a"))
|
||||
(2914000, uint256S("0x000000edf84a56f70de7e9ab444e9940bc1c5607dff2e5efcf6b7259bcd5bd53"))
|
||||
(2915000, uint256S("0x0000023faea5d5354dd535323d7cad9ee5246b060be4e78bddbb15aece65d6af"))
|
||||
(2916000, uint256S("0x000001242376ee75e582ab05898533a1dc0a48aa3a661ce3bf733b30c2d3eb8b"))
|
||||
(2917000, uint256S("0x000001ee27b35fc196cba13fee45d75e3eddad3f1d40610045d0f445c579a6d3"))
|
||||
(2918000, uint256S("0x000000f2749ba8b02fd0f008c7bbc084660274af778e03a17784413c84704185"))
|
||||
(2919000, uint256S("0x00000189484712fa646fff55d24adc7c312f300b9b8d1c4b0b755779cb0e7794"))
|
||||
(2920000, uint256S("0x0000012ac689d379a42028bfe52ca7b8ffe203202b525c1b0271ee2b35ad6402"))
|
||||
(2921000, uint256S("0x00000041006270f03bbb9e37ede42e75e32e257cac45696f28d225a4b43e6777"))
|
||||
(2922000, uint256S("0x0000012a7cd291103fc118e49a040c24a08ab1ee04745b2b0f2ea6b2f81e0cca"))
|
||||
(2923000, uint256S("0x000002d505a407c118df5ed44edae441c0bdc228559ae5a74c5fc3069b92f446"))
|
||||
(2924000, uint256S("0x000001cffdd7e67d52ba4218752fbf45dfeb9d9c595e00399517a99daecadf69"))
|
||||
(2925000, uint256S("0x0000011d18edd59d9af62f5a028bc79ead29dee6f8c88e5f7dd87109588cbecb"))
|
||||
(2926000, uint256S("0x0000015e595dfdb4b59126c9d867f148a7d921d38711bb1533324a52757a4272"))
|
||||
(2927000, uint256S("0x00000096eb782ea4ba9e047359b02f5848cd6799877c7bc5835cb4bbb299b14f"))
|
||||
(2928000, uint256S("0x000001c190235fb344abf38fc1dcaa75a3a9c55b8b5f5dd199b1f95cf8d201ed"))
|
||||
(2929000, uint256S("0x000000c58a7468d05737541fa5df2c1aba6d310fc5c03303e8d72b13d3cb277f"))
|
||||
(2930000, uint256S("0x00000169380c6a08fb48b379a5ca5d866f18fd9d453a13d6e0b8cf3348f75df1"))
|
||||
(2931000, uint256S("0x0000019761664eb4d775090c4e24505b1add18096cf42ff856715afa0d1a3e1c"))
|
||||
(2932000, uint256S("0x0000009f88c63b6c57d1a0b09ab2540eb5f09982234430d0630746991db4e2fb"))
|
||||
(2933000, uint256S("0x000001829533eec65e9241b1b63742e1359cf55198f089c14f183effe76e8629"))
|
||||
(2934000, uint256S("0x000000a4040f326a6b4c1fb64a6c3df87b4e9695a944bbac713fb0cfecd8d551"))
|
||||
(2935000, uint256S("0x000001e58d485a5400e800643db600eb6df312d2832054bac1785207212ca059"))
|
||||
(2936000, uint256S("0x000001d1bf65e88819f93e9fae9d7d682ac18708756b2f1b7d5d2c7779384942"))
|
||||
(2937000, uint256S("0x000001df066b347d3037a0ff5769bce7a7832591775116847e52cfece1725c62"))
|
||||
(2938000, uint256S("0x00000029d4607a9ef85fdfefd8427f6d3ba8585d3e96edf82487c6593fb4f22e"))
|
||||
(2939000, uint256S("0x000001c21ec8f18b48b6a05817b133630d60c9880bc3be26610f1e3d9dd6a8b0"))
|
||||
(2940000, uint256S("0x000001c936816ab5875198fe942e3a33bbb030ae8a5defb457e80d01246a5a71"))
|
||||
(2941000, uint256S("0x0000016ba0563ce8e727c92689b0adf945071d95a6adcce7f65f5f4507762a94"))
|
||||
(2942000, uint256S("0x00000118035772133444f5facdae2a84b939b12dae23a9658cd39684d4548908"))
|
||||
(2943000, uint256S("0x000000fa1fbab3a011da37bbe13d2eda64496e69a531705720cb49d3cce8e714"))
|
||||
(2944000, uint256S("0x0000018a1c728b6e2c6fa6e53ae329810134cceb1203795601cb5e196eba0525"))
|
||||
(2945000, uint256S("0x000001abab339ba3e21bbc3c65d684f9da927abf65fd0a9308f116b76cf0888e"))
|
||||
(2946000, uint256S("0x000000532f16a10c6e51877041ba94c946c15a607e4e4c1c654e7e810f95266b"))
|
||||
(2947000, uint256S("0x00000020c9d00eb7d964325826f2d7d4313b4d24aeac9da2b96951a70ec8cd1a"))
|
||||
(2948000, uint256S("0x00000189c6bd01c4d8a912dc6f68e59415033e9d99f20a6f8ab895d50af65a72"))
|
||||
(2949000, uint256S("0x00000222769cdcb49042f93ba8e449292807a2d998123d30d3508c3759c82a93"))
|
||||
(2950000, uint256S("0x00000116cbd21afdd1da358c3953de242eb4d2710540338e3f92fa66e768420e"))
|
||||
(2951000, uint256S("0x000000bc86ed75d1e906cf75e7b471a4ffe31ee4ca13a8e69c1c7a4fef3b4069"))
|
||||
(2952000, uint256S("0x0000020e923c424ae4769114c453a3e31d3215b355703daf631c0afa1dfaab20"))
|
||||
(2953000, uint256S("0x00000113603a84890a04ce15deecfe025e26bf770a2ae8ec207774851ec851c6"))
|
||||
(2954000, uint256S("0x000001f3ccfa445ef2603c3c3ed7d41582aeaf3e3da4fdff4587248a586e99b1"))
|
||||
(2955000, uint256S("0x0000036a1b2e6ac72888d4b97002944f70b4285fe4ac10d970a96ae6e4e5e5b0"))
|
||||
(2956000, uint256S("0x0000021057f0f2e55f888146dd54726b6b2e94192895a8990fa6e885cbf2cc84"))
|
||||
(2957000, uint256S("0x00000029aeed195efd6421ec6906b5308a4f54fceaeb2c6066618bd170d9107a"))
|
||||
(2958000, uint256S("0x000000ea5208d69660156ffacf152a63fc387e9bd92ace8f49e57fa1e6ef60e4"))
|
||||
(2959000, uint256S("0x000001a8847661b4fdf27afbfb4b187ab405039a86a3d79e612d43649b311196"))
|
||||
(2960000, uint256S("0x0000017cff8dc295e80f9ed4834bf017a316c2cdd0aff7f11976cc232d9fc30f"))
|
||||
(2961000, uint256S("0x000001951de8a1b055c0ad7bb01a6042d8041889fba2435676161eaa96807770"))
|
||||
(2962000, uint256S("0x000001955036b0967f1f30f7e34cf7b146a821b3058081354f4e6eca3263d11b"))
|
||||
(2963000, uint256S("0x000000cb9e41e2a62dc2ce7678bd73d36f9094686a50ce981394c51f0a7727b6"))
|
||||
(2964000, uint256S("0x000001277f60eecb3afefae842dcbc73afe966cf218637cb15bc96bfb7c248c4"))
|
||||
(2965000, uint256S("0x00000024ab8135f931081c88304b55524a5786764da435a2b90109644b5d7386"))
|
||||
(2966000, uint256S("0x000000dc386c492b9dec3e057c58e4890aeb3befde5b808cedde00d55bd25576"))
|
||||
(2967000, uint256S("0x0000009a3b16a5fda24ddb6586b455924e604cc9253e257c022c205dc2d8840a"))
|
||||
(2968000, uint256S("0x000001f135a1265c9b4db02b1e3e4f9cb0b29c97d6804d132c096ebb46330a33"))
|
||||
(2969000, uint256S("0x000001bd3dc836d79d6b43f7ec6462451f507c49c2c3823cc03a3307c1e57d56"))
|
||||
(2970000, uint256S("0x0000009c0b2841782b18762e4d14e69bd4c51e75de7c0f161150fc994c175823"))
|
||||
(2971000, uint256S("0x0000012108b5e9367921844def97f020b0e7a1d80f222c0bf6027ca98535be5c"))
|
||||
(2972000, uint256S("0x00000167122a71c4fbb63a0ebac10008a9fca86ac21bdcf4e8ae5a0dc4ac0652"))
|
||||
(2973000, uint256S("0x000001379c7d87e695027690387dfd38512410872fa700b30d4c35dc46600bae"))
|
||||
(2974000, uint256S("0x0000026fce7c4bb0697f528f2ea14c638e33f51ab2935bcffc7c02b310415ed4"))
|
||||
(2975000, uint256S("0x0000018a159510b754e5a2ff7692f2235652130a585a13effe7da7294eedd724"))
|
||||
(2976000, uint256S("0x0000015269b6e9cce2b30466d29abfc2e5d8ae0ae7e4f7cabeabeedd27ea36f2"))
|
||||
(2977000, uint256S("0x000000243f50f7a27c54469830274f1ab8bf07b417c091aa43d2043d4c731b32"))
|
||||
(2978000, uint256S("0x000001f171890e15e79ee12b1b3b9af9b788d13533fb09521d584d4989119246"))
|
||||
(2979000, uint256S("0x000000957807cc7dc451bf5c4df3e2498aecde2927affb9c4b5cdcd824d5f6d9"))
|
||||
(2980000, uint256S("0x00000207c9d63400d53e902d1ba329a3d80f2e19e412fa3642e21d8d4c642904"))
|
||||
(2981000, uint256S("0x00000201716079224da4472579700f12449f6d75d34b83c4905d6ba3f3cb2ec7"))
|
||||
(2982000, uint256S("0x0000002ad8bc77565cdd7e11975ba154c8d92f928bf1579d32e1934e8c44f865"))
|
||||
(2983000, uint256S("0x000001b88c42d70e22f1907f7705ec09e22407525b1d015a38804a8b5663381a"))
|
||||
(2984000, uint256S("0x0000006b31e10c313337ac9c8f95c7020bf5873fe9443c65fa8888bcf75cdff2"))
|
||||
(2985000, uint256S("0x00000235bc5682cd7ffb0dae2255c774165f8a79e43e023db03412d80d128888"))
|
||||
(2986000, uint256S("0x000000e0d64aa4eae99ce92c3daaa3c724d4771b465a3db45375f3317d77d42a"))
|
||||
(2987000, uint256S("0x0000021ed226af96bd30a9d34d1944f4d0c9776eeb01bd1792c545a6bebfbf87"))
|
||||
(2988000, uint256S("0x000000ab09b2d5cc61b7eacaf8c5c75d738c06eef778793569dbfe14249176c6"))
|
||||
(2989000, uint256S("0x00000045fa48a265d64baa3d6fa0de317280792eb0c90a56d18a805685e1a548"))
|
||||
(2990000, uint256S("0x0000019e091bf24b8c9b8c8797288dfa9a93f1fb455250b536c048ca491972bb"))
|
||||
(2991000, uint256S("0x0000025c0b080404dcf8d102ca5e71fa763b3505665bf0d65d2531acea680d75"))
|
||||
(2992000, uint256S("0x000003055b316b5b22ecd1717eb2190593b188c3b8c413b00cbffbdde0c34b27"))
|
||||
(2993000, uint256S("0x000000a064bd95745fd853afd6ad73b731d1cd5ff9869c63ce63015fd2fb3bae"))
|
||||
(2994000, uint256S("0x0000004b0af656104a4235f8aef67de14fafe2326c3141a0c789bec175f91bdc"))
|
||||
(2995000, uint256S("0x000001c6c7295656d51de8c9707f4360de0d6e545067ff153fa89ec2e47e735e"))
|
||||
(2996000, uint256S("0x00000125d4e3321be70bfca41bc6de3f7a13b866e1b55663f09d37c4847ed6c2"))
|
||||
(2997000, uint256S("0x000001e84e1b66cdc616563f3396276635960026f26836f1c7a9baae69174039"))
|
||||
(2998000, uint256S("0x000001206067731907e85eecc3e0d34b7c1b4fa3ba3dfae97c469ac32282cf9b"))
|
||||
(2999000, uint256S("0x000000fb239083b6c855bf26455908662aa53c00f076e549e094a9aabbda6f89"))
|
||||
(3000000, uint256S("0x00000219b1672bb1caabd27e2906287856a042529b15b15d05a2e2ff3efcfe41"))
|
||||
(3001000, uint256S("0x000000d9d692717108a856946b98e588f49f7331e4f5c04f914d64104aedc61e"))
|
||||
(3002000, uint256S("0x000000bee95e740ac3c6a6898958517542998189780a4992aa0b233089798653"))
|
||||
(3003000, uint256S("0x00000102b4ce62b897c5c04eb20ab56a3d439f582e354e84fbb2ab5b03476569"))
|
||||
(3004000, uint256S("0x00000117ad571f5197fe272be8c19a1b6ce1a72b2c58f0af82c4bb2a700055c9"))
|
||||
(3005000, uint256S("0x0000018ff82a917dfb1125231f34bcf3dee4142d5b5591cbea8c52d6c97dd25c"))
|
||||
(3006000, uint256S("0x0000023a1c1b99ee74b10bb4ca8271f9c94aaea42fee9155af21222f20a46f7d"))
|
||||
(3007000, uint256S("0x0000027c56e38915a13965e04bae1c19968aa5302f463c3a05195fb9795736b6"))
|
||||
(3008000, uint256S("0x000002dc226002e6df7aac0776a3a8691a5a8b8ac9ea8a44ec36e96c05e48ccb"))
|
||||
(3009000, uint256S("0x0000018ebbed8ee0047e9797aa8ac2739a0796bfbdded7f862dbb91fbdeeb2cd"))
|
||||
(3010000, uint256S("0x000002c5039f54af5d24f6b64463a6d5578c2122bc5393553557bfd991b8503d"))
|
||||
(3011000, uint256S("0x0000015a1fdfc18774a9a984cead52e7fcddfbeda55981885fc0f3badc76d774"))
|
||||
(3012000, uint256S("0x00000171fb194aee4f625996a18707883d2d6b381ec3696ea858170039f9c54c"))
|
||||
(3013000, uint256S("0x0000015877955e47bb3a076dcc880facb15ca91a84c1173a534731f1ae327116"))
|
||||
(3014000, uint256S("0x000000e6b8403f98e24474a424fb8e33c94bccaf98ab0ecbea35092b1ac41fe8"))
|
||||
(3015000, uint256S("0x000001346352a5dc10c77ab0e3d201e3e1e5d40e747e0f572764267281deaa98"))
|
||||
(3016000, uint256S("0x0000018e531c45772cf1b63764b899c50f3ad1d21f9f38c360e12030a90dbb8d"))
|
||||
(3017000, uint256S("0x0000001794d412d1a32e065a3d3a152de00049d5bd64cd109a05d7c93a0bf8dd"))
|
||||
(3018000, uint256S("0x000001eebb3aaf056a77d129564462bf989ce02693274a3356474d18c3dc9c09"))
|
||||
(3019000, uint256S("0x000000a32aee249f482dba222b894d2fb42decbad676c18fd6377fd14050a3e9"))
|
||||
(3020000, uint256S("0x000000e8ddbacb95d03f440ba2943b1ce76ffe4e9f26103554e5cdc7caed3c5a"))
|
||||
(3021000, uint256S("0x000003b5471ea15bfc621cc24bcc900e4d4f8baa9be34eccb1cc0476180d9e87"))
|
||||
(3022000, uint256S("0x0000007c7d8e8ca2d4f90bfaeb40362242771ff455b4001609cb1091e5f33870"))
|
||||
(3023000, uint256S("0x0000015c222a8680576fde9424f9f694c17e3f19a62580e48986479b145cbb2f"))
|
||||
(3024000, uint256S("0x000000913cdc5f2f4c35a0f342f02332f01a4b48f2a5a5a695bdedf915bc3634"))
|
||||
(3025000, uint256S("0x0000026468663c251c23cb52805122582c4339d6c543b46259918ca318994b1c"))
|
||||
(3026000, uint256S("0x000001198858f36489e2b27eea39cd62a8534b41dbc63b86a12d3eb99e7e22f0"))
|
||||
(3027000, uint256S("0x000002008ec698cb00791d34a8e530c040d8b1aae8115afa1a26637dc645d28d"))
|
||||
(3028000, uint256S("0x0000015e83194a90f0c59947d2b3f80948decf308b743bd6908ea936cd3894af"))
|
||||
(3029000, uint256S("0x000000a95be914f4ad8055d9d005ab3d7ad4c8966e93cb5c44117a7bae92b2c2"))
|
||||
(3030000, uint256S("0x000000368719c99a34efc081085fcefb4bd3b88dc4a720d40f8673702a9862e3"))
|
||||
(3031000, uint256S("0x0000005f44110660dc10ebca6ca87673ddb85b9b61fca435e3a5d4ec04a17fc5"))
|
||||
(3032000, uint256S("0x000002a6ffc3d6b4507102b9e2341f7a0a88928ffbdabf973b8e307f7e779ab6"))
|
||||
(3033000, uint256S("0x0000010a99fa68c65193d4a987acc42ac3b8a5c5f30807be75ca055f93804dcb"))
|
||||
(3034000, uint256S("0x00000247b6d12d8b55ab1967e27c60bce02715516cf5b87268c69759223a2eda"))
|
||||
(3035000, uint256S("0x0000010cb46aac3ac625e91a3eb4a033256a2c78ff971f83c4d5a28be9491d3b"))
|
||||
(3036000, uint256S("0x000000faabdf86474cc7cf1a79dfe3272a7fcf3329f3efa0111711db71dbe27d"))
|
||||
(3037000, uint256S("0x00000066bf1146fc2b79f78fa0ebbee0145bfc8d378759b0945b1e8af5185f8d"))
|
||||
(3038000, uint256S("0x000002200065ea197bd831f4395c52697f03b7fb010aafb930a5d4367c7948aa"))
|
||||
(3039000, uint256S("0x00000351ffd7806659b7ed9ce9206e9d14c45906bc7948aed3288161bfb27c09"))
|
||||
(3040000, uint256S("0x000002160907e189c825f3ae55081e0818d3a261884e18d56558441ebf95726b"))
|
||||
(3041000, uint256S("0x00000124dbac3245d964761b3065fcfe998c1dcc17f0760f931531f980cad927"))
|
||||
(3042000, uint256S("0x00000101ab03831aa195bae540dce8e424a35d4a81cb5c0caf9580939f1be380"))
|
||||
(3043000, uint256S("0x0000004688fae99072abcf0106b12e06b5e75447cd16f022ad6eb74761249ba8"))
|
||||
(3044000, uint256S("0x000000228198f80b17094a4bc4a81a4e207b0ef6f50a2ea79b27ed8e65f8a453"))
|
||||
(3045000, uint256S("0x0000015986950a34fb2cc2698941d110c6eb83484ac62b77fd40931ec34ac62c"))
|
||||
(3046000, uint256S("0x00000023339b1041301db20fcfd4b48141d6807105fd62f4e45d8b1b7d1f9688"))
|
||||
(3047000, uint256S("0x0000002c5b54deaa41b058dcd310f82035b4336ed0d5f84e4d2f62446a63399c"))
|
||||
(3048000, uint256S("0x0000001d191f3c24dcc0d6923b520b11598e7f53e17b11aab8d1de63a4f2c38d"))
|
||||
(3049000, uint256S("0x000000875c9d83457911687246a5076f892bdf128f8c10bde1629f3d0f179b5d"))
|
||||
(3050000, uint256S("0x0000043f02004859e2aa5f017529ac20e17bd0b79b9d87d0e7961095b2f68af8"))
|
||||
(3051000, uint256S("0x000003ae5ab267c9880b89cfdc5015bdff626bd845b3fc5abec814b4ff7b0c2b"))
|
||||
(3052000, uint256S("0x0000008c0e12c19e5e463a01028628bd462054fd04bc50cce15b85630eb920c3"))
|
||||
(3053000, uint256S("0x000001d89eacab8a0a3932c575fbd608f418e3f608127255b3cc14ea18b40e5a"))
|
||||
(3054000, uint256S("0x000003ce6e0bb1d7d4ba63ee9327125d7fd006fd4451f4f5895193cb1e2b5b3b"))
|
||||
(3055000, uint256S("0x000001f78ddcf7f9240686c2a49dbdf75c0b194721e28dc879636d914ac62f9d"))
|
||||
(3056000, uint256S("0x000000d8166fbb0615d907037478ea00e5901b92046918578e0257229775a682"))
|
||||
(3057000, uint256S("0x00001cbe0ed4b0688520567a4bce719d14de4fc91bde5d4f4566680a681e937a"))
|
||||
(3058000, uint256S("0x0000025f2460a0a968dda345c8e70f3e879176c2469c83e701f44cb38f52757c"))
|
||||
(3059000, uint256S("0x00000057dcad6c12ea3c82e28f329870d6a2050c851897a0f56ee0c76e9f0162"))
|
||||
(3060000, uint256S("0x000001a55a082a6b1d942b3b1b9cc28a03337aa256a1be0ac02d842acfb1466f"))
|
||||
(3061000, uint256S("0x000000a9e328c963548c95730e3cda618efd9a4e59825c1d373a2608d0be7b5b"))
|
||||
(3062000, uint256S("0x00000037877d10b82a5f27eb8d2e2a3ff036771b02d5822f4a7aa0d2ab6c9916"))
|
||||
(3063000, uint256S("0x0000005285d80a1f134d5a93d4ab22c221ec8f81a18a39c2ce0d4b0fb141031a"))
|
||||
(3064000, uint256S("0x00000025cec143c75f0a512e3764ef35d88248b98a32c2b3c70fa8eb1d4c484f"))
|
||||
(3065000, uint256S("0x0000004d0a40e92fc32ee167cd1483acefc47f9c3829c3c53bdcd5071fbd8e4d"))
|
||||
(3066000, uint256S("0x00000080e0be0ff49413f1c008a7c0efbee6489ba759e940b0cb0a24f3bef660"))
|
||||
(3067000, uint256S("0x000000a588ec95f08994f6eb4a75b8c5c292122cbc84bd137c9256a296458c39"))
|
||||
(3068000, uint256S("0x000000d3e004372c419d50838d88a884f5c9d16f2ac86f681a7b6a590ab58a81"))
|
||||
(3069000, uint256S("0x000000343322d8cf3339d463ac84e0a3e824b62857ee531901e13fb690f29108"))
|
||||
(3070000, uint256S("0x000000b5f47d0bedda6805322811da7960af2ba3fc44b327be7bad34b6e598c3"))
|
||||
(3071000, uint256S("0x0000007812b4eb5028e26a03b46a13f72ace2881844dad9a05655fd4ac270039"))
|
||||
(3072000, uint256S("0x00000040557067d6dd006fb7b9eed2092d9a61f2a623199f2f0df2222bf5f29e"))
|
||||
(3073000, uint256S("0x0000004bab6defde89d8e44307cbc0fd37fc7d35d2a86ce3b89a7eaf42a4789f"))
|
||||
(3074000, uint256S("0x000000234b6b83375ce7f10bb779926eab9369fc739dd0796d9eec0d90e3b2b1"))
|
||||
(3075000, uint256S("0x0000009303a35d265c46875d5f2d7d60ed6717d84f682c27b7152cb18efb9def"))
|
||||
(3076000, uint256S("0x000000a0e4cf5b19c37e1cc80175caa636d480b19837d76687a56faffbdac155"))
|
||||
(3077000, uint256S("0x000000ce1e51298888d8632fda2a9dc0e28dd434fc49e18e290e61e82ed8034a"))
|
||||
(3078000, uint256S("0x0000001a3446535b04bc8d09822fde3f28bc0d3346155c50dae160b63a4511cd"))
|
||||
(3079000, uint256S("0x000000f671d6df74a89ec74cb3516fe9e438e60ece06854825372cf6f69830f2"))
|
||||
(3080000, uint256S("0x000001188832e4820fe420a7ad184ea76c287073aebd09d5c2818492f8af2627"))
|
||||
(3081000, uint256S("0x0000001320c27f48fa621f99379ecb84304df231bfb07b7e7c527b8a1d684dd7"))
|
||||
(3082000, uint256S("0x000000b0157a3ecbc11024ba084073f52c2e0c6b65427c34cbf68c76d6ae571f"))
|
||||
(3083000, uint256S("0x0000004799f50ffe153c16700d064b491f01d07a2e6a8176151fedc01991fcb0"))
|
||||
(3084000, uint256S("0x00000052c88e3057099d4a2b755efd4addf29d27228093bb5f6783ec955a1706"))
|
||||
(3085000, uint256S("0x000000033ea79bbc96b0ab2b75fe32b32209510b4768de003edefee17ea77826"))
|
||||
(3086000, uint256S("0x000000711fcc88d264fc72b62a79e8e64d92f5ab02a3889c69cd61c0407e4474"))
|
||||
(3087000, uint256S("0x0000002c333ba65cec4eadea04dbf6266af2b1adf0f135540e2d1a4613427204"))
|
||||
(3088000, uint256S("0x00000098583783298ad125bd415604f068e9ce5579eb4cd9cea63a62b668c4b8"))
|
||||
(3089000, uint256S("0x000000b28d476e2da580f7b6ea6375536076cd6761409ac219c283661c271681"))
|
||||
(3090000, uint256S("0x000000529a6ab02121007876e3d940db0df8f66f0b6234170ed5d0cbdef6e85c"))
|
||||
(3091000, uint256S("0x0000005bb23a78e9c124f2a541bd20e4891ff0ca2ec1b2bbc9a2690807000e5a"))
|
||||
(3092000, uint256S("0x00000050dd2c2fd8831c30d0e83d18f2d9451e6bd9f73e753ab0b3e0f0ad8bf0"))
|
||||
(3093000, uint256S("0x00000043ac606b0dce50c3e7ef9709084e99e6ee7abdecd698a0e8e8bbc764e9"))
|
||||
(3094000, uint256S("0x00000002491170b68e6ba66dd0f8cd92bc6667b28675fcd8225b01870106cb8f"))
|
||||
(3095000, uint256S("0x000000b05deaf78e215a7f0102065c57c31ce7d51e64980085b396ebd057a697"))
|
||||
(3096000, uint256S("0x000000ac352984c4ef4a86d26bc35f7a0111c3602d243bc35dba96872d2a118e"))
|
||||
(3097000, uint256S("0x000000ed65d9033eec729348ce9aa3f79d3d09a0a4c0a63f39c37240c00b9b02"))
|
||||
(3098000, uint256S("0x000000391a40a7112eae5164d24add9beaf97f95bee6205afd96c1f8e106c4f9"))
|
||||
(3099000, uint256S("0x000000bb66eedf2c837f6782ee6d3a409f07237e138ffd1b75fff541fc8233c9"))
|
||||
(3100000, uint256S("0x0000005a501d4322cdb6d67504586362ab7902d116f5ad226b6bfd3d8df23d61"))
|
||||
(3101000, uint256S("0x000001163c9fd3eec4dd127dc705286b6cf213d607d09074536be8b69188a1a0"))
|
||||
(3102000, uint256S("0x000000c772df8f9650f3104e7b4db6de8b323da3057fa9a63f18fd101f4d01e9"))
|
||||
(3103000, uint256S("0x000000c1383c6f337890136f3f59841e45eb0b4c5774ae200c9c01acf88c3e68"))
|
||||
(3104000, uint256S("0x000000a9926b402d77d7fb5b8a0ee805a62ed40454d7b61ccede5af2af9dd0ea"))
|
||||
(3105000, uint256S("0x0000006ee4adf77a7c6912fd9ea6e5b505b9d141a35d7159f8bc9a6b4910dbbe"))
|
||||
(3106000, uint256S("0x0000002bbac066dc52c61e41e33c24f9db9906b5807fa4feff6e235e9f84c1a8"))
|
||||
(3107000, uint256S("0x000000339fd4105912b6dff330902d45d78e5ab025e0c21b8e2f5d2bd69ecfa7"))
|
||||
(3108000, uint256S("0x000000bf7d2c31afe10ce9a45aa74d01c5ffe2ab88687bbd9365f7ade64afd62"))
|
||||
(3109000, uint256S("0x00000017d43890279cbbcf11c169b8c182aaa0e92ee9190ff4445a7eef3f900b"))
|
||||
(3110000, uint256S("0x0000007c461b5ad5b99029d575f17c58d6ff8a4b662686f1cf7795500f785b43"))
|
||||
(3111000, uint256S("0x0000008781b7a2e56d7ce5ef4ef2d82760a493dc208542a11a9d4d2b5b54c619"))
|
||||
(3112000, uint256S("0x00000070c859a6d3763862b231e377bd53ea13e94e1126be02a8538f46404942"))
|
||||
(3113000, uint256S("0x000000205e90cfa5844d046f76e3fafa4cdaa2f094344361d75b253294a2c679"))
|
||||
(3114000, uint256S("0x00000029666033d0c6a4bee567d4b4dc124cd4f3b0cc1e9d9700cfbe10e010d3"))
|
||||
(3115000, uint256S("0x000000a8460d428de6367c79ef5099da8bc2714f61b1c878af9a8ff1d0f591f2"))
|
||||
(3116000, uint256S("0x0000002fdd9b54d39ea7e1e4c01c6d5e20439373d0d824e5fb8855c77e68355f"))
|
||||
(3117000, uint256S("0x00000020f413e1f5b61f60bcd35b652cc2f54b8719baef556531975c6977fd47"))
|
||||
(3118000, uint256S("0x000000742a686626900a16971ba608e3cfe6d892f6c9168d79b80677d3f099f2"))
|
||||
(3119000, uint256S("0x00000076af5aecd68eddacf266095593ac46926dc746751b38c21498e5f0a3f3"))
|
||||
(3120000, uint256S("0x000000725ba5ce0adcc752ea1eb5e9e7bf7154093ed5bf33f6895078900a98de"))
|
||||
(3121000, uint256S("0x000000b8eb16919a565650daeb00701dabe2c8d44c8d9358ae5e48d3d9a7e2a2"))
|
||||
(3122000, uint256S("0x0000007da03c22e99a008dc5386ed46bd06c5f77f270e08b6714bd4d14247381"))
|
||||
(3123000, uint256S("0x0000015c6195b1e64f1834f13fdabf2877f5e63349a555c7c5061baebbb03273"))
|
||||
(3124000, uint256S("0x000001030dd752304091361b353f028b5e2d9a514391e2c7fdb28d5d24858553"))
|
||||
(3125000, uint256S("0x00000089c9ab264ae498bdb3597655a5ccf36c7139bf58c6c02c58c7162fd2f3"))
|
||||
(3126000, uint256S("0x0000004d29525a897b4e6c1d70ed2f02cb38f5a27557497c23345ec69932884d"))
|
||||
(3127000, uint256S("0x0000004efb4bc01160b06186a01ea40cd9ac331347dc2fb966cd52c7191d3aa6"))
|
||||
(3128000, uint256S("0x000000ef678db1b12ba5fc0441d1c335b8bb630fb4d5a3d404187322861db039"))
|
||||
(3129000, uint256S("0x000000527da1f996e1c87d030c4de67749ac78a1a725982237d879f314aa379f"))
|
||||
(3130000, uint256S("0x000000daea0276b41195fd48ad9333ad82d2ee181af09da773eeecb0eed1c2cb"))
|
||||
(3131000, uint256S("0x00000044826c11188c2bab69afe0f6b02772899bf714152fb10e03ee9b0661d0"))
|
||||
(3132000, uint256S("0x00000079d9daef2612b90005ce8aaa67dab9e294ff2dd5aa9e513179610fc7ab"))
|
||||
(3133000, uint256S("0x000000329fb093db7f21b7333bd70738b4c8691c878d8ffd33d0e9bf5ff6bd18"))
|
||||
(3134000, uint256S("0x00000076b0392040b5c4c8cf5e790da58b639d3d775172592a91530120e5c35d"))
|
||||
(3135000, uint256S("0x000000f47aae8156dba2b7366fbab94adb9e4d23a9d0ba32568bc5d633ac3924"))
|
||||
(3136000, uint256S("0x00000139e9f09bd83f69ce3dc8a403e7bcabfc771c496f9a7b64b15a399ccaeb"))
|
||||
(3137000, uint256S("0x000000b44c44279f8f87b8695d05c9baa32f6f988a7fafedd2e3e3a330c44735"))
|
||||
(3138000, uint256S("0x000000000806995bac02073f889fce51b84b3cef43a7b874caa7047542442952"))
|
||||
(3139000, uint256S("0x00000176bc78f1daefe43a2ef7554646a0238f74fe79c5fecca19f9ec65ef6c2"))
|
||||
(3140000, uint256S("0x000000f1964b55b2f8ca7394fa922353c28457903de925fcb12d60e70d6442f5"))
|
||||
(3141000, uint256S("0x000000ce310267356a033299a53413c09f503268aa42e730b9e18f14cd16de99"))
|
||||
(3142000, uint256S("0x0000005ec5af349cadbf59506d69eac221b9802ee84a8aa4f85a380ee9206a85"))
|
||||
(3143000, uint256S("0x0000000aebc3645e845eb5e7ee46e71891a31e9006fec22d16e5cf9461306783"))
|
||||
(3144000, uint256S("0x000000ad31bce3cd6065c49f01dcba984c57d9cae9ee60d6b8f509ac0dbcd8bd"))
|
||||
(3145000, uint256S("0x0000006aba80796f496e4449e57fe5e966ce50fb784564cb6b624e5ac51a7cc6"))
|
||||
(3146000, uint256S("0x000000ea02b55f81a5baf0091fdb30001e233cbbe61e947f584d9fc0b678efca"))
|
||||
(3147000, uint256S("0x00000109d8c6b002b587d710a1f573a9a917cfc5197a2bd37b9ec87f365c0535"))
|
||||
(3148000, uint256S("0x000000b02ad9ce869bb38a6d778e89485f59a880bedbf515d894370e9a1c8fa4"))
|
||||
(3149000, uint256S("0x00000000dfa5469377e5d5789bdec173669cf7e1421b4ce1538c54461ee62b17"))
|
||||
(3150000, uint256S("0x0000002ecdc7a4fc85881fcb8c38bdc56f2c91511f9f9e6a8539d16bf2796ab3"))
|
||||
(3151000, uint256S("0x000000b0ef3e86bf1c6d7cf0e7a9771125688cc034efdc4f0f81483bd6ac5188"))
|
||||
(3152000, uint256S("0x000000e6b734457317240d3b4c6e1f557b87176d18066229c78c284ba56d8449"))
|
||||
(3153000, uint256S("0x0000009179388d04e83e1e2ac9f841534dca48232ad5f07a8cd88ce76a9b5ed8"))
|
||||
(3154000, uint256S("0x000000bf1af2c983b0d44ae46fd625e07c40bfd3e5a28c999344f7b20b373333"))
|
||||
(3155000, uint256S("0x000000f04ac08ae6358b72b15c1ee77dfaa3746db6ab56da3dba3917ffc1043d"))
|
||||
(3156000, uint256S("0x00000050afb4db81fd1bbd0cb815ad14af965bb7053bb4bb44720609ef04bf10"))
|
||||
(3157000, uint256S("0x000001382033d8a5a5aa3f7b6e3ce071bd36c98deb149e226b07606b3a7b7e30"))
|
||||
(3158000, uint256S("0x000001009ca876f6805a6cb96e6dbba41e9100e82f693e927fad6eebc05d22bc"))
|
||||
(3159000, uint256S("0x00000183a3f3406d83c258608e4f3db8f4ed019a4a9899b7d297d4c83fb79223"))
|
||||
(3160000, uint256S("0x000000960ba4a97484d3d16372084e139972634c7cf4d360d1c804178af88b3a"))
|
||||
(3161000, uint256S("0x0000004db0a19d395855d9b7c2b4680536fdd11decf7d6867504cb67e41c8434"))
|
||||
(3162000, uint256S("0x00000057f32d96d91e4af0c8c989c3fd3c3e553308b2c44f40ef3b2a548ce941"))
|
||||
(3163000, uint256S("0x0000006f8bb942cdfc57f00817d4ae2dfa2e88fd1e0a9ab593c3ec34a0bb985f"))
|
||||
(3164000, uint256S("0x000000f977348314bc2fd571056df11c26779f107de5523ccc05236a75f945e0"))
|
||||
(3165000, uint256S("0x0000006e5e0cfeff6306b19ad8aa291e8df95bd002ce004085b1a2a5a086392e"))
|
||||
(3166000, uint256S("0x0000014b28939d4a482e9db58d152328cc040b118379f9a5e104dd8fabb9505d"))
|
||||
(3167000, uint256S("0x000000228c00ea599df8bb7af090901c243a3b6a9bb210420ab6a82fc0a379cd"))
|
||||
(3168000, uint256S("0x000001c3f654d7d1f307060e3bd4f5102582d9f6dfc0bd826b735ef0d24eb7e3"))
|
||||
(3169000, uint256S("0x00000294fbb1ba4fb799e28048eba52216b65130005f67c235129b43af2f2e6a"))
|
||||
(3170000, uint256S("0x00000017d5572bcd7250eca0ee1533b30dbfd1288f53176a8c7210c3c12514a8"))
|
||||
(3171000, uint256S("0x00000075a53ba47066a5c9956782166ab67694cf43a2eea255261c011baa8a1f"))
|
||||
(3172000, uint256S("0x000000c6c61726ca942713df477ac245182809b3d80532e227aa1e3df01b7e9e"))
|
||||
(3173000, uint256S("0x000000787fd12c128e6bdfe9701e528421cbfdf204ada723733167844fddadd8"))
|
||||
(3174000, uint256S("0x00000084b72cfe91dbd1624ce8d6dd7b565c76f8b2aa52c05be49e1894580402"))
|
||||
(3175000, uint256S("0x00000090df5c3a1a35ee87c4bbb67ed828211e320a783b4bd5aa504258856d9e"))
|
||||
(3176000, uint256S("0x000000c9cd391b1f8baf52ffcd74114ddd6d062b1c74c0203bf16b19d8bc52f2"))
|
||||
(3177000, uint256S("0x0000000e093102ffd8c978c4786ab345196000b2789325bae1777599de1a7cf4"))
|
||||
(3178000, uint256S("0x000000e4d359d3b0a1730eac385d9d6bc0c9e2e4cac055492f98c7064c9a2385"))
|
||||
(3179000, uint256S("0x000000c52858873d506ebf04ab7f767ac9f4144d211a791bfb4c81ac5960ae55"))
|
||||
(3180000, uint256S("0x000000b70fd9ea8ccc360593d3a75f7ac6400acd40eee22364f39e40092f6772"))
|
||||
(3181000, uint256S("0x0000010871b5c6ea2c3909d4e1740ecb1d0eb4f435d82f5dec3bfdeb0bf5e649"))
|
||||
(3182000, uint256S("0x000000b5bf1cbe1871c3aa7bc4b280449803eae763a149ed70cbcafea7ae12ef"))
|
||||
(3183000, uint256S("0x00000153bf68c72eb0975699099f292dd5987ca42fec7d9bb57dc8a594f92565"))
|
||||
(3184000, uint256S("0x000000449b96d6723728a7c5e7c6cb7a0509224185f52e91fbb53132e8f993d5"))
|
||||
(3185000, uint256S("0x0000015b0ec4dacd96c97d8a83cd9942ebc8ad877f9a088c6875391b3beb0523"))
|
||||
(3186000, uint256S("0x0000003636a9508d1c33f5dc7132f9612eec3960e14aafff853d59d352d78756"))
|
||||
(3187000, uint256S("0x0000013c19d114806898c4734dd653ebd28158de083adaec3101ddac3b28ee32"))
|
||||
(3188000, uint256S("0x00000132e633d30682c9939fa914a5f1fbc6c56877a0ea131eb1e5341aa81683"))
|
||||
(3189000, uint256S("0x0000001d74ce8246a098ac7d95eb5609245dcf7d9df0f0d337696efa33d5e66b"))
|
||||
(3190000, uint256S("0x0000010e099fb54b916a9d5f0f4495adddad1bb9c947b33d837ed607fa257a7d"))
|
||||
(3191000, uint256S("0x00000053c6beb3b7788f5ccd5877dc4162eef98a00fe13b0cdc87cd6faf10d6a"))
|
||||
(3192000, uint256S("0x000000733a88ba30bee717951134f48cdef4acd021d368edaada61e8ec870d0c"))
|
||||
(3193000, uint256S("0x0000002f8b6b7c7846f94e304b17888c060fb22170b944a46921b3af6461cc47"))
|
||||
(3194000, uint256S("0x00000026340cc6a617885b3f0a77bd24750f89ab6d43560beb637399d1a45ce1"))
|
||||
(3195000, uint256S("0x000000a21fad1caa6397cd68bef24a999bb8b61c229e1e450c09392255c93a30"))
|
||||
(3196000, uint256S("0x00000008e283d3b23e7384d46896550024b1a78c21acb6969baee8a62b83448b"))
|
||||
(3197000, uint256S("0x00000068f83587ceb858f477db5f0bcdd5934ca2d7e93168e5c64c0710d06f95"))
|
||||
(3198000, uint256S("0x0000009d94f70e4fe72388913f110578c10aafd8466cd3f3b2f6b21c5066feba"))
|
||||
(3199000, uint256S("0x00000040c554cfea75cbf9b1ce977c536173951011125c2cf37b32c3fb29e86b"))
|
||||
(3200000, uint256S("0x0000004a1c3bc726562379a7d7805ba610113ba72816bcbc1f85788a9d6d4ef9"))
|
||||
(3201000, uint256S("0x000000145ffdbfcfc521b2334509fe3201ad4eeb667ec7e183c4bf9e888fbbb1"))
|
||||
(3202000, uint256S("0x0000001ded77185502b724ae961a8eac5734b75c0057b4d351e58aaa5962acd6"))
|
||||
(3203000, uint256S("0x00000056740ca653e79406d309a91728c4825f546f3673757bde15f6ec3f468c"))
|
||||
(3204000, uint256S("0x00000085da8e803be0813d5617a6a461cc6914ef533abaeaef5d824054516218"))
|
||||
(3205000, uint256S("0x0000002b7f3ef3b42f132916aa94e4b3d61c0ffeb98c7aca57b51601a3ab61b9"))
|
||||
(3206000, uint256S("0x00000077bb73b9f7df4c6a6d55217ebdc48ed36b44826479e320d0925eade58e"))
|
||||
(3207000, uint256S("0x0000007522193a2ef39d4cd7e9512502af313e55d5d8968ccf3226bb9a94bba0"))
|
||||
(3208000, uint256S("0x00000060832ff1400278327d1f2e5c1a5eb872bee8b9b383e7777ff90fcdd009"))
|
||||
(3209000, uint256S("0x00000007d8dc51b7ff58651725dc01d65f5dc696d0757d6c14a26e4d8e750e43"))
|
||||
(3210000, uint256S("0x000000541ff2ced91b5d31a42ec0f405b5e9aa24e95e76c38ce23e904ac10e52"))
|
||||
(3211000, uint256S("0x000000727913ea052a4a00845fd19fc7024c57e8fae341f5022bfbd4a62bb774"))
|
||||
(3212000, uint256S("0x0000000ea83220465ca147e8eab43d71776fdedad9cd79e33347db2c98a4c89e"))
|
||||
(3213000, uint256S("0x00000013b82d62857feacb10aed6c86417c8310431037e386bdf6e352c906bc8"))
|
||||
(3214000, uint256S("0x00000046e777e7e3a0a20f4e4a16bf8c881c4b32cec5ed6c46b9271c184922f8"))
|
||||
(3215000, uint256S("0x0000002668dc21fe0252168397ce5b27acf6ff4c39c937c847e62ddacf0f4b36"))
|
||||
(3216000, uint256S("0x00000060af593fe53d77063152de1bf341eb2e4c72c4f714b7be021a776cede0"))
|
||||
(3217000, uint256S("0x000000905d46326bcfb55169956a7e73c0885b98bba32defde31c1b9c6ab723a"))
|
||||
(3218000, uint256S("0x0000004c178fff51b6c7329417988724d19903f47e370ced6edd3ee2bec49c00"))
|
||||
(3219000, uint256S("0x0000007819ed1bc19308a116eec0837b9826ed78e68ca41d4f718d2a164fcf6f"))
|
||||
(3220000, uint256S("0x000000f29bdf2175086feb09ee2603d14685a4e58e2641be7ebb03e58402e9f3"))
|
||||
(3221000, uint256S("0x000000726da4da04803e39d1ad6bc8bd4a956eee49f6629059154564cafe84eb"))
|
||||
(3222000, uint256S("0x0000005c77415666ca339c55de179d1cd43f4db4de2ac105326b51de549c066c"))
|
||||
(3223000, uint256S("0x000000a63e3d529ef124c34a64cf168d6db8ba8a22461a20298654bc70aded2c"))
|
||||
(3224000, uint256S("0x00000068d7fdfc5bf76b1465afd89b2cc05c60f191a28dfedfedd54f68b1d632"))
|
||||
(3225000, uint256S("0x00000063b8bd774216c7d438c1f07f6d7cc73a1bd03a38a0c7e2d861db679cee"))
|
||||
(3226000, uint256S("0x0000005a5d9c7a9ffa27f23cad423c2c3e3fcd341be06c912819815cd64135cc"))
|
||||
,(int64_t) 1787132038, // time of last checkpointed block
|
||||
(int64_t) 3415780, // total txs
|
||||
(double) 3012 // txs in the last day before block 3226000
|
||||
|
||||
};
|
||||
} else {
|
||||
|
||||
@@ -117,6 +117,10 @@ public:
|
||||
void SetNValue(uint64_t n) { nEquihashN = n; }
|
||||
void SetKValue(uint64_t k) { nEquihashK = k; }
|
||||
void SetMiningRequiresPeers(bool flag) { fMiningRequiresPeers = flag; }
|
||||
//! Drop any inherited peer seeds. An assetchain gets its params by copying a
|
||||
//! base network and overriding pieces; without this it silently keeps the base
|
||||
//! chain's DNS and fixed seeds, which are wrong for it by definition.
|
||||
void ClearSeeds() { vSeeds.clear(); vFixedSeeds.clear(); }
|
||||
|
||||
CMessageHeader::MessageStartChars pchMessageStart;
|
||||
Consensus::Params consensus;
|
||||
|
||||
@@ -11,14 +11,16 @@
|
||||
// Each line contains a BIP155 serialized address.
|
||||
//
|
||||
static const uint8_t chainparams_seed_main[] = {
|
||||
0x01,0x04,0xd4,0x38,0x29,0x3f,0x00,0x00, // 212.56.41.63
|
||||
0x01,0x04,0xc2,0x8c,0xc6,0xb0,0x00,0x00, // 194.140.198.176
|
||||
0x01,0x04,0xd4,0x38,0x29,0x2f,0x00,0x00, // 212.56.41.47
|
||||
0x01,0x04,0x90,0x7e,0x93,0xa5,0x00,0x00, // 144.126.147.165
|
||||
0x01,0x04,0xb0,0x7e,0x57,0xf1,0x00,0x00, // 176.126.87.241
|
||||
0x01,0x04,0xd4,0x38,0x29,0x3f,0x55,0x08, // 212.56.41.63:21768
|
||||
0x01,0x04,0xc2,0x8c,0xc6,0xb0,0x55,0x08, // 194.140.198.176:21768
|
||||
0x01,0x04,0xd4,0x38,0x29,0x2f,0x55,0x08, // 212.56.41.47:21768
|
||||
0x01,0x04,0x90,0x7e,0x93,0xa5,0x55,0x08, // 144.126.147.165:21768
|
||||
0x01,0x04,0xb0,0x7e,0x57,0xf1,0x55,0x08, // 176.126.87.241:21768
|
||||
0x01,0x04,0x0d,0x8c,0x3a,0xfb,0x55,0x08, // 13.140.58.251:21768
|
||||
0x01,0x04,0x05,0x68,0x53,0x64,0x55,0x08, // 5.104.83.100:21768
|
||||
};
|
||||
|
||||
static const uint8_t chainparams_seed_test[] = {
|
||||
0x01,0x04,0x01,0x02,0x03,0x04,0x00,0x00, // 1.2.3.4
|
||||
};
|
||||
#endif // HUSH_CHAINPARAMSSEEDS_H
|
||||
#endif // DRAGONX_CHAINPARAMSSEEDS_H
|
||||
|
||||
@@ -62,11 +62,15 @@ const std::string CLIENT_NAME = GetArg("-clientname", "DragonX");
|
||||
#endif
|
||||
|
||||
//TODO: clean up this stuff
|
||||
//! git will put "#define GIT_ARCHIVE 1" on the next line inside archives.
|
||||
#define GIT_ARCHIVE 1
|
||||
//! git archives get "#define GIT_ARCHIVE 1" substituted on the next line by export-subst.
|
||||
//! Do NOT hardcode it: the BUILD_DESC chain below falls back to GIT_COMMIT_ID whenever
|
||||
//! build.h carries no BUILD_DESC -- any build without git metadata, e.g. from a tarball --
|
||||
//! so a hardcoded id makes those binaries claim an identity that is not theirs. Until
|
||||
//! 2026-08-21 this asserted Komodo commit a86845f3dc, dated Feb 2018, on every such build.
|
||||
//! With it gone that case reports "-unk", which is honest and greppable.
|
||||
#ifdef GIT_ARCHIVE
|
||||
#define GIT_COMMIT_ID "a86845f3dc"
|
||||
#define GIT_COMMIT_DATE "Wed, 21 Feb 2018 16:15:11 +0200"
|
||||
#define GIT_COMMIT_ID "$Format:%h$"
|
||||
#define GIT_COMMIT_DATE "$Format:%cD$"
|
||||
#endif
|
||||
|
||||
#define RENDER_BETA_STRING(num) "-beta" DO_STRINGIZE(num)
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
|
||||
// Must be kept in sync with configure.ac , ugh!
|
||||
#define CLIENT_VERSION_MAJOR 1
|
||||
#define CLIENT_VERSION_MINOR 0
|
||||
#define CLIENT_VERSION_REVISION 3
|
||||
#define CLIENT_VERSION_MINOR 2
|
||||
#define CLIENT_VERSION_REVISION 0
|
||||
#define CLIENT_VERSION_BUILD 50
|
||||
|
||||
//! Set to true for release, false for prerelease or test build
|
||||
|
||||
@@ -139,3 +139,103 @@ TEST(mnemonic_compat, RawEntropyDiffersFromMnemonicSeed)
|
||||
const std::string entropyT = DeriveTAddrFromSeedBytes(zeros); // wrong (32-byte)
|
||||
EXPECT_NE(seedT, entropyT);
|
||||
}
|
||||
// New storage form: the HD seed IS the expanded 64-byte BIP39 seed, the chain is
|
||||
// NOT flagged mnemonic, and the phrase comes from the separate entropy record.
|
||||
TEST(mnemonic_compat, ExpandedSeedIsStoredDirectly)
|
||||
{
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
CWallet wallet;
|
||||
ASSERT_TRUE(wallet.SetHDSeedFromMnemonic(ABANDON_ART));
|
||||
|
||||
// Stored bytes == the 64-byte BIP39 seed, fed to derivation unchanged.
|
||||
HDSeed stored;
|
||||
ASSERT_TRUE(wallet.GetHDSeed(stored));
|
||||
auto raw = stored.RawSeed();
|
||||
EXPECT_EQ(raw.size(), (size_t)64);
|
||||
EXPECT_EQ(HexStr(raw.begin(), raw.end()), std::string(SEED64_HEX));
|
||||
|
||||
// No CHDChain version bump / no mnemonic flag: an older binary reads this
|
||||
// wallet and derives the same tree.
|
||||
EXPECT_FALSE(wallet.GetHDChain().fMnemonicSeed);
|
||||
EXPECT_LT(wallet.GetHDChain().nVersion, CHDChain::VERSION_HD_MNEMONIC);
|
||||
|
||||
HDSeed forDerivation;
|
||||
ASSERT_TRUE(wallet.GetHDSeedForDerivation(forDerivation));
|
||||
EXPECT_EQ(forDerivation.RawSeed(), raw);
|
||||
|
||||
// The phrase is still exportable, and IsMnemonicSeed() (which gates
|
||||
// z_exportmnemonic) still says yes.
|
||||
EXPECT_TRUE(wallet.IsMnemonicSeed());
|
||||
EXPECT_TRUE(wallet.HaveMnemonicEntropy());
|
||||
std::string exported;
|
||||
ASSERT_TRUE(wallet.GetMnemonicPhrase(exported));
|
||||
EXPECT_EQ(exported, std::string(ABANDON_ART));
|
||||
}
|
||||
|
||||
// Backwards compatibility: a wallet in the OLD form (stored HD seed == 32-byte
|
||||
// entropy, fMnemonicSeed = true) must still derive and still export its phrase.
|
||||
TEST(mnemonic_compat, LegacyEntropySeedStillWorks)
|
||||
{
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
RawHDSeed zeros(32, 0), seed64;
|
||||
ASSERT_TRUE(Bip39SeedFromEntropy(zeros, seed64));
|
||||
|
||||
CWallet wallet;
|
||||
{
|
||||
LOCK(wallet.cs_wallet);
|
||||
RawHDSeed entropy(32, 0);
|
||||
HDSeed legacy(entropy);
|
||||
ASSERT_TRUE(wallet.InstallHDSeed(legacy, true, 1));
|
||||
}
|
||||
EXPECT_TRUE(wallet.GetHDChain().fMnemonicSeed);
|
||||
EXPECT_FALSE(wallet.HaveMnemonicEntropy());
|
||||
EXPECT_TRUE(wallet.IsMnemonicSeed());
|
||||
|
||||
// Still expanded on read -> same key tree as the new form.
|
||||
HDSeed forDerivation;
|
||||
ASSERT_TRUE(wallet.GetHDSeedForDerivation(forDerivation));
|
||||
EXPECT_EQ(forDerivation.RawSeed(), seed64);
|
||||
{
|
||||
LOCK(wallet.cs_wallet);
|
||||
EXPECT_EQ(EncodePaymentAddress(wallet.GenerateNewSaplingZKey()),
|
||||
DeriveZAddrFromSeed64(seed64));
|
||||
}
|
||||
|
||||
std::string exported;
|
||||
ASSERT_TRUE(wallet.GetMnemonicPhrase(exported));
|
||||
EXPECT_EQ(exported, std::string(ABANDON_ART));
|
||||
}
|
||||
|
||||
// The entropy record refuses replacement, and a phrase that does not restore the
|
||||
// installed seed is never printed.
|
||||
TEST(mnemonic_compat, MnemonicEntropyGuards)
|
||||
{
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
RawHDSeed zeros(32, 0), ones(32, 1);
|
||||
|
||||
// Refuse-to-replace.
|
||||
CWallet wallet;
|
||||
ASSERT_TRUE(wallet.SetHDSeedFromMnemonic(ABANDON_ART));
|
||||
EXPECT_FALSE(wallet.SetMnemonicEntropy(ones));
|
||||
EXPECT_FALSE(wallet.SetMnemonicEntropy(RawHDSeed())); // empty is not "installed"
|
||||
|
||||
// Mismatched entropy -> no phrase. Install a 64-byte seed that is NOT the
|
||||
// expansion of `zeros`, then attach `zeros` as entropy.
|
||||
RawHDSeed otherSeed64;
|
||||
ASSERT_TRUE(Bip39SeedFromEntropy(ones, otherSeed64));
|
||||
CWallet mismatched;
|
||||
ASSERT_TRUE(mismatched.SetHDSeedFromHex(HexStr(otherSeed64.begin(), otherSeed64.end())));
|
||||
ASSERT_TRUE(mismatched.SetMnemonicEntropy(zeros));
|
||||
std::string phrase;
|
||||
EXPECT_FALSE(mismatched.GetMnemonicPhrase(phrase));
|
||||
|
||||
// Matching entropy attached to a hex-restored wallet -> phrase available.
|
||||
CWallet matched;
|
||||
ASSERT_TRUE(matched.SetHDSeedFromHex(std::string(SEED64_HEX)));
|
||||
ASSERT_TRUE(matched.SetMnemonicEntropy(zeros));
|
||||
ASSERT_TRUE(matched.GetMnemonicPhrase(phrase));
|
||||
EXPECT_EQ(phrase, std::string(ABANDON_ART));
|
||||
}
|
||||
|
||||
@@ -1799,8 +1799,17 @@ void hush_args(char *argv0)
|
||||
|
||||
LogPrint("net", "%s: isdragonx=%d\n", __func__, isdragonx);
|
||||
if (isdragonx) {
|
||||
// node8-node10 are PLACEHOLDERS with no DNS records yet. A hostname that
|
||||
// does not resolve is harmless here: ThreadOpenAddedConnections just fails
|
||||
// to open the connection and retries on its 2-minute cycle. Reserving the
|
||||
// names in the binary means a future seed can be brought into the -addnode
|
||||
// set by creating one DNS record, with no release and no waiting for users
|
||||
// to upgrade. (seed.dragonx.is already provides that for the DNS-seed path;
|
||||
// this extends the same property to the addnode path.)
|
||||
DRAGONX_nodes = {"node1.dragonx.is","node2.dragonx.is","node3.dragonx.is",
|
||||
"node4.dragonx.is","node5.dragonx.is"
|
||||
"node4.dragonx.is","node5.dragonx.is","node6.dragonx.is",
|
||||
"node7.dragonx.is","node8.dragonx.is","node9.dragonx.is",
|
||||
"node10.dragonx.is"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
135
src/init.cpp
135
src/init.cpp
@@ -60,6 +60,7 @@
|
||||
#include "wallet/wallet.h"
|
||||
#include "wallet/walletdb.h"
|
||||
#include "wallet/asyncrpcoperation_saplingconsolidation.h"
|
||||
#include "wallet/asyncrpcoperation_autoshieldcoinbase.h"
|
||||
#include "wallet/asyncrpcoperation_sweep.h"
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
@@ -471,7 +472,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
strUsage += HelpMessageOpt("-hdtransparent", strprintf(_("Derive transparent addresses from the HD seed so they can be recovered from it (default: %u)"), 1));
|
||||
strUsage += HelpMessageOpt("-hdseed=<hex>", _("Restore a fresh/empty wallet from a 32- or 64-byte HD seed hex (the value shown in z_exportwallet's '# HDSeed=' line). WARNING: exposes the seed to your shell history and process list."));
|
||||
strUsage += HelpMessageOpt("-mnemonic=<words>", _("Restore/create a fresh/empty wallet from a BIP39 seed phrase, compatible with SilentDragonXLite (English, no passphrase; cross-wallet restore parity is mainnet-only -- testnet/regtest derive a different HD coin_type). WARNING: exposes the phrase to your shell history and process list; prefer DRAGONX.conf with tight permissions."));
|
||||
strUsage += HelpMessageOpt("-usemnemonic", strprintf(_("Create new wallets from a fresh BIP39 seed phrase so the 24 words can be exported (z_exportmnemonic) and used in SilentDragonXLite (default: %u)"), 0));
|
||||
strUsage += HelpMessageOpt("-usemnemonic", strprintf(_("Create new wallets from a fresh BIP39 seed phrase so the 24 words can be exported (z_exportmnemonic) and used in SilentDragonXLite. Set to 0 for a raw random seed with no recovery phrase; existing wallets are never changed (default: %u)"), 1));
|
||||
strUsage += HelpMessageOpt("-hdtransparentgaplimit=<n>", strprintf(_("On -mnemonic/-hdseed restore, pre-derive this many HD transparent keys so a rescan can find coinbase paid to them (default: %u)"), 1000));
|
||||
strUsage += HelpMessageOpt("-mnemonicsaplinggap=<n>", strprintf(_("On -mnemonic/-hdseed restore, pre-derive this many shielded (Sapling) addresses so a rescan can find notes sent to them (default: %u)"), 100));
|
||||
strUsage += HelpMessageOpt("-consolidation", _("Enable auto Sapling note consolidation (default: false)"));
|
||||
@@ -489,6 +490,12 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
strUsage += HelpMessageOpt("-zsweepexternal", _("Enable sweeping to an external wallet (default false)"));
|
||||
strUsage += HelpMessageOpt("-zsweepexclude", _("Addresses to exclude from sweeping (default none)"));
|
||||
|
||||
strUsage += HelpMessageOpt("-autoshield", _("Automatically shield matured coinbase (mining rewards) into a seed-derived wallet z-address (default: true for wallets created or restored by this software, false when the HD seed provenance is unknown). No-op when not mining or wallet is locked."));
|
||||
strUsage += HelpMessageOpt("-autoshieldinterval", strprintf(_("Block interval between automatic coinbase-shielding rounds (default: %i, min 5)"), 25));
|
||||
strUsage += HelpMessageOpt("-autoshieldaddress=<zaddr>", _("Destination Sapling z-address for auto-shielded coinbase (default: reuse or create a wallet z-address). Must be spendable by this wallet."));
|
||||
strUsage += HelpMessageOpt("-autoshieldfee", strprintf(_("Fee in puposhis for automatic coinbase-shielding transactions (default: %i)"), 10000));
|
||||
strUsage += HelpMessageOpt("-autoshieldminutxos", strprintf(_("Only auto-shield once at least this many matured coinbase UTXOs exist (default: %i)"), 1));
|
||||
|
||||
strUsage += HelpMessageOpt("-deletetx", _("Enable Old Transaction Deletion"));
|
||||
strUsage += HelpMessageOpt("-deleteinterval", strprintf(_("Delete transaction every <n> blocks during inital block download (default: %i)"), DEFAULT_TX_DELETE_INTERVAL));
|
||||
strUsage += HelpMessageOpt("-keeptxnum", strprintf(_("Keep the last <n> transactions (default: %i)"), DEFAULT_TX_RETENTION_LASTTX));
|
||||
@@ -2264,7 +2271,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (nLoadWalletRet != DB_LOAD_OK)
|
||||
{
|
||||
if (nLoadWalletRet == DB_CORRUPT)
|
||||
strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n";
|
||||
strErrors << _("Error loading wallet.dat: Wallet corrupted. If this wallet was last opened "
|
||||
"by an older version, move wallet.dat aside and restore from your seed "
|
||||
"phrase with -mnemonic=\"<your seed phrase>\" -rescan (see debug.log for "
|
||||
"the specific record at fault).") << "\n";
|
||||
else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
|
||||
{
|
||||
string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data"
|
||||
@@ -2301,6 +2311,23 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
|
||||
if (!pwalletMain->HaveHDSeed())
|
||||
{
|
||||
// Does this wallet predate the seed we are about to install? If so,
|
||||
// that seed cannot appear in any backup the user already holds.
|
||||
// Checked BEFORE installing, and before the restore path pre-derives
|
||||
// its gap of keys.
|
||||
bool fWalletHadContent = false;
|
||||
{
|
||||
LOCK(pwalletMain->cs_wallet);
|
||||
std::set<CKeyID> setExistingKeys;
|
||||
pwalletMain->GetKeys(setExistingKeys); // keystore.h:60 / crypter.h:212
|
||||
std::set<libzcash::SaplingPaymentAddress> setExistingZAddrs;
|
||||
pwalletMain->GetSaplingPaymentAddresses(setExistingZAddrs); // keystore.h:226-238
|
||||
fWalletHadContent = !setExistingKeys.empty() ||
|
||||
!setExistingZAddrs.empty() ||
|
||||
!pwalletMain->mapWallet.empty() || // wallet.h:1041
|
||||
pwalletMain->IsCrypted(); // crypter.h:174
|
||||
}
|
||||
|
||||
std::string mnemonic = GetArg("-mnemonic", "");
|
||||
std::string hdSeedHex = GetArg("-hdseed", "");
|
||||
bool restoring = false;
|
||||
@@ -2332,6 +2359,19 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
pwalletMain->GenerateNewSeed();
|
||||
}
|
||||
|
||||
pwalletMain->SetHDSeedOrigin(restoring
|
||||
? CWallet::HDSEED_ORIGIN_RESTORED
|
||||
: (fWalletHadContent ? CWallet::HDSEED_ORIGIN_RETROFIT
|
||||
: CWallet::HDSEED_ORIGIN_CREATED));
|
||||
if (pwalletMain->hdSeedOrigin == CWallet::HDSEED_ORIGIN_RETROFIT)
|
||||
{
|
||||
LogPrintf("%s: WARNING: generated a new HD seed for a wallet that already held keys or "
|
||||
"transactions. This seed is in NO backup you made before now.\n", __func__);
|
||||
InitWarning(_("A new HD seed was generated for this pre-existing wallet. Any backup you "
|
||||
"made before now does not contain it: back the wallet up again "
|
||||
"(z_exportwallet) before receiving funds to newly derived addresses."));
|
||||
}
|
||||
|
||||
if (restoring)
|
||||
{
|
||||
// Pre-derive keys (birthday = genesis) so the startup rescan finds
|
||||
@@ -2350,6 +2390,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
LogPrintf("%s: pre-derived %d transparent and %d sapling keys for restore rescan\n", __func__, (int)tGap, (int)zGap);
|
||||
}
|
||||
}
|
||||
else if (pwalletMain->hdSeedOrigin == CWallet::HDSEED_ORIGIN_UNRECORDED)
|
||||
{
|
||||
// The seed was installed by a build that predates this record, so
|
||||
// we cannot tell whether it was minted onto a pre-existing wallet
|
||||
// (and is therefore absent from the user's older backups). Assume
|
||||
// the worst; the user can still opt in explicitly.
|
||||
pwalletMain->SetHDSeedOrigin(CWallet::HDSEED_ORIGIN_UNKNOWN);
|
||||
LogPrintf("%s: HD seed predates seed-provenance recording; recorded origin as unknown\n", __func__);
|
||||
}
|
||||
|
||||
//Set Sapling Consolidation
|
||||
pwalletMain->fSaplingConsolidationEnabled = GetBoolArg("-consolidation", false);
|
||||
@@ -2451,6 +2500,88 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
}
|
||||
}
|
||||
|
||||
//Set Automatic Coinbase Shielding (default ON, conditional: self-guards
|
||||
//on nodes where it cannot act - no owned coinbase, external mineraddress,
|
||||
//or locked wallet). Closes the transparent-coinbase leak for miners.
|
||||
// Default ON only when this wallet's HD seed provenance says the
|
||||
// destination is genuinely recoverable. Autoshield sends mined coinbase to
|
||||
// a seed-derived z-address (resolveDestination), so for a seed retrofitted
|
||||
// onto a pre-existing wallet - or one predating provenance recording - we
|
||||
// cannot assume the user holds it. Those wallets opt in with -autoshield=1
|
||||
// after backing the seed up.
|
||||
const bool fAutoShieldSeedKnown =
|
||||
(pwalletMain->hdSeedOrigin == CWallet::HDSEED_ORIGIN_CREATED ||
|
||||
pwalletMain->hdSeedOrigin == CWallet::HDSEED_ORIGIN_RESTORED);
|
||||
pwalletMain->fAutoShieldEnabled = GetBoolArg("-autoshield", fAutoShieldSeedKnown);
|
||||
if (!fAutoShieldSeedKnown && !mapArgs.count("-autoshield")) {
|
||||
LogPrintf("%s: autoshield left OFF by default: HD seed origin %d is not known-recoverable. "
|
||||
"Back the seed up (z_exportwallet, or z_exportmnemonic on a mnemonic wallet) and "
|
||||
"pass -autoshield=1 to enable.\n", __func__, pwalletMain->hdSeedOrigin);
|
||||
}
|
||||
if (pwalletMain->fAutoShieldEnabled) {
|
||||
int autoShieldInterval = GetArg("-autoshieldinterval", 25);
|
||||
if (autoShieldInterval < 5) {
|
||||
fprintf(stderr,"%s: Invalid autoshield interval of %d < 5, setting to default of 25\n", __func__, autoShieldInterval);
|
||||
autoShieldInterval = 25;
|
||||
}
|
||||
pwalletMain->autoShieldInterval = autoShieldInterval;
|
||||
pwalletMain->nextAutoShield = pwalletMain->autoShieldInterval + chainActive.Height();
|
||||
|
||||
// Validate the fee: floor it above the relay minimum and cap it to
|
||||
// guard against a fat-finger (e.g. -autoshieldfee=5000000000) that
|
||||
// would otherwise build an over-fee or malformed shield tx that
|
||||
// fails mempool admission every round.
|
||||
CAmount autoShieldFee = GetArg("-autoshieldfee", 10000);
|
||||
const CAmount AUTOSHIELD_MIN_FEE = 1000; // comfortably above minRelayTxFee for a small tx
|
||||
const CAmount AUTOSHIELD_MAX_FEE = 10000000; // 0.1 DRGX; no sane autoshield fee exceeds this
|
||||
if (autoShieldFee < AUTOSHIELD_MIN_FEE || autoShieldFee > AUTOSHIELD_MAX_FEE) {
|
||||
fprintf(stderr,"%s: -autoshieldfee=%lld out of range [%lld,%lld], using default 10000\n",
|
||||
__func__, (long long)autoShieldFee, (long long)AUTOSHIELD_MIN_FEE, (long long)AUTOSHIELD_MAX_FEE);
|
||||
autoShieldFee = 10000;
|
||||
}
|
||||
pwalletMain->autoShieldFee = autoShieldFee;
|
||||
pwalletMain->autoShieldMinUtxos = GetArg("-autoshieldminutxos", 1);
|
||||
if (pwalletMain->autoShieldMinUtxos < 1) {
|
||||
pwalletMain->autoShieldMinUtxos = 1;
|
||||
}
|
||||
LogPrintf("%s: autoshield enabled, nextAutoShield=%d interval=%d\n", __func__, pwalletMain->nextAutoShield, pwalletMain->autoShieldInterval);
|
||||
|
||||
//Optional explicit destination z-address. Must be a Sapling zaddr the
|
||||
//wallet can spend, else the shielded coinbase would be unrecoverable.
|
||||
std::string autoShieldAddress = GetArg("-autoshieldaddress", "");
|
||||
if (!autoShieldAddress.empty()) {
|
||||
auto zdest = DecodePaymentAddress(autoShieldAddress);
|
||||
if (!IsValidPaymentAddress(zdest) ||
|
||||
boost::get<libzcash::SaplingPaymentAddress>(&zdest) == nullptr) {
|
||||
return InitError("Invalid -autoshieldaddress: must be a Sapling z-address");
|
||||
}
|
||||
auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zdest);
|
||||
if (!hasSpendingKey) {
|
||||
return InitError("Wallet must hold the spending key of -autoshieldaddress (else shielded coinbase would be unrecoverable)");
|
||||
}
|
||||
pwalletMain->autoShieldAddress = autoShieldAddress;
|
||||
} else {
|
||||
// No explicit destination. Resolve the seed-derived one now, read-only,
|
||||
// so z_autoshieldstatus can say where coinbase will go BEFORE the first
|
||||
// round rather than reporting an empty string until one fires. This
|
||||
// never generates a key: a fresh account must not be a side effect of
|
||||
// populating a status field. A brand-new wallet holds nothing in-gap
|
||||
// yet, so the field stays empty and the RPC explains why.
|
||||
LOCK(pwalletMain->cs_wallet);
|
||||
if (!pwalletMain->IsLocked()) {
|
||||
libzcash::SaplingPaymentAddress destAddr;
|
||||
std::string destStr;
|
||||
uint32_t destAccount = AUTOSHIELD_ACCOUNT_NONE;
|
||||
if (ResolveAutoShieldDestinationReadOnly(destAddr, destStr, destAccount)
|
||||
== AutoShieldDestStatus::Resolved) {
|
||||
pwalletMain->autoShieldAddress = destStr;
|
||||
LogPrintf("%s: autoshield destination %s (seed-derived sapling account %u)\n",
|
||||
__func__, destStr, (unsigned)destAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Set Transaction Deletion Options
|
||||
fTxDeleteEnabled = GetBoolArg("-deletetx", false);
|
||||
fTxConflictDeleteEnabled = GetBoolArg("-deleteconflicttx", true);
|
||||
|
||||
@@ -68,6 +68,42 @@ bool CBasicKeyStore::GetHDSeed(HDSeed& seedOut) const
|
||||
}
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::SetMnemonicEntropy(const RawHDSeed& entropy)
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
if (entropy.empty()) {
|
||||
// Never "install" nothing: HaveMnemonicEntropy() would stay false while
|
||||
// the caller was told the call succeeded.
|
||||
return false;
|
||||
}
|
||||
if (!mnemonicEntropy.empty()) {
|
||||
// Same refuse-to-replace rule as SetHDSeed above, for a sharper reason:
|
||||
// this is the printable form of the seed. If it could be swapped while
|
||||
// hdSeed stayed put, the wallet would print a seed phrase that does not
|
||||
// restore it -- strictly worse than printing none.
|
||||
return false;
|
||||
}
|
||||
mnemonicEntropy = entropy;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::HaveMnemonicEntropy() const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
return !mnemonicEntropy.empty();
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::GetMnemonicEntropy(RawHDSeed& entropyOut) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
if (mnemonicEntropy.empty()) {
|
||||
return false;
|
||||
} else {
|
||||
entropyOut = mnemonicEntropy;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
|
||||
@@ -117,6 +117,12 @@ class CBasicKeyStore : public CKeyStore
|
||||
{
|
||||
protected:
|
||||
HDSeed hdSeed;
|
||||
// BIP39 entropy for a mnemonic-recoverable wallet, kept BESIDE hdSeed, never
|
||||
// instead of it. hdSeed holds the bytes actually fed to derivation (the
|
||||
// expanded 64-byte BIP39 seed on new wallets); this record exists only so the
|
||||
// seed phrase can be reprinted. Empty on legacy and hex-restored wallets,
|
||||
// which is a normal state, not an error.
|
||||
RawHDSeed mnemonicEntropy;
|
||||
KeyMap mapKeys;
|
||||
ScriptMap mapScripts;
|
||||
WatchOnlySet setWatchOnly;
|
||||
@@ -129,6 +135,18 @@ public:
|
||||
bool SetHDSeed(const HDSeed& seed);
|
||||
bool HaveHDSeed() const;
|
||||
bool GetHDSeed(HDSeed& seedOut) const;
|
||||
//! Mnemonic entropy: optional, present only on phrase-recoverable wallets.
|
||||
//! Unlike the three seed accessors above -- which override pure virtuals on
|
||||
//! CKeyStore (keystore.h:48-51) and therefore dispatch dynamically -- these
|
||||
//! are plain non-virtual members: CKeyStore declares nothing for them and
|
||||
//! nothing reaches the entropy through a base pointer. Every caller holds a
|
||||
//! CWallet*, whose static type resolves to the CCryptoKeyStore overloads.
|
||||
//! Do NOT add them to CKeyStore: that would force all four subclasses
|
||||
//! (CBasicKeyStore, CCryptoKeyStore, CWallet, gtest's TestCCryptoKeyStore)
|
||||
//! to implement them for zero call sites.
|
||||
bool SetMnemonicEntropy(const RawHDSeed& entropy);
|
||||
bool HaveMnemonicEntropy() const;
|
||||
bool GetMnemonicEntropy(RawHDSeed& entropyOut) const;
|
||||
|
||||
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
|
||||
bool HaveKey(const CKeyID &address) const
|
||||
|
||||
18
src/main.cpp
18
src/main.cpp
@@ -5155,10 +5155,15 @@ int32_t hush_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height);
|
||||
// RAII: save+restore the thread-local RandomX-skip flag around the verify-once dedup in CheckBlock,
|
||||
// so it can never clobber the miner's own fSkipRandomXValidation (TestBlockValidity -> ConnectBlock
|
||||
// re-entry) nor leak TRUE on an exception thrown out of hush_checkPOW.
|
||||
// `arm` is false on paths where CheckBlockHeader did NOT reach its own RandomX check, so the
|
||||
// dedup can never suppress the only verification a block gets.
|
||||
struct ScopedRandomXSkip {
|
||||
bool prev;
|
||||
ScopedRandomXSkip() : prev(GetSkipRandomXValidation()) { SetSkipRandomXValidation(true); }
|
||||
~ScopedRandomXSkip() { SetSkipRandomXValidation(prev); }
|
||||
bool armed;
|
||||
explicit ScopedRandomXSkip(bool arm) : prev(GetSkipRandomXValidation()), armed(arm) {
|
||||
if (armed) SetSkipRandomXValidation(true);
|
||||
}
|
||||
~ScopedRandomXSkip() { if (armed) SetSkipRandomXValidation(prev); }
|
||||
};
|
||||
bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state,
|
||||
libzcash::ProofVerifier& verifier,
|
||||
@@ -5168,7 +5173,8 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C
|
||||
// These are checks that are independent of context.
|
||||
hash = block.GetHash();
|
||||
// Check that the header is valid (particularly PoW). This is mostly redundant with the call in AcceptBlockHeader.
|
||||
if (!CheckBlockHeader(futureblockp,height,pindex,block,state,fCheckPOW))
|
||||
const bool fHeaderChecked = CheckBlockHeader(futureblockp,height,pindex,block,state,fCheckPOW);
|
||||
if (!fHeaderChecked)
|
||||
{
|
||||
if ( *futureblockp == 0 )
|
||||
{
|
||||
@@ -5196,7 +5202,11 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C
|
||||
// that dominates IBD). The scoped guard saves/restores the skip flag (never hardcodes false)
|
||||
// so the miner's own skip is preserved and nothing leaks on throw. Equihash + PoW-target in
|
||||
// hush_checkPOW still run.
|
||||
ScopedRandomXSkip _rxskip;
|
||||
// ARMED ONLY IF fHeaderChecked: CheckBlockHeader returns early -- BEFORE its RandomX
|
||||
// check -- for a future-timestamped block (*futureblockp==1), and CheckBlock keeps
|
||||
// going on that path. There hush_checkPOW is the ONLY RandomX verification, so
|
||||
// arming unconditionally drops the check for that class of block.
|
||||
ScopedRandomXSkip _rxskip(fHeaderChecked);
|
||||
if ( hush_checkPOW(1,(CBlock *)&block,height) < 0 )
|
||||
return state.DoS(100, error("CheckBlock: failed slow_checkPOW"),REJECT_INVALID, "failed-slow_checkPOW");
|
||||
}
|
||||
|
||||
@@ -1581,6 +1581,7 @@ void ThreadDNSAddressSeed()
|
||||
CAddress addr = CAddress(CService(ip, ASSETCHAINS_P2PPORT));
|
||||
addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old
|
||||
vAdd.push_back(addr);
|
||||
found++;
|
||||
}
|
||||
}
|
||||
// TODO: The seed name resolve may fail, yielding an IP of [::], which results in
|
||||
|
||||
@@ -369,6 +369,7 @@ extern UniValue z_gettotalbalance(const UniValue& params, bool fHelp, const CPub
|
||||
extern UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
|
||||
extern UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
|
||||
extern UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
|
||||
extern UniValue z_autoshieldstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
|
||||
extern UniValue z_consolidationstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
|
||||
extern UniValue z_shieldcoinbase(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
|
||||
extern UniValue z_getoperationstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
|
||||
|
||||
512
src/wallet/asyncrpcoperation_autoshieldcoinbase.cpp
Normal file
512
src/wallet/asyncrpcoperation_autoshieldcoinbase.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
// Copyright (c) 2016-2024 The Hush developers
|
||||
// Copyright (c) 2024-2026 The DragonX developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
#include "asyncrpcoperation_autoshieldcoinbase.h"
|
||||
#include "asyncrpcoperation_shieldcoinbase.h" // for ShieldCoinbaseUTXO
|
||||
#include "consensus/upgrades.h"
|
||||
#include "hush_defs.h" // ASSETCHAINS_TIMELOCKGTE
|
||||
#include "init.h"
|
||||
#include "key_io.h"
|
||||
#include "main.h"
|
||||
#include "rpc/protocol.h"
|
||||
#include "sync.h"
|
||||
#include "tinyformat.h"
|
||||
#include "transaction_builder.h"
|
||||
#include "util.h"
|
||||
#include "utilmoneystr.h"
|
||||
#include "wallet.h"
|
||||
|
||||
// Sietch dummy zaddr generator (defined in wallet/rpcwallet.cpp)
|
||||
extern std::string randomSietchZaddr();
|
||||
|
||||
// Serialized-size estimates for one spent input (kept in sync with rpcwallet.cpp)
|
||||
static const size_t AUTOSHIELD_CTXIN_DUST_SIZE = 148;
|
||||
// Every autoshield tx carries THREE Sapling OutputDescriptions -- the change
|
||||
// note to destZaddr plus the two Sietch dummies -- at ~948 bytes each. Reserving
|
||||
// 2000 for "header + sietch outputs" was ~900 bytes short before a single input
|
||||
// was counted, so a large enough round could build a tx over MAX_TX_SIZE.
|
||||
static const size_t AUTOSHIELD_SAPLING_OUTPUT_SIZE = 948;
|
||||
static const size_t AUTOSHIELD_TX_OVERHEAD = (3 * AUTOSHIELD_SAPLING_OUTPUT_SIZE) + 256;
|
||||
// Hard cap on inputs per round, mirroring z_shieldcoinbase's
|
||||
// SHIELD_COINBASE_DEFAULT_LIMIT. The byte estimate alone is not a safe bound:
|
||||
// with a P2PKH coinbase (-mineraddress) the 148-byte figure is exact rather than
|
||||
// conservative, so an under-estimate translates directly into an oversize tx.
|
||||
// The remainder is simply shielded on the next round.
|
||||
static const size_t AUTOSHIELD_MAX_INPUTS = 400;
|
||||
// Unrelated to the cap above despite sharing the value: this is a SIZE IN BYTES for
|
||||
// one spent P2SH input, mirroring CTXIN_SPEND_P2SH_SIZE in rpcwallet.cpp.
|
||||
static const size_t AUTOSHIELD_CTXIN_P2SH_SIZE = 400;
|
||||
// Expire unmined autoshield txs after this many blocks, so a tx cannot straddle
|
||||
// a network-upgrade activation.
|
||||
static const int AUTOSHIELD_EXPIRY_DELTA = 15;
|
||||
|
||||
AsyncRPCOperation_autoshieldcoinbase::AsyncRPCOperation_autoshieldcoinbase(int targetHeight)
|
||||
: targetHeight_(targetHeight) {}
|
||||
|
||||
AsyncRPCOperation_autoshieldcoinbase::~AsyncRPCOperation_autoshieldcoinbase() {}
|
||||
|
||||
void AsyncRPCOperation_autoshieldcoinbase::main() {
|
||||
if (isCancelled()) {
|
||||
// Only the CURRENT op owns the scheduler flag; a stale/cancelled op must
|
||||
// not clear it out from under a freshly-enqueued successor.
|
||||
if (pwalletMain) {
|
||||
LOCK(pwalletMain->cs_wallet);
|
||||
if (getId() == pwalletMain->saplingAutoShieldOperationId) {
|
||||
pwalletMain->fAutoShieldRunning = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
set_state(OperationStatus::EXECUTING);
|
||||
start_execution_clock();
|
||||
|
||||
bool success = false;
|
||||
|
||||
try {
|
||||
success = main_impl();
|
||||
} catch (const UniValue& objError) {
|
||||
int code = find_value(objError, "code").get_int();
|
||||
std::string message = find_value(objError, "message").get_str();
|
||||
set_error_code(code);
|
||||
set_error_message(message);
|
||||
} catch (const runtime_error& e) {
|
||||
set_error_code(-1);
|
||||
set_error_message("runtime error: " + string(e.what()));
|
||||
} catch (const logic_error& e) {
|
||||
set_error_code(-1);
|
||||
set_error_message("logic error: " + string(e.what()));
|
||||
} catch (const exception& e) {
|
||||
set_error_code(-1);
|
||||
set_error_message("general exception: " + string(e.what()));
|
||||
} catch (...) {
|
||||
set_error_code(-2);
|
||||
set_error_message("unknown error");
|
||||
}
|
||||
|
||||
stop_execution_clock();
|
||||
|
||||
// ALWAYS advance the interval and clear the running flag, on success AND
|
||||
// failure AND exception, so a failed/oversized/locked round still lets the
|
||||
// next round fire. Only the CURRENT op does this bookkeeping: if a newer op
|
||||
// has already superseded this one, leave its state untouched.
|
||||
if (pwalletMain) {
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
if (getId() == pwalletMain->saplingAutoShieldOperationId) {
|
||||
int tipHeight = (chainActive.Tip() != NULL) ? chainActive.Tip()->GetHeight() : targetHeight_;
|
||||
pwalletMain->nextAutoShield = pwalletMain->autoShieldInterval + tipHeight;
|
||||
pwalletMain->fAutoShieldRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
set_state(success ? OperationStatus::SUCCESS : OperationStatus::FAILED);
|
||||
setResult();
|
||||
|
||||
LogPrintf("%s: autoshield operation finished (status=%s, txs=%d, shielded=%s)\n",
|
||||
getId(), getStateAsString(), numTxCreated_, FormatMoney(amountShielded_));
|
||||
}
|
||||
|
||||
// Read-only half of destination resolution, shared with init.cpp so the answer to
|
||||
// "where will auto-shielding send?" is available before the first round runs rather
|
||||
// than only after one has fired. Mutates nothing: no key generation, no caching.
|
||||
AutoShieldDestStatus ResolveAutoShieldDestinationReadOnly(
|
||||
libzcash::SaplingPaymentAddress& destOut, std::string& destStrOut, uint32_t& accountOut) {
|
||||
accountOut = AUTOSHIELD_ACCOUNT_NONE;
|
||||
|
||||
// 1. Explicit -autoshieldaddress override (validated as a spendable Sapling
|
||||
// zaddr in init.cpp). This doubles as the per-process cache for whatever
|
||||
// the derivation below resolved on an earlier round.
|
||||
if (!pwalletMain->autoShieldAddress.empty()) {
|
||||
auto decoded = DecodePaymentAddress(pwalletMain->autoShieldAddress);
|
||||
if (boost::get<libzcash::SaplingPaymentAddress>(&decoded) != nullptr) {
|
||||
destOut = boost::get<libzcash::SaplingPaymentAddress>(decoded);
|
||||
destStrOut = pwalletMain->autoShieldAddress;
|
||||
return AutoShieldDestStatus::Resolved;
|
||||
}
|
||||
return AutoShieldDestStatus::InvalidOverride;
|
||||
}
|
||||
|
||||
// 2. Walk the restore window m/32'/coin'/[0, gap)' derived from the seed.
|
||||
HDSeed seed;
|
||||
if (!pwalletMain->GetHDSeedForDerivation(seed)) {
|
||||
return AutoShieldDestStatus::NoSeed;
|
||||
}
|
||||
|
||||
// Mirror init.cpp's own clamp, and cap into the hardened index space so
|
||||
// (i | ZIP32_HARDENED_KEY_LIMIT) below stays well formed.
|
||||
int64_t gapArg = GetArg("-mnemonicsaplinggap", 100);
|
||||
if (gapArg < 0) {
|
||||
gapArg = 0;
|
||||
}
|
||||
if (gapArg > (int64_t)ZIP32_HARDENED_KEY_LIMIT) {
|
||||
gapArg = (int64_t)ZIP32_HARDENED_KEY_LIMIT;
|
||||
}
|
||||
const uint32_t saplingGap = (uint32_t)gapArg;
|
||||
|
||||
// Same derivation path as CWallet::GenerateNewSaplingZKey (wallet.cpp:139-152).
|
||||
const uint32_t bip44CoinType = Params().BIP44CoinType();
|
||||
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
|
||||
auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT);
|
||||
auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT);
|
||||
|
||||
for (uint32_t i = 0; i < saplingGap; i++) {
|
||||
auto xsk = m_32h_cth.Derive(i | ZIP32_HARDENED_KEY_LIMIT);
|
||||
auto addr = xsk.DefaultAddress();
|
||||
|
||||
// Spendable AND registered: GetSaplingExtendedSpendingKey resolves
|
||||
// addr -> ivk -> fvk -> spending key (keystore.cpp:215-223), so a hit
|
||||
// means the wallet both recognises notes sent to `addr` and can spend
|
||||
// them. Exactly the pair of properties the shield needs. Lowest index
|
||||
// wins: stable for the life of the wallet and reproducible from the seed
|
||||
// alone, unlike std::set order over the random diversifier
|
||||
// (zcash/Address.hpp:95-98).
|
||||
libzcash::SaplingExtendedSpendingKey held;
|
||||
if (pwalletMain->GetSaplingExtendedSpendingKey(addr, held)) {
|
||||
destOut = addr;
|
||||
destStrOut = EncodePaymentAddress(addr);
|
||||
accountOut = i;
|
||||
return AutoShieldDestStatus::Resolved;
|
||||
}
|
||||
}
|
||||
|
||||
return AutoShieldDestStatus::NotFound;
|
||||
}
|
||||
|
||||
// Resolve the Sapling destination for auto-shielded coinbase.
|
||||
//
|
||||
// Recoverability is the hard requirement: coinbase we shield must land in an
|
||||
// address that a bare -mnemonic/-hdseed restore of THIS wallet's seed re-derives
|
||||
// on its own. A restore pre-derives exactly -mnemonicsaplinggap sapling accounts
|
||||
// starting at index 0, with saplingAccountCounter reset to 0 (init.cpp:2349-2355),
|
||||
// so the only self-recoverable destinations are the default addresses of
|
||||
// m/32'/<coin>'/i' for i < gap.
|
||||
//
|
||||
// We therefore DERIVE those accounts from the seed and pick the lowest index the
|
||||
// wallet already holds. Deriving is the only authoritative test. In particular
|
||||
// CKeyMetadata is NOT evidence of provenance: z_importkey / z_importwallet copy
|
||||
// both hdKeypath and seedFp verbatim out of the import source
|
||||
// (wallet.cpp:5522-5529 <- rpcdump.cpp:511-516), so a foreign key can claim any
|
||||
// keypath and any seed fingerprint. Filtering on metadata would let an imported
|
||||
// key win as "account 0" and silently receive every shielded reward.
|
||||
//
|
||||
// Caller must hold cs_wallet and must already have checked the wallet is unlocked.
|
||||
bool AsyncRPCOperation_autoshieldcoinbase::resolveDestination(
|
||||
libzcash::SaplingPaymentAddress& destOut, std::string& destStrOut) {
|
||||
|
||||
uint32_t account = AUTOSHIELD_ACCOUNT_NONE;
|
||||
switch (ResolveAutoShieldDestinationReadOnly(destOut, destStrOut, account)) {
|
||||
case AutoShieldDestStatus::Resolved:
|
||||
// Cache for the life of the process; the override branch of the resolver
|
||||
// short-circuits later rounds. Safe: we only cache post-validation.
|
||||
pwalletMain->autoShieldAddress = destStrOut;
|
||||
if (account == AUTOSHIELD_ACCOUNT_NONE) {
|
||||
LogPrintf("%s: autoshield destination %s (configured)\n", getId(), destStrOut);
|
||||
} else {
|
||||
LogPrintf("%s: autoshield destination %s (seed-derived sapling account %u)\n",
|
||||
getId(), destStrOut, (unsigned)account);
|
||||
}
|
||||
return true;
|
||||
case AutoShieldDestStatus::InvalidOverride:
|
||||
LogPrintf("%s: configured -autoshieldaddress is not a valid Sapling address\n", getId());
|
||||
return false;
|
||||
case AutoShieldDestStatus::NoSeed:
|
||||
LogPrintf("%s: no HD seed available; refusing to pick an autoshield destination\n", getId());
|
||||
return false;
|
||||
case AutoShieldDestStatus::NotFound:
|
||||
break; // nothing in the window yet: fall through and derive one
|
||||
}
|
||||
|
||||
// Re-establish the derivation context the resolver used, for step 3 below.
|
||||
HDSeed seed;
|
||||
if (!pwalletMain->GetHDSeedForDerivation(seed)) {
|
||||
LogPrintf("%s: no HD seed available; refusing to pick an autoshield destination\n", getId());
|
||||
return false;
|
||||
}
|
||||
int64_t gapArg = GetArg("-mnemonicsaplinggap", 100);
|
||||
if (gapArg < 0) {
|
||||
gapArg = 0;
|
||||
}
|
||||
if (gapArg > (int64_t)ZIP32_HARDENED_KEY_LIMIT) {
|
||||
gapArg = (int64_t)ZIP32_HARDENED_KEY_LIMIT;
|
||||
}
|
||||
const uint32_t saplingGap = (uint32_t)gapArg;
|
||||
const uint32_t bip44CoinType = Params().BIP44CoinType();
|
||||
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
|
||||
auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT);
|
||||
auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT);
|
||||
|
||||
// 3. Nothing usable in the window yet: derive the next account, but only if
|
||||
// GenerateNewSaplingZKey will land INSIDE the window. It does NOT derive
|
||||
// at saplingAccountCounter: its do/while skips every index whose spending
|
||||
// key we already hold (wallet.cpp:150-157), so a bare "counter < gap"
|
||||
// test is unsound - counter 98 with gap 100 can still land on 100.
|
||||
// Predict min{ i >= counter : we do not hold i } instead.
|
||||
const uint32_t counter = pwalletMain->GetHDChain().saplingAccountCounter;
|
||||
uint32_t predicted = saplingGap; // sentinel: "would land outside the window"
|
||||
for (uint32_t i = counter; i < saplingGap; i++) {
|
||||
auto xsk = m_32h_cth.Derive(i | ZIP32_HARDENED_KEY_LIMIT);
|
||||
if (!pwalletMain->HaveSaplingSpendingKey(xsk.expsk.full_viewing_key())) {
|
||||
predicted = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (predicted == saplingGap) {
|
||||
LogPrintf("%s: no free sapling account below -mnemonicsaplinggap=%u (account counter is %u). "
|
||||
"A newly derived z-address would NOT be re-derived by a seed restore, so the "
|
||||
"shielded coinbase could not be recovered from the seed alone. Refusing to "
|
||||
"autoshield this round. Fix: point -autoshieldaddress at an existing in-gap "
|
||||
"wallet z-address, or raise -mnemonicsaplinggap here AND use the same value on "
|
||||
"any future restore.\n",
|
||||
getId(), (unsigned)saplingGap, (unsigned)counter);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pwalletMain->IsLocked()) {
|
||||
LogPrintf("%s: wallet is locked; cannot derive an autoshield destination z-address\n", getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
auto expectedAddr = m_32h_cth.Derive(predicted | ZIP32_HARDENED_KEY_LIMIT).DefaultAddress();
|
||||
libzcash::SaplingPaymentAddress newAddr = pwalletMain->GenerateNewSaplingZKey();
|
||||
|
||||
// Post-verify rather than trust the prediction: cheap, and it closes the
|
||||
// whole class of "the counter moved further than expected" bugs.
|
||||
if (!(newAddr == expectedAddr)) {
|
||||
LogPrintf("%s: newly derived z-address is not sapling account %u as predicted "
|
||||
"(counter %u -> %u); not using it as the autoshield destination\n",
|
||||
getId(), (unsigned)predicted, (unsigned)counter,
|
||||
(unsigned)pwalletMain->GetHDChain().saplingAccountCounter);
|
||||
return false;
|
||||
}
|
||||
|
||||
destOut = newAddr;
|
||||
destStrOut = EncodePaymentAddress(newAddr);
|
||||
pwalletMain->autoShieldAddress = destStrOut;
|
||||
LogPrintf("%s: generated new autoshield destination z-address %s (seed-derived sapling account %u)\n",
|
||||
getId(), destStrOut, (unsigned)predicted);
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("%s: could not generate a destination z-address: %s\n", getId(), e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncRPCOperation_autoshieldcoinbase::main_impl() {
|
||||
auto opid = getId();
|
||||
LogPrintf("%s: Beginning asyncrpcoperation_autoshieldcoinbase.\n", opid);
|
||||
auto consensusParams = Params().GetConsensus();
|
||||
|
||||
int tipHeight;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
tipHeight = (chainActive.Tip() != NULL) ? chainActive.Tip()->GetHeight() : targetHeight_;
|
||||
}
|
||||
|
||||
// Don't create a tx that could be mined before, but expire after, a NU
|
||||
// activation. Key this off tipHeight (the height we actually set the expiry
|
||||
// from below), not the stale enqueue-time targetHeight_, so a queue delay
|
||||
// cannot slip a straddling expiry past this guard.
|
||||
auto nextActivationHeight = NextActivationHeight(tipHeight, consensusParams);
|
||||
if (nextActivationHeight && tipHeight + AUTOSHIELD_EXPIRY_DELTA >= nextActivationHeight.get()) {
|
||||
LogPrintf("%s: autoshield tx could expire across a NU activation. Skipping this round.\n", opid);
|
||||
return true;
|
||||
}
|
||||
|
||||
libzcash::SaplingPaymentAddress destZaddr;
|
||||
std::string destStr;
|
||||
std::vector<ShieldCoinbaseUTXO> inputs;
|
||||
|
||||
// Proof building below runs WITHOUT cs_wallet (deliberately, so wallet RPCs
|
||||
// are not stalled), which leaves a multi-second window in which a manual
|
||||
// z_shieldcoinbase or z_sendmany over the same miner address would re-select
|
||||
// these same coinbase outputs. AvailableCoins honours IsLockedCoin, so lock
|
||||
// them for the duration exactly as z_shieldcoinbase does. RAII because there
|
||||
// are several early returns between here and commit, and a leaked lock would
|
||||
// silently exclude those coins from every future round.
|
||||
struct ScopedCoinLocks {
|
||||
std::vector<COutPoint> locked;
|
||||
~ScopedCoinLocks() {
|
||||
// A destructor is noexcept by default; letting the lock acquisition
|
||||
// escape would turn a contended mutex into std::terminate.
|
||||
try {
|
||||
if (locked.empty()) return;
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
// UnlockCoin takes a non-const reference (upstream signature).
|
||||
for (COutPoint& op : locked) pwalletMain->UnlockCoin(op);
|
||||
} catch (...) {}
|
||||
}
|
||||
} coinLocks;
|
||||
CAmount shieldedValue = 0;
|
||||
unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING;
|
||||
|
||||
{
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
// Defensive: the scheduler already skips while locked, but the wallet
|
||||
// could have been locked between enqueue and execution.
|
||||
if (pwalletMain->IsLocked()) {
|
||||
LogPrintf("%s: wallet is locked, skipping autoshield round\n", opid);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!resolveDestination(destZaddr, destStr)) {
|
||||
LogPrintf("%s: no spendable destination z-address available, skipping\n", opid);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Gather matured, spendable coinbase UTXOs, byte-capped to a single tx.
|
||||
// AvailableCoins excludes immature coinbase unconditionally (wallet.cpp,
|
||||
// `IsCoinBase() && GetBlocksToMaturity() > 0`) and only ever returns outputs
|
||||
// we own, so external -mineraddress / pool coinbase yields zero inputs. The
|
||||
// second argument here is fOnlyConfirmed, not fOnlySpendable.
|
||||
size_t estimatedTxSize = AUTOSHIELD_TX_OVERHEAD;
|
||||
std::vector<COutput> vecOutputs;
|
||||
pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, true);
|
||||
for (const COutput& out : vecOutputs) {
|
||||
if (!out.fSpendable || !out.tx->IsCoinBase()) {
|
||||
continue;
|
||||
}
|
||||
CTxDestination address;
|
||||
if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
|
||||
continue;
|
||||
}
|
||||
size_t increase = (boost::get<CScriptID>(&address) != nullptr)
|
||||
? AUTOSHIELD_CTXIN_P2SH_SIZE : AUTOSHIELD_CTXIN_DUST_SIZE;
|
||||
if (inputs.size() >= AUTOSHIELD_MAX_INPUTS) {
|
||||
LogPrintf("%s: reached per-round input cap (%d); deferring remaining coinbase to next round\n",
|
||||
opid, (int)AUTOSHIELD_MAX_INPUTS);
|
||||
break;
|
||||
}
|
||||
if (estimatedTxSize + increase >= max_tx_size) {
|
||||
// Size-safe batch; the remainder is shielded next round.
|
||||
LogPrintf("%s: reached per-tx size cap; deferring remaining coinbase to next round\n", opid);
|
||||
break;
|
||||
}
|
||||
estimatedTxSize += increase;
|
||||
ShieldCoinbaseUTXO utxo = { out.tx->GetHash(), out.i,
|
||||
out.tx->vout[out.i].scriptPubKey,
|
||||
out.tx->vout[out.i].nValue };
|
||||
inputs.push_back(utxo);
|
||||
shieldedValue += out.tx->vout[out.i].nValue;
|
||||
}
|
||||
|
||||
for (const ShieldCoinbaseUTXO& t : inputs) {
|
||||
COutPoint outpt(t.txid, t.vout);
|
||||
pwalletMain->LockCoin(outpt);
|
||||
coinLocks.locked.push_back(outpt);
|
||||
}
|
||||
}
|
||||
|
||||
CAmount fee = pwalletMain->autoShieldFee;
|
||||
|
||||
if (inputs.size() < (size_t)pwalletMain->autoShieldMinUtxos) {
|
||||
LogPrintf("%s: %d matured coinbase utxo(s) < min %d, skipping this round\n",
|
||||
opid, (int)inputs.size(), pwalletMain->autoShieldMinUtxos);
|
||||
return true;
|
||||
}
|
||||
if (shieldedValue <= fee) {
|
||||
LogPrintf("%s: matured coinbase value %s <= fee %s, skipping\n",
|
||||
opid, FormatMoney(shieldedValue), FormatMoney(fee));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Common outgoing viewing key derived from the HD seed, exactly as
|
||||
// z_shieldcoinbase does for t->z (keeps the note recoverable).
|
||||
HDSeed seed;
|
||||
if (!pwalletMain->GetHDSeedForDerivation(seed)) {
|
||||
LogPrintf("%s: HD seed not available, skipping\n", opid);
|
||||
return true;
|
||||
}
|
||||
uint256 ovk = ovkForShieldingFromTaddr(seed);
|
||||
|
||||
// Build the t->z shield tx. Proof generation happens in Build() WITHOUT
|
||||
// holding cs_wallet (mirrors the sweep op) so we don't stall wallet RPCs.
|
||||
// tipHeight, not targetHeight_: the builder's height selects the consensus
|
||||
// branch id (transaction_builder.cpp CurrentEpochBranchId), and the NU-straddle
|
||||
// guard above plus SetExpiryHeight below are both keyed off tipHeight. Using the
|
||||
// stale enqueue-time height here meant the guard was checking a height the
|
||||
// transaction was not actually signed against.
|
||||
auto builder = TransactionBuilder(consensusParams, tipHeight, pwalletMain);
|
||||
builder.SetExpiryHeight(tipHeight + AUTOSHIELD_EXPIRY_DELTA);
|
||||
builder.SetFee(fee);
|
||||
|
||||
for (const auto& t : inputs) {
|
||||
if (t.amount >= ASSETCHAINS_TIMELOCKGTE) {
|
||||
builder.SetLockTime((uint32_t)tipHeight);
|
||||
builder.AddTransparentInput(COutPoint(t.txid, t.vout), t.scriptPubKey, t.amount, 0xfffffffe);
|
||||
} else {
|
||||
builder.AddTransparentInput(COutPoint(t.txid, t.vout), t.scriptPubKey, t.amount);
|
||||
}
|
||||
}
|
||||
|
||||
// All input value (less fee) goes back to our own z-address as change.
|
||||
builder.SendChangeTo(destZaddr, ovk);
|
||||
|
||||
// Sietch padding: mirror z_shieldcoinbase's two dummy zouts so autoshield
|
||||
// txs are structurally indistinguishable from manual coinbase shields.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
auto zdust = DecodePaymentAddress(randomSietchZaddr());
|
||||
if (IsValidPaymentAddress(zdust)) {
|
||||
builder.AddSaplingOutput(ovk, boost::get<libzcash::SaplingPaymentAddress>(zdust), 0);
|
||||
}
|
||||
}
|
||||
|
||||
auto maybe_tx = builder.Build();
|
||||
if (!maybe_tx) {
|
||||
LogPrintf("%s: Failed to build autoshield transaction.\n", opid);
|
||||
return false;
|
||||
}
|
||||
CTransaction tx = maybe_tx.get();
|
||||
|
||||
if (isCancelled()) {
|
||||
LogPrintf("%s: Cancelled before commit.\n", opid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pwalletMain->CommitAutomatedTx(tx)) {
|
||||
LogPrintf("%s: shielded %s coinbase (%d utxos) into %s via txid=%s\n",
|
||||
opid, FormatMoney(shieldedValue - fee), (int)inputs.size(),
|
||||
destStr, tx.GetHash().ToString());
|
||||
amountShielded_ += shieldedValue - fee;
|
||||
shieldTxIds_.push_back(tx.GetHash().ToString());
|
||||
numTxCreated_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
LogPrintf("%s: autoshield tx FAILED in CommitTransaction, txid=%s\n", opid, tx.GetHash().ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
void AsyncRPCOperation_autoshieldcoinbase::setResult() {
|
||||
UniValue res(UniValue::VOBJ);
|
||||
res.push_back(Pair("num_tx_created", numTxCreated_));
|
||||
res.push_back(Pair("amount_shielded", FormatMoney(amountShielded_)));
|
||||
UniValue txIds(UniValue::VARR);
|
||||
for (const std::string& txId : shieldTxIds_) {
|
||||
txIds.push_back(txId);
|
||||
}
|
||||
res.push_back(Pair("shield_txids", txIds));
|
||||
set_result(res);
|
||||
}
|
||||
|
||||
void AsyncRPCOperation_autoshieldcoinbase::cancel() {
|
||||
// Cancelling is how the scheduler stops an in-flight round, so unlike the base
|
||||
// class this must be able to move an EXECUTING operation to CANCELLED. What it
|
||||
// must not do is overwrite a state that is already terminal: the scheduler
|
||||
// cancels the previous operation when it enqueues the next one, and that one may
|
||||
// have already SUCCEEDED, whose result would otherwise be relabelled as cancelled.
|
||||
if (isSuccess() || isFailed() || isCancelled())
|
||||
return;
|
||||
set_state(OperationStatus::CANCELLED);
|
||||
}
|
||||
|
||||
UniValue AsyncRPCOperation_autoshieldcoinbase::getStatus() const {
|
||||
UniValue v = AsyncRPCOperation::getStatus();
|
||||
UniValue obj = v.get_obj();
|
||||
obj.push_back(Pair("method", "autoshieldcoinbase"));
|
||||
obj.push_back(Pair("target_height", targetHeight_));
|
||||
return obj;
|
||||
}
|
||||
78
src/wallet/asyncrpcoperation_autoshieldcoinbase.h
Normal file
78
src/wallet/asyncrpcoperation_autoshieldcoinbase.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2016-2024 The Hush developers
|
||||
// Copyright (c) 2024-2026 The DragonX developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
#ifndef ASYNCRPCOPERATION_AUTOSHIELDCOINBASE_H
|
||||
#define ASYNCRPCOPERATION_AUTOSHIELDCOINBASE_H
|
||||
|
||||
#include "amount.h"
|
||||
#include "asyncrpcoperation.h"
|
||||
#include "univalue.h"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "zcash/zip32.h"
|
||||
|
||||
// Default fee for automatic coinbase-shielding transactions
|
||||
static const CAmount DEFAULT_AUTOSHIELD_FEE = 10000;
|
||||
|
||||
// Sentinel for "not a derived account" (i.e. the configured -autoshieldaddress).
|
||||
static const uint32_t AUTOSHIELD_ACCOUNT_NONE = UINT32_MAX;
|
||||
|
||||
enum class AutoShieldDestStatus {
|
||||
Resolved, // destOut/destStrOut are set
|
||||
NotFound, // no in-gap account held yet; the operation will derive one
|
||||
InvalidOverride, // -autoshieldaddress is set but is not a Sapling address
|
||||
NoSeed, // no HD seed available (e.g. locked wallet)
|
||||
};
|
||||
|
||||
// Resolve the auto-shield destination WITHOUT mutating the wallet: the configured
|
||||
// -autoshieldaddress if set, else the lowest in-gap seed-derived account the wallet
|
||||
// already holds. It deliberately does NOT generate a key, so init can call it purely
|
||||
// to answer "where will this send?" -- deriving a fresh account as a side effect of
|
||||
// populating a status field would be wrong. The operation's own resolveDestination
|
||||
// falls through to generation when this returns NotFound.
|
||||
// Caller must hold cs_wallet.
|
||||
AutoShieldDestStatus ResolveAutoShieldDestinationReadOnly(
|
||||
libzcash::SaplingPaymentAddress& destOut, std::string& destStrOut, uint32_t& accountOut);
|
||||
|
||||
// A periodic, wallet-local operation that drains matured *transparent* coinbase
|
||||
// UTXOs into a wallet-owned Sapling z-address in size-bounded batches. It is the
|
||||
// automatic sibling of the manual z_shieldcoinbase RPC and mirrors the dispatch
|
||||
// model of AsyncRPCOperation_sweep (self-gathers on the async worker thread,
|
||||
// commits via CWallet::CommitAutomatedTx). It never mints a transparent output,
|
||||
// so it respects the ac_private=1 transparent-output ban, and it deliberately
|
||||
// does NOT toggle mining (unlike z_shieldcoinbase) so it can run every interval
|
||||
// on a mining node without thrashing the miner.
|
||||
class AsyncRPCOperation_autoshieldcoinbase : public AsyncRPCOperation
|
||||
{
|
||||
public:
|
||||
AsyncRPCOperation_autoshieldcoinbase(int targetHeight);
|
||||
virtual ~AsyncRPCOperation_autoshieldcoinbase();
|
||||
|
||||
// We don't want to be copied or moved around
|
||||
AsyncRPCOperation_autoshieldcoinbase(AsyncRPCOperation_autoshieldcoinbase const&) = delete;
|
||||
AsyncRPCOperation_autoshieldcoinbase(AsyncRPCOperation_autoshieldcoinbase&&) = delete;
|
||||
AsyncRPCOperation_autoshieldcoinbase& operator=(AsyncRPCOperation_autoshieldcoinbase const&) = delete;
|
||||
AsyncRPCOperation_autoshieldcoinbase& operator=(AsyncRPCOperation_autoshieldcoinbase&&) = delete;
|
||||
|
||||
virtual void main();
|
||||
virtual void cancel();
|
||||
virtual UniValue getStatus() const;
|
||||
|
||||
private:
|
||||
int targetHeight_;
|
||||
int numTxCreated_ = 0;
|
||||
CAmount amountShielded_ = 0;
|
||||
std::vector<std::string> shieldTxIds_;
|
||||
|
||||
bool main_impl();
|
||||
|
||||
// Resolve a spendable, wallet-owned Sapling destination: the configured
|
||||
// -autoshieldaddress if set, else the first spendable z-addr the wallet
|
||||
// holds, else a freshly generated one (requires an unlocked wallet).
|
||||
// Returns false if none is available (e.g. locked wallet with no z-addr).
|
||||
bool resolveDestination(libzcash::SaplingPaymentAddress& destOut, std::string& destStrOut);
|
||||
|
||||
void setResult();
|
||||
};
|
||||
|
||||
#endif /* ASYNCRPCOPERATION_AUTOSHIELDCOINBASE_H */
|
||||
@@ -28,8 +28,17 @@ AsyncRPCOperation_saplingconsolidation::AsyncRPCOperation_saplingconsolidation(i
|
||||
AsyncRPCOperation_saplingconsolidation::~AsyncRPCOperation_saplingconsolidation() {}
|
||||
|
||||
void AsyncRPCOperation_saplingconsolidation::main() {
|
||||
if (isCancelled())
|
||||
if (isCancelled()) {
|
||||
// Only the current op owns the scheduler flag; a stale/cancelled op must
|
||||
// not clear it out from under a freshly-enqueued successor.
|
||||
if (pwalletMain) {
|
||||
LOCK(pwalletMain->cs_wallet);
|
||||
if (getId() == pwalletMain->saplingConsolidationOperationId) {
|
||||
pwalletMain->fConsolidationRunning = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
set_state(OperationStatus::EXECUTING);
|
||||
start_execution_clock();
|
||||
@@ -76,6 +85,21 @@ void AsyncRPCOperation_saplingconsolidation::main() {
|
||||
LogPrintf("%s", s);
|
||||
unlock_notes(); // clean up
|
||||
LogPrint("zrpc", "%s: consolidation input notes unlocked\n", getId());
|
||||
|
||||
// Advance the interval and clear the running flag on EVERY terminal state
|
||||
// (success, failure, exception) so consolidation runs once per interval
|
||||
// instead of every block, and a failed round still lets the next one fire.
|
||||
// Only the CURRENT op does this bookkeeping. This fixes the pre-existing
|
||||
// wedge where nextConsolidation never advanced and fConsolidationRunning
|
||||
// was never set/reset.
|
||||
if (pwalletMain) {
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
if (getId() == pwalletMain->saplingConsolidationOperationId) {
|
||||
int tipHeight = (chainActive.Tip() != NULL) ? chainActive.Tip()->GetHeight() : targetHeight_;
|
||||
pwalletMain->nextConsolidation = pwalletMain->consolidationInterval + tipHeight;
|
||||
pwalletMain->fConsolidationRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncRPCOperation_saplingconsolidation::main_impl() {
|
||||
@@ -281,6 +305,13 @@ void AsyncRPCOperation_saplingconsolidation::setConsolidationResult(int numTxCre
|
||||
}
|
||||
|
||||
void AsyncRPCOperation_saplingconsolidation::cancel() {
|
||||
// Cancelling is how the scheduler stops an in-flight round, so unlike the base
|
||||
// class this must be able to move an EXECUTING operation to CANCELLED. What it
|
||||
// must not do is overwrite a state that is already terminal: the scheduler
|
||||
// cancels the previous operation when it enqueues the next one, and that one may
|
||||
// have already SUCCEEDED, whose result would otherwise be relabelled as cancelled.
|
||||
if (isSuccess() || isFailed() || isCancelled())
|
||||
return;
|
||||
set_state(OperationStatus::CANCELLED);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,17 @@ AsyncRPCOperation_sweep::AsyncRPCOperation_sweep(int targetHeight, bool fromRpc)
|
||||
AsyncRPCOperation_sweep::~AsyncRPCOperation_sweep() {}
|
||||
|
||||
void AsyncRPCOperation_sweep::main() {
|
||||
if (isCancelled())
|
||||
if (isCancelled()) {
|
||||
// Only the current op owns the scheduler flag; a stale/cancelled op must
|
||||
// not clear it out from under a freshly-enqueued successor.
|
||||
if (pwalletMain) {
|
||||
LOCK(pwalletMain->cs_wallet);
|
||||
if (getId() == pwalletMain->saplingSweepOperationId) {
|
||||
pwalletMain->fSweepRunning = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
set_state(OperationStatus::EXECUTING);
|
||||
start_execution_clock();
|
||||
@@ -64,6 +73,23 @@ void AsyncRPCOperation_sweep::main() {
|
||||
set_state(OperationStatus::FAILED);
|
||||
}
|
||||
|
||||
// Scheduler bookkeeping, done here so it runs on success AND failure AND
|
||||
// exception (main_impl's terminal code is skipped when it throws). Only the
|
||||
// current op mutates scheduler state. Preserves the "keep draining every
|
||||
// block until swept" model: on a successful-but-incomplete round we leave
|
||||
// fSweepRunning set and nextSweep unadvanced so the next block continues.
|
||||
// On completion OR on failure/exception we release fSweepRunning and back
|
||||
// off one interval — critically, a persistently failing sweep no longer
|
||||
// leaves fSweepRunning stuck true and wedges consolidation + autoshield.
|
||||
if (pwalletMain) {
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
if (getId() == pwalletMain->saplingSweepOperationId && (!success || sweepComplete_)) {
|
||||
int tipHeight = (chainActive.Tip() != NULL) ? chainActive.Tip()->GetHeight() : targetHeight_;
|
||||
pwalletMain->nextSweep = pwalletMain->sweepInterval + tipHeight;
|
||||
pwalletMain->fSweepRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string s = strprintf("%s: Sweep operation finished. (status=%s", getId(), getStateAsString());
|
||||
if (success) {
|
||||
s += strprintf(", success)\n");
|
||||
@@ -314,10 +340,11 @@ bool AsyncRPCOperation_sweep::main_impl() {
|
||||
}
|
||||
}
|
||||
|
||||
if (sweepComplete) {
|
||||
pwalletMain->nextSweep = pwalletMain->sweepInterval + chainActive.Tip()->GetHeight();
|
||||
pwalletMain->fSweepRunning = false;
|
||||
}
|
||||
// Record whether the wallet is fully swept; the scheduler bookkeeping
|
||||
// (advancing nextSweep / clearing fSweepRunning) is done in main() so it
|
||||
// also runs on the failure/exception/cancel paths and cannot wedge the
|
||||
// shared fSweepRunning flag (which now also gates consolidation + autoshield).
|
||||
sweepComplete_ = sweepComplete;
|
||||
|
||||
LogPrintf("%s: Created %d transactions with total output amount=%s, status=%d\n", getId(), numTxCreated, FormatMoney(amountSwept), (int)status);
|
||||
setSweepResult(numTxCreated, amountSwept, sweepTxIds);
|
||||
@@ -337,6 +364,13 @@ void AsyncRPCOperation_sweep::setSweepResult(int numTxCreated, const CAmount& am
|
||||
}
|
||||
|
||||
void AsyncRPCOperation_sweep::cancel() {
|
||||
// Cancelling is how the scheduler stops an in-flight round, so unlike the base
|
||||
// class this must be able to move an EXECUTING operation to CANCELLED. What it
|
||||
// must not do is overwrite a state that is already terminal: the scheduler
|
||||
// cancels the previous operation when it enqueues the next one, and that one may
|
||||
// have already SUCCEEDED, whose result would otherwise be relabelled as cancelled.
|
||||
if (isSuccess() || isFailed() || isCancelled())
|
||||
return;
|
||||
set_state(OperationStatus::CANCELLED);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ public:
|
||||
private:
|
||||
int targetHeight_;
|
||||
bool fromRPC_;
|
||||
// Set by main_impl(): true iff there was nothing left to sweep this round.
|
||||
// Read by main() to decide scheduler bookkeeping. Defaults false so an
|
||||
// exception (which skips main_impl's assignment) is treated as "not done".
|
||||
bool sweepComplete_ = false;
|
||||
|
||||
bool main_impl();
|
||||
|
||||
|
||||
@@ -159,6 +159,34 @@ static bool DecryptHDSeed(
|
||||
return seed.Fingerprint() == seedFp;
|
||||
}
|
||||
|
||||
uint256 MnemonicEntropyFingerprint(const RawHDSeed& entropy)
|
||||
{
|
||||
// The local copy is not gratuitous -- see the declaration in crypter.h.
|
||||
// It is secure_allocator-backed, so it is memory_cleanse()d on destruction
|
||||
// (support/allocators/secure.h:45-52).
|
||||
RawHDSeed tmp(entropy);
|
||||
return HDSeed(tmp).Fingerprint();
|
||||
}
|
||||
|
||||
static bool DecryptMnemonicEntropy(
|
||||
const CKeyingMaterial& vMasterKey,
|
||||
const std::vector<unsigned char>& vchCryptedSecret,
|
||||
const uint256& entropyFp,
|
||||
RawHDSeed& entropyOut)
|
||||
{
|
||||
CKeyingMaterial vchSecret;
|
||||
|
||||
// Use the entropy's fingerprint as IV, mirroring DecryptHDSeed above.
|
||||
if (!DecryptSecret(vMasterKey, vchCryptedSecret, entropyFp, vchSecret))
|
||||
return false;
|
||||
|
||||
// RawHDSeed and CKeyingMaterial are the SAME type (both are
|
||||
// std::vector<unsigned char, secure_allocator<unsigned char>>), so this is a
|
||||
// plain copy of the same bytes, not a reinterpretation.
|
||||
entropyOut = vchSecret;
|
||||
return MnemonicEntropyFingerprint(entropyOut) == entropyFp;
|
||||
}
|
||||
|
||||
static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key)
|
||||
{
|
||||
CKeyingMaterial vchSecret;
|
||||
@@ -233,6 +261,19 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
|
||||
keyPass = true;
|
||||
}
|
||||
}
|
||||
// Deliberately NO arm here for cryptedMnemonicEntropy. This function is
|
||||
// the "some keys decrypt but not all" corruption detector and a keyFail
|
||||
// ends at the assert(false) below. The mnemonic entropy is an optional,
|
||||
// non-spending, display-only record: legacy wallets, hex-restored
|
||||
// wallets and every wallet predating this feature legitimately have a
|
||||
// seed and no entropy, and a wallet whose every key decrypts while its
|
||||
// entropy does not is not corrupt in any sense that should abort the
|
||||
// process -- it simply cannot print its seed phrase. It is decrypted
|
||||
// lazily in GetMnemonicEntropy() instead, so that case becomes a false
|
||||
// return from one RPC while derivation and spending (which read the
|
||||
// seed, not the entropy) carry on. Note the arm above caches nothing
|
||||
// either -- `seed` is discarded; it only votes keyPass/keyFail -- so
|
||||
// nothing is lost by omitting one here.
|
||||
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
|
||||
for (; mi != mapCryptedKeys.end(); ++mi)
|
||||
{
|
||||
@@ -344,6 +385,82 @@ bool CCryptoKeyStore::GetHDSeed(HDSeed& seedOut) const
|
||||
return DecryptHDSeed(vMasterKey, cryptedHDSeed.second, cryptedHDSeed.first, seedOut);
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::SetMnemonicEntropy(const RawHDSeed& entropy)
|
||||
{
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
if (!IsCrypted()) {
|
||||
return CBasicKeyStore::SetMnemonicEntropy(entropy);
|
||||
}
|
||||
|
||||
if (IsLocked())
|
||||
return false;
|
||||
|
||||
if (entropy.empty())
|
||||
return false;
|
||||
|
||||
std::vector<unsigned char> vchCryptedSecret;
|
||||
// Use the entropy's fingerprint as IV
|
||||
// TODO: Handle this properly when we make encryption a supported feature
|
||||
auto entropyFp = MnemonicEntropyFingerprint(entropy);
|
||||
// RawHDSeed IS CKeyingMaterial, so `entropy` binds directly here.
|
||||
if (!EncryptSecret(vMasterKey, entropy, entropyFp, vchCryptedSecret))
|
||||
return false;
|
||||
|
||||
// Virtual: this calls into CWallet to store the crypted entropy to disk.
|
||||
if (!SetCryptedMnemonicEntropy(entropyFp, vchCryptedSecret))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::SetCryptedMnemonicEntropy(
|
||||
const uint256& entropyFp,
|
||||
const std::vector<unsigned char>& vchCryptedSecret)
|
||||
{
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
if (!IsCrypted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cryptedMnemonicEntropy.first.IsNull()) {
|
||||
// Don't allow existing entropy to be changed, mirroring
|
||||
// SetCryptedHDSeed: a phrase that no longer matches the installed
|
||||
// seed is worse than no phrase at all.
|
||||
return false;
|
||||
}
|
||||
|
||||
cryptedMnemonicEntropy = std::make_pair(entropyFp, vchCryptedSecret);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::HaveMnemonicEntropy() const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
if (!IsCrypted())
|
||||
return CBasicKeyStore::HaveMnemonicEntropy();
|
||||
|
||||
return !cryptedMnemonicEntropy.second.empty();
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::GetMnemonicEntropy(RawHDSeed& entropyOut) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
if (!IsCrypted())
|
||||
return CBasicKeyStore::GetMnemonicEntropy(entropyOut);
|
||||
|
||||
if (cryptedMnemonicEntropy.second.empty())
|
||||
return false;
|
||||
|
||||
// Decrypted lazily, on demand, and deliberately NOT in Unlock(): see the
|
||||
// comment there for why the entropy must not vote in the keyPass/keyFail
|
||||
// corruption detector.
|
||||
return DecryptMnemonicEntropy(vMasterKey, cryptedMnemonicEntropy.second,
|
||||
cryptedMnemonicEntropy.first, entropyOut);
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
|
||||
{
|
||||
{
|
||||
@@ -505,6 +622,30 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
|
||||
}
|
||||
hdSeed = HDSeed();
|
||||
}
|
||||
if (!mnemonicEntropy.empty()) {
|
||||
{
|
||||
std::vector<unsigned char> vchCryptedSecret;
|
||||
// Use the entropy's fingerprint as IV
|
||||
// TODO: Handle this properly when we make encryption a supported feature
|
||||
auto entropyFp = MnemonicEntropyFingerprint(mnemonicEntropy);
|
||||
if (!EncryptSecret(vMasterKeyIn, mnemonicEntropy, entropyFp, vchCryptedSecret)) {
|
||||
return false;
|
||||
}
|
||||
// Virtual: calls into CWallet to store the crypted entropy to disk.
|
||||
if (!SetCryptedMnemonicEntropy(entropyFp, vchCryptedSecret)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Drop the plaintext. swap() rather than `= RawHDSeed()`: assigning a
|
||||
// shorter vector destroys the elements but KEEPS the capacity, so the
|
||||
// old bytes would linger in the locked buffer. swap() hands the buffer
|
||||
// to a temporary whose destructor deallocates it, and
|
||||
// secure_allocator::deallocate memory_cleanse()s
|
||||
// (support/allocators/secure.h:45-52). The `hdSeed = HDSeed();` above
|
||||
// has the same weakness but cannot be fixed here: HDSeed's raw vector
|
||||
// is private with no swap accessor (zip32.h:23-33).
|
||||
RawHDSeed().swap(mnemonicEntropy);
|
||||
}
|
||||
BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys)
|
||||
{
|
||||
const CKey &key = mKey.second;
|
||||
|
||||
@@ -138,6 +138,21 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** Keystore which keeps the private keys encrypted.
|
||||
* It derives from the basic key store, which is used if no encryption is active.
|
||||
*/
|
||||
//! Fingerprint of a BIP39 entropy blob, computed exactly as HDSeed::Fingerprint
|
||||
//! does (BLAKE2b, ZCASH_HD_SEED_FP_PERSONAL). It is an IV / integrity tag and a
|
||||
//! wallet.dat record key -- never a key-derivation input. Declared here rather
|
||||
//! than duplicated because three call sites must produce identical bytes:
|
||||
//! CCryptoKeyStore::SetMnemonicEntropy, CCryptoKeyStore::EncryptKeys, and
|
||||
//! CWallet::SetMnemonicEntropy (which keys the plaintext record with it).
|
||||
//!
|
||||
//! It takes a copy internally on purpose: HDSeed's constructor takes a NON-const
|
||||
//! RawHDSeed& (zip32.h:28), so HDSeed(entropy).Fingerprint() does not compile
|
||||
//! against a const reference or a member read from a const method.
|
||||
uint256 MnemonicEntropyFingerprint(const RawHDSeed& entropy);
|
||||
|
||||
/** Keystore which keeps the private keys encrypted.
|
||||
* It derives from the basic key store, which is used if no encryption is active.
|
||||
*/
|
||||
@@ -145,6 +160,10 @@ class CCryptoKeyStore : public CBasicKeyStore
|
||||
{
|
||||
private:
|
||||
std::pair<uint256, std::vector<unsigned char>> cryptedHDSeed;
|
||||
// Encrypted mnemonic entropy, shaped exactly like cryptedHDSeed above:
|
||||
// .first is the entropy's fingerprint (AES IV + integrity tag on decrypt),
|
||||
// .second is the ciphertext.
|
||||
std::pair<uint256, std::vector<unsigned char>> cryptedMnemonicEntropy;
|
||||
CryptedKeyMap mapCryptedKeys;
|
||||
//CryptedSproutSpendingKeyMap mapCryptedSproutSpendingKeys;
|
||||
CryptedSaplingSpendingKeyMap mapCryptedSaplingSpendingKeys;
|
||||
@@ -194,6 +213,14 @@ public:
|
||||
bool SetHDSeed(const HDSeed& seed);
|
||||
bool HaveHDSeed() const;
|
||||
bool GetHDSeed(HDSeed& seedOut) const;
|
||||
//! Mnemonic entropy, mirroring the four HD-seed members above.
|
||||
//! SetCryptedMnemonicEntropy MUST stay virtual for the same reason
|
||||
//! SetCryptedHDSeed is: CWallet overrides it to persist the record, and
|
||||
//! SetMnemonicEntropy() below reaches that override through the vtable.
|
||||
virtual bool SetCryptedMnemonicEntropy(const uint256& entropyFp, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
bool SetMnemonicEntropy(const RawHDSeed& entropy);
|
||||
bool HaveMnemonicEntropy() const;
|
||||
bool GetMnemonicEntropy(RawHDSeed& entropyOut) const;
|
||||
|
||||
virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
|
||||
|
||||
@@ -1039,8 +1039,10 @@ UniValue z_exportmnemonic(const UniValue& params, bool fHelp, const CPubKey& myp
|
||||
"\nReveal the wallet's BIP39 seed phrase (24 words).\n"
|
||||
"The phrase is byte-compatible with SilentDragonXLite: the same words\n"
|
||||
"restore the same transparent and shielded addresses in either wallet.\n"
|
||||
"Only works for wallets created or restored from a mnemonic (see the\n"
|
||||
"-mnemonic and -usemnemonic options). Requires the wallet be unlocked.\n"
|
||||
"New wallets get a seed phrase by default (-usemnemonic=0 opts out);\n"
|
||||
"wallets restored with -mnemonic have one too. Wallets created before\n"
|
||||
"this feature, or from a raw -hdseed, have no phrase -- use\n"
|
||||
"z_exportwallet for those. Requires the wallet be unlocked.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"mnemonic\" : \"word1 ... word24\", (string) the BIP39 seed phrase\n"
|
||||
|
||||
@@ -3345,6 +3345,83 @@ UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue z_autoshieldstatus(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() > 0)
|
||||
throw runtime_error(
|
||||
"z_autoshieldstatus\n"
|
||||
"\nReport the state of automatic coinbase shielding: whether it is on, where it sends,\n"
|
||||
"and -- when it is off -- why.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"autoshield\" : true|false, (boolean) whether auto-shielding is enabled\n"
|
||||
" \"running\" : true|false, (boolean) whether a round is in flight\n"
|
||||
" \"next_autoshield\" : n, (numeric) height of the next round\n"
|
||||
" \"autoshieldinterval\" : n, (numeric) blocks between rounds\n"
|
||||
" \"autoshieldaddress\" : \"zaddr\", (string) resolved destination; empty until first resolved\n"
|
||||
" \"autoshieldfee\" : n, (numeric) fee in puposhis\n"
|
||||
" \"autoshieldminutxos\" : n, (numeric) minimum matured coinbase utxos per round\n"
|
||||
" \"hdseedorigin\" : n, (numeric) 0 unrecorded, 1 created, 2 restored, 3 retrofit, 4 unknown\n"
|
||||
" \"hdseedorigin_desc\" : \"...\", (string) readable form of hdseedorigin\n"
|
||||
" \"seed_recoverable\" : true|false, (boolean) whether a seed phrase can be exported\n"
|
||||
" \"disabled_reason\" : \"...\" (string) why auto-shielding is not running, if it is not\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("z_autoshieldstatus", "")
|
||||
+ HelpExampleRpc("z_autoshieldstatus", "")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.push_back(Pair("autoshield", pwalletMain->fAutoShieldEnabled));
|
||||
ret.push_back(Pair("running", pwalletMain->fAutoShieldRunning));
|
||||
ret.push_back(Pair("next_autoshield", pwalletMain->nextAutoShield));
|
||||
ret.push_back(Pair("autoshieldinterval", pwalletMain->autoShieldInterval));
|
||||
ret.push_back(Pair("autoshieldaddress", pwalletMain->autoShieldAddress));
|
||||
ret.push_back(Pair("autoshieldfee", pwalletMain->autoShieldFee));
|
||||
ret.push_back(Pair("autoshieldminutxos", pwalletMain->autoShieldMinUtxos));
|
||||
|
||||
int origin = pwalletMain->hdSeedOrigin;
|
||||
std::string desc;
|
||||
switch (origin) {
|
||||
case CWallet::HDSEED_ORIGIN_CREATED: desc = "created on an empty wallet"; break;
|
||||
case CWallet::HDSEED_ORIGIN_RESTORED: desc = "restored from -mnemonic/-hdseed"; break;
|
||||
case CWallet::HDSEED_ORIGIN_RETROFIT: desc = "retrofitted onto a pre-existing wallet"; break;
|
||||
case CWallet::HDSEED_ORIGIN_UNKNOWN: desc = "predates provenance recording"; break;
|
||||
default: desc = "not yet recorded"; break;
|
||||
}
|
||||
ret.push_back(Pair("hdseedorigin", origin));
|
||||
ret.push_back(Pair("hdseedorigin_desc", desc));
|
||||
ret.push_back(Pair("seed_recoverable", pwalletMain->IsMnemonicSeed()));
|
||||
|
||||
// Say why it is off. A silent "false" is exactly what made the destination
|
||||
// un-inspectable in the first place.
|
||||
std::string why = "";
|
||||
if (!pwalletMain->fAutoShieldEnabled) {
|
||||
if (origin != CWallet::HDSEED_ORIGIN_CREATED && origin != CWallet::HDSEED_ORIGIN_RESTORED)
|
||||
why = "HD seed origin is not known-recoverable; back the seed up and pass -autoshield=1";
|
||||
else
|
||||
why = "disabled by -autoshield=0";
|
||||
} else if (pwalletMain->IsLocked()) {
|
||||
why = "wallet is locked; rounds are skipped until it is unlocked";
|
||||
} else if (pwalletMain->fSweepRunning || pwalletMain->fConsolidationRunning) {
|
||||
// Autoshield is mutually exclusive with sweep and consolidation. Without
|
||||
// this the RPC reports autoshield=true, running=false and an empty
|
||||
// reason while no round can actually start.
|
||||
why = strprintf("deferred while %s is running; rounds resume when it finishes",
|
||||
pwalletMain->fSweepRunning ? "z_sweep" : "sapling consolidation");
|
||||
} else if (pwalletMain->autoShieldAddress.empty()) {
|
||||
why = "no destination resolved yet; one will be derived from the HD seed on the first round";
|
||||
}
|
||||
ret.push_back(Pair("disabled_reason", why));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue z_listreceivedaddress(const UniValue& params, bool fHelp,const CPubKey&)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
@@ -5466,7 +5543,10 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
// Create operation and add to global queue
|
||||
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
||||
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(builder, contextualTx, fromaddress, taddrRecipients, zaddrRecipients, saplingNoteInputs, nMinDepth, nFee, contextInfo, opret) );
|
||||
q->addOperation(operation);
|
||||
if (!q->addOperation(operation)) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR,
|
||||
"Async RPC queue is shutting down; the operation was not queued");
|
||||
}
|
||||
|
||||
if(fZdebug)
|
||||
LogPrintf("%s: Submitted to async queue\n", __FUNCTION__);
|
||||
@@ -5694,7 +5774,13 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp, const CPubKey& myp
|
||||
// Create operation and add to global queue
|
||||
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
||||
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(builder, contextualTx, inputs, destaddress, nFee, donation, contextInfo) );
|
||||
q->addOperation(operation);
|
||||
// The constructor has already locked the selected coins. Coin locks are
|
||||
// memory-only, so a refused queue at shutdown reclaims them with the process;
|
||||
// what must not happen is returning an opid for work that will never run.
|
||||
if (!q->addOperation(operation)) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR,
|
||||
"Async RPC queue is shutting down; the operation was not queued");
|
||||
}
|
||||
AsyncRPCOperationId operationId = operation->getId();
|
||||
|
||||
// Return continuation information
|
||||
@@ -6048,7 +6134,10 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
|
||||
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
||||
std::shared_ptr<AsyncRPCOperation> operation(
|
||||
new AsyncRPCOperation_mergetoaddress(builder, contextualTx, utxoInputs, saplingNoteInputs, recipient, nFee, contextInfo) );
|
||||
q->addOperation(operation);
|
||||
if (!q->addOperation(operation)) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR,
|
||||
"Async RPC queue is shutting down; the operation was not queued");
|
||||
}
|
||||
AsyncRPCOperationId operationId = operation->getId();
|
||||
|
||||
// Return continuation information
|
||||
@@ -6349,6 +6438,7 @@ static const CRPCCommand commands[] =
|
||||
{ "wallet", "z_gettotalbalance", &z_gettotalbalance, false },
|
||||
{ "wallet", "z_mergetoaddress", &z_mergetoaddress, false },
|
||||
{ "wallet", "z_sweepstatus", &z_sweepstatus, true },
|
||||
{ "wallet", "z_autoshieldstatus", &z_autoshieldstatus, true },
|
||||
{ "wallet", "z_consolidationstatus", &z_consolidationstatus, true },
|
||||
{ "wallet", "z_sendmany", &z_sendmany, false },
|
||||
{ "wallet", "z_shieldcoinbase", &z_shieldcoinbase, false },
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "coins.h"
|
||||
#include "wallet/asyncrpcoperation_saplingconsolidation.h"
|
||||
#include "wallet/asyncrpcoperation_sweep.h"
|
||||
#include "wallet/asyncrpcoperation_autoshieldcoinbase.h"
|
||||
#include <random>
|
||||
#include <limits>
|
||||
#include <thread>
|
||||
@@ -555,6 +556,9 @@ void CWallet::ChainTip(const CBlockIndex *pindex,
|
||||
if (fSweepEnabled) {
|
||||
RunSaplingSweep(pindex->GetHeight());
|
||||
}
|
||||
if (fAutoShieldEnabled) {
|
||||
RunAutoShieldCoinbase(pindex->GetHeight());
|
||||
}
|
||||
if (fTxDeleteEnabled) {
|
||||
DeleteWalletTransactions(pindex);
|
||||
}
|
||||
@@ -581,7 +585,38 @@ void CWallet::RunSaplingSweep(int blockHeight) {
|
||||
if (blockHeight == 0)
|
||||
return;
|
||||
|
||||
AssertLockHeld(cs_wallet);
|
||||
// Take cs_wallet ourselves: ChainTip (the notify-thread caller) does NOT
|
||||
// hold it here, and we mutate fSweepRunning/nextSweep/saplingSweepOperationId
|
||||
// and enqueue below. Matches RunSaplingConsolidation/RunAutoShieldCoinbase.
|
||||
// (The old AssertLockHeld(cs_wallet) was a no-op in release builds and thus
|
||||
// masked an unsynchronized mutation.) cs_wallet is recursive, so this is
|
||||
// safe even on any path that already holds it.
|
||||
LOCK(cs_wallet);
|
||||
|
||||
// Stale-baton guard. A successful-but-incomplete sweep round deliberately
|
||||
// returns with fSweepRunning still set and nextSweep unadvanced (see
|
||||
// AsyncRPCOperation_sweep::main), as a "continue draining next block" baton.
|
||||
// But every early return below leaves that baton set WITHOUT re-dispatching,
|
||||
// and RunSaplingConsolidation -- which is gated on fSweepRunning -- then
|
||||
// returns without advancing nextConsolidation, so the "consolidation is
|
||||
// within 5 blocks" blackout at the top of this function never lifts. That
|
||||
// is a self-sustaining three-way deadlock: sweep waits on consolidation,
|
||||
// consolidation waits on sweep, and autoshield shares the same gate, so a
|
||||
// wedged sweep silently disables coinbase shielding forever.
|
||||
// Only honour the baton while a sweep operation genuinely is in flight.
|
||||
if (fSweepRunning) {
|
||||
std::shared_ptr<AsyncRPCQueue> sweepQueue = getAsyncRPCQueue();
|
||||
std::shared_ptr<AsyncRPCOperation> inFlightSweep =
|
||||
(sweepQueue != nullptr) ? sweepQueue->getOperationForId(saplingSweepOperationId) : nullptr;
|
||||
bool inFlight = (inFlightSweep != nullptr) &&
|
||||
(inFlightSweep->isReady() || inFlightSweep->isExecuting());
|
||||
if (!inFlight) {
|
||||
LogPrintf("%s: clearing stale fSweepRunning at blockHeight=%d (no sweep operation in flight)\n",
|
||||
__func__, blockHeight);
|
||||
fSweepRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fSweepEnabled) {
|
||||
return;
|
||||
}
|
||||
@@ -604,17 +639,33 @@ void CWallet::RunSaplingSweep(int blockHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Don't Run While auto-shield is running.
|
||||
if (fAutoShieldRunning) {
|
||||
LogPrintf("%s: not sweeping since autoshield is currently running at height=%d\n", __func__, blockHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
fSweepRunning = true;
|
||||
|
||||
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
||||
std::shared_ptr<AsyncRPCOperation> lastOperation = q->getOperationForId(saplingSweepOperationId);
|
||||
if (lastOperation != nullptr) {
|
||||
lastOperation->cancel();
|
||||
// Drop it from the queue's map as well. Nothing else ever removes these:
|
||||
// popOperationForId is only reached from z_getoperationresult, so on a node
|
||||
// running this every interval the map grew without bound.
|
||||
q->popOperationForId(saplingSweepOperationId);
|
||||
}
|
||||
pendingSaplingSweepTxs.clear();
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sweep(blockHeight + 5));
|
||||
saplingSweepOperationId = operation->getId();
|
||||
q->addOperation(operation);
|
||||
if (!q->addOperation(operation)) {
|
||||
// Queue is closing (shutdown). Release the flag we just set, or it stays
|
||||
// set with no operation in flight and blocks every later round.
|
||||
LogPrintf("%s: async queue is not accepting operations; skipping this round\n", __func__);
|
||||
fSweepRunning = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CWallet::RunSaplingConsolidation(int blockHeight) {
|
||||
@@ -634,6 +685,12 @@ void CWallet::RunSaplingConsolidation(int blockHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Self-guard: an op is already in flight (nextConsolidation only advances
|
||||
// when it completes). Don't cancel + re-enqueue a fresh op every block.
|
||||
if (fConsolidationRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogPrintf("%s: consolidation enabled at blockHeight=%d fSweepRunning=%d\n", __func__, blockHeight, fSweepRunning );
|
||||
|
||||
if (fSweepRunning) {
|
||||
@@ -641,22 +698,104 @@ void CWallet::RunSaplingConsolidation(int blockHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fAutoShieldRunning) {
|
||||
LogPrintf("%s: not consolidating since autoshield is currently running at height=%d\n", __func__, blockHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
LogPrintf("%s: creating consolidation operation at blockHeight=%d\n", __func__, blockHeight);
|
||||
fConsolidationRunning = true;
|
||||
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
||||
std::shared_ptr<AsyncRPCOperation> lastOperation = q->getOperationForId(saplingConsolidationOperationId);
|
||||
if (lastOperation != nullptr) {
|
||||
lastOperation->cancel();
|
||||
// Drop it from the queue's map as well. Nothing else ever removes these:
|
||||
// popOperationForId is only reached from z_getoperationresult, so on a node
|
||||
// running this every interval the map grew without bound.
|
||||
q->popOperationForId(saplingConsolidationOperationId);
|
||||
}
|
||||
pendingSaplingConsolidationTxs.clear();
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_saplingconsolidation(blockHeight + 5));
|
||||
saplingConsolidationOperationId = operation->getId();
|
||||
q->addOperation(operation);
|
||||
if (!q->addOperation(operation)) {
|
||||
// Queue is closing (shutdown). Release the flag we just set, or it stays
|
||||
// set with no operation in flight and blocks every later round.
|
||||
LogPrintf("%s: async queue is not accepting operations; skipping this round\n", __func__);
|
||||
fConsolidationRunning = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Periodically drain matured transparent coinbase into a wallet-owned Sapling
|
||||
// z-address. Default-ON but conditional: this is enqueue-only (all gathering
|
||||
// happens on the async worker thread inside the op, which is why we must not
|
||||
// take cs_main here — ChainTip runs from the wallet-notify context). It is a
|
||||
// silent no-op wherever it cannot act (locked wallet, no owned coinbase,
|
||||
// external -mineraddress), so it is safe to run on every node.
|
||||
void CWallet::RunAutoShieldCoinbase(int blockHeight) {
|
||||
// Sapling is always active from height 1 on DragonX+HACs.
|
||||
if (blockHeight == 0)
|
||||
return;
|
||||
|
||||
LOCK(cs_wallet);
|
||||
|
||||
if (!fAutoShieldEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextAutoShield > blockHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Self-guard: an op is already in flight (nextAutoShield only advances when
|
||||
// it completes). Don't cancel + re-enqueue a fresh op every block.
|
||||
if (fAutoShieldRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mutual exclusion: sweep/consolidation share the single async worker and
|
||||
// cs_wallet; don't queue an autoshield in the same connected block.
|
||||
if (fSweepRunning || fConsolidationRunning) {
|
||||
LogPrintf("%s: not autoshielding since sweep/consolidation is running at height=%d\n", __func__, blockHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
// Silent no-op while locked: we can neither sign the shield nor derive a
|
||||
// destination z-addr. Advance the interval so we don't retry every block.
|
||||
if (IsLocked()) {
|
||||
LogPrintf("%s: wallet locked; matured coinbase will accumulate until unlocked (height=%d)\n", __func__, blockHeight);
|
||||
nextAutoShield = autoShieldInterval + blockHeight;
|
||||
return;
|
||||
}
|
||||
|
||||
fAutoShieldRunning = true;
|
||||
|
||||
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
||||
std::shared_ptr<AsyncRPCOperation> lastOperation = q->getOperationForId(saplingAutoShieldOperationId);
|
||||
if (lastOperation != nullptr) {
|
||||
lastOperation->cancel();
|
||||
// Drop it from the queue's map as well. Nothing else ever removes these:
|
||||
// popOperationForId is only reached from z_getoperationresult, so on a node
|
||||
// running this every interval the map grew without bound.
|
||||
q->popOperationForId(saplingAutoShieldOperationId);
|
||||
}
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_autoshieldcoinbase(blockHeight + 5));
|
||||
saplingAutoShieldOperationId = operation->getId();
|
||||
if (!q->addOperation(operation)) {
|
||||
// Queue is closing (shutdown). Release the flag we just set, or it stays
|
||||
// set with no operation in flight and blocks every later round.
|
||||
LogPrintf("%s: async queue is not accepting operations; skipping this round\n", __func__);
|
||||
fAutoShieldRunning = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool CWallet::CommitAutomatedTx(const CTransaction& tx) {
|
||||
CWalletTx wtx(this, tx);
|
||||
CReserveKey reservekey(pwalletMain);
|
||||
fprintf(stderr,"%s: %s\n",__func__,tx.ToString().c_str());
|
||||
// No tx dump here: CommitTransaction already LogPrintf's the same wtx.ToString(),
|
||||
// and ToString() emits a line per vin, so with the 400-input autoshield cap this
|
||||
// printed tens of KB to stderr on every automated round.
|
||||
return CommitTransaction(wtx, reservekey);
|
||||
}
|
||||
|
||||
@@ -2388,30 +2527,47 @@ void CWallet::GenerateNewSeed()
|
||||
|
||||
// Opt-in: create the wallet from a fresh BIP39 mnemonic so its 24 words can
|
||||
// be exported (z_exportmnemonic) and used in SilentDragonXLite.
|
||||
if (GetBoolArg("-usemnemonic", false)) {
|
||||
//
|
||||
// NO SILENT FALLBACK. Falling back to a random seed here produced a wallet
|
||||
// that looks mnemonic-capable but whose words can never be exported
|
||||
// (z_exportmnemonic refuses non-mnemonic wallets, rpcdump.cpp:1031+) and
|
||||
// that no seed phrase can restore. A user who asked for -usemnemonic must
|
||||
// get that or a hard failure.
|
||||
if (GetBoolArg("-usemnemonic", true)) {
|
||||
RawHDSeed entropy;
|
||||
if (GenerateMnemonicEntropy(256, entropy)) {
|
||||
HDSeed seed(entropy);
|
||||
if (InstallHDSeed(seed, true, nCreationTime))
|
||||
if (!GenerateMnemonicEntropy(256, entropy))
|
||||
throw std::runtime_error(std::string(__func__) + ": -usemnemonic entropy generation failed");
|
||||
|
||||
// Store the EXPANDED 64-byte BIP39 seed as the HD seed, with
|
||||
// fMnemonic = false. Every binary -- old or new -- then feeds the stored
|
||||
// bytes straight into derivation, so the key tree is identical
|
||||
// everywhere and no CHDChain version bump or minversion fence is needed.
|
||||
// The 32-byte entropy is kept in a separate, display-only record purely
|
||||
// so the phrase can be reprinted. Addresses are unchanged from the
|
||||
// previous format, which expanded the stored entropy on every read.
|
||||
RawHDSeed seed64;
|
||||
if (!Bip39SeedFromEntropy(entropy, seed64))
|
||||
throw std::runtime_error(std::string(__func__) + ": BIP39 seed expansion failed");
|
||||
HDSeed seed(seed64);
|
||||
|
||||
// ORDER IS LOAD-BEARING: seed first, entropy second, never the reverse.
|
||||
// A crash between the two leaves a wallet with a seed and no phrase --
|
||||
// recoverable via z_exportwallet, merely inconvenient. The reverse order
|
||||
// would leave entropy with no seed; the next start would mint a
|
||||
// DIFFERENT seed while the wallet still held a phrase for the old one.
|
||||
// (GetMnemonicPhrase cross-checks the two and would refuse to print it,
|
||||
// but do not rely on that here.)
|
||||
if (!InstallHDSeed(seed, false, nCreationTime))
|
||||
throw std::runtime_error(std::string(__func__) + ": installing the mnemonic HD seed failed");
|
||||
if (!SetMnemonicEntropy(entropy))
|
||||
throw std::runtime_error(std::string(__func__) + ": storing the mnemonic entropy failed");
|
||||
return;
|
||||
}
|
||||
LogPrintf("%s: -usemnemonic seed generation failed, falling back to a random seed\n", __func__);
|
||||
}
|
||||
|
||||
auto seed = HDSeed::Random(HD_WALLET_SEED_LENGTH);
|
||||
|
||||
// If the wallet is encrypted and locked, this will fail.
|
||||
if (!SetHDSeed(seed))
|
||||
auto seed = HDSeed::Random(HD_WALLET_SEED_LENGTH);
|
||||
if (!InstallHDSeed(seed, false, nCreationTime))
|
||||
throw std::runtime_error(std::string(__func__) + ": SetHDSeed failed");
|
||||
|
||||
// store the key creation time together with
|
||||
// the child index counter in the database
|
||||
// as a hdchain object
|
||||
CHDChain newHdChain;
|
||||
newHdChain.nVersion = CHDChain::VERSION_HD_TRANSPARENT;
|
||||
newHdChain.seedFp = seed.Fingerprint();
|
||||
newHdChain.nCreateTime = nCreationTime;
|
||||
SetHDChain(newHdChain, false);
|
||||
}
|
||||
|
||||
bool CWallet::SetHDSeed(const HDSeed& seed)
|
||||
@@ -2445,14 +2601,38 @@ bool CWallet::SetCryptedHDSeed(const uint256& seedFp, const std::vector<unsigned
|
||||
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
if (pwalletdbEncryption)
|
||||
return pwalletdbEncryption->WriteCryptedHDSeed(seedFp, vchCryptedSecret);
|
||||
else
|
||||
return CWalletDB(strWalletFile).WriteCryptedHDSeed(seedFp, vchCryptedSecret);
|
||||
// Write the encrypted record, then drop the plaintext one. Both go
|
||||
// through the same CWalletDB (and therefore the same transaction when
|
||||
// EncryptWallet supplied pwalletdbEncryption), because CDB::Rewrite at
|
||||
// the end of EncryptWallet copies every surviving record into the fresh
|
||||
// file -- a leftover plaintext "hdseed" would keep the unencrypted seed
|
||||
// on disk for the life of the wallet.
|
||||
//
|
||||
// The erase is deliberately best-effort: a hard failure here propagates
|
||||
// into CCryptoKeyStore::EncryptKeys, which CWallet::EncryptWallet turns
|
||||
// into assert(false) with half the keys encrypted in memory. A logged
|
||||
// warning is strictly better than that.
|
||||
if (pwalletdbEncryption) {
|
||||
if (!pwalletdbEncryption->WriteCryptedHDSeed(seedFp, vchCryptedSecret))
|
||||
return false;
|
||||
if (!pwalletdbEncryption->EraseHDSeed(seedFp))
|
||||
LogPrintf("%s: WARNING: could not erase the plaintext hdseed record; "
|
||||
"the unencrypted HD seed may remain in wallet.dat\n", __func__);
|
||||
return true;
|
||||
} else {
|
||||
CWalletDB walletdb(strWalletFile);
|
||||
if (!walletdb.WriteCryptedHDSeed(seedFp, vchCryptedSecret))
|
||||
return false;
|
||||
if (!walletdb.EraseHDSeed(seedFp))
|
||||
LogPrintf("%s: WARNING: could not erase the plaintext hdseed record; "
|
||||
"the unencrypted HD seed may remain in wallet.dat\n", __func__);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CWallet::SetHDChain(const CHDChain& chain, bool memonly)
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
@@ -2462,6 +2642,20 @@ void CWallet::SetHDChain(const CHDChain& chain, bool memonly)
|
||||
hdChain = chain;
|
||||
}
|
||||
|
||||
void CWallet::SetHDSeedOrigin(int origin)
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
|
||||
hdSeedOrigin = origin;
|
||||
|
||||
// Deliberately non-fatal, unlike SetHDChain: losing this record must never
|
||||
// stop a node from starting. The cost of a failed write is that the next
|
||||
// start re-classifies the wallet, and re-classification of an already-seeded
|
||||
// wallet yields HDSEED_ORIGIN_UNKNOWN, i.e. the safe answer.
|
||||
if (fFileBacked && !CWalletDB(strWalletFile).WriteHDSeedOrigin((int64_t)origin))
|
||||
LogPrintf("%s: WARNING: could not record HD seed origin %d in wallet.dat\n", __func__, origin);
|
||||
}
|
||||
|
||||
bool CWallet::LoadHDSeed(const HDSeed& seed)
|
||||
{
|
||||
return CBasicKeyStore::SetHDSeed(seed);
|
||||
@@ -2471,21 +2665,94 @@ bool CWallet::LoadCryptedHDSeed(const uint256& seedFp, const std::vector<unsigne
|
||||
{
|
||||
return CCryptoKeyStore::SetCryptedHDSeed(seedFp, seed);
|
||||
}
|
||||
bool CWallet::SetMnemonicEntropy(const RawHDSeed& entropy)
|
||||
{
|
||||
if (!CCryptoKeyStore::SetMnemonicEntropy(entropy)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fFileBacked) {
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
if (!IsCrypted()) {
|
||||
// Keyed by fingerprint exactly as "hdseed" is, so ReadKeyValue can
|
||||
// integrity-check it and EraseMnemonicEntropy can find it later.
|
||||
return CWalletDB(strWalletFile).WriteMnemonicEntropy(
|
||||
MnemonicEntropyFingerprint(entropy), entropy);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::SetCryptedMnemonicEntropy(const uint256& entropyFp, const std::vector<unsigned char>& vchCryptedSecret)
|
||||
{
|
||||
if (!CCryptoKeyStore::SetCryptedMnemonicEntropy(entropyFp, vchCryptedSecret)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fFileBacked) {
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
// Same write-then-erase discipline as SetCryptedHDSeed: CDB::Rewrite at
|
||||
// the end of EncryptWallet copies every surviving record, so a leftover
|
||||
// plaintext "mnementropy" would keep the seed phrase recoverable from an
|
||||
// encrypted wallet.dat. The erase is best-effort for the same reason: a
|
||||
// hard failure would propagate into EncryptKeys -> assert(false).
|
||||
if (pwalletdbEncryption) {
|
||||
if (!pwalletdbEncryption->WriteCryptedMnemonicEntropy(entropyFp, vchCryptedSecret))
|
||||
return false;
|
||||
if (!pwalletdbEncryption->EraseMnemonicEntropy(entropyFp))
|
||||
LogPrintf("%s: WARNING: could not erase the plaintext mnementropy record\n", __func__);
|
||||
return true;
|
||||
} else {
|
||||
CWalletDB walletdb(strWalletFile);
|
||||
if (!walletdb.WriteCryptedMnemonicEntropy(entropyFp, vchCryptedSecret))
|
||||
return false;
|
||||
if (!walletdb.EraseMnemonicEntropy(entropyFp))
|
||||
LogPrintf("%s: WARNING: could not erase the plaintext mnementropy record\n", __func__);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CWallet::LoadMnemonicEntropy(const RawHDSeed& entropy)
|
||||
{
|
||||
return CBasicKeyStore::SetMnemonicEntropy(entropy);
|
||||
}
|
||||
|
||||
bool CWallet::LoadCryptedMnemonicEntropy(const uint256& entropyFp, const std::vector<unsigned char>& vchCryptedSecret)
|
||||
{
|
||||
return CCryptoKeyStore::SetCryptedMnemonicEntropy(entropyFp, vchCryptedSecret);
|
||||
}
|
||||
|
||||
bool CWallet::InstallHDSeed(const HDSeed& seed, bool fMnemonic, int64_t nCreateTime)
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
|
||||
if (!SetHDSeed(seed))
|
||||
return false;
|
||||
|
||||
// Chain BEFORE seed. A crash between the two records must never leave a
|
||||
// wallet that holds a seed with no hdchain: on the next load hdChain would
|
||||
// silently revert to its defaults, clearing fMnemonicSeed (which switches
|
||||
// the derivation input, wallet.cpp:2615-2633) and resetting
|
||||
// saplingAccountCounter. The opposite torn state — chain without seed — is
|
||||
// harmless and self-healing: HaveHDSeed() is false, so init installs a seed
|
||||
// again and overwrites the chain.
|
||||
CHDChain newHdChain;
|
||||
newHdChain.nVersion = fMnemonic ? CHDChain::VERSION_HD_MNEMONIC
|
||||
: CHDChain::VERSION_HD_TRANSPARENT;
|
||||
newHdChain.seedFp = seed.Fingerprint();
|
||||
newHdChain.nCreateTime = nCreateTime;
|
||||
newHdChain.fMnemonicSeed = fMnemonic;
|
||||
SetHDChain(newHdChain, false);
|
||||
SetHDChain(newHdChain, false); // throws if the write fails
|
||||
|
||||
if (!SetHDSeed(seed))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2524,10 +2791,29 @@ bool CWallet::SetHDSeedFromMnemonic(const std::string& phrase)
|
||||
if (!MnemonicToEntropy(phrase, entropy))
|
||||
return false;
|
||||
|
||||
// Store the BIP39 entropy as the HDSeed (SilentDragonXLite's on-disk
|
||||
// convention); the 64-byte seed is expanded from it on demand.
|
||||
HDSeed seed(entropy);
|
||||
return InstallHDSeed(seed, true, 1); // birthday = genesis for a restore
|
||||
// Store the EXPANDED 64-byte BIP39 seed as the HD seed (fMnemonic = false);
|
||||
// the entropy goes in its own record and is used only to reprint the phrase.
|
||||
// Derivation therefore reads the stored bytes directly on any binary, and
|
||||
// the resulting addresses are byte-identical to the previous format, which
|
||||
// expanded the stored entropy on every derivation. SilentDragonXLite
|
||||
// interop is unaffected: the same words still yield the same seed64.
|
||||
RawHDSeed seed64;
|
||||
if (!Bip39SeedFromEntropy(entropy, seed64))
|
||||
return false;
|
||||
HDSeed seed(seed64);
|
||||
|
||||
// Seed first, entropy second -- see the ordering note in GenerateNewSeed.
|
||||
if (!InstallHDSeed(seed, false, 1)) // birthday = genesis for a restore
|
||||
return false;
|
||||
|
||||
// Non-fatal on a restore, unlike GenerateNewSeed: the user already holds the
|
||||
// phrase (they just typed it), the seed is installed and the wallet is fully
|
||||
// functional; only z_exportmnemonic is lost.
|
||||
if (!SetMnemonicEntropy(entropy)) {
|
||||
LogPrintf("%s: WARNING: HD seed installed but the mnemonic entropy record could not be "
|
||||
"stored; z_exportmnemonic will be unavailable on this wallet\n", __func__);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::GetHDSeedForDerivation(HDSeed& seedOut) const
|
||||
@@ -2552,6 +2838,36 @@ bool CWallet::GetHDSeedForDerivation(HDSeed& seedOut) const
|
||||
|
||||
bool CWallet::GetMnemonicPhrase(std::string& phraseOut) const
|
||||
{
|
||||
// Preferred form: the HD seed is the EXPANDED 64-byte BIP39 seed and the
|
||||
// entropy sits in its own record.
|
||||
RawHDSeed entropy;
|
||||
if (GetMnemonicEntropy(entropy)) { // false on an encrypted+locked wallet
|
||||
// NEVER hand out a phrase that does not restore THIS wallet. Prove the
|
||||
// entropy expands to the exact bytes derivation consumes; if it does
|
||||
// not (a torn install, a wallet.dat edited by hand, an entropy record
|
||||
// paired with a different seed), refuse rather than print a phrase that
|
||||
// silently restores someone else's key tree. Costs one PBKDF2 on a
|
||||
// user-initiated RPC.
|
||||
RawHDSeed seed64;
|
||||
if (!Bip39SeedFromEntropy(entropy, seed64))
|
||||
return false;
|
||||
|
||||
HDSeed derivationSeed;
|
||||
if (!GetHDSeedForDerivation(derivationSeed))
|
||||
return false;
|
||||
|
||||
if (derivationSeed.RawSeed() != seed64) {
|
||||
LogPrintf("%s: refusing to export a seed phrase: the stored mnemonic entropy does not "
|
||||
"expand to this wallet's HD seed\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
return EntropyToMnemonic(entropy, phraseOut);
|
||||
}
|
||||
|
||||
// Legacy form (earlier builds of this branch): the stored HD seed IS the
|
||||
// 32-byte BIP39 entropy, expanded on every derivation. Consistent by
|
||||
// construction, so no cross-check is possible or needed.
|
||||
if (!hdChain.fMnemonicSeed)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -784,10 +784,8 @@ private:
|
||||
TxNullifiers mapTxSaplingNullifiers;
|
||||
|
||||
std::vector<CTransaction> pendingSaplingConsolidationTxs;
|
||||
AsyncRPCOperationId saplingConsolidationOperationId;
|
||||
|
||||
std::vector<CTransaction> pendingSaplingSweepTxs;
|
||||
AsyncRPCOperationId saplingSweepOperationId;
|
||||
|
||||
void AddToTransparentSpends(const COutPoint& outpoint, const uint256& wtxid);
|
||||
void AddToSaplingSpends(const uint256& nullifier, const uint256& wtxid);
|
||||
@@ -802,6 +800,9 @@ public:
|
||||
int64_t nWitnessCacheSize;
|
||||
bool needsRescan = false;
|
||||
int nextConsolidation = 0;
|
||||
// Id of the in-flight consolidation op; read by the op to confirm it is
|
||||
// still the current one before mutating scheduler state.
|
||||
AsyncRPCOperationId saplingConsolidationOperationId;
|
||||
|
||||
bool fSaplingConsolidationEnabled = false;
|
||||
bool fConsolidationRunning = false;
|
||||
@@ -809,6 +810,12 @@ public:
|
||||
bool fSweepExternalEnabled = false;
|
||||
bool fSweepRunning = false;
|
||||
|
||||
// Automatic coinbase shielding (t->z). Default ON but conditional: it is a
|
||||
// silent no-op on nodes where it cannot act (no wallet, external
|
||||
// -mineraddress, non-mining, or locked wallet). See RunAutoShieldCoinbase.
|
||||
bool fAutoShieldEnabled = true;
|
||||
bool fAutoShieldRunning = false;
|
||||
|
||||
std::atomic<bool> fAbortRescan{false};
|
||||
// abort current rescan
|
||||
void AbortRescan() { fAbortRescan = true; }
|
||||
@@ -823,6 +830,9 @@ public:
|
||||
int rescanStartHeight = 0;
|
||||
|
||||
int nextSweep = 0;
|
||||
// Id of the in-flight sweep op; read by the op to confirm it is still the
|
||||
// current one before mutating scheduler state.
|
||||
AsyncRPCOperationId saplingSweepOperationId;
|
||||
int amountSwept = 0;
|
||||
int amountConsolidated = 0;
|
||||
int sweepInterval = 10;
|
||||
@@ -833,6 +843,31 @@ public:
|
||||
std::vector<std::string> sweepExcludeAddresses;
|
||||
std::string consolidationAddress = "";
|
||||
|
||||
int nextAutoShield = 0;
|
||||
int autoShieldInterval = 25;
|
||||
CAmount autoShieldFee = 10000;
|
||||
// Minimum matured coinbase UTXOs before a round fires, to avoid per-interval
|
||||
// fee churn on a single freshly-matured reward.
|
||||
int autoShieldMinUtxos = 1;
|
||||
// Configured destination z-addr override; also used to cache the resolved
|
||||
// wallet-owned destination so we keep reusing one address.
|
||||
std::string autoShieldAddress = "";
|
||||
// Id of the in-flight autoshield op; read by the op to confirm it is still
|
||||
// the current one before mutating scheduler state.
|
||||
AsyncRPCOperationId saplingAutoShieldOperationId;
|
||||
// Provenance of this wallet's HD seed, recorded once in wallet.dat the
|
||||
// first time a build that knows about it opens the wallet. Features that
|
||||
// move funds into addresses only the seed can re-derive must not turn
|
||||
// themselves ON by default unless the user can actually restore that seed.
|
||||
enum HDSeedOrigin {
|
||||
HDSEED_ORIGIN_UNRECORDED = 0, // no record in wallet.dat yet
|
||||
HDSEED_ORIGIN_CREATED = 1, // minted onto a brand-new empty wallet
|
||||
HDSEED_ORIGIN_RESTORED = 2, // user supplied -mnemonic / -hdseed
|
||||
HDSEED_ORIGIN_RETROFIT = 3, // minted onto a pre-existing seedless wallet
|
||||
HDSEED_ORIGIN_UNKNOWN = 4, // seed predates this record
|
||||
};
|
||||
int hdSeedOrigin = HDSEED_ORIGIN_UNRECORDED;
|
||||
|
||||
void ClearNoteWitnessCache();
|
||||
|
||||
int64_t NullifierCount();
|
||||
@@ -1224,6 +1259,7 @@ public:
|
||||
const CBlock *pblock,
|
||||
boost::optional<std::pair<SproutMerkleTree, SaplingMerkleTree>> added);
|
||||
void RunSaplingConsolidation(int blockHeight);
|
||||
void RunAutoShieldCoinbase(int blockHeight);
|
||||
bool CommitAutomatedTx(const CTransaction& tx);
|
||||
/** Saves witness caches and best block locator to disk. */
|
||||
void SetBestChain(const CBlockLocator& loc);
|
||||
@@ -1309,6 +1345,14 @@ public:
|
||||
|
||||
bool SetHDSeed(const HDSeed& seed);
|
||||
bool SetCryptedHDSeed(const uint256& seedFp, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
/* Record this wallet's BIP39 entropy so its seed phrase can be reprinted.
|
||||
Display-only: derivation never reads it (the HD seed holds the bytes that
|
||||
are actually derived from). Refuses to replace an existing record.
|
||||
SetCryptedMnemonicEntropy overrides the CCryptoKeyStore virtual so the
|
||||
record reaches disk; SetMnemonicEntropy merely hides the base version,
|
||||
which is safe because no call site holds a base pointer. */
|
||||
bool SetMnemonicEntropy(const RawHDSeed& entropy);
|
||||
bool SetCryptedMnemonicEntropy(const uint256& entropyFp, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
|
||||
/* Restore a wallet's HD seed from a hex string (as exported in the
|
||||
z_exportwallet "# HDSeed=" comment): 32 bytes for a legacy raw seed, or
|
||||
@@ -1327,8 +1371,14 @@ public:
|
||||
wallet and the seed is available (unlocked). Returns false otherwise. */
|
||||
bool GetMnemonicPhrase(std::string& phraseOut) const;
|
||||
|
||||
/* True if the HD seed was derived from a BIP39 mnemonic (stored as entropy). */
|
||||
bool IsMnemonicSeed() const { return hdChain.fMnemonicSeed; }
|
||||
/* True if this wallet has a BIP39 seed phrase available. Two storage forms
|
||||
qualify:
|
||||
- current: the HD seed is the EXPANDED 64-byte BIP39 seed and the
|
||||
entropy lives in its own record (HaveMnemonicEntropy());
|
||||
- legacy: hdChain.fMnemonicSeed, where the stored HD seed IS the
|
||||
32-byte entropy and is expanded on every derivation.
|
||||
Gates z_exportmnemonic (rpcdump.cpp). */
|
||||
bool IsMnemonicSeed() const { return hdChain.fMnemonicSeed || HaveMnemonicEntropy(); }
|
||||
|
||||
/* Return the seed to feed into HD derivation. For mnemonic wallets this
|
||||
expands the stored 32-byte entropy into the 64-byte BIP39 seed; for legacy
|
||||
@@ -1349,11 +1399,22 @@ public:
|
||||
void SetHDChain(const CHDChain& chain, bool memonly);
|
||||
const CHDChain& GetHDChain() const { return hdChain; }
|
||||
|
||||
/* Record (in memory and in wallet.dat) how this wallet's HD seed came to
|
||||
exist. Best-effort: a failed write is logged, not fatal — the next start
|
||||
simply re-classifies, and re-classification always errs toward
|
||||
HDSEED_ORIGIN_UNKNOWN, which is the conservative answer. */
|
||||
void SetHDSeedOrigin(int origin);
|
||||
|
||||
/* Set the current HD seed, without saving it to disk (used by LoadWallet) */
|
||||
bool LoadHDSeed(const HDSeed& key);
|
||||
|
||||
/* Set the current encrypted HD seed, without saving it to disk (used by LoadWallet) */
|
||||
bool LoadCryptedHDSeed(const uint256& seedFp, const std::vector<unsigned char>& seed);
|
||||
/* Set the mnemonic entropy, without saving it to disk (used by LoadWallet) */
|
||||
bool LoadMnemonicEntropy(const RawHDSeed& entropy);
|
||||
|
||||
/* Set the encrypted mnemonic entropy, without saving it to disk (used by LoadWallet) */
|
||||
bool LoadCryptedMnemonicEntropy(const uint256& entropyFp, const std::vector<unsigned char>& vchCryptedSecret);
|
||||
|
||||
/* Find notes filtered by payment address, min depth, ability to spend */
|
||||
void GetFilteredNotes(std::vector<SaplingNoteEntry>& saplingEntries,
|
||||
|
||||
@@ -34,6 +34,15 @@
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
// Out-of-line definitions for CHDChain's in-class static constants. These are
|
||||
// only initialised in the class body, so any ODR use -- binding one to a const
|
||||
// reference, which is exactly what gtest's EXPECT_*/ASSERT_* macros do -- needs
|
||||
// a definition or the link fails. hush-gtest hit this on VERSION_HD_MNEMONIC.
|
||||
const int CHDChain::VERSION_HD_BASE;
|
||||
const int CHDChain::VERSION_HD_TRANSPARENT;
|
||||
const int CHDChain::VERSION_HD_MNEMONIC;
|
||||
const int CHDChain::CURRENT_VERSION;
|
||||
|
||||
using namespace std;
|
||||
|
||||
static uint64_t nAccountingEntryNumber = 0;
|
||||
@@ -216,6 +225,12 @@ bool CWalletDB::WriteWitnessCacheSize(int64_t nWitnessCacheSize)
|
||||
return Write(std::string("witnesscachesize"), nWitnessCacheSize);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteHDSeedOrigin(int64_t nOrigin)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
return Write(std::string("hdseedorigin"), nOrigin);
|
||||
}
|
||||
|
||||
bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
|
||||
{
|
||||
return Read(std::make_pair(std::string("pool"), nPool), keypool);
|
||||
@@ -403,12 +418,19 @@ public:
|
||||
bool fAnyUnordered;
|
||||
int nFileVersion;
|
||||
vector<uint256> vWalletUpgrade;
|
||||
// True once a well-formed "hdchain" record has been loaded.
|
||||
bool fHDChainRead;
|
||||
// True when that record had to be repaired on read (see the "hdchain" case
|
||||
// in ReadKeyValue); LoadWallet rewrites it in full form afterwards.
|
||||
bool fHDChainRepaired;
|
||||
|
||||
CWalletScanState() {
|
||||
nKeys = nCKeys = nKeyMeta = nZKeys = nCZKeys = nZKeyMeta = nSapZAddrs = 0;
|
||||
fIsEncrypted = false;
|
||||
fAnyUnordered = false;
|
||||
nFileVersion = 0;
|
||||
fHDChainRead = false;
|
||||
fHDChainRepaired = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -833,9 +855,90 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
||||
else if (strType == "hdchain")
|
||||
{
|
||||
CHDChain chain;
|
||||
// Keep an untouched copy: a failed >> has already consumed part of ssValue.
|
||||
CDataStream ssRetry(ssValue.begin(), ssValue.end(), ssValue.GetType(), ssValue.GetVersion());
|
||||
try {
|
||||
ssValue >> chain;
|
||||
} catch (...) {
|
||||
// Downgrade repair. A build predating VERSION_HD_TRANSPARENT writes
|
||||
// this record back with only the four base fields while leaving
|
||||
// nVersion at whatever it read, so the version-gated reads above run
|
||||
// off the end. Without this, one address generated under such a build
|
||||
// makes the wallet unopenable here ("Wallet corrupted") even though
|
||||
// nothing is actually lost.
|
||||
//
|
||||
// Recovering is safe for a v1/v2 record: everything derivation needs
|
||||
// is either in the four-field prefix or in the separate hdseed record,
|
||||
// and the trailing counters are self-healing -- DeriveNewChildKey and
|
||||
// GenerateNewSaplingZKey both skip indices whose key the wallet
|
||||
// already holds, so restarting a counter at 0 re-walks past existing
|
||||
// keys instead of reissuing them.
|
||||
chain = CHDChain();
|
||||
try {
|
||||
ssRetry >> chain.nVersion;
|
||||
ssRetry >> chain.seedFp;
|
||||
ssRetry >> chain.nCreateTime;
|
||||
ssRetry >> chain.saplingAccountCounter;
|
||||
} catch (...) {
|
||||
// Short even in the base fields: genuinely corrupt.
|
||||
strErr = "Error reading wallet database: hdchain record is corrupt";
|
||||
return false;
|
||||
}
|
||||
if (chain.nVersion >= CHDChain::VERSION_HD_MNEMONIC) {
|
||||
// A record claiming to carry fMnemonicSeed must not have it
|
||||
// guessed: that flag selects the derivation input, so defaulting
|
||||
// it wrong yields a different key tree in silence. Fail loud, as
|
||||
// this branch always did.
|
||||
strErr = "Error reading wallet database: hdchain record is corrupt";
|
||||
return false;
|
||||
}
|
||||
chain.transparentChildCounter = 0;
|
||||
chain.fMnemonicSeed = false;
|
||||
wss.fHDChainRepaired = true;
|
||||
LogPrintf("Repairing a truncated hdchain record (nVersion=%d): it was last written by "
|
||||
"a wallet build that predates the transparent HD counter\n", chain.nVersion);
|
||||
}
|
||||
wss.fHDChainRead = true;
|
||||
pwallet->SetHDChain(chain, true);
|
||||
}
|
||||
else if (strType == "hdseedorigin")
|
||||
{
|
||||
int64_t nOrigin = 0;
|
||||
ssValue >> nOrigin;
|
||||
pwallet->hdSeedOrigin = (int)nOrigin;
|
||||
}
|
||||
else if (strType == "mnementropy")
|
||||
{
|
||||
uint256 entropyFp;
|
||||
RawHDSeed entropy;
|
||||
ssKey >> entropyFp;
|
||||
ssValue >> entropy;
|
||||
|
||||
if (MnemonicEntropyFingerprint(entropy) != entropyFp)
|
||||
{
|
||||
strErr = "Error reading wallet database: mnemonic entropy corrupt";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pwallet->LoadMnemonicEntropy(entropy))
|
||||
{
|
||||
strErr = "Error reading wallet database: LoadMnemonicEntropy failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (strType == "cmnementropy")
|
||||
{
|
||||
uint256 entropyFp;
|
||||
vector<unsigned char> vchCryptedSecret;
|
||||
ssKey >> entropyFp;
|
||||
ssValue >> vchCryptedSecret;
|
||||
if (!pwallet->LoadCryptedMnemonicEntropy(entropyFp, vchCryptedSecret))
|
||||
{
|
||||
strErr = "Error reading wallet database: LoadCryptedMnemonicEntropy failed";
|
||||
return false;
|
||||
}
|
||||
wss.fIsEncrypted = true;
|
||||
}
|
||||
} catch (...)
|
||||
{
|
||||
return false;
|
||||
@@ -847,6 +950,10 @@ static bool IsKeyType(string strType)
|
||||
{
|
||||
return (strType == "key" || strType == "wkey" ||
|
||||
strType == "hdseed" || strType == "chdseed" ||
|
||||
// The mnemonic entropy must survive a keys-only salvage: without it
|
||||
// a recovered wallet keeps its seed (and stays fully spendable) but
|
||||
// silently loses the ability to reprint its seed phrase.
|
||||
strType == "mnementropy" || strType == "cmnementropy" ||
|
||||
strType == "zkey" || strType == "czkey" ||
|
||||
strType == "sapzkey" || strType == "csapzkey" ||
|
||||
strType == "vkey" ||
|
||||
@@ -947,6 +1054,35 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
|
||||
if (fNoncriticalErrors && result == DB_LOAD_OK)
|
||||
result = DB_NONCRITICAL_ERROR;
|
||||
|
||||
// Rewrite a repaired record in full form so the next load is clean and the
|
||||
// transparent counter starts being persisted again.
|
||||
if (wss.fHDChainRepaired && pwallet->HaveHDSeed())
|
||||
{
|
||||
try {
|
||||
pwallet->SetHDChain(pwallet->GetHDChain(), false);
|
||||
LogPrintf("Rewrote the repaired hdchain record in full form\n");
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("Could not rewrite the repaired hdchain record: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// A wallet that holds an HD seed but whose hdchain record is missing or
|
||||
// unreadable is NOT safe to run. hdChain would fall back to its SetNull
|
||||
// defaults (walletdb.h:105-113), which (a) clears fMnemonicSeed, switching
|
||||
// HD derivation from the 64-byte BIP39 seed to the raw 32-byte entropy
|
||||
// (CWallet::GetHDSeedForDerivation, wallet.cpp:2615-2633) -> an entirely
|
||||
// different key tree, and (b) resets saplingAccountCounter to 0, so the
|
||||
// next GenerateNewSaplingZKey walks back over accounts that already exist.
|
||||
// Both are silent today (a bad hdchain read is only DB_NONCRITICAL_ERROR).
|
||||
// Fail loud instead of quietly deriving into the wrong tree.
|
||||
if (pwallet->HaveHDSeed() && !wss.fHDChainRead)
|
||||
{
|
||||
LogPrintf("Error loading wallet.dat: HD seed present but the hdchain record is missing or corrupt. "
|
||||
"Recover by restoring from the seed phrase: move wallet.dat aside and start with "
|
||||
"-mnemonic=\"<your seed phrase>\" -rescan\n");
|
||||
return DB_CORRUPT;
|
||||
}
|
||||
|
||||
// Any wallet corruption at all: skip any rewriting or
|
||||
// upgrading, we don't want to make it worse.
|
||||
if (result != DB_LOAD_OK)
|
||||
@@ -1240,7 +1376,13 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKe
|
||||
fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
|
||||
wss, strType, strErr);
|
||||
}
|
||||
if (!IsKeyType(strType))
|
||||
// "hdchain" is not a key type, but it must survive a keys-only
|
||||
// salvage: a recovered wallet that keeps its seed while losing its
|
||||
// hdchain silently derives from a different key tree (fMnemonicSeed
|
||||
// cleared -> raw entropy instead of the 64-byte BIP39 seed) and
|
||||
// re-issues sapling accounts from 0. CWalletDB::LoadWallet now
|
||||
// refuses such a wallet outright, so preserve the record here.
|
||||
if (!IsKeyType(strType) && strType != "hdchain")
|
||||
continue;
|
||||
if (!fReadOK)
|
||||
{
|
||||
@@ -1290,6 +1432,34 @@ bool CWalletDB::WriteCryptedHDSeed(const uint256& seedFp, const std::vector<unsi
|
||||
return Write(std::make_pair(std::string("chdseed"), seedFp), vchCryptedSecret);
|
||||
}
|
||||
|
||||
bool CWalletDB::EraseHDSeed(const uint256& seedFp)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
// CDB::Erase honours activeTxn, so when this runs inside EncryptWallet's
|
||||
// transaction the erase commits or aborts atomically with the chdseed write.
|
||||
// It also returns true for DB_NOTFOUND, so erasing a record that was never
|
||||
// written (e.g. a wallet encrypted at creation time) is not a failure.
|
||||
return Erase(std::make_pair(std::string("hdseed"), seedFp));
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteMnemonicEntropy(const uint256& entropyFp, const RawHDSeed& entropy)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
return Write(std::make_pair(std::string("mnementropy"), entropyFp), entropy);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteCryptedMnemonicEntropy(const uint256& entropyFp, const std::vector<unsigned char>& vchCryptedSecret)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
return Write(std::make_pair(std::string("cmnementropy"), entropyFp), vchCryptedSecret);
|
||||
}
|
||||
|
||||
bool CWalletDB::EraseMnemonicEntropy(const uint256& entropyFp)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
return Erase(std::make_pair(std::string("mnementropy"), entropyFp));
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteHDChain(const CHDChain& chain)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
|
||||
@@ -191,6 +191,9 @@ public:
|
||||
|
||||
bool WriteWitnessCacheSize(int64_t nWitnessCacheSize);
|
||||
|
||||
//! Record how this wallet's HD seed came to exist (CWallet::HDSeedOrigin).
|
||||
bool WriteHDSeedOrigin(int64_t nOrigin);
|
||||
|
||||
bool ReadPool(int64_t nPool, CKeyPool& keypool);
|
||||
bool WritePool(int64_t nPool, const CKeyPool& keypool);
|
||||
bool ErasePool(int64_t nPool);
|
||||
@@ -219,6 +222,17 @@ public:
|
||||
|
||||
bool WriteHDSeed(const HDSeed& seed);
|
||||
bool WriteCryptedHDSeed(const uint256& seedFp, const std::vector<unsigned char>& vchCryptedSecret);
|
||||
//! Remove the PLAINTEXT hdseed record. Must be called once the seed has been
|
||||
//! written in encrypted form: CDB::Rewrite (invoked at the end of
|
||||
//! CWallet::EncryptWallet) copies whatever records still exist into the new
|
||||
//! file, so a leftover "hdseed" leaves the unencrypted seed on disk forever.
|
||||
bool EraseHDSeed(const uint256& seedFp);
|
||||
//! BIP39 entropy for a phrase-recoverable wallet. Display-only: derivation
|
||||
//! never reads it. Record names are deliberately distinct prefixes from
|
||||
//! "hdseed"/"chdseed" so they cannot collide.
|
||||
bool WriteMnemonicEntropy(const uint256& entropyFp, const RawHDSeed& entropy);
|
||||
bool WriteCryptedMnemonicEntropy(const uint256& entropyFp, const std::vector<unsigned char>& vchCryptedSecret);
|
||||
bool EraseMnemonicEntropy(const uint256& entropyFp);
|
||||
//! write the hdchain model (external chain child index counter)
|
||||
bool WriteHDChain(const CHDChain& chain);
|
||||
|
||||
|
||||
@@ -35,7 +35,13 @@ then
|
||||
ls -la build/librandomx*
|
||||
else
|
||||
mkdir build && cd build
|
||||
CC="${CC} -g " CXX="${CXX} -g " cmake -DARCH=native ..
|
||||
# ARCH=default, NOT native. RandomX's CMakeLists maps ARCH=native to -march=native, which
|
||||
# tunes the binary to the BUILD machine. Measured 2026-08-21: a build on the Zen4 pool box
|
||||
# emitted 746 AVX-512 (zmm) instructions into librandomx.a, and every seed reports
|
||||
# avx512f=no -- that binary SIGILLs inside RandomX on the whole fleet, and on any user CPU
|
||||
# older than the build host. ARCH=default still enables -maes plus per-file -mssse3/-mavx2
|
||||
# for argon2, so the portable baseline costs ~nothing.
|
||||
CC="${CC} -g " CXX="${CXX} -g " cmake -DARCH=default ..
|
||||
make
|
||||
fi
|
||||
|
||||
|
||||
@@ -147,7 +147,13 @@ then
|
||||
ls -la build/librandomx*
|
||||
else
|
||||
mkdir build && cd build
|
||||
cmake -DARCH=native ..
|
||||
# ARCH=default, NOT native. RandomX's CMakeLists maps ARCH=native to -march=native, which
|
||||
# tunes the binary to the BUILD machine. Measured 2026-08-21: a build on the Zen4 pool box
|
||||
# emitted 746 AVX-512 (zmm) instructions into librandomx.a, and every seed reports
|
||||
# avx512f=no -- that binary SIGILLs inside RandomX on the whole fleet, and on any user CPU
|
||||
# older than the build host. ARCH=default still enables -maes plus per-file -mssse3/-mavx2
|
||||
# for argon2, so the portable baseline costs ~nothing.
|
||||
cmake -DARCH=default ..
|
||||
# pass along potential -jX and other args
|
||||
time make "$@"
|
||||
fi
|
||||
|
||||
@@ -134,7 +134,13 @@ then
|
||||
ls -la build/librandomx*
|
||||
else
|
||||
mkdir build && cd build
|
||||
cmake -DARCH=native ..
|
||||
# ARCH=default, NOT native. RandomX's CMakeLists maps ARCH=native to -march=native, which
|
||||
# tunes the binary to the BUILD machine. Measured 2026-08-21: a build on the Zen4 pool box
|
||||
# emitted 746 AVX-512 (zmm) instructions into librandomx.a, and every seed reports
|
||||
# avx512f=no -- that binary SIGILLs inside RandomX on the whole fleet, and on any user CPU
|
||||
# older than the build host. ARCH=default still enables -maes plus per-file -mssse3/-mavx2
|
||||
# for argon2, so the portable baseline costs ~nothing.
|
||||
cmake -DARCH=default ..
|
||||
make
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user