Compare commits
79
Commits
v1.0.3
...
7131b3673f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7131b3673f | ||
|
|
70b35ac11a | ||
|
|
5b36d7ad24 | ||
|
|
9cb31326e3 | ||
|
|
d91e069da3 | ||
|
|
69bb035e7e | ||
|
|
da191e447d | ||
|
|
fc26e2e0a0 | ||
|
|
1d2d5f32d0 | ||
|
|
c1040028e4 | ||
|
|
a6b6f80db0 | ||
|
|
3aac75e94f | ||
|
|
5634aed750 | ||
|
|
db42091ce3 | ||
|
|
6d282db216 | ||
|
|
c5fde12485 | ||
|
|
e2e10f6ef8 | ||
|
|
af7d9e2300 | ||
|
|
4cc7e0491a | ||
|
|
5e0a706839 | ||
|
|
60d66022f6 | ||
|
|
3b2aa866aa | ||
|
|
2d6359ea74 | ||
|
|
fa16e740b6 | ||
|
|
5f40c8ede0 | ||
|
|
b3e81f1eda | ||
|
|
d05302d450 | ||
|
|
2232868d9f | ||
|
|
1745ee4e63 | ||
|
|
11766ec61b | ||
|
|
004808403f | ||
|
|
e55c67b0eb | ||
|
|
4a2e531cb6 | ||
|
|
c92e69a13f | ||
|
|
81f803948c | ||
|
|
817c6b2d0e | ||
|
|
cf15b0a399 | ||
|
|
267e6f7ad5 | ||
|
|
0de30bbbd1 | ||
|
|
b7060c7de0 | ||
|
|
f76382f8e7 | ||
|
|
aff6101987 | ||
|
|
798eccc624 | ||
|
|
ac95106abe | ||
|
|
02b4d03fc6 | ||
|
|
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 |
+2
-2
@@ -131,7 +131,6 @@ src/cc/rogue/rogue
|
||||
src/cc/rogue/rogue.so
|
||||
|
||||
src/cc/rogue/test.zip
|
||||
src/cc/dapps/a.out
|
||||
src/checkfile
|
||||
|
||||
src/foo.zip
|
||||
@@ -154,14 +153,15 @@ src/rogue.scr
|
||||
src/cc/rogue/confdefs.h
|
||||
src/cc/rogue/x64
|
||||
|
||||
src/cc/dapps/a.out
|
||||
src/Makefile.in
|
||||
doc/man/Makefile.in
|
||||
Makefile.in
|
||||
src/libcc.so
|
||||
src/libcc.dll
|
||||
src/libcc.dylib
|
||||
src/cc/customcc.so
|
||||
src/cc/customcc.dll
|
||||
src/cc/customcc.dylib
|
||||
src/HUSH3_7776
|
||||
REGTEST_7776
|
||||
src/cc/librogue.so
|
||||
|
||||
+16
-1
@@ -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
|
||||
|
||||
@@ -23,6 +27,17 @@ RUN rm -rf /build/depends/built /build/depends/work \
|
||||
&& rm -rf /build/src/cc/*.o /build/src/cc/*.a \
|
||||
&& rm -f /build/config.status /build/config.log
|
||||
|
||||
# The build context excludes .git (see .dockerignore), so genbuild.sh cannot derive
|
||||
# a version and would stamp the binaries "-unk". build.sh computes the real one
|
||||
# on the host and passes it in here.
|
||||
ARG BUILD_DESC=
|
||||
ENV DRAGONX_BUILD_DESC=${BUILD_DESC}
|
||||
|
||||
RUN if [ -z "$DRAGONX_BUILD_DESC" ]; then \
|
||||
echo "WARNING: no BUILD_DESC build-arg -- binaries will be stamped -unk." >&2; \
|
||||
echo " Prefer ./build.sh --linux-compat, or pass --build-arg BUILD_DESC=..." >&2; \
|
||||
fi
|
||||
|
||||
RUN cd /build && ./util/build.sh --disable-tests -j$(nproc)
|
||||
|
||||
# Strip binaries inside the container so extracted files are already small
|
||||
|
||||
@@ -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
|
||||
@@ -140,7 +164,34 @@ if [ $BUILD_LINUX_COMPAT -eq 1 ] || [ $BUILD_LINUX_RELEASE -eq 1 ] || [ $BUILD_W
|
||||
COMPAT_RELEASE_DIR="$RELEASE_DIR/dragonx-$VERSION-$COMPAT_PLATFORM"
|
||||
|
||||
echo "Building Docker image (Ubuntu 20.04 base)..."
|
||||
$DOCKER_CMD build -f Dockerfile.compat -t "$DOCKER_IMAGE" .
|
||||
# .dockerignore excludes .git, so genbuild.sh inside the container cannot
|
||||
# derive the version and would stamp the binaries "-unk". Compute it on the
|
||||
# host, mirroring util/genbuild.sh exactly, and pass it in via --build-arg.
|
||||
# NB: build.sh runs under `set -eu -o pipefail`, so every git call here must be
|
||||
# non-fatal -- a source tarball, a machine without git, or a branch whose only
|
||||
# reachable tags are lightweight (v1.0.1-v1.0.3) would otherwise abort the build.
|
||||
git diff >/dev/null 2>&1 || true # refresh index: touched-but-unmodified are not dirty
|
||||
COMPAT_BUILD_DESC=""
|
||||
COMPAT_RAWDESC=$(git describe --abbrev=0 2>/dev/null || true)
|
||||
if [ -n "$COMPAT_RAWDESC" ] \
|
||||
&& [ "$(git rev-parse HEAD 2>/dev/null)" = "$(git rev-list -1 "$COMPAT_RAWDESC" 2>/dev/null)" ] \
|
||||
&& git diff-index --quiet HEAD -- 2>/dev/null; then
|
||||
COMPAT_BUILD_DESC="$COMPAT_RAWDESC"
|
||||
else
|
||||
COMPAT_SUFFIX=$(git rev-parse --short HEAD 2>/dev/null || true)
|
||||
if [ -n "$COMPAT_SUFFIX" ]; then
|
||||
git diff-index --quiet HEAD -- 2>/dev/null || COMPAT_SUFFIX="$COMPAT_SUFFIX-dirty"
|
||||
COMPAT_BUILD_DESC="v$VERSION-$COMPAT_SUFFIX"
|
||||
fi
|
||||
fi
|
||||
if [ -n "$COMPAT_BUILD_DESC" ]; then
|
||||
echo "Stamping container build as: $COMPAT_BUILD_DESC"
|
||||
else
|
||||
echo "Warning: no usable git metadata; container binaries will be stamped -unk"
|
||||
fi
|
||||
$DOCKER_CMD build -f Dockerfile.compat \
|
||||
--build-arg BUILD_DESC="$COMPAT_BUILD_DESC" \
|
||||
-t "$DOCKER_IMAGE" .
|
||||
|
||||
echo "Extracting binaries from Docker image..."
|
||||
CONTAINER_ID=$($DOCKER_CMD create "$DOCKER_IMAGE")
|
||||
|
||||
+18
-2
@@ -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, 3)
|
||||
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)))
|
||||
@@ -833,6 +833,22 @@ AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin])
|
||||
AM_CONDITIONAL([BUILD_DARWIN], [test x$BUILD_OS = xdarwin])
|
||||
AM_CONDITIONAL([TARGET_LINUX], [test x$TARGET_OS = xlinux])
|
||||
AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows])
|
||||
|
||||
dnl mingw ld is single-pass: bracket the internal static archives in a link group
|
||||
dnl so it re-scans and resolves the cross-references DragonX added between them
|
||||
dnl (libbitcoin_util/common objects using UniValue; util<->common mutual deps).
|
||||
dnl Delivered via AC_SUBST (not an automake conditional) so automake does not
|
||||
dnl reject the linker flags inside _LDADD. Empty elsewhere (macOS ld64 rejects the
|
||||
dnl flag; GNU ld on Linux re-scans archives already).
|
||||
if test "x$TARGET_OS" = "xwindows"; then
|
||||
LINK_GROUP_START="-Wl,--start-group"
|
||||
LINK_GROUP_END="-Wl,--end-group"
|
||||
else
|
||||
LINK_GROUP_START=""
|
||||
LINK_GROUP_END=""
|
||||
fi
|
||||
AC_SUBST(LINK_GROUP_START)
|
||||
AC_SUBST(LINK_GROUP_END)
|
||||
AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes])
|
||||
AM_CONDITIONAL([ENABLE_MINING],[test x$enable_mining = xyes])
|
||||
AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes])
|
||||
|
||||
+103
-1
@@ -1,3 +1,89 @@
|
||||
dragonx (1.3.0) stable; urgency=medium
|
||||
|
||||
* RandomX stratum mining support: the daemon can serve stratum clients
|
||||
directly, with a reference miner behind the stratummine RPC for testing.
|
||||
* Stratum fixes found by audit: a malformed 63-character job_id no longer
|
||||
aborts the daemon; each block is paid to the miner that actually found it
|
||||
rather than to whichever client asked for work first; and a low-difficulty
|
||||
share is now rejected before it costs a RandomX hash.
|
||||
* Fix -connect never dialing its targets, so a node pinned to specific peers
|
||||
reaches them instead of silently falling through to peer discovery.
|
||||
* Remove roughly 7,100 lines of dead code, including the CBOPRET price
|
||||
validation in the coinbase check, whose guard could not be true on any
|
||||
chain, and adaptive-PoW difficulty logic that DragonX does not enable.
|
||||
* Honor a command-line -rpcpassword across restarts, and fix a misspelled
|
||||
-rpcusername key that silently discarded the configured RPC user.
|
||||
* Default -checkpoints off on regtest so an isolated node leaves initial
|
||||
block download, instead of staying in IBD forever and disabling every
|
||||
operation gated on it.
|
||||
* Release cs_main and the mempool lock on the miner's isStake error paths;
|
||||
the leak presented as a permanent stall rather than a slow response.
|
||||
* Windows cross-build: the mingw target links and finds librustzcash, and a
|
||||
fs::path::c_str() regression in init no longer breaks the build.
|
||||
* Stamp container builds with the real version instead of "-unk".
|
||||
* Repair the qa/rpc-tests harness far enough to start a DragonX node and
|
||||
build the shared test chain; it previously started mainnet nodes.
|
||||
|
||||
-- DragonX Developers <dev@dragonx.is> Mon, 31 Aug 2026 03:46:19 +0000
|
||||
|
||||
dragonx (1.2.0) stable; urgency=medium
|
||||
|
||||
* 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 +94,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 +117,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
|
||||
|
||||
@@ -53,6 +53,6 @@ endif
|
||||
define $(package)_stage_cmds
|
||||
mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \
|
||||
mkdir $($(package)_staging_dir)$(host_prefix)/include/ && \
|
||||
cp $($(package)_library_file) $($(package)_staging_dir)$(host_prefix)/lib/ && \
|
||||
cp $($(package)_library_file) $($(package)_staging_dir)$(host_prefix)/lib/librustzcash.a && \
|
||||
cp librustzcash/include/librustzcash.h $($(package)_staging_dir)$(host_prefix)/include/
|
||||
endef
|
||||
|
||||
@@ -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.
|
||||
+18
-21
@@ -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.3.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.3.0
|
||||
.SH DESCRIPTION
|
||||
DragonX RPC client version v1.0.3\-95aeaed0c\-dirty
|
||||
DragonX RPC client version v1.3.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>.
|
||||
|
||||
+22
-3
@@ -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.3.0" "User Commands"
|
||||
.SH NAME
|
||||
dragonx-tx \- DragonX transaction utility
|
||||
dragonx-tx \- manual page for dragonx-tx v1.3.0
|
||||
.SH DESCRIPTION
|
||||
hush\-tx utility version v1.0.3\-4caf2fc68
|
||||
hush\-tx utility version v1.3.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>.
|
||||
|
||||
+72
-33
@@ -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.3.0" "User Commands"
|
||||
.SH NAME
|
||||
DragonX \- manual page for DragonX Daemon version v1.0.3-4caf2fc68
|
||||
dragonxd \- manual page for dragonxd v1.3.0
|
||||
.SH DESCRIPTION
|
||||
DragonX Daemon version v1.0.3\-4caf2fc68
|
||||
DragonX Daemon version v1.3.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,40 @@ 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\-sietch\-min\-zouts=\fR<n>
|
||||
.IP
|
||||
Minimum number of shielded (Sapling) outputs Sietch adds to each
|
||||
z_sendmany transaction as decoys, strengthening
|
||||
amount/linkability privacy. Higher values add privacy at the cost
|
||||
of larger transactions (default: 7, clamped to the range 3\-50)
|
||||
.HP
|
||||
\fB\-autoshieldminutxos\fR
|
||||
.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 +483,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 +522,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 +591,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
|
||||
@@ -680,6 +717,11 @@ Stratum server options:
|
||||
.IP
|
||||
Enable stratum server (default: off)
|
||||
.HP
|
||||
\fB\-stratumtarget=\fR<hex>
|
||||
.IP
|
||||
Pool share target (64\-hex, big\-endian; larger = easier). Default is the
|
||||
diff\-1 target. Useful for solo/low\-difficulty mining.
|
||||
.HP
|
||||
\fB\-stratumaddress=\fR<address>
|
||||
.IP
|
||||
Mining address to use when special address of 'x' is sent by miner
|
||||
@@ -804,25 +846,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
File diff suppressed because it is too large
Load Diff
+27
-16
@@ -8,32 +8,32 @@ It is best to keep doc/relnotes/README.md up to date as changes and bug fixes ar
|
||||
|
||||
## Branch model
|
||||
|
||||
Development happens on the `dev` branch. Releases are cut on the default branch, `dragonx`. There is no `master` branch. Code changes should land on `dev` first and undergo testing before being merged into `dragonx`.
|
||||
Development happens on the `dev` branch. Releases are cut on the default branch, `master`. Code changes should land on `dev` first and undergo testing before being merged into `master`.
|
||||
|
||||
## Check for changes on dragonx that should be on dev
|
||||
## Check for changes on master that should be on dev
|
||||
|
||||
Occasionally trivial changes are made directly on the `dragonx` branch, such as documentation changes. In theory, no code changes should happen on `dragonx` without being on `dev` first, but it's better to be safe than sorry. We want the `dev` branch which undergoes testing to be as close as possible to what the `dragonx` branch will become, so we don't want to merge `dev` into `dragonx` and just assume everything works. So it's best to merge the `dragonx` branch into `dev` just before merging the `dev` branch into `dragonx`.
|
||||
Occasionally trivial changes are made directly on the `master` branch, such as documentation changes. In theory, no code changes should happen on `master` without being on `dev` first, but it's better to be safe than sorry. We want the `dev` branch which undergoes testing to be as close as possible to what the `master` branch will become, so we don't want to merge `dev` into `master` and just assume everything works. So it's best to merge the `master` branch into `dev` just before merging the `dev` branch into `master`.
|
||||
|
||||
To check if the `dragonx` branch has any changes that the `dev` branch does not:
|
||||
To check if the `master` branch has any changes that the `dev` branch does not:
|
||||
|
||||
```
|
||||
# this assumes you are working with https://git.dragonx.is/DragonX/dragonx as your remote
|
||||
git checkout dev
|
||||
git pull # make sure dev is up to date
|
||||
git checkout dragonx
|
||||
git pull # make sure dragonx is up to date
|
||||
git diff dev...dragonx # look at the set of changes which exist in dragonx but not dev
|
||||
git checkout master
|
||||
git pull # make sure master is up to date
|
||||
git diff dev...master # look at the set of changes which exist in master but not dev
|
||||
```
|
||||
|
||||
If the last command has no output, congrats, there is nothing to do. If the last command has output, then you should merge `dragonx` into `dev`:
|
||||
If the last command has no output, congrats, there is nothing to do. If the last command has output, then you should merge `master` into `dev`:
|
||||
|
||||
```
|
||||
git checkout dev
|
||||
git merge dragonx
|
||||
git merge master
|
||||
git push origin dev
|
||||
```
|
||||
|
||||
Use the `--no-ff` flag when merging `dev` into `dragonx` for a release (see below). The `--no-ff` flag makes sure to make a merge commit, no matter what, even if a "fast forward" could be done. For those in the future looking back, it's much better to see evidence of when branches were merged.
|
||||
Use the `--no-ff` flag when merging `dev` into `master` for a release (see below). The `--no-ff` flag makes sure to make a merge commit, no matter what, even if a "fast forward" could be done. For those in the future looking back, it's much better to see evidence of when branches were merged.
|
||||
|
||||
|
||||
### Git Issues
|
||||
@@ -66,6 +66,7 @@ Install deps on Linux:
|
||||
- Run "make seeds"
|
||||
- Commit the result
|
||||
- Update version in configure.ac and src/clientversion.h to update the dragonxd version
|
||||
- **The new version MUST be higher than every version already tagged**, including tags that were never built or published. Check with `git tag -l --sort=-v:refname | head`. Two trees stamped with the same `CLIENT_VERSION` are indistinguishable on the wire, in `getnetworkinfo`, and to the wallet's in-app updater — and a published archive that does not match its tag destroys the only provenance check users have.
|
||||
- In src/clientversion.h you update `CLIENT_VERSION_*` variables. Usually you will just update `CLIENT_VERSION_REVISION`
|
||||
- If there is a consensus change, it may be a good idea to update `CLIENT_VERSION_MINOR` or `CLIENT_VERSION_MAJOR`
|
||||
- To make a pre-release "beta" you can modify `CLIENT_VERSION_BUILD` but that is rarely done.
|
||||
@@ -97,17 +98,27 @@ Install deps on Linux:
|
||||
- Try to generate checkpoints as close to the release as possible, so you can have a recent block height be protected.
|
||||
- For instance, don't update checkpoints and then do a release a month later. You can always update checkpoint data again or multiple times
|
||||
- Update doc/relnotes/README.md
|
||||
- To get the stats of file changes: `git diff --stat dragonx...dev`
|
||||
- To get the stats of file changes: `git diff --stat master...dev`
|
||||
- Do a fresh clone and fresh sync with new checkpoints
|
||||
- Stop node, wait 20 minutes, and then do a partial sync with new checkpoints
|
||||
- Merge dev into dragonx: `git checkout dev && git pull && git checkout dragonx && git pull && git merge --no-ff dev && git push`
|
||||
- Merge dev into master: `git checkout dev && git pull && git checkout master && git pull && git merge --no-ff dev && git push`
|
||||
- The above command makes sure that your local dev branch is up to date before doing anything
|
||||
- The above command will not merge if "git pull" creates a merge conflict
|
||||
- The above command will not push if there is a problem with merging dev
|
||||
- Make Gitea release with git tag from the dragonx branch (make sure to merge dev in first)
|
||||
- Make Gitea release with git tag from the master branch (make sure to merge dev in first)
|
||||
- Make sure git tag starts with a `v` such as `v1.0.3`
|
||||
- Use util/gen-linux-binary-release.sh to make a Linux release binary
|
||||
- Upload Linux binary to Gitea release and add SHA256 sum
|
||||
- **The tag MUST be annotated** (`git tag -a v1.3.0 -m 'DragonX v1.3.0'`), not lightweight. `util/genbuild.sh` calls `git describe` *without* `--tags`, which only ever sees annotated tags — a lightweight tag makes the build stamp itself `v<older-tag>-<sha>` instead of the release version. v1.0.0 through v1.0.3 are lightweight, which is why their builds are labelled that way.
|
||||
- Verify before building: `git describe` must print exactly the tag, with no `-<n>-g<sha>` suffix.
|
||||
- Use `./build.sh` (container-based, see doc/build-containers.md) or util/gen-linux-binary-release.sh to make a Linux release binary
|
||||
- **Sign every archive and publish the signatures.** This step is mandatory and was missing from this document until v1.3.0 — its absence is why v1.1.0 and v1.2.0 were tagged but never became installable releases.
|
||||
- The wallet's in-app daemon updater pins an ed25519 public key in `ObsidianDragon/src/util/daemon_updater.h` and sets `kDaemonRequireSignature = true`. **An update is refused outright unless a valid `<archive>.sig` is published beside the archive.** No signature means every existing user silently stays on their old daemon.
|
||||
- Sign with `ObsidianDragon/scripts/sign-daemon-release.sh`:
|
||||
- `scripts/sign-daemon-release.sh sign <secret.key> <archive>...` produces `<archive>.sig` (base64 of a detached 64-byte ed25519 signature over the exact archive bytes)
|
||||
- or `scripts/sign-daemon-release.sh release <secret.key> <version>` to zip, sign, and print the checksum table in one step
|
||||
- Keep the secret key offline, mode 600. The matching base64 public key must already be pinned in `kDaemonSignaturePublicKeyBase64`.
|
||||
- Upload each Linux binary archive **and its `.sig`** to the Gitea release
|
||||
- **Paste the SHA-256 checksum table into the release body** as markdown rows of the form `| <archive>.zip | `<sha256hex>` |`. The updater parses this table and will not install an archive that is absent from it.
|
||||
- Confirm the release is actually consumable before announcing it: the updater looks for an asset whose name contains `"-" + platformToken + ".zip"` (`linux-amd64`, `macos`, `win64`). An archive named for a distro variant instead of the platform token is invisible to it.
|
||||
- Create an x86 Debian package for the release:
|
||||
- Edit contrib/debian/changelog to add information about the new release
|
||||
- Use `util/build-debian-package.sh` to make an x86 Debian package for the release
|
||||
@@ -115,7 +126,7 @@ Install deps on Linux:
|
||||
- `lintian` is an optional dependency, it's not needed to build the .deb
|
||||
- Upload .deb to Gitea release
|
||||
- Add SHA256 checksum of .deb to release
|
||||
- Use util/build-debian-package-ARM.sh (does this still work?) to make an ARM Debian package for the release
|
||||
- ARM Debian package: `util/build-debian-package-ARM.sh` is referenced here historically but **is not present in the tree**. Skip, or restore the script first.
|
||||
- Upload the debian packages to the Gitea release page, with SHA256 sums
|
||||
|
||||
## Platform-specific notes
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
shim/libfdguard.so
|
||||
shim/fdguard_test
|
||||
*.o
|
||||
shim/t/
|
||||
@@ -0,0 +1,55 @@
|
||||
# fd-guard: catch stray close() of files a thread doesn't own
|
||||
|
||||
This is the harness that found the wolfSSL `CloseSocket` macro bug. With that bug, P2P disconnects closed their fd number a second time, after another thread had reused it, and BDB then wrote wallet pages into `blocks/*.dat`.
|
||||
|
||||
It has two parts:
|
||||
|
||||
- `shim/`: an `LD_PRELOAD` guard around `close`, `fclose`, `dup2` and `dup3`.
|
||||
- Before each close it resolves `/proc/self/fd/N`.
|
||||
- It logs a backtrace when a non-owner closes `wallet.dat`, `database/log.*` or `blocks/*.dat`, and for every close that fails with `EBADF` (the second half of a double close).
|
||||
- `FDGUARD_ABORT=1` makes the first hit abort.
|
||||
- `repro/`: isolated regtest runs that put a shimmed `dragonxd` under load:
|
||||
- **rpc:** impatient and RST clients, half-open connections, work-queue bursts and long-polls;
|
||||
- **p2p:** TLS peers churned by `disconnectnode`/ban/kill, plus misbehaving raw TCP/TLS clients;
|
||||
- **tor:** a fake control port;
|
||||
- **mining:** with `blocknotify` and autoshield.
|
||||
|
||||
Each run executes in its own unprivileged network namespace: loopback only, no route.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux.
|
||||
- `unshare -rn` (unprivileged user namespaces), `nsenter`, `ip`, python3, gcc, and `addr2line` for symbolizing.
|
||||
- A `dragonxd`/`dragonx-cli` built with `-g`. Adding `-rdynamic` makes backtraces readable without `addr2line`.
|
||||
- A regtest datadir with a grown wallet to copy from (`GOLDEN`). It is never modified.
|
||||
|
||||
## Running
|
||||
|
||||
```sh
|
||||
qa/fd-guard/shim/build.sh # builds libfdguard.so
|
||||
qa/fd-guard/shim/run_tests.sh # self-test: double close, foreign close of a guarded file
|
||||
cd qa/fd-guard/repro
|
||||
export GOLDEN=/path/to/regtest/datadir BIN=/path/to/build/src # RUNS defaults to ~/fd-guard-runs
|
||||
LOADS=p2pmin,mine DISC_INT=5 GUARD=all ./go.sh p2pmin 300 # 1 peer, disconnectnode every 5 s
|
||||
LOADS=rpc,p2p,tor,mine WQ=64 RT=8 GUARD=all ./go.sh full 1200 # everything, production-like work queue
|
||||
python3 tools/tally.py "$RUNS/<name>/fdguard.log" # who closed what
|
||||
python3 ../shim/symbolize.py --summary "$RUNS/<name>/fdguard.log"
|
||||
python3 tools/scan_bdb.py "$RUNS/<name>"/target/regtest/blocks/*.dat # BDB pages inside block files
|
||||
```
|
||||
|
||||
Each run writes `run.txt` (full log plus summary), `fdguard.log` and the target's `debug.log` and `db.log` under `$RUNS/<name>/`.
|
||||
|
||||
## What to expect
|
||||
|
||||
Measured on a 2041-note regtest wallet, before and after the fix (`net: stop wolfSSL's CloseSocket macro ...`), under the same loads:
|
||||
|
||||
| Run | Binary | EBADF closes | Non-owner closes (wallet/log/blk) | libevent `Epoll` failures | `db.log` |
|
||||
|---|---|---|---|---|---|
|
||||
| `p2pmin`, 300 s | before | 51 (≈ 1 per disconnect) | 1 | 0 | empty |
|
||||
| | after | 0 | 0 | 0 | empty |
|
||||
| `full`, 1200 s, `WQ=64 RT=8` | before | 930 | 186 (37) | 111 | 632 KB: 7500 × `BDB0060 PANIC`, `write failed for page N` |
|
||||
| | after | 0 | 0 | 0 | empty |
|
||||
|
||||
- RPC-only and Tor-only loads give 0 reports, even before the fix: P2P disconnects are the only origin.
|
||||
- `scan_bdb.py` found no BDB pages in block files in any run. The landing of a page in a block file needs the reused number to be a writable block file at the moment BDB writes. Production did hit that (blk00011/blk00020), but it is rare.
|
||||
- `SSL_write ... closing connection` lines are expected under `tcpchurn`. They are how the load triggers disconnects, not a symptom.
|
||||
@@ -0,0 +1,72 @@
|
||||
# shellcheck shell=bash
|
||||
# Shared config + helpers for the fd-double-close REPRODUCTION harness.
|
||||
# Regtest only. Meant to run INSIDE an unprivileged network namespace (see ns.sh),
|
||||
# so every port is private to the namespace and nothing can reach a real network.
|
||||
set +e
|
||||
|
||||
REPRO=${REPRO:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}
|
||||
SRCROOT=$(cd "$REPRO/../../.." && pwd)
|
||||
BIN=${BIN:-$SRCROOT/src} # dir with dragonxd + dragonx-cli (build with -g -rdynamic for backtraces)
|
||||
DRAGONXD=$BIN/dragonxd
|
||||
CLI=$BIN/dragonx-cli
|
||||
RUNS=${RUNS:-$HOME/fd-guard-runs} # run output (block files, logs): keep it out of the repo
|
||||
GOLDEN=${GOLDEN:?set GOLDEN to a regtest datadir with a grown wallet (it is only ever copied)}
|
||||
ASMAP=${ASMAP:-$SRCROOT/asmap.dat}
|
||||
SP=${SP:-$HOME/.zcash-params/sapling-spend.params}
|
||||
OP=${OP:-$HOME/.zcash-params/sapling-output.params}
|
||||
SHIM=${SHIM:-$REPRO/../shim/libfdguard.so}
|
||||
|
||||
RPCUSER=ab
|
||||
RPCPASS=abpass
|
||||
RPC_T=18901 ; P2P_T=18911
|
||||
RPC_H1=18902; P2P_H1=18912
|
||||
RPC_H2=18903; P2P_H2=18913
|
||||
TORPORT=18950
|
||||
|
||||
clin() { local dd=$1 rp=$2; shift 2; "$CLI" -regtest -datadir="$dd" -rpcuser=$RPCUSER -rpcpassword=$RPCPASS -rpcport=$rp "$@"; }
|
||||
|
||||
write_conf() { printf 'rpcuser=%s\nrpcpassword=%s\nrpcport=%s\n' "$RPCUSER" "$RPCPASS" "$2" > "$1/DRAGONX.conf"; }
|
||||
|
||||
# prep_golden <dd> : copy of the golden wallet datadir (2041 notes, txindex=1, height 286)
|
||||
prep_golden() {
|
||||
local dd=$1
|
||||
rm -rf "$dd"; cp -a "$GOLDEN" "$dd"
|
||||
cp -f "$ASMAP" "$dd/asmap.dat"; cp -f "$SP" "$dd/sapling-spend.params"; cp -f "$OP" "$dd/sapling-output.params"
|
||||
write_conf "$dd" "$RPC_T"
|
||||
: > "$dd/regtest/debug.log"; rm -f "$dd/regtest/db.log"
|
||||
}
|
||||
|
||||
# prep_fresh <dd> <rpcport> : empty regtest datadir (helpers)
|
||||
prep_fresh() {
|
||||
local dd=$1 rp=$2
|
||||
rm -rf "$dd"; mkdir -p "$dd"
|
||||
cp -f "$ASMAP" "$dd/asmap.dat"; cp -f "$SP" "$dd/sapling-spend.params"; cp -f "$OP" "$dd/sapling-output.params"
|
||||
write_conf "$dd" "$rp"
|
||||
}
|
||||
|
||||
start_helper() { # <dd> <p2p> <rpc> <logfile>
|
||||
local dd=$1 p2p=$2 rp=$3 lg=$4
|
||||
( cd "$dd" && exec nice -n 10 "$DRAGONXD" -regtest -datadir="$dd" -testnode=1 -checkpoints=0 \
|
||||
-listen=1 -bind=127.0.0.1 -port=$p2p -connect=127.0.0.1:1 -addnode=127.0.0.1:$P2P_T \
|
||||
-dnsseed=0 -dns=0 -asmap="$dd/asmap.dat" -autoshield=0 \
|
||||
-rpcuser=$RPCUSER -rpcpassword=$RPCPASS -rpcport=$rp -listenonion=0 -maxinboundfromip=100 \
|
||||
-debug=net -debug=tls -daemon ) >> "$lg" 2>&1
|
||||
}
|
||||
|
||||
waitrpc() { # <dd> <rpc> [secs]
|
||||
local dd=$1 rp=$2 n=${3:-300} i
|
||||
for i in $(seq 1 "$n"); do clin "$dd" "$rp" getblockcount >/dev/null 2>&1 && return 0; sleep 1; done
|
||||
return 1
|
||||
}
|
||||
|
||||
stop_daemon() { # <dd> <rpc> (helpers, started with -daemon)
|
||||
local dd=$1 rp=$2 pid i
|
||||
pid=$(cat "$dd/regtest/hushd.pid" 2>/dev/null)
|
||||
clin "$dd" "$rp" stop >/dev/null 2>&1
|
||||
for i in $(seq 1 90); do { [ -z "$pid" ] || ! kill -0 "$pid" 2>/dev/null; } && return 0; sleep 1; done
|
||||
[ -n "$pid" ] && kill -9 "$pid" 2>/dev/null
|
||||
}
|
||||
|
||||
MARKERS=('libevent: Epoll' 'Socket close failed' 'Bad file descriptor' 'Operation not permitted' 'Illegal seek'
|
||||
'SSL_write' 'Unable to seek' 'socket select error' 'write failed for page' 'PANIC' 'error scanning DB'
|
||||
'terminate' 'Assertion' 'EXCEPTION')
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# go.sh <name> <seconds> -- create a private network namespace (loopback only, no route),
|
||||
# run run.sh inside it, then tear the namespace down. Env knobs pass through to run.sh.
|
||||
export REPRO=${REPRO:-$(cd "$(dirname "$0")" && pwd)}
|
||||
export RUNS=${RUNS:-$HOME/fd-guard-runs}
|
||||
OUT=$RUNS/$1; mkdir -p "$OUT"
|
||||
setsid unshare -rn sh -c "ip link set lo up && echo \$\$ > $OUT/ns.pid && exec sleep infinity" </dev/null >/dev/null 2>&1 &
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do [ -s "$OUT/ns.pid" ] && break; sleep 0.3; done
|
||||
NSP=$(cat "$OUT/ns.pid")
|
||||
cp "$REPRO/run.sh" "$OUT/run.sh.snapshot" # run a frozen copy: editing run.sh cannot disturb a live run
|
||||
nsenter -t "$NSP" -U -n --preserve-credentials bash "$OUT/run.sh.snapshot" "$@" > "$OUT/go.txt" 2>&1
|
||||
rc=$?
|
||||
kill "$NSP" 2>/dev/null
|
||||
echo "go.sh: run.sh rc=$rc, namespace holder $NSP killed" >> "$OUT/go.txt"
|
||||
Executable
+156
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env bash
|
||||
# run.sh <name> <seconds> -- run INSIDE the network namespace (go.sh does that).
|
||||
# Env knobs:
|
||||
# LOADS=rpc,p2p,tor,mine which load generators run (default all four); p2pmin = 1 helper + disconnectnode every DISC_INT s
|
||||
# WQ=4 RT=2 -rpcworkqueue / -rpcthreads of the target
|
||||
# GUARD=all FDGUARD_GUARD (db,blk | all)
|
||||
# SHIMON=1 run the target under LD_PRELOAD=libfdguard.so
|
||||
# STRACE=0 1 = also wrap the target in strace (failed fd syscalls only)
|
||||
# TRACE=0 1 = FDGUARD_TRACE_ALL (one line per open/close; big)
|
||||
# MINE_INT=3 seconds between generate 1
|
||||
# HAMMER_ARGS=... override the hammer thread mix
|
||||
# STOP_ON_HIT=0 1 = end the run 60 s after the first NON-OWNER db/blk close
|
||||
# EXTRA=... extra target args
|
||||
source "$REPRO/common.sh"
|
||||
NAME=$1; DUR=${2:-600}
|
||||
OUT=$RUNS/$NAME
|
||||
LOADS=${LOADS:-rpc,p2p,tor,mine}; WQ=${WQ:-4}; RT=${RT:-2}; GUARD=${GUARD:-all}
|
||||
SHIMON=${SHIMON:-1}; STRACE=${STRACE:-0}; TRACE=${TRACE:-0}; MINE_INT=${MINE_INT:-3}
|
||||
STOP_ON_HIT=${STOP_ON_HIT:-0}
|
||||
HAMMER_ARGS=${HAMMER_ARGS:---normal 8 --impatient 8 --slowfire 2 --halfopen 3 --pipeline 3 --garbage 2 --slowread 2 --longpoll 4 --burst-size 16 --burst-every 2}
|
||||
has() { case ",$LOADS," in *",$1,"*) return 0;; esac; return 1; }
|
||||
|
||||
mkdir -p "$OUT/logs"; rm -f "$OUT/STOP"
|
||||
DD_T=$OUT/target; DD_H1=$OUT/helper1; DD_H2=$OUT/helper2
|
||||
exec > >(tee -a "$OUT/run.txt") 2>&1
|
||||
echo "=== repro run $NAME $(date -u +%FT%TZ) dur=${DUR}s loads=$LOADS wq=$WQ rt=$RT guard=$GUARD shim=$SHIMON strace=$STRACE"
|
||||
echo "netns check (must be ENETUNREACH=101): $(python3 -c 'import socket;print(socket.socket().connect_ex(("1.1.1.1",80)))')"
|
||||
echo "binary: $DRAGONXD build-id: $(readelf -n "$DRAGONXD" | awk '/Build ID/{print $3}')"
|
||||
echo "dragonxd already running in this namespace? $(pgrep -a -x dragonxd | wc -l) (other agents' nodes live in other namespaces)"
|
||||
|
||||
PIDS=()
|
||||
cleanup() {
|
||||
echo "--- cleanup $(date -u +%T)"
|
||||
touch "$OUT/STOP"
|
||||
for p in "${PIDS[@]}"; do kill "$p" 2>/dev/null; done
|
||||
stop_daemon "$DD_H2" "$RPC_H2"; stop_daemon "$DD_H1" "$RPC_H1"
|
||||
if [ -n "$TPID" ] && kill -0 "$TPID" 2>/dev/null; then
|
||||
timeout 20 "$CLI" -regtest -datadir="$DD_T" -rpcuser=$RPCUSER -rpcpassword=$RPCPASS -rpcport=$RPC_T stop >/dev/null 2>&1
|
||||
for _ in $(seq 1 60); do kill -0 "$TPID" 2>/dev/null || break; sleep 1; done
|
||||
# abandoned longpoll getblocktemplate calls can hold every -rpcthreads worker, so RPC stop may never run:
|
||||
# fall back to SIGTERM (same clean StartShutdown path), SIGKILL only as a last resort
|
||||
kill -0 "$TPID" 2>/dev/null && { echo "RPC stop did not finish in 60 s -> SIGTERM"; kill -TERM "$TPID"; }
|
||||
for _ in $(seq 1 180); do kill -0 "$TPID" 2>/dev/null || break; sleep 1; done
|
||||
kill -0 "$TPID" 2>/dev/null && { echo "target did not stop -> SIGKILL"; kill -9 "$TPID"; }
|
||||
fi
|
||||
[ -n "$WPID" ] && wait "$WPID" 2>/dev/null; echo "target exit status: $?"
|
||||
for p in "${PIDS[@]}"; do kill -9 "$p" 2>/dev/null; done
|
||||
summarize
|
||||
}
|
||||
summarize() {
|
||||
local DL=$DD_T/regtest/debug.log
|
||||
echo "=== markers in target debug.log"
|
||||
for m in "${MARKERS[@]}"; do printf ' [%6d] %s\n' "$(grep -c -- "$m" "$DL" 2>/dev/null)" "$m"; done
|
||||
echo " db.log bytes: $(stat -c %s "$DD_T/regtest/db.log" 2>/dev/null || echo absent)"
|
||||
grep -q 'Shutdown: done' "$DL" && echo " clean shutdown: yes" || echo " clean shutdown: NO"
|
||||
if [ "$SHIMON" = 1 ]; then
|
||||
echo "=== fdguard reports"; grep -o '=== FDGUARD #[0-9]* [A-Z-]*' "$OUT/fdguard.log" | awk '{print $4}' | sort | uniq -c
|
||||
grep '^S ' "$OUT/fdguard.log" | tail -1
|
||||
python3 "$REPRO/../shim/symbolize.py" --summary "$OUT/fdguard.log" > "$OUT/fdguard.summary.txt" 2>&1
|
||||
head -60 "$OUT/fdguard.summary.txt"
|
||||
fi
|
||||
echo "=== BDB-page-in-blockfile detector"
|
||||
python3 "$REPRO/tools/scan_bdb.py" "$DD_T"/regtest/blocks/blk*.dat "$DD_T"/regtest/blocks/rev*.dat 2>&1 | tail -15
|
||||
echo "=== end $(date -u +%FT%TZ)"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
trap 'exit 130' INT TERM
|
||||
|
||||
echo "--- prep datadirs"
|
||||
prep_golden "$DD_T"
|
||||
prep_fresh "$DD_H1" "$RPC_H1"; prep_fresh "$DD_H2" "$RPC_H2"
|
||||
cat > "$OUT/blocknotify.sh" <<EOF
|
||||
#!/bin/sh
|
||||
# pool-like blocknotify: a couple of dragonx-cli calls per block
|
||||
$CLI -regtest -datadir=$DD_T -rpcuser=$RPCUSER -rpcpassword=$RPCPASS -rpcport=$RPC_T getblock "\$1" >/dev/null 2>&1
|
||||
$CLI -regtest -datadir=$DD_T -rpcuser=$RPCUSER -rpcpassword=$RPCPASS -rpcport=$RPC_T getblocktemplate >/dev/null 2>&1
|
||||
echo "\$(date +%s) \$1" >> $OUT/logs/blocknotify.log
|
||||
EOF
|
||||
chmod +x "$OUT/blocknotify.sh"
|
||||
|
||||
if has tor; then
|
||||
nice -n 10 python3 "$REPRO/tools/faketor.py" --port $TORPORT > "$OUT/logs/faketor.txt" 2>&1 & PIDS+=($!)
|
||||
TORARGS="-listenonion=1 -torcontrol=127.0.0.1:$TORPORT -debug=tor"
|
||||
else
|
||||
TORARGS="-listenonion=0"
|
||||
fi
|
||||
if has p2p || has p2pmin; then P2PARGS="-listen=1 -bind=127.0.0.1 -port=$P2P_T -allowlist=127.0.0.0/8 -maxinboundfromip=100"
|
||||
else P2PARGS="-listen=0 -port=$P2P_T"; fi
|
||||
|
||||
TARGS=(-regtest -datadir="$DD_T" -testnode=1 -checkpoints=0 $P2PARGS -connect=127.0.0.1:1 -dnsseed=0 -dns=0
|
||||
-asmap="$DD_T/asmap.dat" -rpcuser=$RPCUSER -rpcpassword=$RPCPASS -rpcport=$RPC_T
|
||||
-txindex=1 -autoshield=1 -rpcworkqueue=$WQ -rpcthreads=$RT
|
||||
"-blocknotify=$OUT/blocknotify.sh %s" $TORARGS -debug=net -debug=tls -printtoconsole=0 -daemon=0 $EXTRA)
|
||||
echo "target args: ${TARGS[*]}" | sed "s/$RPCPASS/***/g"
|
||||
ENVV=()
|
||||
if [ "$SHIMON" = 1 ]; then
|
||||
ENVV=(FDGUARD_LOG="$OUT/fdguard.log" FDGUARD_GUARD="$GUARD" FDGUARD_ABORT=0 FDGUARD_STATS_SEC=60 FDGUARD_MAXREPORTS=2000 LD_PRELOAD="$SHIM")
|
||||
[ "$TRACE" = 1 ] && ENVV+=(FDGUARD_TRACE_ALL=1)
|
||||
fi
|
||||
WRAP=()
|
||||
[ "$STRACE" = 1 ] && WRAP=(strace -f -tt -qq -e trace=close,openat,accept4,socket,dup2,dup3 -e status=failed -o "$OUT/strace.txt")
|
||||
( cd "$DD_T" && exec env "${ENVV[@]}" nice -n 10 "${WRAP[@]}" "$DRAGONXD" "${TARGS[@]}" ) > "$OUT/logs/target_stdout.txt" 2>&1 &
|
||||
WPID=$!
|
||||
echo "--- target launched (wrapper pid $WPID); waiting for RPC"
|
||||
waitrpc "$DD_T" "$RPC_T" 600 || { echo "target never answered RPC"; exit 1; }
|
||||
TPID=$(cat "$DD_T/regtest/hushd.pid" 2>/dev/null); [ -z "$TPID" ] && TPID=$WPID
|
||||
echo "target up pid=$TPID height=$(clin "$DD_T" "$RPC_T" getblockcount) $(date -u +%T)"
|
||||
|
||||
T0=$(date +%s)
|
||||
if has p2p; then
|
||||
start_helper "$DD_H1" "$P2P_H1" "$RPC_H1" "$OUT/logs/helper1.txt"
|
||||
start_helper "$DD_H2" "$P2P_H2" "$RPC_H2" "$OUT/logs/helper2.txt"
|
||||
waitrpc "$DD_H1" "$RPC_H1" 120; waitrpc "$DD_H2" "$RPC_H2" 120
|
||||
nice -n 10 python3 "$REPRO/tools/tcpchurn.py" --port $P2P_T --duration "$DUR" --concurrency 8 > "$OUT/logs/tcpchurn.txt" 2>&1 & PIDS+=($!)
|
||||
nice -n 10 bash "$REPRO/tools/rpcchurn.sh" "$OUT" "$DUR" > "$OUT/logs/rpcchurn.txt" 2>&1 & PIDS+=($!)
|
||||
fi
|
||||
if has p2pmin; then
|
||||
# minimal P2P: ONE well-behaved helper peer, and an ordinary "disconnectnode" every DISC_INT s
|
||||
# (the helper's -addnode re-dials the target). No raw TCP/TLS churn, no bans, no kills.
|
||||
start_helper "$DD_H1" "$P2P_H1" "$RPC_H1" "$OUT/logs/helper1.txt"; waitrpc "$DD_H1" "$RPC_H1" 120
|
||||
( k=0; while [ ! -e "$OUT/STOP" ]; do sleep "${DISC_INT:-5}"; for a in $(clin "$DD_T" "$RPC_T" getpeerinfo 2>/dev/null | python3 -c 'import json,sys; [print(p["addr"]) for p in json.load(sys.stdin)]' 2>/dev/null); do
|
||||
clin "$DD_T" "$RPC_T" disconnectnode "$a" >/dev/null 2>&1 && { k=$((k+1)); echo "$(date -u +%T.%N) disconnectnode $a" >> "$OUT/logs/disconnects.log"; }; done
|
||||
sleep 1; clin "$DD_H1" "$RPC_H1" addnode "127.0.0.1:$P2P_T" onetry >/dev/null 2>&1 # helper re-dials now (its -addnode retry is ~2 min)
|
||||
echo "$k" > "$OUT/logs/disconnects.txt"; done ) & PIDS+=($!)
|
||||
fi
|
||||
if has rpc; then
|
||||
MINEARG=""; has mine && MINEARG="--mine --mine-interval $MINE_INT"
|
||||
nice -n 10 python3 "$REPRO/tools/hammer.py" --port $RPC_T --user $RPCUSER --password $RPCPASS --duration "$DUR" \
|
||||
$HAMMER_ARGS $MINEARG --pid "$TPID" --logfile "$OUT/logs/hammer.log" > "$OUT/logs/hammer_stdout.txt" 2>&1 & PIDS+=($!)
|
||||
elif has mine; then
|
||||
( while [ ! -e "$OUT/STOP" ]; do clin "$DD_T" "$RPC_T" generate 1 >/dev/null 2>&1; sleep "$MINE_INT"; done ) & PIDS+=($!)
|
||||
fi
|
||||
|
||||
# ---- poll
|
||||
end=$(( T0 + DUR )); n=0; hit_at=0
|
||||
while [ "$(date +%s)" -lt "$end" ] && [ ! -e "$OUT/STOP" ]; do
|
||||
sleep 30; n=$((n+1))
|
||||
alive=no; kill -0 "$TPID" 2>/dev/null && alive=yes
|
||||
DL=$DD_T/regtest/debug.log
|
||||
h=$(clin "$DD_T" "$RPC_T" getblockcount 2>/dev/null); c=$(clin "$DD_T" "$RPC_T" getconnectioncount 2>/dev/null)
|
||||
rep=$(grep -c '^=== FDGUARD' "$OUT/fdguard.log" 2>/dev/null); noc=$(grep -c 'NON-OWNER-CLOSE' "$OUT/fdguard.log" 2>/dev/null)
|
||||
ebf=$(grep -c 'EBADF-CLOSE' "$OUT/fdguard.log" 2>/dev/null); epl=$(grep -c '=== FDGUARD #[0-9]* EPOLL' "$OUT/fdguard.log" 2>/dev/null)
|
||||
dbnoc=$(grep -A3 'NON-OWNER-CLOSE' "$OUT/fdguard.log" 2>/dev/null | grep -c 'class=\(db\|blk\)')
|
||||
printf '[poll %3d %s +%4ds] alive=%s h=%s conns=%s | fdguard reports=%s nonowner=%s(db/blk %s) ebadf=%s epoll=%s | Epoll=%s SockCloseFail=%s EBADF=%s EPERM=%s SSL_write=%s seek=%s sel=%s | db.log=%s\n' \
|
||||
$n "$(date -u +%T)" $(( $(date +%s) - T0 )) $alive "$h" "$c" "$rep" "$noc" "$dbnoc" "$ebf" "$epl" \
|
||||
"$(grep -c 'libevent: Epoll' "$DL")" "$(grep -c 'Socket close failed' "$DL")" "$(grep -c 'Bad file descriptor' "$DL")" \
|
||||
"$(grep -c 'Operation not permitted' "$DL")" "$(grep -c 'SSL_write' "$DL")" "$(grep -c 'Unable to seek' "$DL")" \
|
||||
"$(grep -c 'socket select error' "$DL")" "$(stat -c %s "$DD_T/regtest/db.log" 2>/dev/null)" | tee -a "$OUT/poll.txt"
|
||||
[ "$alive" = no ] && { echo "!!! target died"; break; }
|
||||
if [ "$STOP_ON_HIT" = 1 ] && [ "${dbnoc:-0}" -gt 0 ]; then
|
||||
[ "$hit_at" = 0 ] && hit_at=$(date +%s)
|
||||
[ $(( $(date +%s) - hit_at )) -ge 60 ] && { echo "--- stop-on-hit"; break; }
|
||||
fi
|
||||
done
|
||||
cat "$OUT/logs/tcpchurn.txt" "$OUT/logs/rpcchurn.txt" 2>/dev/null
|
||||
grep -E 'TOTAL|totals' -A12 "$OUT/logs/hammer.log" 2>/dev/null | tail -14
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Fake Tor control port for the fd-bug harness.
|
||||
|
||||
dragonxd's torcontrol.cpp opens a libevent bufferevent to -torcontrol=<host:port>,
|
||||
speaks the control protocol (PROTOCOLINFO / AUTHCHALLENGE / AUTHENTICATE / ADD_ONION),
|
||||
and on EOF/ERROR reconnects with exponential backoff. We give it a server on a
|
||||
loopback port that behaves badly in several ways so the reconnect/teardown loop
|
||||
(bufferevent_free with BEV_OPT_CLOSE_ON_FREE, the event_base, the reconnect timer)
|
||||
runs constantly. Each accepted connection picks a behaviour at random:
|
||||
|
||||
close : accept, then immediately close (EOF right away)
|
||||
rst : accept, set SO_LINGER 0, close -> sends RST
|
||||
stall : accept, read nothing, hold the socket open then drop it
|
||||
garbage : send non-control-protocol bytes then close
|
||||
halfok : answer PROTOCOLINFO plausibly, then drop before AUTHENTICATE completes
|
||||
|
||||
Loopback only. Run with --port. Ctrl-C / SIGTERM stops it.
|
||||
"""
|
||||
import argparse, random, socket, struct, threading, time, sys, signal
|
||||
|
||||
STOP = threading.Event()
|
||||
|
||||
def handle(conn, mode):
|
||||
try:
|
||||
conn.settimeout(5.0)
|
||||
if mode == "close":
|
||||
conn.close(); return
|
||||
if mode == "rst":
|
||||
conn.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
|
||||
struct.pack("ii", 1, 0))
|
||||
conn.close(); return
|
||||
if mode == "stall":
|
||||
# read whatever the controller sends but never reply; hold 2-4s
|
||||
try:
|
||||
conn.recv(4096)
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(random.uniform(2.0, 4.0))
|
||||
conn.close(); return
|
||||
if mode == "garbage":
|
||||
try:
|
||||
conn.recv(4096)
|
||||
except Exception:
|
||||
pass
|
||||
conn.sendall(b"\x00\xff\x01garbage not a tor reply\r\n" * 4)
|
||||
conn.close(); return
|
||||
if mode == "halfok":
|
||||
# read the PROTOCOLINFO line, give a plausible 250 reply advertising
|
||||
# NULL auth, then drop the connection before AUTHENTICATE finishes.
|
||||
try:
|
||||
conn.recv(4096)
|
||||
except Exception:
|
||||
pass
|
||||
conn.sendall(b"250-PROTOCOLINFO 1\r\n"
|
||||
b"250-AUTH METHODS=NULL\r\n"
|
||||
b'250-VERSION Tor="0.4.7.13"\r\n'
|
||||
b"250 OK\r\n")
|
||||
time.sleep(random.uniform(0.2, 1.0))
|
||||
conn.close(); return
|
||||
except Exception:
|
||||
try: conn.close()
|
||||
except Exception: pass
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--port", type=int, required=True)
|
||||
ap.add_argument("--host", default="127.0.0.1")
|
||||
args = ap.parse_args()
|
||||
|
||||
modes = ["close", "rst", "stall", "garbage", "halfok"]
|
||||
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
srv.bind((args.host, args.port))
|
||||
srv.listen(16)
|
||||
srv.settimeout(1.0)
|
||||
print(f"faketor: listening on {args.host}:{args.port}", flush=True)
|
||||
|
||||
def onsig(*_): STOP.set()
|
||||
signal.signal(signal.SIGTERM, onsig)
|
||||
signal.signal(signal.SIGINT, onsig)
|
||||
|
||||
n = 0
|
||||
while not STOP.is_set():
|
||||
try:
|
||||
conn, _ = srv.accept()
|
||||
except socket.timeout:
|
||||
continue
|
||||
except Exception:
|
||||
break
|
||||
mode = modes[n % len(modes)]
|
||||
n += 1
|
||||
threading.Thread(target=handle, args=(conn, mode), daemon=True).start()
|
||||
srv.close()
|
||||
print(f"faketor: stopped after {n} connections", flush=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+796
@@ -0,0 +1,796 @@
|
||||
#!/usr/bin/env python3
|
||||
# hammer.py -- JSON-RPC robustness/load harness for a LOCAL regtest dragonxd.
|
||||
#
|
||||
# Scope: connects only to the --host/--port you pass (meant to be 127.0.0.1 and a
|
||||
# regtest RPC port). Its job is to reproduce a known file-descriptor bug in the
|
||||
# libevent-2.1.8 HTTP RPC server (src/httpserver.cpp) by exercising the same
|
||||
# request patterns the production pool box sees: many concurrent clients, clients
|
||||
# that disconnect mid-request/mid-response (the explorer indexer's 120 s client
|
||||
# timeout), work-queue overflow, keep-alive pipelining, and malformed input.
|
||||
# stdlib only.
|
||||
#
|
||||
# Correctness oracle: every request carries a unique JSON-RPC id; if a reply comes
|
||||
# back with a different id on the same socket, that is cross-talk (the server wrote
|
||||
# one client's reply onto another client's fd) and is logged + counted.
|
||||
#
|
||||
# See README.txt for the exact invocation.
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import collections
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import socket
|
||||
import struct
|
||||
import threading
|
||||
import time
|
||||
|
||||
# ------------------------------------------------------------------ config/state
|
||||
|
||||
class C:
|
||||
host = "127.0.0.1"
|
||||
port = 19461
|
||||
auth = ""
|
||||
ctimeout = 15.0
|
||||
|
||||
CFG = C()
|
||||
STOP = threading.Event()
|
||||
SHARED = {"hashes": [], "lpid": None, "zaddrs": [], "rawblock": None}
|
||||
SHARED_LOCK = threading.Lock()
|
||||
_ID = [random.SystemRandom().randrange(1 << 40)]
|
||||
_ID_LOCK = threading.Lock()
|
||||
LOGLOCK = threading.Lock()
|
||||
LOGF = [None]
|
||||
|
||||
def next_id():
|
||||
with _ID_LOCK:
|
||||
_ID[0] += 1
|
||||
return _ID[0]
|
||||
|
||||
def log(msg):
|
||||
line = "%s %s" % (time.strftime("%H:%M:%S"), msg)
|
||||
with LOGLOCK:
|
||||
print(line, flush=True)
|
||||
if LOGF[0]:
|
||||
LOGF[0].write(line + "\n"); LOGF[0].flush()
|
||||
|
||||
# ------------------------------------------------------------------ stats
|
||||
|
||||
KEYS = ["sent", "ok", "rpcerr", "wq500", "http401", "http4xx", "httpother",
|
||||
"early_rst", "early_fin", "srvreset", "srveof", "ctimeout", "connfail",
|
||||
"idmismatch", "malformed", "mined", "half_open", "half_srvclose"]
|
||||
|
||||
class Stats:
|
||||
def __init__(self):
|
||||
self.lk = threading.Lock()
|
||||
self.total = collections.Counter()
|
||||
self.win = collections.Counter()
|
||||
self.anom = []
|
||||
def inc(self, k, n=1):
|
||||
with self.lk:
|
||||
self.total[k] += n; self.win[k] += n
|
||||
def anomaly(self, kind, detail):
|
||||
with self.lk:
|
||||
if len(self.anom) < 300:
|
||||
self.anom.append((time.time(), kind, detail))
|
||||
log("ANOMALY %s %s" % (kind, detail))
|
||||
def swap(self):
|
||||
with self.lk:
|
||||
w = self.win; self.win = collections.Counter(); return w
|
||||
|
||||
ST = Stats()
|
||||
|
||||
# ------------------------------------------------------------------ socket helpers
|
||||
|
||||
def auth_header(u, p):
|
||||
return "Basic " + base64.b64encode(("%s:%s" % (u, p)).encode()).decode()
|
||||
|
||||
def mkconn(timeout=None, rcvbuf=None):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
if rcvbuf:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, rcvbuf)
|
||||
s.settimeout(timeout if timeout is not None else CFG.ctimeout)
|
||||
s.connect((CFG.host, CFG.port))
|
||||
return s
|
||||
|
||||
def end_rst(s):
|
||||
# linger 0 -> TCP RST on close (models a client that dies / resets abruptly)
|
||||
try:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0))
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
s.close()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def end_fin(s):
|
||||
try:
|
||||
s.shutdown(socket.SHUT_RDWR)
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
s.close()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def end_early(s, how=None):
|
||||
how = how or random.choice(("rst", "fin"))
|
||||
if how == "rst":
|
||||
end_rst(s); ST.inc("early_rst")
|
||||
else:
|
||||
end_fin(s); ST.inc("early_fin")
|
||||
|
||||
def req_bytes(method, params, rid, keepalive=True, auth=None, uri="/",
|
||||
httpmethod="POST", body=None, clen=None):
|
||||
if body is None:
|
||||
body = json.dumps({"jsonrpc": "1.0", "id": rid,
|
||||
"method": method, "params": params}).encode()
|
||||
head = [
|
||||
"%s %s HTTP/1.1" % (httpmethod, uri),
|
||||
"Host: %s:%d" % (CFG.host, CFG.port),
|
||||
"Authorization: %s" % (auth if auth is not None else CFG.auth),
|
||||
"Content-Type: application/json",
|
||||
"Content-Length: %d" % (clen if clen is not None else len(body)),
|
||||
"Connection: %s" % ("keep-alive" if keepalive else "close"),
|
||||
"", "",
|
||||
]
|
||||
return "\r\n".join(head).encode() + body
|
||||
|
||||
class Reader:
|
||||
"""Incremental HTTP/1.1 reply reader over one socket."""
|
||||
def __init__(self, s):
|
||||
self.s = s; self.buf = b""
|
||||
def _fill(self):
|
||||
d = self.s.recv(65536)
|
||||
if not d:
|
||||
raise EOFError()
|
||||
self.buf += d
|
||||
def read(self):
|
||||
while b"\r\n\r\n" not in self.buf:
|
||||
self._fill()
|
||||
if len(self.buf) > (1 << 20):
|
||||
raise ValueError("oversize header")
|
||||
head, self.buf = self.buf.split(b"\r\n\r\n", 1)
|
||||
lines = head.split(b"\r\n")
|
||||
p = lines[0].split(b" ", 2)
|
||||
if len(p) < 2 or not p[0].startswith(b"HTTP/1."):
|
||||
raise ValueError("bad status line %r" % lines[0][:80])
|
||||
status = int(p[1])
|
||||
hdr = {}
|
||||
for ln in lines[1:]:
|
||||
if b":" in ln:
|
||||
k, v = ln.split(b":", 1)
|
||||
hdr[k.strip().lower()] = v.strip()
|
||||
if hdr.get(b"transfer-encoding", b"").lower() == b"chunked":
|
||||
body = b""
|
||||
while True:
|
||||
while b"\r\n" not in self.buf:
|
||||
self._fill()
|
||||
ln, self.buf = self.buf.split(b"\r\n", 1)
|
||||
n = int(ln.split(b";")[0], 16)
|
||||
while len(self.buf) < n + 2:
|
||||
self._fill()
|
||||
body += self.buf[:n]; self.buf = self.buf[n + 2:]
|
||||
if n == 0:
|
||||
break
|
||||
elif b"content-length" in hdr:
|
||||
n = int(hdr[b"content-length"])
|
||||
while len(self.buf) < n:
|
||||
self._fill()
|
||||
body, self.buf = self.buf[:n], self.buf[n:]
|
||||
else:
|
||||
try:
|
||||
while True:
|
||||
self._fill()
|
||||
except EOFError:
|
||||
pass
|
||||
body, self.buf = self.buf, b""
|
||||
return status, hdr, body
|
||||
|
||||
def account(status, body, rid, method, expect401=False):
|
||||
if status == 401:
|
||||
ST.inc("http401")
|
||||
if not expect401:
|
||||
ST.anomaly("unexpected401", (method,))
|
||||
return None
|
||||
if status == 500 and b"Work queue depth exceeded" in body:
|
||||
ST.inc("wq500"); return None
|
||||
j = None
|
||||
if body[:1] in (b"{", b"["):
|
||||
try:
|
||||
j = json.loads(body)
|
||||
except ValueError:
|
||||
j = None
|
||||
if j is None:
|
||||
if 400 <= status < 500:
|
||||
ST.inc("http4xx")
|
||||
else:
|
||||
ST.inc("httpother")
|
||||
if status == 200:
|
||||
ST.anomaly("non-json-200", (method, body[:120].decode("latin1")))
|
||||
return None
|
||||
if isinstance(j, dict) and rid is not None and j.get("id") != rid:
|
||||
ST.inc("idmismatch")
|
||||
ST.anomaly("idmismatch", {"method": method, "sent": rid, "got": j.get("id"),
|
||||
"body": body[:200].decode("latin1")})
|
||||
if isinstance(j, dict) and j.get("error"):
|
||||
ST.inc("rpcerr")
|
||||
else:
|
||||
ST.inc("ok")
|
||||
return j
|
||||
|
||||
def call(s, rd, method, params, keepalive=True, expect401=False, auth=None):
|
||||
rid = next_id()
|
||||
try:
|
||||
s.sendall(req_bytes(method, params, rid, keepalive=keepalive, auth=auth))
|
||||
ST.inc("sent")
|
||||
status, hdr, body = rd.read()
|
||||
except socket.timeout:
|
||||
ST.inc("ctimeout"); return None, False
|
||||
except (ConnectionResetError, BrokenPipeError):
|
||||
ST.inc("srvreset"); return None, False
|
||||
except EOFError:
|
||||
ST.inc("srveof"); return None, False
|
||||
except ValueError as e:
|
||||
ST.inc("malformed"); ST.anomaly("malformed", (method, str(e))); return None, False
|
||||
except OSError:
|
||||
ST.inc("srvreset"); return None, False
|
||||
j = account(status, body, rid, method, expect401)
|
||||
alive = keepalive and hdr.get(b"connection", b"").lower() != b"close"
|
||||
return j, alive
|
||||
|
||||
# ------------------------------------------------------------------ menus
|
||||
|
||||
def a_hash():
|
||||
with SHARED_LOCK:
|
||||
return random.choice(SHARED["hashes"]) if SHARED["hashes"] else None
|
||||
|
||||
def a_zaddr():
|
||||
with SHARED_LOCK:
|
||||
return random.choice(SHARED["zaddrs"]) if SHARED["zaddrs"] else None
|
||||
|
||||
def normal_menu():
|
||||
# Mostly fast calls (sub-ms) so a well-behaved client interleaves with mining.
|
||||
# Heavy wallet calls (z_gettotalbalance ~2.3s on the golden wallet) appear but
|
||||
# are rare, so they don't permanently peg the small worker pool on their own.
|
||||
fast = [("getinfo", []), ("getblockcount", []), ("getbestblockhash", []),
|
||||
("getrawmempool", []), ("getmininginfo", []), ("getdifficulty", []),
|
||||
("getblockchaininfo", []), ("getpeerinfo", []),
|
||||
("getblocktemplate", []), ("getnetworkinfo", [])]
|
||||
h = a_hash()
|
||||
if h:
|
||||
fast.append(("getblock", [h, 2]))
|
||||
fast.append(("getblockheader", [h]))
|
||||
# "slow" here is only mildly-larger fast calls; the genuinely heavy wallet calls
|
||||
# live in the paced slow-fire class so a normal dashboard poller never pegs the
|
||||
# worker pool.
|
||||
slow = [("coinsupply", []), ("listtransactions", ["*", 10]),
|
||||
("listunspent", [0]), ("getblock", [a_hash() or "", 2])]
|
||||
return random.choice(slow) if random.random() < 0.1 else random.choice(fast)
|
||||
|
||||
def medium_menu():
|
||||
# Fast to compute (~4 ms on this node) but a non-trivial reply to write, so they
|
||||
# are good targets for a mid-response disconnect. The fd-reuse race fires on the
|
||||
# disconnect, not on call duration, so firing these at a HIGH rate drives the
|
||||
# race hard while the worker frees in milliseconds (mining keeps its slots).
|
||||
m = [("listtransactions", ["*", 10000]), ("getrawmempool", []),
|
||||
("getrawmempool", [True]), ("getblockchaininfo", []),
|
||||
("getblocktemplate", []), ("getchaintips", []), ("getinfo", []),
|
||||
("coinsupply", []), ("listunspent", [0])]
|
||||
h = a_hash()
|
||||
if h:
|
||||
m.append(("getblock", [h, 2]))
|
||||
m.append(("getblock", [h, 1]))
|
||||
return random.choice(m)
|
||||
|
||||
def heavy_menu():
|
||||
# Genuinely slow calls (z_gettotalbalance ~1.7 s, z_listunspent ~5.7 s on the
|
||||
# golden wallet). Fired only by the small, PACED slow-fire class so a worker
|
||||
# keeps running a multi-second call long after the client disconnected (the
|
||||
# production indexer/coinsupply scenario that widens the fd-reuse window),
|
||||
# without the fire-and-disconnect backlog pinning the whole pool.
|
||||
m = [("z_gettotalbalance", [0]), ("z_listunspent", [0])]
|
||||
z = a_zaddr()
|
||||
if z:
|
||||
m.append(("z_listreceivedbyaddress", [z, 0]))
|
||||
return random.choice(m)
|
||||
|
||||
# ------------------------------------------------------------------ clients
|
||||
|
||||
def client_normal(cid):
|
||||
while not STOP.is_set():
|
||||
keepalive = random.random() < 0.7
|
||||
try:
|
||||
s = mkconn()
|
||||
except OSError:
|
||||
ST.inc("connfail"); time.sleep(0.05); continue
|
||||
rd = Reader(s)
|
||||
n = random.randint(1, 8) if keepalive else 1
|
||||
try:
|
||||
for _ in range(n):
|
||||
if STOP.is_set():
|
||||
break
|
||||
method, params = normal_menu()
|
||||
j, alive = call(s, rd, method, params, keepalive=keepalive)
|
||||
if not alive:
|
||||
break
|
||||
finally:
|
||||
(end_fin if random.random() < 0.8 else end_rst)(s)
|
||||
time.sleep(random.uniform(0.01, 0.15))
|
||||
|
||||
def client_impatient(cid):
|
||||
# High-rate fast-disconnect churn: send a (medium) request, then disconnect
|
||||
# after 1-500 ms at a random phase. This is the main driver of the fd-reuse
|
||||
# race -- a disconnect concurrent with the server writing the reply / re-arming
|
||||
# the socket -- and the worker frees in ms so mining is unaffected.
|
||||
while not STOP.is_set():
|
||||
method, params = medium_menu()
|
||||
rid = next_id()
|
||||
body = json.dumps({"jsonrpc": "1.0", "id": rid, "method": method,
|
||||
"params": params}).encode()
|
||||
phase = random.choice(("mid_request", "after_send", "mid_response"))
|
||||
try:
|
||||
s = mkconn(timeout=2.0)
|
||||
except OSError:
|
||||
ST.inc("connfail"); continue
|
||||
try:
|
||||
if phase == "mid_request":
|
||||
full = req_bytes(method, params, rid, keepalive=False, body=body)
|
||||
cut = random.randint(len(full) - len(body), len(full) - 1)
|
||||
s.sendall(full[:cut])
|
||||
time.sleep(random.uniform(0.001, 0.5))
|
||||
end_early(s); continue
|
||||
s.sendall(req_bytes(method, params, rid, keepalive=False, body=body))
|
||||
ST.inc("sent")
|
||||
time.sleep(random.uniform(0.001, 0.5))
|
||||
if phase == "mid_response":
|
||||
try:
|
||||
s.recv(random.randint(0, 64)) # read a few bytes, then bail
|
||||
except OSError:
|
||||
pass
|
||||
end_early(s)
|
||||
except (OSError, socket.timeout):
|
||||
ST.inc("srvreset")
|
||||
end_rst(s)
|
||||
time.sleep(random.uniform(0.05, 0.4))
|
||||
|
||||
def client_slowfire(cid, pace):
|
||||
# Paced indexer/coinsupply client: fire ONE genuinely-slow request, disconnect
|
||||
# after 1-500 ms (so the worker runs the multi-second call with no client left
|
||||
# == the production fd-reuse window), then wait `pace` s before the next so a
|
||||
# fire-and-disconnect backlog cannot pin the whole worker pool. Phase: cut the
|
||||
# request mid-body, or send it whole and abort while the worker is busy, or read
|
||||
# a few bytes of the reply then abort.
|
||||
while not STOP.is_set():
|
||||
method, params = heavy_menu()
|
||||
rid = next_id()
|
||||
body = json.dumps({"jsonrpc": "1.0", "id": rid, "method": method,
|
||||
"params": params}).encode()
|
||||
phase = random.choice(("mid_request", "after_send", "mid_response"))
|
||||
try:
|
||||
s = mkconn(timeout=2.0)
|
||||
except OSError:
|
||||
ST.inc("connfail"); time.sleep(pace); continue
|
||||
try:
|
||||
if phase == "mid_request":
|
||||
full = req_bytes(method, params, rid, keepalive=False, body=body)
|
||||
cut = random.randint(len(full) - len(body), len(full) - 1)
|
||||
s.sendall(full[:cut])
|
||||
time.sleep(random.uniform(0.001, 0.5)); end_early(s)
|
||||
else:
|
||||
s.sendall(req_bytes(method, params, rid, keepalive=False, body=body))
|
||||
ST.inc("sent")
|
||||
time.sleep(random.uniform(0.001, 0.5))
|
||||
if phase == "mid_response":
|
||||
try:
|
||||
s.recv(random.randint(0, 64))
|
||||
except OSError:
|
||||
pass
|
||||
end_early(s)
|
||||
except (OSError, socket.timeout):
|
||||
ST.inc("srvreset"); end_rst(s)
|
||||
time.sleep(random.uniform(pace * 0.5, pace * 1.5))
|
||||
|
||||
def client_half_open(cid):
|
||||
# connect, send nothing / partial headers, trickle, then vanish
|
||||
while not STOP.is_set():
|
||||
try:
|
||||
s = mkconn(timeout=1.0)
|
||||
except OSError:
|
||||
ST.inc("connfail"); time.sleep(0.1); continue
|
||||
ST.inc("half_open")
|
||||
style = random.choice(("silent", "partial_headers", "full_headers_no_body"))
|
||||
try:
|
||||
if style == "partial_headers":
|
||||
s.sendall(b"POST / HTTP/1.1\r\nHost: x\r\n")
|
||||
elif style == "full_headers_no_body":
|
||||
s.sendall(b"POST / HTTP/1.1\r\nHost: x\r\nAuthorization: " +
|
||||
CFG.auth.encode() + b"\r\nContent-Length: 1000\r\n\r\n")
|
||||
deadline = time.monotonic() + random.uniform(0.1, 20.0)
|
||||
while time.monotonic() < deadline and not STOP.is_set():
|
||||
time.sleep(random.uniform(0.2, 1.5))
|
||||
try:
|
||||
s.sendall(b" ") # one byte, keep it half-alive
|
||||
except OSError:
|
||||
ST.inc("half_srvclose"); break
|
||||
try:
|
||||
s.settimeout(0.01)
|
||||
if s.recv(1) == b"":
|
||||
ST.inc("half_srvclose"); break
|
||||
except socket.timeout:
|
||||
pass
|
||||
except OSError:
|
||||
ST.inc("half_srvclose"); break
|
||||
finally:
|
||||
end_early(s)
|
||||
|
||||
def client_burst(cid, size, every):
|
||||
while not STOP.is_set():
|
||||
batch = []
|
||||
for _ in range(size):
|
||||
try:
|
||||
s = mkconn(timeout=3.0)
|
||||
except OSError:
|
||||
ST.inc("connfail"); continue
|
||||
batch.append(s)
|
||||
for s in batch:
|
||||
try:
|
||||
method, params = medium_menu()
|
||||
s.sendall(req_bytes(method, params, next_id(), keepalive=False))
|
||||
ST.inc("sent")
|
||||
except OSError:
|
||||
pass
|
||||
# most close immediately (before the 500/reply is written); a few read it
|
||||
keep = random.sample(batch, max(0, len(batch) // 4)) if batch else []
|
||||
for s in batch:
|
||||
if s in keep:
|
||||
continue
|
||||
end_early(s)
|
||||
for s in keep:
|
||||
try:
|
||||
rd = Reader(s); st, hd, bd = rd.read(); account(st, bd, None, "burst")
|
||||
except Exception:
|
||||
ST.inc("srvreset")
|
||||
finally:
|
||||
end_fin(s)
|
||||
for _ in range(int(every * 10)):
|
||||
if STOP.is_set():
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
def client_pipeline(cid):
|
||||
while not STOP.is_set():
|
||||
try:
|
||||
s = mkconn()
|
||||
except OSError:
|
||||
ST.inc("connfail"); time.sleep(0.05); continue
|
||||
n = random.randint(2, 8)
|
||||
ids = []
|
||||
blob = b""
|
||||
for _ in range(n):
|
||||
rid = next_id(); ids.append(rid)
|
||||
method, params = normal_menu()
|
||||
blob += req_bytes(method, params, rid, keepalive=True)
|
||||
try:
|
||||
s.sendall(blob); ST.inc("sent", n)
|
||||
rd = Reader(s)
|
||||
toread = random.randint(0, n) # sometimes leave replies unread, then close
|
||||
for k in range(toread):
|
||||
st, hd, bd = rd.read()
|
||||
account(st, bd, ids[k], "pipeline")
|
||||
except (OSError, EOFError, ValueError):
|
||||
ST.inc("srvreset")
|
||||
finally:
|
||||
end_early(s)
|
||||
time.sleep(random.uniform(0, 0.03))
|
||||
|
||||
def client_garbage(cid):
|
||||
probes = [
|
||||
("submitblock", lambda: ["ab" * random.randint(1, 4000)]),
|
||||
("submitblock", lambda: [os.urandom(random.randint(1, 200)).hex()]),
|
||||
("submitblock", lambda: ["zzzz"]),
|
||||
("submitblock", lambda: [""]),
|
||||
]
|
||||
while not STOP.is_set():
|
||||
pick = random.random()
|
||||
try:
|
||||
s = mkconn(timeout=5.0)
|
||||
except OSError:
|
||||
ST.inc("connfail"); continue
|
||||
try:
|
||||
if pick < 0.5:
|
||||
method, mk = random.choice(probes)
|
||||
j, _ = call(s, Reader(s), method, mk(), keepalive=False)
|
||||
elif pick < 0.65:
|
||||
# malformed JSON body
|
||||
s.sendall(req_bytes("x", [], next_id(), keepalive=False,
|
||||
body=b'{"jsonrpc":"1.0","method":'))
|
||||
ST.inc("sent")
|
||||
try:
|
||||
account(*((lambda r: (r[0], r[2]))(Reader(s).read())), rid=None, method="badjson")
|
||||
except Exception:
|
||||
ST.inc("srvreset")
|
||||
elif pick < 0.8:
|
||||
# unknown method / wrong URI
|
||||
s.sendall(req_bytes("getinfo", [], next_id(), keepalive=False, uri="/nope"))
|
||||
ST.inc("sent")
|
||||
try:
|
||||
st, hd, bd = Reader(s).read(); account(st, bd, None, "wronguri")
|
||||
except Exception:
|
||||
ST.inc("srvreset")
|
||||
else:
|
||||
# Content-Length larger than the body we send, then bail
|
||||
body = b'{"id":1}'
|
||||
s.sendall(req_bytes("getinfo", [], 1, keepalive=False, body=body, clen=len(body) + 5000))
|
||||
ST.inc("sent")
|
||||
time.sleep(random.uniform(0.001, 0.3))
|
||||
end_early(s)
|
||||
continue
|
||||
except (OSError, socket.timeout):
|
||||
ST.inc("srvreset")
|
||||
finally:
|
||||
end_early(s)
|
||||
time.sleep(random.uniform(0.02, 0.2))
|
||||
|
||||
def client_slowread(cid):
|
||||
# ask for a big reply with a tiny recv buffer and do not read it, stalling the
|
||||
# server write side, then close abruptly
|
||||
while not STOP.is_set():
|
||||
try:
|
||||
s = mkconn(timeout=3.0, rcvbuf=512)
|
||||
except OSError:
|
||||
ST.inc("connfail"); continue
|
||||
try:
|
||||
s.sendall(req_bytes("listtransactions", ["*", 10000], next_id(), keepalive=False))
|
||||
ST.inc("sent")
|
||||
time.sleep(random.uniform(0.05, 1.0))
|
||||
except OSError:
|
||||
ST.inc("srvreset")
|
||||
finally:
|
||||
end_early(s)
|
||||
time.sleep(random.uniform(0, 0.05))
|
||||
|
||||
def _stale_lpid():
|
||||
# A longpollid whose hash != the current tip makes getblocktemplate return at
|
||||
# once (the server's wait loop condition is immediately false), so it exercises
|
||||
# the long-poll request path (lpval parsing, cs_main leave/enter) and the
|
||||
# mid-response disconnect WITHOUT parking a worker. On a tiny -rpcthreads pool a
|
||||
# genuinely parking long-poll + RPC-driven mining self-deadlocks (both workers
|
||||
# wait for a block that only a third worker could mine); that is a pool-size
|
||||
# artifact, not the fd bug, so the harness avoids it by default.
|
||||
with SHARED_LOCK:
|
||||
lpid = SHARED["lpid"]
|
||||
if lpid and len(lpid) > 64:
|
||||
h = lpid[:64]
|
||||
flip = "0" if h[0] != "0" else "1"
|
||||
return flip + h[1:] + lpid[64:]
|
||||
return ("0" * 64) + "0"
|
||||
|
||||
def client_longpoll(cid):
|
||||
while not STOP.is_set():
|
||||
try:
|
||||
s = mkconn(timeout=10.0)
|
||||
except OSError:
|
||||
ST.inc("connfail"); time.sleep(0.2); continue
|
||||
rd = Reader(s)
|
||||
params = [{"longpollid": _stale_lpid()}]
|
||||
rid = next_id()
|
||||
try:
|
||||
s.sendall(req_bytes("getblocktemplate", params, rid, keepalive=False))
|
||||
ST.inc("sent")
|
||||
if random.random() < 0.5:
|
||||
# impatient pool worker: abandon the request after 1-500 ms
|
||||
time.sleep(random.uniform(0.001, 0.5))
|
||||
end_early(s); continue
|
||||
st, hd, bd = rd.read()
|
||||
j = account(st, bd, rid, "getblocktemplate(lp)")
|
||||
if j and isinstance(j, dict) and j.get("result"):
|
||||
with SHARED_LOCK:
|
||||
SHARED["lpid"] = j["result"].get("longpollid")
|
||||
except (OSError, socket.timeout, EOFError, ValueError):
|
||||
ST.inc("srvreset")
|
||||
finally:
|
||||
end_fin(s)
|
||||
time.sleep(random.uniform(0.02, 0.2))
|
||||
|
||||
def miner(interval):
|
||||
# Mine a block every `interval` seconds. Retry through work-queue rejections so
|
||||
# blocks still land under heavy load (the production box mines steadily too).
|
||||
while not STOP.is_set():
|
||||
got = False
|
||||
deadline = time.monotonic() + interval + 30
|
||||
while not got and not STOP.is_set() and time.monotonic() < deadline:
|
||||
try:
|
||||
s = mkconn(timeout=60.0)
|
||||
j, _ = call(s, Reader(s), "generate", [1], keepalive=False)
|
||||
end_fin(s)
|
||||
if j and not j.get("error") and j.get("result"):
|
||||
ST.inc("mined"); got = True
|
||||
h = j["result"][0] if isinstance(j["result"], list) else None
|
||||
if h:
|
||||
with SHARED_LOCK:
|
||||
SHARED["hashes"].append(h)
|
||||
SHARED["hashes"] = SHARED["hashes"][-200:]
|
||||
else:
|
||||
time.sleep(0.2) # likely work-queue 500; back off and retry
|
||||
except Exception:
|
||||
time.sleep(0.2)
|
||||
for _ in range(int(interval * 10)):
|
||||
if STOP.is_set():
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
# ------------------------------------------------------------------ bootstrap shared state
|
||||
|
||||
def bootstrap():
|
||||
try:
|
||||
s = mkconn(timeout=30.0); rd = Reader(s)
|
||||
j, _ = call(s, rd, "getbestblockhash", [], keepalive=True)
|
||||
if j and j.get("result"):
|
||||
with SHARED_LOCK:
|
||||
SHARED["hashes"].append(j["result"])
|
||||
j, _ = call(s, rd, "getblockcount", [], keepalive=True)
|
||||
cnt = j.get("result") if j else None
|
||||
if isinstance(cnt, int):
|
||||
for hh in range(max(0, cnt - 30), cnt + 1):
|
||||
jj, _ = call(s, rd, "getblockhash", [hh], keepalive=True)
|
||||
if jj and jj.get("result"):
|
||||
with SHARED_LOCK:
|
||||
SHARED["hashes"].append(jj["result"])
|
||||
j, _ = call(s, rd, "listaddresses", [], keepalive=True)
|
||||
j, _ = call(s, rd, "z_listaddresses", [], keepalive=True)
|
||||
if j and isinstance(j.get("result"), list):
|
||||
with SHARED_LOCK:
|
||||
SHARED["zaddrs"] = j["result"][:16]
|
||||
j, _ = call(s, rd, "getblocktemplate", [{}], keepalive=True)
|
||||
if j and isinstance(j.get("result"), dict):
|
||||
with SHARED_LOCK:
|
||||
SHARED["lpid"] = j["result"].get("longpollid")
|
||||
end_fin(s)
|
||||
log("bootstrap: %d hashes, %d zaddrs, lpid=%s" %
|
||||
(len(SHARED["hashes"]), len(SHARED["zaddrs"]), bool(SHARED["lpid"])))
|
||||
except Exception as e:
|
||||
log("bootstrap failed: %s" % e)
|
||||
|
||||
# ------------------------------------------------------------------ fd accounting
|
||||
|
||||
def count_fds(pid):
|
||||
if not pid:
|
||||
return (None, None, None)
|
||||
base = "/proc/%d/fd" % pid
|
||||
try:
|
||||
fds = os.listdir(base)
|
||||
except OSError:
|
||||
return (None, None, None)
|
||||
sock = files = 0
|
||||
for fd in fds:
|
||||
try:
|
||||
tgt = os.readlink(os.path.join(base, fd))
|
||||
except OSError:
|
||||
continue
|
||||
if tgt.startswith("socket:"):
|
||||
sock += 1
|
||||
elif tgt.startswith("/"):
|
||||
files += 1
|
||||
return (len(fds), sock, files)
|
||||
|
||||
def pid_alive(pid):
|
||||
if not pid:
|
||||
return True
|
||||
try:
|
||||
os.kill(pid, 0); return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
# ------------------------------------------------------------------ main
|
||||
|
||||
def reporter(pid):
|
||||
t0 = time.monotonic()
|
||||
while not STOP.is_set():
|
||||
for _ in range(10):
|
||||
if STOP.is_set():
|
||||
break
|
||||
time.sleep(0.1)
|
||||
w = ST.swap()
|
||||
tot, sk, fl = count_fds(pid)
|
||||
fdstr = ""
|
||||
if tot is not None:
|
||||
fdstr = " | fds=%d sock=%d file=%d" % (tot, sk, fl)
|
||||
log("t=%4ds ok=%d rpcerr=%d wq500=%d 4xx=%d other=%d | "
|
||||
"earlyRST=%d earlyFIN=%d srvRST=%d srvEOF=%d ctmo=%d connfail=%d | "
|
||||
"idmis=%d malf=%d | mined=%d halfopen=%d%s" % (
|
||||
int(time.monotonic() - t0),
|
||||
w["ok"], w["rpcerr"], w["wq500"], w["http4xx"], w["httpother"],
|
||||
w["early_rst"], w["early_fin"], w["srvreset"], w["srveof"],
|
||||
w["ctimeout"], w["connfail"], w["idmismatch"], w["malformed"],
|
||||
w["mined"], w["half_open"], fdstr))
|
||||
if pid and not pid_alive(pid):
|
||||
log("!!! NODE PROCESS %d IS GONE -- stopping harness" % pid)
|
||||
STOP.set()
|
||||
break
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--host", default="127.0.0.1")
|
||||
ap.add_argument("--port", type=int, default=19461)
|
||||
ap.add_argument("--user", default="ab")
|
||||
ap.add_argument("--password", default="abpass")
|
||||
ap.add_argument("--duration", type=int, default=300)
|
||||
ap.add_argument("--ctimeout", type=float, default=15.0)
|
||||
ap.add_argument("--normal", type=int, default=24)
|
||||
ap.add_argument("--impatient", type=int, default=10)
|
||||
ap.add_argument("--slowfire", type=int, default=3,
|
||||
help="paced clients that fire a genuinely-slow call then disconnect")
|
||||
ap.add_argument("--slowfire-pace", type=float, default=2.0)
|
||||
ap.add_argument("--halfopen", type=int, default=6)
|
||||
ap.add_argument("--pipeline", type=int, default=6)
|
||||
ap.add_argument("--garbage", type=int, default=4)
|
||||
ap.add_argument("--slowread", type=int, default=2)
|
||||
ap.add_argument("--longpoll", type=int, default=6)
|
||||
# burst-size must exceed (rpcthreads + rpcworkqueue) to overflow the queue even
|
||||
# when calls are fast; with -rpcthreads=8 -rpcworkqueue=4 that threshold is 12.
|
||||
ap.add_argument("--burst-threads", type=int, default=1)
|
||||
ap.add_argument("--burst-size", type=int, default=24)
|
||||
ap.add_argument("--burst-every", type=float, default=1.5)
|
||||
ap.add_argument("--mine", action="store_true", help="run a miner thread")
|
||||
ap.add_argument("--mine-interval", type=float, default=4.0)
|
||||
ap.add_argument("--pid", type=int, default=0, help="node pid, for /proc fd + liveness")
|
||||
ap.add_argument("--logfile", default="")
|
||||
args = ap.parse_args()
|
||||
|
||||
CFG.host = args.host; CFG.port = args.port
|
||||
CFG.auth = auth_header(args.user, args.password); CFG.ctimeout = args.ctimeout
|
||||
if args.logfile:
|
||||
LOGF[0] = open(args.logfile, "a")
|
||||
|
||||
log("hammer start host=%s port=%d duration=%ds pid=%s" %
|
||||
(CFG.host, CFG.port, args.duration, args.pid or "-"))
|
||||
bootstrap()
|
||||
|
||||
threads = []
|
||||
def spawn(fn, n, *a):
|
||||
for i in range(n):
|
||||
t = threading.Thread(target=fn, args=(i,) + a, daemon=True)
|
||||
t.start(); threads.append(t)
|
||||
|
||||
spawn(client_normal, args.normal)
|
||||
spawn(client_impatient, args.impatient)
|
||||
spawn(client_slowfire, args.slowfire, args.slowfire_pace)
|
||||
spawn(client_half_open, args.halfopen)
|
||||
spawn(client_pipeline, args.pipeline)
|
||||
spawn(client_garbage, args.garbage)
|
||||
spawn(client_slowread, args.slowread)
|
||||
spawn(client_longpoll, args.longpoll)
|
||||
for i in range(args.burst_threads):
|
||||
t = threading.Thread(target=client_burst, args=(i, args.burst_size, args.burst_every), daemon=True)
|
||||
t.start(); threads.append(t)
|
||||
if args.mine:
|
||||
t = threading.Thread(target=miner, args=(args.mine_interval,), daemon=True)
|
||||
t.start(); threads.append(t)
|
||||
|
||||
rt = threading.Thread(target=reporter, args=(args.pid,), daemon=True)
|
||||
rt.start()
|
||||
|
||||
end = time.monotonic() + args.duration
|
||||
try:
|
||||
while time.monotonic() < end and not STOP.is_set():
|
||||
time.sleep(0.2)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
STOP.set()
|
||||
time.sleep(1.0)
|
||||
|
||||
log("=== TOTALS ===")
|
||||
for k in KEYS:
|
||||
if ST.total[k]:
|
||||
log(" %-14s %d" % (k, ST.total[k]))
|
||||
log("anomalies: %d" % len(ST.anom))
|
||||
for t, kind, detail in ST.anom[:40]:
|
||||
log(" %s %s %s" % (time.strftime("%H:%M:%S", time.localtime(t)), kind, detail))
|
||||
if args.pid:
|
||||
log("node alive at end: %s" % pid_alive(args.pid))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
# RPC-driven P2P peer churn on the target (same mix as fdbug/p2p/rpcchurn.sh):
|
||||
# disconnectnode / addnode onetry / setban+clearbanned / kill -9 + restart of helper2.
|
||||
# usage: rpcchurn.sh <run dir> <seconds>
|
||||
source "$REPRO/common.sh"
|
||||
OUT=$1; DUR=${2:-300}
|
||||
DD_T=$OUT/target; DD_H1=$OUT/helper1; DD_H2=$OUT/helper2
|
||||
end=$(( $(date +%s) + DUR )); i=0; restarts=0
|
||||
while [ "$(date +%s)" -lt "$end" ] && [ ! -e "$OUT/STOP" ]; do
|
||||
i=$((i+1))
|
||||
case $(( i % 5 )) in
|
||||
0) clin "$DD_T" "$RPC_T" disconnectnode "127.0.0.1:$P2P_H1" >/dev/null 2>&1 ;;
|
||||
1) clin "$DD_T" "$RPC_T" addnode "127.0.0.1:$P2P_H1" onetry >/dev/null 2>&1 ;;
|
||||
2) clin "$DD_T" "$RPC_T" setban "127.0.0.1" add 1 >/dev/null 2>&1; sleep 0.3
|
||||
clin "$DD_T" "$RPC_T" clearbanned >/dev/null 2>&1
|
||||
clin "$DD_H2" "$RPC_H2" addnode "127.0.0.1:$P2P_T" onetry >/dev/null 2>&1 ;;
|
||||
3) pid=$(cat "$DD_H2/regtest/hushd.pid" 2>/dev/null)
|
||||
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||||
kill -9 "$pid" 2>/dev/null
|
||||
for _ in $(seq 1 20); do kill -0 "$pid" 2>/dev/null || break; sleep 0.5; done
|
||||
start_helper "$DD_H2" "$P2P_H2" "$RPC_H2" "$OUT/logs/helper2.txt"
|
||||
waitrpc "$DD_H2" "$RPC_H2" 120 >/dev/null 2>&1
|
||||
restarts=$((restarts+1))
|
||||
fi ;;
|
||||
4) clin "$DD_T" "$RPC_T" addnode "127.0.0.1:$P2P_H2" onetry >/dev/null 2>&1 ;;
|
||||
esac
|
||||
sleep 0.7
|
||||
done
|
||||
echo "rpcchurn: $i cycles, $restarts helper2 restarts"
|
||||
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
scan_bdb.py -- find Berkeley DB pages written into files that must not contain them
|
||||
(blocks/blk*.dat, blocks/rev*.dat). Stand-in for the production incident tool.
|
||||
|
||||
BDB writes page P of a database at file offset P*pagesize through pwrite(fd, ...). If
|
||||
fd has been closed and its number reused by a block file, the page lands in the block
|
||||
file at exactly P*pagesize. So at every pagesize-aligned offset we decode the PAGE
|
||||
header (db_page.h, all little-endian on x86):
|
||||
0 u32 lsn.file 4 u32 lsn.offset 8 u32 pgno 12 u32 prev_pgno
|
||||
16 u32 next_pgno 20 u16 entries 22 u16 hf_offset
|
||||
24 u8 level 25 u8 type
|
||||
and a metadata page (pgno 0 / any meta type) also carries magic at 12 and pagesize at 20.
|
||||
|
||||
A hit needs ALL of:
|
||||
* pgno == offset // pagesize (the self-locating property)
|
||||
* type is a real BDB page type (1..16 except 0)
|
||||
* level consistent with type (overflow/meta/hash: 0; btree leaf: 1; internal: 2..32)
|
||||
* hf_offset <= pagesize; entries <= pagesize/2
|
||||
* for overflow (7): entries (refcount) >= 1 and 0 < hf_offset (data length) <= pagesize-26
|
||||
Strength: "STRONG" if lsn == (0,1) (pages rewritten by lsn_reset -- the production
|
||||
signature) or a meta page with a valid magic, else "weak".
|
||||
Also reports any aligned BDB meta magic (btree 0x053162, hash 0x061561, queue 0x042253,
|
||||
heap 0x074582) even when pgno does not match.
|
||||
|
||||
usage: scan_bdb.py [--pagesize 4096] [--all-offsets] files...
|
||||
exit 0 = clean, 2 = hits.
|
||||
"""
|
||||
import argparse, struct, sys
|
||||
|
||||
TYPES = {1: "duplicate", 2: "hash_unsorted", 3: "ibtree", 4: "irecno", 5: "lbtree", 6: "lrecno",
|
||||
7: "overflow", 8: "hashmeta", 9: "btreemeta", 10: "qammeta", 11: "qamdata", 12: "ldup",
|
||||
13: "hash", 14: "heapmeta", 15: "heap", 16: "iheap"}
|
||||
MAGIC = {0x053162: "btree", 0x061561: "hash", 0x042253: "queue", 0x074582: "heap"}
|
||||
META = {8, 9, 10, 14}
|
||||
|
||||
|
||||
def level_ok(t, lvl):
|
||||
if t in (5, 6, 12): # leaf pages
|
||||
return lvl == 1
|
||||
if t in (3, 4): # internal pages
|
||||
return 2 <= lvl <= 32
|
||||
return lvl == 0 # overflow / meta / hash / queue / heap
|
||||
|
||||
|
||||
def check(buf, off, ps):
|
||||
if len(buf) < 26:
|
||||
return None
|
||||
lf, lo, pgno, prv, nxt, ent, hfo, lvl, typ = struct.unpack_from("<IIIIIHHBB", buf, 0)
|
||||
if typ not in TYPES or pgno != off // ps or not level_ok(typ, lvl):
|
||||
return None
|
||||
if typ in META:
|
||||
magic, ver, psz = struct.unpack_from("<III", buf, 12)
|
||||
if magic not in MAGIC:
|
||||
return None
|
||||
return ("STRONG", f"{TYPES[typ]} magic={MAGIC[magic]} ver={ver} pagesize={psz} lsn=({lf},{lo})")
|
||||
if hfo > ps or ent > ps // 2:
|
||||
return None
|
||||
if typ == 7 and not (ent >= 1 and 0 < hfo <= ps - 26):
|
||||
return None
|
||||
strength = "STRONG" if (lf, lo) == (0, 1) else "weak"
|
||||
return (strength, f"{TYPES[typ]} lsn=({lf},{lo}) prev={prv} next={nxt} entries={ent} hf_offset={hfo} level={lvl}")
|
||||
|
||||
|
||||
def scan(path, ps):
|
||||
hits, magic_only = [], []
|
||||
with open(path, "rb") as f:
|
||||
off = 0
|
||||
while True:
|
||||
buf = f.read(ps)
|
||||
if not buf:
|
||||
break
|
||||
r = check(buf, off, ps)
|
||||
if r:
|
||||
hits.append((off, off // ps) + r)
|
||||
elif len(buf) >= 16:
|
||||
m = struct.unpack_from("<I", buf, 12)[0]
|
||||
if m in MAGIC and buf[25] in META:
|
||||
magic_only.append((off, MAGIC[m]))
|
||||
off += len(buf)
|
||||
return hits, magic_only, off
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--pagesize", type=int, default=4096)
|
||||
ap.add_argument("--quiet", action="store_true")
|
||||
ap.add_argument("files", nargs="+")
|
||||
a = ap.parse_args()
|
||||
total = strong = 0
|
||||
for p in a.files:
|
||||
try:
|
||||
hits, mo, size = scan(p, a.pagesize)
|
||||
except OSError as e:
|
||||
print(f"{p}: ERROR {e}")
|
||||
continue
|
||||
s = sum(1 for h in hits if h[2] == "STRONG")
|
||||
total += len(hits); strong += s
|
||||
if hits or mo:
|
||||
print(f"{p}: {len(hits)} BDB page(s) ({s} strong), {len(mo)} stray meta magic, size={size}")
|
||||
if not a.quiet:
|
||||
for h in hits[:20]:
|
||||
print(f" off={h[0]} pgno={h[1]} {h[2]} {h[3]}")
|
||||
for m in mo[:5]:
|
||||
print(f" meta-magic off={m[0]} {m[1]}")
|
||||
else:
|
||||
print(f"{p}: clean ({size} bytes)")
|
||||
print(f"TOTAL BDB pages found: {total} ({strong} strong)")
|
||||
return 2 if total else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""tally.py <fdguard.log> -- attribute every fdguard report to (type, closer function, target class)
|
||||
and, for EBADF/NON-OWNER reports, to the code that made the previous close of that fd number.
|
||||
|
||||
The point: show that the stray closes ORIGINATE in CNode code (net.cpp CloseSocketDisconnect /
|
||||
~CNode closing a number they already closed) and that libevent / BDB / CreateNodeFromAcceptedSocket /
|
||||
GetTransaction only appear as victims-turned-amplifiers downstream of that.
|
||||
"""
|
||||
import collections, re, sys
|
||||
|
||||
ORIGIN = ("_ZN5CNode21CloseSocketDisconnectEv", "_ZN5CNodeD1Ev", "_ZN5CNodeD2Ev", "_ZN11CNetCleanupD2Ev")
|
||||
NAMES = {"_ZN5CNode21CloseSocketDisconnectEv": "CNode::CloseSocketDisconnect (net.cpp:572)",
|
||||
"_ZN5CNodeD1Ev": "CNode::~CNode (net.cpp:2819)",
|
||||
"_ZN11CNetCleanupD2Ev": "CNetCleanup::~CNetCleanup (net.cpp:2465, shutdown)",
|
||||
"evhttp_connection_free": "libevent evhttp_connection_free (http.c:1244)",
|
||||
"__os_closehandle": "BDB __os_closehandle",
|
||||
"_Z28CreateNodeFromAcceptedSocketjbRK8CAddressS1_": "CreateNodeFromAcceptedSocket (net.cpp:1336)",
|
||||
"_Z14GetTransactionRK7uint256R12CTransactionRS_b": "GetTransaction fclose (main.cpp)",
|
||||
"epoll_nochangelist_add": "libevent epoll ADD", "epoll_nochangelist_del": "libevent epoll DEL/MOD"}
|
||||
|
||||
|
||||
def fn(frame_line):
|
||||
m = re.search(r"\+0x[0-9a-f]+ (\S+?)\+0x", frame_line)
|
||||
return m.group(1) if m else "?"
|
||||
|
||||
|
||||
def main(path):
|
||||
reps, cur = [], None
|
||||
for line in open(path, errors="replace"):
|
||||
if line.startswith("=== FDGUARD #"):
|
||||
cur = {"hdr": line.strip(), "lines": []}
|
||||
reps.append(cur)
|
||||
elif cur is not None:
|
||||
if line.startswith("=== END"):
|
||||
cur = None
|
||||
else:
|
||||
cur["lines"].append(line.rstrip("\n"))
|
||||
tally = collections.Counter()
|
||||
prev_tally = collections.Counter()
|
||||
db_blk = []
|
||||
for r in reps:
|
||||
typ = r["hdr"].split()[3]
|
||||
L = r["lines"]
|
||||
cls = next((re.search(r"class=(\S+)", l).group(1) for l in L if " target=" in l and "class=" in l), "?")
|
||||
try:
|
||||
i = next(k for k, l in enumerate(L) if l.strip() == "closer stack:")
|
||||
closer = fn(L[i + 1])
|
||||
except StopIteration:
|
||||
closer = "?"
|
||||
prev = "-"
|
||||
for k, l in enumerate(L):
|
||||
if "previous successful close of this fd number" in l:
|
||||
# first frame line after the target= line
|
||||
for l2 in L[k + 1:k + 4]:
|
||||
if l2.strip().startswith("#0"):
|
||||
prev = fn(l2)
|
||||
break
|
||||
break
|
||||
tally[(typ, NAMES.get(closer, closer), cls)] += 1
|
||||
if typ in ("EBADF-CLOSE", "NON-OWNER-CLOSE"):
|
||||
prev_tally[NAMES.get(prev, prev)] += 1
|
||||
if typ == "NON-OWNER-CLOSE" and cls in ("db", "blk"):
|
||||
tgt = next((l.split("target=")[1].split()[0] for l in L if " target=" in l), "?")
|
||||
db_blk.append((r["hdr"].split()[2], r["hdr"].split()[4], NAMES.get(closer, closer), tgt.rsplit("/", 2)[-2:]))
|
||||
print(f"{len(reps)} reports in {path}")
|
||||
print("\n count type class closer")
|
||||
for (typ, closer, cls), n in tally.most_common():
|
||||
print(f"{n:6d} {typ:17s} {cls:6s} {closer}")
|
||||
print("\nEBADF + NON-OWNER reports by the code that made the PREVIOUS close of the same number:")
|
||||
for k, n in prev_tally.most_common():
|
||||
print(f"{n:6d} {k}")
|
||||
o = sum(n for (typ, c, cls), n in tally.items() if typ == "NON-OWNER-CLOSE" and cls in ("db", "blk") and c.startswith(("CNode", "CNetCleanup")))
|
||||
a = sum(n for (typ, c, cls), n in tally.items() if typ == "NON-OWNER-CLOSE" and cls in ("db", "blk")) - o
|
||||
print(f"\nNON-OWNER closes of wallet/BDB-log/block files: {o + a} (by CNode code directly: {o}; by an amplifier: {a})")
|
||||
for d in db_blk[:40]:
|
||||
print(" ", d[0], d[1], "closer=" + d[2], "target=" + "/".join(d[3]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1])
|
||||
Executable
+147
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Raw TCP / TLS client churn against the target node's P2P port.
|
||||
|
||||
dragonxd P2P is TLS 1.3 only (wolfSSL), self-signed, no cert verification. The
|
||||
accept path is net.cpp CreateNodeFromAcceptedSocket -> tlsmanager.accept()
|
||||
(wolfSSL_accept), and teardown is CloseSocketDisconnect / ~CNode
|
||||
(wolfSSL_shutdown + wolfSSL_free + CloseSocket). This driver pokes the accept and
|
||||
TLS-teardown/error paths (SSL_read/SSL_write at net.cpp ~1005 and tlsmanager.cpp
|
||||
~660-680) by opening many short-lived connections that each misbehave:
|
||||
|
||||
rst : connect, then abort with RST (SO_LINGER 0) before any TLS bytes
|
||||
fin : connect, then close cleanly (FIN) before any TLS bytes
|
||||
garbage : connect, send non-ClientHello bytes, then drop
|
||||
stall : connect, send a few bytes, hold mid-handshake, drop
|
||||
tlsjunk : complete a real TLS 1.3 handshake (verify off), send junk, drop
|
||||
tlshalf : start TLS, send a partial ClientHello record, drop
|
||||
|
||||
Loopback only, against a port this harness started. --duration seconds.
|
||||
"""
|
||||
import argparse, random, socket, ssl, struct, sys, time, os
|
||||
|
||||
def mk():
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(3.0)
|
||||
return s
|
||||
|
||||
def rst_close(s):
|
||||
try:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0))
|
||||
except Exception:
|
||||
pass
|
||||
try: s.close()
|
||||
except Exception: pass
|
||||
|
||||
def do_rst(host, port):
|
||||
s = mk()
|
||||
try:
|
||||
s.connect((host, port)); rst_close(s)
|
||||
except Exception:
|
||||
rst_close(s)
|
||||
|
||||
def do_fin(host, port):
|
||||
s = mk()
|
||||
try:
|
||||
s.connect((host, port)); s.close()
|
||||
except Exception:
|
||||
try: s.close()
|
||||
except Exception: pass
|
||||
|
||||
def do_garbage(host, port):
|
||||
s = mk()
|
||||
try:
|
||||
s.connect((host, port))
|
||||
s.sendall(os.urandom(random.randint(16, 400)))
|
||||
time.sleep(random.uniform(0.0, 0.3))
|
||||
except Exception:
|
||||
pass
|
||||
rst_close(s)
|
||||
|
||||
def do_stall(host, port):
|
||||
s = mk()
|
||||
try:
|
||||
s.connect((host, port))
|
||||
# a couple of bytes that look like the start of a TLS record, then wait
|
||||
s.sendall(b"\x16\x03\x01")
|
||||
time.sleep(random.uniform(0.5, 2.0))
|
||||
except Exception:
|
||||
pass
|
||||
rst_close(s)
|
||||
|
||||
def _ctx():
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
try:
|
||||
ctx.minimum_version = ssl.TLSVersion.TLSv1_3
|
||||
except Exception:
|
||||
pass
|
||||
return ctx
|
||||
|
||||
def do_tlsjunk(host, port):
|
||||
raw = mk()
|
||||
try:
|
||||
raw.connect((host, port))
|
||||
tls = _ctx().wrap_socket(raw, server_hostname=None)
|
||||
# handshake completed; the node now expects a P2P version message.
|
||||
# send junk inside the TLS channel, then tear down.
|
||||
try:
|
||||
tls.sendall(os.urandom(random.randint(8, 128)))
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(random.uniform(0.0, 0.4))
|
||||
try:
|
||||
tls.close()
|
||||
except Exception:
|
||||
rst_close(raw)
|
||||
except Exception:
|
||||
rst_close(raw)
|
||||
|
||||
def do_tlshalf(host, port):
|
||||
s = mk()
|
||||
try:
|
||||
s.connect((host, port))
|
||||
# a well-formed TLS record header claiming a long handshake, but we send
|
||||
# only part of it, then drop -> server sits in wolfSSL_accept WANT_READ.
|
||||
body = os.urandom(40)
|
||||
rec = b"\x16\x03\x03" + struct.pack(">H", 512) + body
|
||||
s.sendall(rec)
|
||||
time.sleep(random.uniform(0.3, 1.5))
|
||||
except Exception:
|
||||
pass
|
||||
rst_close(s)
|
||||
|
||||
HANDLERS = [do_rst, do_fin, do_garbage, do_stall, do_tlsjunk, do_tlshalf]
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--host", default="127.0.0.1")
|
||||
ap.add_argument("--port", type=int, required=True)
|
||||
ap.add_argument("--duration", type=float, default=300.0)
|
||||
ap.add_argument("--concurrency", type=int, default=6)
|
||||
ap.add_argument("--rate", type=float, default=0.03, help="sleep between bursts")
|
||||
args = ap.parse_args()
|
||||
|
||||
end = time.time() + args.duration
|
||||
counts = {h.__name__: 0 for h in HANDLERS}
|
||||
import threading
|
||||
lock = threading.Lock()
|
||||
|
||||
def worker():
|
||||
while time.time() < end:
|
||||
h = random.choice(HANDLERS)
|
||||
h(args.host, args.port)
|
||||
with lock:
|
||||
counts[h.__name__] += 1
|
||||
time.sleep(random.uniform(0.0, args.rate))
|
||||
|
||||
ts = [threading.Thread(target=worker, daemon=True) for _ in range(args.concurrency)]
|
||||
for t in ts: t.start()
|
||||
for t in ts: t.join()
|
||||
|
||||
total = sum(counts.values())
|
||||
print(f"tcpchurn: {total} connections in {args.duration:.0f}s -> {counts}", flush=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# Build the fdguard LD_PRELOAD shim and its self-test program.
|
||||
# ./build.sh -> libfdguard.so, fdguard_test
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
CC=${CC:-gcc}
|
||||
$CC -O2 -g -fPIC -shared -Wall -Wextra -Wno-unused-parameter -fvisibility=default \
|
||||
-o libfdguard.so.tmp fdguard.c -ldl -lpthread
|
||||
# rename, never overwrite in place: a running process may have the old one mapped
|
||||
mv -f libfdguard.so.tmp libfdguard.so
|
||||
# the test program keeps its symbols (no -rdynamic on purpose: like dragonxd,
|
||||
# owner detection must work from .symtab alone) and frame pointers for clarity
|
||||
$CC -O1 -g -fno-omit-frame-pointer -fno-inline -Wall -o fdguard_test.tmp fdguard_test.c -lpthread
|
||||
mv -f fdguard_test.tmp fdguard_test
|
||||
echo "built: $(pwd)/libfdguard.so $(pwd)/fdguard_test"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* fdguard_test.c -- exercises libfdguard.so.
|
||||
*
|
||||
* The function names imitate the real owners inside dragonxd so the shim's
|
||||
* built-in domain patterns apply unchanged:
|
||||
* __os_openhandle / __os_closehandle Berkeley DB os layer (domain bdb)
|
||||
* OpenDiskFile / FlushBlockFile block-file code (domain blk)
|
||||
* ConnectSocketDirectly / CloseSocket P2P sockets (domain p2p)
|
||||
* foreign_stale_close / foreign_* somebody who holds a stale number
|
||||
*
|
||||
* usage: fdguard_test <dir> <case> (see main() for the list)
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define NI __attribute__((noinline))
|
||||
static char D[512];
|
||||
static char *P(const char *rel) { static char b[4][700]; static int i; i = (i + 1) & 3; snprintf(b[i], sizeof b[i], "%s/%s", D, rel); return b[i]; }
|
||||
static void say(const char *m) { fprintf(stderr, "[test] %s\n", m); }
|
||||
|
||||
/* --- "Berkeley DB" --- */
|
||||
NI int __os_openhandle(const char *path) { return open(path, O_RDWR | O_CREAT, 0600); }
|
||||
NI int __os_closehandle(int fd) { return close(fd); }
|
||||
/* --- "block files" --- */
|
||||
NI FILE *OpenDiskFile(const char *path) { FILE *f = fopen(path, "rb+"); if (!f) f = fopen(path, "wb+"); return f; }
|
||||
NI int FlushBlockFile(FILE *f) { fflush(f); return fclose(f); }
|
||||
/* --- "P2P" --- */
|
||||
NI int ConnectSocketDirectly(void) { return socket(AF_INET, SOCK_STREAM, 0); }
|
||||
NI int CloseSocket(int *s) { int r = close(*s); if (r) fprintf(stderr, "[test] Socket close failed: %d. Error: %s\n", *s, strerror(errno)); return r; }
|
||||
/* --- foreigners --- */
|
||||
NI int foreign_stale_close(int fd) { return close(fd); }
|
||||
NI int foreign_stale_fclose(FILE *f) { return fclose(f); }
|
||||
NI int foreign_dup2(int fd) { int dn = open("/dev/null", O_RDWR); int r = dup2(dn, fd); close(dn); return r; }
|
||||
NI int foreign_close_range(int fd) { return close_range((unsigned)fd, (unsigned)fd, 0); }
|
||||
NI void foreign_closefrom(int fd) { closefrom(fd); }
|
||||
/* --- plain code that opens and closes in the same function --- */
|
||||
NI int copy_like(const char *src) { int fd = open(src, O_RDONLY); char b[16]; if (fd >= 0) { (void)!read(fd, b, sizeof b); close(fd); } return fd; }
|
||||
NI int plain_open(const char *p) { return open(p, O_RDWR | O_CREAT, 0600); }
|
||||
NI int plain_close(int fd) { return close(fd); }
|
||||
|
||||
static int fd_valid(int fd) { return fcntl(fd, F_GETFD) != -1; }
|
||||
|
||||
static int c_double(void)
|
||||
{
|
||||
int fd = plain_open(P("plain.txt"));
|
||||
plain_close(fd);
|
||||
int r = plain_close(fd); /* EBADF */
|
||||
return r == -1 && errno == EBADF ? 0 : 1;
|
||||
}
|
||||
static int c_dbowner(void) { int fd = __os_openhandle(P("regtest/wallet.dat")); int r = __os_closehandle(fd); return r; }
|
||||
static int c_dbforeign(void)
|
||||
{
|
||||
int fd = __os_openhandle(P("regtest/wallet.dat"));
|
||||
foreign_stale_close(fd); /* HIT */
|
||||
int r = __os_closehandle(fd); /* BDB's own close then fails: EBADF (unless deny) */
|
||||
(void)r;
|
||||
return 0;
|
||||
}
|
||||
static int c_deny(void)
|
||||
{
|
||||
int fd = __os_openhandle(P("regtest/wallet.dat"));
|
||||
foreign_stale_close(fd); /* HIT + DENIED */
|
||||
if (!fd_valid(fd)) { say("deny FAILED: fd closed"); return 1; }
|
||||
if (write(fd, "x", 1) != 1) { say("deny FAILED: write"); return 1; }
|
||||
return __os_closehandle(fd);
|
||||
}
|
||||
static int c_blkowner(void) { FILE *f = OpenDiskFile(P("regtest/blocks/blk00000.dat")); fputs("blk", f); return FlushBlockFile(f); }
|
||||
static int c_blkraw(void)
|
||||
{
|
||||
FILE *f = OpenDiskFile(P("regtest/blocks/blk00001.dat"));
|
||||
foreign_stale_close(fileno(f)); /* HIT: raw close under a live FILE */
|
||||
FlushBlockFile(f); /* EBADF from fclose */
|
||||
return 0;
|
||||
}
|
||||
static int c_stalefile(void)
|
||||
{
|
||||
FILE *f = OpenDiskFile(P("regtest/blocks/blk00002.dat"));
|
||||
int n = fileno(f);
|
||||
foreign_stale_close(n); /* HIT #1 */
|
||||
int w = __os_openhandle(P("regtest/wallet.dat"));
|
||||
if (w != n) fprintf(stderr, "[test] note: wallet got fd %d, FILE had %d\n", w, n);
|
||||
FlushBlockFile(f); /* HIT #2: fclose of a stale FILE whose number is now wallet.dat */
|
||||
if (w == n) __os_closehandle(w); /* EBADF: BDB lost its fd */
|
||||
return 0;
|
||||
}
|
||||
static int c_reuse(void)
|
||||
{
|
||||
int s = ConnectSocketDirectly();
|
||||
int keep = s;
|
||||
CloseSocket(&s); /* legit */
|
||||
int w = __os_openhandle(P("regtest/wallet.dat")); /* reuses the number */
|
||||
if (w != keep) fprintf(stderr, "[test] note: wallet got fd %d, socket was %d\n", w, keep);
|
||||
s = keep;
|
||||
CloseSocket(&s); /* HIT: stale socket number now = wallet.dat */
|
||||
__os_closehandle(w); /* EBADF */
|
||||
return 0;
|
||||
}
|
||||
static int c_dup2(void) { int w = __os_openhandle(P("regtest/wallet.dat")); foreign_dup2(w); __os_closehandle(w); return 0; }
|
||||
static int c_range(void)
|
||||
{
|
||||
int w = __os_openhandle(P("regtest/wallet.dat"));
|
||||
foreign_close_range(w); /* HIT */
|
||||
w = __os_openhandle(P("regtest/database/log.0000000001"));
|
||||
int hi = fcntl(w, F_DUPFD, 600); /* move it high so closefrom only hits it */
|
||||
close(w);
|
||||
foreign_closefrom(hi); /* HIT (domain mismatch: opened by test main via fcntl dup? -> fallback db) */
|
||||
return 0;
|
||||
}
|
||||
static int c_owner_misc(void)
|
||||
{
|
||||
/* nothing here may be flagged */
|
||||
copy_like(P("regtest/wallet.dat")); /* common-caller */
|
||||
int fd = __os_openhandle(P("regtest/blocks/blk00003.dat"));
|
||||
FILE *f = fdopen(fd, "rb+"); /* ownership moves to FILE */
|
||||
FlushBlockFile(f);
|
||||
int s = ConnectSocketDirectly(); CloseSocket(&s);
|
||||
fd = (int)syscall(SYS_openat, AT_FDCWD, P("regtest/wallet.dat"), O_RDWR); /* untracked open */
|
||||
__os_closehandle(fd); /* fallback:bdb */
|
||||
return 0;
|
||||
}
|
||||
static int c_fallback_hit(void)
|
||||
{
|
||||
int fd = (int)syscall(SYS_openat, AT_FDCWD, P("regtest/wallet.dat"), O_RDWR); /* untracked open */
|
||||
foreign_stale_close(fd); /* HIT via fallback rule */
|
||||
return 0;
|
||||
}
|
||||
static int c_epoll(void)
|
||||
{
|
||||
int ep = epoll_create1(0);
|
||||
int fd = plain_open(P("plain2.txt"));
|
||||
struct epoll_event ev = { .events = EPOLLIN };
|
||||
int r = epoll_ctl(ep, EPOLL_CTL_ADD, fd, &ev); /* EPERM: regular file */
|
||||
close(fd);
|
||||
r = epoll_ctl(ep, EPOLL_CTL_ADD, fd, &ev); /* EBADF */
|
||||
close(ep);
|
||||
return r == -1 ? 0 : 1;
|
||||
}
|
||||
static int c_selfclose(void)
|
||||
{
|
||||
/* close every fd from 3 to 1100 by number, like a buggy daemonizer */
|
||||
for (int i = 3; i < 1100; i++) close(i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* concurrency + micro-benchmark: open/close churn in many threads */
|
||||
static int nthr = 8, iters = 20000;
|
||||
static void *worker(void *arg)
|
||||
{
|
||||
long id = (long)arg;
|
||||
char p[700]; snprintf(p, sizeof p, "%s/thr%ld.txt", D, id);
|
||||
for (int i = 0; i < iters; i++) {
|
||||
int s = ConnectSocketDirectly(); CloseSocket(&s);
|
||||
int fd = plain_open(p); plain_close(fd);
|
||||
FILE *f = fopen(p, "r"); if (f) fclose(f);
|
||||
if ((i & 255) == 0) { int w = __os_openhandle(P("regtest/wallet.dat")); __os_closehandle(w); }
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static int c_threads(void)
|
||||
{
|
||||
const char *e = getenv("T_ITERS"); if (e) iters = atoi(e);
|
||||
pthread_t t[64];
|
||||
struct timespec a, b; clock_gettime(CLOCK_MONOTONIC, &a);
|
||||
for (long i = 0; i < nthr; i++) pthread_create(&t[i], NULL, worker, (void *)i);
|
||||
for (int i = 0; i < nthr; i++) pthread_join(t[i], NULL);
|
||||
clock_gettime(CLOCK_MONOTONIC, &b);
|
||||
double ns = (b.tv_sec - a.tv_sec) * 1e9 + (b.tv_nsec - a.tv_nsec);
|
||||
long ops = (long)nthr * iters * 3;
|
||||
printf("threads=%d iters=%d ops=%ld (socket/open/fopen + close each) wall=%.3fs %.0f ns/op-pair/thread\n",
|
||||
nthr, iters, ops, ns / 1e9, ns * nthr / ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) { fprintf(stderr, "usage: %s <dir> <case>\n", argv[0]); return 2; }
|
||||
snprintf(D, sizeof D, "%s", argv[1]);
|
||||
const char *c = argv[2];
|
||||
if (!strcmp(c, "double")) return c_double();
|
||||
if (!strcmp(c, "dbowner")) return c_dbowner();
|
||||
if (!strcmp(c, "dbforeign")) return c_dbforeign();
|
||||
if (!strcmp(c, "deny")) return c_deny();
|
||||
if (!strcmp(c, "blkowner")) return c_blkowner();
|
||||
if (!strcmp(c, "blkraw")) return c_blkraw();
|
||||
if (!strcmp(c, "stalefile")) return c_stalefile();
|
||||
if (!strcmp(c, "reuse")) return c_reuse();
|
||||
if (!strcmp(c, "dup2")) return c_dup2();
|
||||
if (!strcmp(c, "range")) return c_range();
|
||||
if (!strcmp(c, "ownermisc")) return c_owner_misc();
|
||||
if (!strcmp(c, "fallback")) return c_fallback_hit();
|
||||
if (!strcmp(c, "epoll")) return c_epoll();
|
||||
if (!strcmp(c, "selfclose")) return c_selfclose();
|
||||
if (!strcmp(c, "threads")) return c_threads();
|
||||
fprintf(stderr, "unknown case %s\n", c);
|
||||
return 2;
|
||||
}
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
# Self-test for libfdguard.so. Every case lists the report types it must
|
||||
# produce (exactly, in order) -- an empty list means "no report at all".
|
||||
# ./run_tests.sh (builds first)
|
||||
set -u
|
||||
cd "$(dirname "$0")"
|
||||
./build.sh >/dev/null || { echo "build failed"; exit 1; }
|
||||
SO=$PWD/libfdguard.so
|
||||
T=$PWD/t
|
||||
rm -rf "$T"; mkdir -p "$T/regtest/blocks" "$T/regtest/database"
|
||||
pass=0; fail=0
|
||||
|
||||
run() { # run <case> [ENV=VAL ...] -> sets RC, LOG
|
||||
local c=$1; shift
|
||||
LOG=$T/$c${SUFFIX:-}.log; rm -f "$LOG"
|
||||
env FDGUARD_EXE=fdguard_test FDGUARD_LOG="$LOG" "$@" LD_PRELOAD="$SO" ./fdguard_test "$T" "$c" >"$T/$c${SUFFIX:-}.out" 2>&1
|
||||
RC=$?
|
||||
}
|
||||
types() { grep '^=== FDGUARD' "$LOG" 2>/dev/null | awk '{print $4}' | tr '\n' ' ' | sed 's/ $//'; }
|
||||
reasons() { grep -o 'reason=[^ ]*' "$LOG" 2>/dev/null | sed 's/reason=//' | tr '\n' ' ' | sed 's/ $//'; }
|
||||
check() { # check <name> <cond-description> <actual> <expected>
|
||||
if [ "$3" == "$4" ]; then pass=$((pass+1)); printf 'PASS %-12s %s\n' "$1" "$2";
|
||||
else fail=$((fail+1)); printf 'FAIL %-12s %s\n got: [%s]\n want: [%s]\n' "$1" "$2" "$3" "$4"; fi
|
||||
}
|
||||
|
||||
# case | expected report types | expected reasons
|
||||
while IFS='|' read -r c want wantr; do
|
||||
[ -z "$c" ] && continue
|
||||
run "$c"
|
||||
check "$c" "reports" "$(types)" "$want"
|
||||
check "$c" "reasons" "$(reasons)" "$wantr"
|
||||
check "$c" "exit 0" "$RC" "0"
|
||||
done <<'EOF'
|
||||
double|EBADF-CLOSE|
|
||||
dbowner||
|
||||
dbforeign|NON-OWNER-CLOSE EBADF-CLOSE|DOMAIN-MISMATCH
|
||||
blkowner||
|
||||
blkraw|NON-OWNER-CLOSE EBADF-CLOSE|stdio:RAW-CLOSE-UNDER-LIVE-FILE
|
||||
stalefile|NON-OWNER-CLOSE NON-OWNER-CLOSE EBADF-CLOSE|stdio:RAW-CLOSE-UNDER-LIVE-FILE stdio:STALE-FILE
|
||||
reuse|NON-OWNER-CLOSE EBADF-CLOSE|DOMAIN-MISMATCH
|
||||
dup2|NON-OWNER-CLOSE|DOMAIN-MISMATCH
|
||||
range|NON-OWNER-CLOSE NON-OWNER-CLOSE NON-OWNER-CLOSE|DOMAIN-MISMATCH DOMAIN-MISMATCH fallback:DB-FILE-CLOSED-OUTSIDE-BDB
|
||||
ownermisc||
|
||||
fallback|NON-OWNER-CLOSE|fallback:DB-FILE-CLOSED-OUTSIDE-BDB
|
||||
epoll|EPOLL-CTL-EPERM EPOLL-CTL-EBADF|epoll_ctl(ADD)-failed epoll_ctl(ADD)-failed
|
||||
EOF
|
||||
|
||||
# the previous close of the reused number must be the first CloseSocket (proves the double close)
|
||||
run reuse
|
||||
check reuse "history shows 1st CloseSocket on a socket" \
|
||||
"$(grep -A2 'previous successful close' "$LOG" | head -3 | grep -c -e 'class=sock dom=p2p' -e 'CloseSocket+')" "2"
|
||||
# the stale FILE report must name wallet.dat as the victim and OpenDiskFile as the FILE's opener
|
||||
run stalefile
|
||||
check stalefile "victim=wallet.dat, FILE opened in OpenDiskFile" \
|
||||
"$(sed -n '/#2 NON-OWNER/,/=== END #2/p' "$LOG" | grep -m1 -c 'target=.*wallet.dat class=db') $(sed -n '/#2 NON-OWNER/,/=== END #2/p' "$LOG" | grep -A3 'which had been opened by: fopen' | grep -c 'OpenDiskFile+')" "1 1"
|
||||
|
||||
# the log fd survives a close-everything loop and the attempt is reported
|
||||
run selfclose
|
||||
check selfclose "log-fd close refused + reported" "$(grep -c 'CLOSE-OF-FDGUARD-LOG-FD' "$LOG")" "1"
|
||||
check selfclose "log still writable afterwards (final stats)" "$(grep -c '^S .* final' "$LOG")" "1"
|
||||
|
||||
# abort mode: first non-owner close -> SIGABRT (rc 134), report already written
|
||||
SUFFIX=_abort run dbforeign FDGUARD_ABORT=1
|
||||
check abort "rc=134 (SIGABRT)" "$RC" "134"
|
||||
check abort "report written before abort" "$(types)" "NON-OWNER-CLOSE"
|
||||
# abort=2 also aborts on EBADF
|
||||
SUFFIX=_abort2 run double FDGUARD_ABORT=2
|
||||
check abort2 "rc=134 on EBADF" "$RC" "134"
|
||||
|
||||
# deny mode: wallet fd survives the foreign close; no EBADF afterwards
|
||||
SUFFIX=_deny run deny FDGUARD_DENY=1
|
||||
check deny "exit 0 (fd still valid + writable)" "$RC" "0"
|
||||
check deny "reported as DENIED, no EBADF" "$(types) $(grep -c 'ACTION=DENIED' "$LOG")" "NON-OWNER-CLOSE 1"
|
||||
|
||||
# extra classes: guard=all with the owner cases must still be silent
|
||||
SUFFIX=_all run ownermisc FDGUARD_GUARD=all
|
||||
check guard-all "ownermisc silent with FDGUARD_GUARD=all" "$(types)" ""
|
||||
|
||||
# trace-all writes one T line per open/close
|
||||
SUFFIX=_trace run dbowner FDGUARD_TRACE_ALL=1
|
||||
check trace "T lines for open+close" "$(grep -c '^T ' "$LOG")" "2"
|
||||
|
||||
# passive in non-matching processes
|
||||
LOG=$T/passive.log; env FDGUARD_EXE=dragonxd FDGUARD_LOG="$LOG" LD_PRELOAD="$SO" ./fdguard_test "$T" dbforeign >/dev/null 2>&1
|
||||
check passive "no log when exe does not match" "$( [ -e "$LOG" ] && echo exists || echo none)" "none"
|
||||
|
||||
# concurrency: 8 threads x 5000 x (socket, open, fopen) churn, guard=all -> no reports
|
||||
SUFFIX=_thr run threads T_ITERS=5000 FDGUARD_GUARD=all
|
||||
check threads "no reports under 8-thread churn (guard=all)" "$(types)" ""
|
||||
check threads "stats: HITS=0 EBADF=0" "$(grep '^S .*final' "$LOG" | grep -o 'HITS=[0-9]* EBADF=[0-9]*')" "HITS=0 EBADF=0"
|
||||
|
||||
# symbolizer resolves file:line for the test binary
|
||||
check symbolize "addr2line resolved CloseSocket to fdguard_test.c" \
|
||||
"$(./symbolize.py "$T/reuse.log" | grep -m1 -o 'in CloseSocket at .*fdguard_test.c:[0-9]*' | sed 's/.*fdguard_test.c:[0-9]*/ok/')" "ok"
|
||||
|
||||
echo "== $pass passed, $fail failed"
|
||||
[ $fail -eq 0 ]
|
||||
Executable
+212
@@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
symbolize.py -- turn fdguard log frames into function + file:line.
|
||||
|
||||
Every stack frame in an fdguard report looks like
|
||||
#3 0x5b96b433c991 /path/to/module+0x1991 c_dbforeign+0x2d [bdb]
|
||||
where "+0x1991" is the ELF virtual address inside that module (runtime address
|
||||
minus the module's load bias, so PIE executables and shared libraries need no
|
||||
further adjustment; for a non-PIE executable it equals the absolute address).
|
||||
Frames are return addresses, so we ask addr2line about vaddr-1 (the call
|
||||
instruction), with -f -C -i (function, demangle, inline chain).
|
||||
|
||||
backtrace_symbols_fd lines "module(+0x1991)[0x...]" are symbolized too.
|
||||
|
||||
usage:
|
||||
symbolize.py [options] LOG [LOG...] annotated log on stdout
|
||||
symbolize.py --summary LOG one line per distinct report
|
||||
options:
|
||||
--map OLD=NEW read module OLD from path NEW instead (repeatable),
|
||||
e.g. --map /path/to/deployed/dragonxd=./dragonxd.unstripped
|
||||
--addr2line PATH addr2line binary (default: addr2line)
|
||||
--only-reports drop T/S lines (trace + stats) from the output
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import defaultdict, OrderedDict
|
||||
|
||||
FRAME_RE = re.compile(r'^(\s*#\d+ 0x[0-9a-f]+ )(\S+)\+0x([0-9a-f]+)(.*)$')
|
||||
BSFD_RE = re.compile(r'^(\S+)\(\+0x([0-9a-f]+)\)\[0x[0-9a-f]+\]\s*$')
|
||||
HDR_RE = re.compile(r'^=== FDGUARD #(\d+) (\S+)')
|
||||
|
||||
|
||||
def parse_args(argv):
|
||||
opts = {'map': {}, 'addr2line': 'addr2line', 'summary': False, 'only': False, 'logs': []}
|
||||
it = iter(argv)
|
||||
for a in it:
|
||||
if a == '--map':
|
||||
old, new = next(it).split('=', 1)
|
||||
opts['map'][old] = new
|
||||
elif a.startswith('--map='):
|
||||
old, new = a[6:].split('=', 1)
|
||||
opts['map'][old] = new
|
||||
elif a == '--addr2line':
|
||||
opts['addr2line'] = next(it)
|
||||
elif a == '--summary':
|
||||
opts['summary'] = True
|
||||
elif a == '--only-reports':
|
||||
opts['only'] = True
|
||||
elif a in ('-h', '--help'):
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
else:
|
||||
opts['logs'].append(a)
|
||||
if not opts['logs']:
|
||||
opts['logs'] = ['-']
|
||||
return opts
|
||||
|
||||
|
||||
def read_lines(paths):
|
||||
out = []
|
||||
for p in paths:
|
||||
f = sys.stdin if p == '-' else open(p, errors='replace')
|
||||
out.extend(f.read().splitlines())
|
||||
if f is not sys.stdin:
|
||||
f.close()
|
||||
return out
|
||||
|
||||
|
||||
def resolve_module(mod, opts, known):
|
||||
if mod in opts['map']:
|
||||
return opts['map'][mod]
|
||||
if os.path.exists(mod):
|
||||
return mod
|
||||
base = os.path.basename(mod)
|
||||
for k in known:
|
||||
if os.path.basename(k) == base and os.path.exists(opts['map'].get(k, k)):
|
||||
return opts['map'].get(k, k)
|
||||
return None
|
||||
|
||||
|
||||
def run_addr2line(binary, addrs, a2l):
|
||||
"""addrs: list of int vaddrs (already -1'd). returns {addr: [(func, loc), ...]}"""
|
||||
res = {}
|
||||
if not addrs:
|
||||
return res
|
||||
uniq = sorted(set(addrs))
|
||||
try:
|
||||
p = subprocess.run([a2l, '-a', '-f', '-C', '-i', '-e', binary] + ['0x%x' % a for a in uniq],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True, check=False)
|
||||
except FileNotFoundError:
|
||||
return res
|
||||
cur = None
|
||||
lines = p.stdout.splitlines()
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
ln = lines[i]
|
||||
if re.match(r'^0x[0-9a-f]+$', ln):
|
||||
cur = int(ln, 16)
|
||||
res[cur] = []
|
||||
i += 1
|
||||
continue
|
||||
func = ln
|
||||
loc = lines[i + 1] if i + 1 < len(lines) else '??:0'
|
||||
if cur is not None:
|
||||
res[cur].append((func, loc))
|
||||
i += 2
|
||||
return res
|
||||
|
||||
|
||||
def main():
|
||||
opts = parse_args(sys.argv[1:])
|
||||
lines = read_lines(opts['logs'])
|
||||
|
||||
if opts['summary']:
|
||||
summary(lines)
|
||||
return
|
||||
|
||||
# pass 1: collect modules + addresses
|
||||
want = defaultdict(set)
|
||||
known = OrderedDict()
|
||||
for ln in lines:
|
||||
m = FRAME_RE.match(ln)
|
||||
if m:
|
||||
known[m.group(2)] = 1
|
||||
want[m.group(2)].add(max(int(m.group(3), 16) - 1, 0))
|
||||
for ln in lines:
|
||||
m = BSFD_RE.match(ln)
|
||||
if m:
|
||||
want[m.group(1)].add(max(int(m.group(2), 16) - 1, 0))
|
||||
|
||||
table = {}
|
||||
for mod, addrs in want.items():
|
||||
path = resolve_module(mod, opts, known)
|
||||
if not path:
|
||||
continue
|
||||
table[mod] = run_addr2line(path, list(addrs), opts['addr2line'])
|
||||
|
||||
def fmt(mod, vaddr):
|
||||
r = table.get(mod, {}).get(vaddr)
|
||||
if not r:
|
||||
return []
|
||||
out = []
|
||||
for j, (func, loc) in enumerate(r):
|
||||
if func == '??' and loc.startswith('??'):
|
||||
continue
|
||||
# addr2line -i lists the innermost (inlined) frame first
|
||||
tag = 'inlined:' if j < len(r) - 1 else 'in'
|
||||
out.append('%s %s at %s' % (tag, func, loc))
|
||||
return out
|
||||
|
||||
w = sys.stdout.write
|
||||
for ln in lines:
|
||||
if opts['only'] and (ln.startswith('T ') or ln.startswith('S ')):
|
||||
continue
|
||||
w(ln + '\n')
|
||||
m = FRAME_RE.match(ln)
|
||||
if m:
|
||||
v = max(int(m.group(3), 16) - 1, 0)
|
||||
for s in fmt(m.group(2), v):
|
||||
w(' -> ' + s + '\n')
|
||||
continue
|
||||
m = BSFD_RE.match(ln)
|
||||
if m:
|
||||
v = max(int(m.group(2), 16) - 1, 0)
|
||||
for s in fmt(m.group(1), v):
|
||||
w(' -> ' + s + '\n')
|
||||
|
||||
|
||||
def summary(lines):
|
||||
"""one line per (type, reason, class, first exe closer frame)"""
|
||||
groups = OrderedDict()
|
||||
cur = None
|
||||
in_closer = False
|
||||
for ln in lines:
|
||||
h = HDR_RE.match(ln)
|
||||
if h:
|
||||
cur = {'type': h.group(2), 'reason': '', 'cls': '', 'top': '', 'call': ''}
|
||||
in_closer = False
|
||||
continue
|
||||
if cur is None:
|
||||
continue
|
||||
s = ln.strip()
|
||||
if s.startswith('call:'):
|
||||
cur['call'] = s.split('(')[0][6:]
|
||||
elif s.startswith('target=') and ' class=' in s and not cur['cls']:
|
||||
cur['cls'] = s.rsplit('class=', 1)[-1]
|
||||
elif s.startswith('verdict='):
|
||||
cur['reason'] = s.split('reason=', 1)[-1].split(' ')[0]
|
||||
elif s == 'closer stack:':
|
||||
in_closer = True
|
||||
elif in_closer and FRAME_RE.match(ln):
|
||||
parts = s.split(' ')
|
||||
if len(parts) >= 4 and not cur['top'] and '/libc.so' not in parts[2] and '/libstdc++' not in parts[2]:
|
||||
cur['top'] = parts[3]
|
||||
elif in_closer and not s.startswith('#'):
|
||||
in_closer = False
|
||||
if s.startswith('=== END'):
|
||||
k = (cur['type'], cur['call'], cur['reason'], cur['cls'], cur['top'])
|
||||
groups[k] = groups.get(k, 0) + 1
|
||||
cur = None
|
||||
print('%6s %-22s %-12s %-6s %-40s %s' % ('count', 'type', 'call', 'class', 'reason', 'first closer frame'))
|
||||
for (t, c, r, cl, top), n in groups.items():
|
||||
print('%6d %-22s %-12s %-6s %-40s %s' % (n, t[:22], c, cl, r[:40], top))
|
||||
stats = [ln for ln in lines if ln.startswith('S ')]
|
||||
if stats:
|
||||
print('\nlast stats line:\n ' + stats[-1])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+87
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env bash
|
||||
# r4 now-tier acceptance harness. MUST run inside a network namespace: the v1.0.x binaries
|
||||
# predate the regtest seed-injection fix and would otherwise dial the live DragonX network.
|
||||
set -u
|
||||
OLD=/home/dev/dragonx/release/dragonx-1.0.1-linux-amd64
|
||||
NEW=/home/dev/dragonx-dev/src
|
||||
ROOT=/tmp/claude-1000/-home-dev/45a644d6-ea0b-4b7c-8ec7-6ccb1a64afa7/scratchpad/r4lab
|
||||
PASS=0; FAIL=0
|
||||
ok(){ echo " PASS $1"; PASS=$((PASS+1)); }
|
||||
no(){ echo " FAIL $1"; FAIL=$((FAIL+1)); }
|
||||
|
||||
# --- hard guardrail: refuse to run unisolated ---
|
||||
if [ "$(ip route show 2>/dev/null | wc -l)" != "0" ]; then
|
||||
echo "REFUSING: not in an isolated netns (routes present). Run under: unshare -rn"; exit 90
|
||||
fi
|
||||
ip link set lo up 2>/dev/null
|
||||
echo "isolation: $(ip route show | wc -l) routes, $(ip -o link | wc -l) interface(s)"
|
||||
|
||||
conf(){ printf 'regtest=1\nrpcuser=t\nrpcpassword=t\nlisten=0\ndnsseed=0\n' > "$1/DRAGONX.conf"; }
|
||||
start(){ # $1=bindir $2=datadir $3=extra
|
||||
"$1/dragonxd" -regtest -datadir="$2" -connect=0 -listen=0 -dnsseed=0 $3 -daemon >/dev/null 2>&1
|
||||
for i in $(seq 40); do "$1/dragonx-cli" -regtest -datadir="$2" -rpcuser=t -rpcpassword=t getblockcount >/dev/null 2>&1 && return 0; sleep 2; done
|
||||
return 1; }
|
||||
cli(){ "$1/dragonx-cli" -regtest -datadir="$2" -rpcuser=t -rpcpassword=t "${@:3}" 2>&1; }
|
||||
stopn(){ cli "$1" "$2" stop >/dev/null 2>&1; sleep 6; }
|
||||
|
||||
rm -rf "$ROOT"; mkdir -p "$ROOT"
|
||||
|
||||
echo; echo "### build victim wallets with the OLD binary (v1.0.1) ###"
|
||||
mkdir -p "$ROOT/base/regtest"; conf "$ROOT/base/regtest"
|
||||
start "$OLD" "$ROOT/base/regtest" "" || { echo "old node failed to start"; exit 91; }
|
||||
[ "$(cli "$OLD" "$ROOT/base/regtest" getconnectioncount)" = "0" ] && ok "victim-maker has 0 peers (isolated)" || no "victim-maker NOT isolated -- ABORT"
|
||||
cli "$OLD" "$ROOT/base/regtest" getnewaddress >/dev/null
|
||||
cli "$OLD" "$ROOT/base/regtest" z_getnewaddress >/dev/null
|
||||
ZBEFORE=$(cli "$OLD" "$ROOT/base/regtest" z_listaddresses | tr -d ' \n')
|
||||
stopn "$OLD" "$ROOT/base/regtest"
|
||||
|
||||
for v in C A B; do cp -a "$ROOT/base" "$ROOT/$v"; done
|
||||
|
||||
# A = salvaged by v1.0.1 (drops hdchain). B = A then USED on v1.0.1 (persists a bogus chain).
|
||||
start "$OLD" "$ROOT/A/regtest" "-salvagewallet" && stopn "$OLD" "$ROOT/A/regtest"
|
||||
start "$OLD" "$ROOT/B/regtest" "-salvagewallet" && stopn "$OLD" "$ROOT/B/regtest"
|
||||
start "$OLD" "$ROOT/B/regtest" "" && { cli "$OLD" "$ROOT/B/regtest" z_getnewaddress >/dev/null; stopn "$OLD" "$ROOT/B/regtest"; }
|
||||
|
||||
echo " A hdchain records: $(strings "$ROOT/A/regtest/regtest/wallet.dat" | grep -c hdchain) (expect 0)"
|
||||
echo " B hdchain records: $(strings "$ROOT/B/regtest/regtest/wallet.dat" | grep -c hdchain) (expect >=1)"
|
||||
|
||||
echo; echo "### open each on the NEW binary ###"
|
||||
for v in C A B; do
|
||||
D="$ROOT/$v/regtest"; L="$D/regtest/debug.log"
|
||||
WDAT="$D/regtest/wallet.dat"
|
||||
[ -f "$WDAT" ] || { echo " FAIL $v: wallet.dat not found at $WDAT"; exit 92; }
|
||||
# NOT a byte-identical check: normal startup (keypool top-up, bestblock) rewrites wallet.dat for
|
||||
# ANY wallet, healthy ones included -- verified with a control. The precise claim is that the
|
||||
# degraded path never SYNTHESISES an hdchain record, so count that instead.
|
||||
HD1=$(strings "$WDAT" | grep -c hdchain)
|
||||
: > "$L" 2>/dev/null
|
||||
if start "$NEW" "$D" "-exportdir=$D/exp"; then
|
||||
STARTED=yes; DEG=$(grep -c "DEGRADED" "$L" 2>/dev/null)
|
||||
MISS=$(grep -c "hdchain record is missing" "$L" 2>/dev/null)
|
||||
MISM=$(grep -c "does not belong to this wallet" "$L" 2>/dev/null)
|
||||
if [ "$v" = "A" ]; then
|
||||
mkdir -p "$D/exp"; EXP=$(cli "$NEW" "$D" z_exportwallet r4dump 2>&1 | head -1)
|
||||
DUMP=$(find "$D" -name 'r4dump' 2>/dev/null | head -1)
|
||||
ZNEW=$(cli "$NEW" "$D" z_getnewaddress); TNEW=$(cli "$NEW" "$D" getnewaddress)
|
||||
fi
|
||||
stopn "$NEW" "$D"
|
||||
else STARTED=no; DEG=0; MISS=0; MISM=0; fi
|
||||
HD2=$(strings "$WDAT" | grep -c hdchain)
|
||||
|
||||
case $v in
|
||||
C) [ "$STARTED" = yes ] && ok "C healthy wallet opens" || no "C healthy wallet failed to open"
|
||||
[ "$DEG" = "0" ] && ok "C no false positive (not flagged degraded)" || no "C FALSE POSITIVE: healthy wallet flagged" ;;
|
||||
A) [ "$STARTED" = yes ] && ok "A salvaged wallet opens (was DB_CORRUPT before)" || no "A salvaged wallet still refuses to open"
|
||||
[ "$MISS" -ge 1 ] && ok "A flagged: hdchain missing" || no "A not flagged as missing-hdchain"
|
||||
[ "$HD1" = "0" ] && [ "$HD2" = "0" ] && ok "A no hdchain synthesised (degraded path persists nothing)" || no "A hdchain record appeared ($HD1 -> $HD2)"
|
||||
echo "$ZNEW" | grep -qi 'error' && ok "A z_getnewaddress refused cleanly (derivation gated)" || no "A z_getnewaddress derived anyway: $ZNEW"
|
||||
echo "$TNEW" | grep -qiE '^R[a-zA-Z0-9]+$' && ok "A getnewaddress still works (legacy random t-key)" || no "A getnewaddress broke: $TNEW"
|
||||
if [ -n "${DUMP:-}" ] && [ -f "$DUMP" ]; then
|
||||
grep -qE '^# HDSeed=[0-9a-f]' "$DUMP" && no "E z_exportwallet emitted an HDSeed line on a degraded wallet" || ok "E z_exportwallet emitted no bogus HDSeed line"
|
||||
else echo " SKIP E (no dump produced: $EXP)"; fi ;;
|
||||
B) [ "$STARTED" = yes ] && ok "B poisoned wallet opens" || no "B poisoned wallet failed to open"
|
||||
[ "$MISM" -ge 1 ] && ok "B CASE-3 DETECTOR FIRED (seedFp mismatch)" || no "B case-3 detector did NOT fire" ;;
|
||||
esac
|
||||
done
|
||||
echo; echo "### $PASS passed, $FAIL failed ###"
|
||||
exit $FAIL
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2016 The Zcash developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2014 The Bitcoin Core developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -52,7 +52,7 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
|
||||
'''
|
||||
|
||||
def run_test(self):
|
||||
print "Warning: this test will take about 70 seconds in the best case. Be patient."
|
||||
print("Warning: this test will take about 70 seconds in the best case. Be patient.")
|
||||
self.nodes[0].generate(10)
|
||||
templat = self.nodes[0].getblocktemplate()
|
||||
longpollid = templat['longpollid']
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2014 The Bitcoin Core developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.authproxy import JSONRPCException
|
||||
from test_framework.util import initialize_chain_clean, start_node
|
||||
|
||||
from binascii import a2b_hex, b2a_hex
|
||||
from hashlib import sha256
|
||||
@@ -69,14 +70,43 @@ def genmrklroot(leaflist):
|
||||
cur = n
|
||||
return cur[0]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sapling v4 transaction layout.
|
||||
#
|
||||
# This test was written against the pre-Overwinter serialization, where a tx
|
||||
# began with a 4-byte nVersion immediately followed by the vin count, so the
|
||||
# first input's prevout hash lived at offset 4+1. DragonX transactions are
|
||||
# Sapling v4: 4-byte header (nVersion | fOverwintered) + 4-byte nVersionGroupId
|
||||
# + vin count, so the prevout hash starts 4 bytes further in. Poking the old
|
||||
# offset corrupts nVersionGroupId and every proposal below just comes back
|
||||
# "Block decode failed" instead of exercising any consensus rule.
|
||||
CB_PREVOUT_OFF = 4+4+1
|
||||
# Likewise the tx no longer ends at nLockTime: nExpiryHeight (4), valueBalance
|
||||
# (8) and the empty vShieldedSpend/vShieldedOutput/vJoinSplit counts (1 each)
|
||||
# trail it, so nLockTime is the 4 bytes at [-19:-15].
|
||||
TX_TAIL_AFTER_LOCKTIME = 4+8+1+1+1
|
||||
|
||||
def tx_seq_off(tx):
|
||||
"""Offset of the first input's nSequence in a Sapling v4 tx."""
|
||||
scriptlen_off = CB_PREVOUT_OFF + 32 + 4 # after prevout hash + prevout.n
|
||||
return scriptlen_off + 1 + tx[scriptlen_off]
|
||||
|
||||
def tx_vout0_value_off(tx):
|
||||
"""Offset of the first output's 8-byte value in a Sapling v4 tx."""
|
||||
return tx_seq_off(tx) + 4 + 1 # after nSequence + vout count
|
||||
|
||||
def template_to_bytes(tmpl, txlist):
|
||||
blkver = pack('<L', tmpl['version'])
|
||||
mrklroot = genmrklroot(list(dblsha(a) for a in txlist))
|
||||
reserved = b'\0'*32
|
||||
# hashFinalSaplingRoot. The all-zeroes placeholder this test used predates
|
||||
# Sapling; a header carrying the wrong root is rejected with
|
||||
# 'bad-sapling-root-in-block', which would sink even Test 11 (valid block).
|
||||
reserved = a2b_hex(tmpl['finalsaplingroothash'])[::-1]
|
||||
timestamp = pack('<L', tmpl['curtime'])
|
||||
nonce = b'\0'*32
|
||||
soln = b'\0'
|
||||
blk = blkver + a2b_hex(tmpl['previousblockhash'])[::-1] + mrklroot + reserved + timestamp + a2b_hex(tmpl['bits'])[::-1] + nonce + soln
|
||||
# bytearray, not bytes: Test 9 mutates one byte of the result in place.
|
||||
blk = bytearray(blkver + a2b_hex(tmpl['previousblockhash'])[::-1] + mrklroot + reserved + timestamp + a2b_hex(tmpl['bits'])[::-1] + nonce + soln)
|
||||
blk += varlenEncode(len(txlist))
|
||||
for tx in txlist:
|
||||
blk += tx
|
||||
@@ -95,6 +125,20 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
|
||||
Test block proposals with getblocktemplate.
|
||||
'''
|
||||
|
||||
def setup_chain(self):
|
||||
print("Initializing test directory "+self.options.tmpdir)
|
||||
initialize_chain_clean(self.options.tmpdir, 1)
|
||||
|
||||
def setup_network(self, split=False):
|
||||
# -daaforkheight=0: ContextualCheckBlockHeader only enforces nBits for a
|
||||
# smart chain above daaForkHeight, which defaults to
|
||||
# ASSETCHAINS_RANDOMX_VALIDATION+62000 (millions of blocks) so that a
|
||||
# fresh sync accepts DragonX's historical bad-nBits window. Without this
|
||||
# flag Test 8 (bad bits) is unreachable at regtest heights and the
|
||||
# daemon happily accepts a block with arbitrary nBits.
|
||||
self.nodes = [ start_node(0, self.options.tmpdir, ['-daaforkheight=0']) ]
|
||||
self.is_network_split = False
|
||||
|
||||
def run_test(self):
|
||||
node = self.nodes[0]
|
||||
node.generate(1) # Mine a block to leave initial block download
|
||||
@@ -117,9 +161,9 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
|
||||
#txlist[0][4+1+36+1+1] -= 1
|
||||
|
||||
# Test 2: Bad input hash for gen tx
|
||||
txlist[0][4+1] += 1
|
||||
txlist[0][CB_PREVOUT_OFF] += 1
|
||||
assert_template(node, tmpl, txlist, 'bad-cb-missing')
|
||||
txlist[0][4+1] -= 1
|
||||
txlist[0][CB_PREVOUT_OFF] -= 1
|
||||
|
||||
# Test 3: Truncated final tx
|
||||
lastbyte = txlist[-1].pop()
|
||||
@@ -136,14 +180,33 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
|
||||
|
||||
# Test 5: Add an invalid tx to the end (non-duplicate)
|
||||
txlist.append(bytearray(txlist[0]))
|
||||
txlist[-1][4+1] = b'\xff'
|
||||
txlist[-1][CB_PREVOUT_OFF] = 0xff
|
||||
# DragonX is a fully private chain (ASSETCHAINS_PRIVATE): a non-coinbase
|
||||
# tx paying a positive amount to a t-addr is killed in CheckTransaction
|
||||
# with 'bad-txns-acprivacy-chain' before ConnectBlock ever looks up its
|
||||
# inputs. Zero the output value -- CheckTransaction exempts zero-value
|
||||
# vouts -- so the tx survives to the missing-input check this case is
|
||||
# actually about.
|
||||
_val_off = tx_vout0_value_off(txlist[-1])
|
||||
txlist[-1][_val_off:_val_off+8] = b'\0'*8
|
||||
assert_template(node, tmpl, txlist, 'bad-txns-inputs-missingorspent')
|
||||
txlist.pop()
|
||||
|
||||
# Test 6: Future tx lock time
|
||||
txlist[0][-4:] = b'\xff\xff\xff\xff'
|
||||
# The server-supplied coinbase carries nSequence=0xffffffff, and
|
||||
# IsFinalTx() short-circuits on all-final inputs, so a future nLockTime
|
||||
# alone leaves the tx final and the block valid. (DragonX's IsFinalTx
|
||||
# also whitelists 0xfffffffe below the Hush hardfork height, which
|
||||
# regtest is.) Make the input genuinely non-final so the future
|
||||
# nLockTime is the thing under test.
|
||||
seq_off = tx_seq_off(txlist[0])
|
||||
realseq = txlist[0][seq_off:seq_off+4]
|
||||
txlist[0][seq_off:seq_off+4] = b'\0\0\0\0'
|
||||
reallocktime = txlist[0][-TX_TAIL_AFTER_LOCKTIME-4:-TX_TAIL_AFTER_LOCKTIME]
|
||||
txlist[0][-TX_TAIL_AFTER_LOCKTIME-4:-TX_TAIL_AFTER_LOCKTIME] = b'\xff\xff\xff\xff'
|
||||
assert_template(node, tmpl, txlist, 'bad-txns-nonfinal')
|
||||
txlist[0][-4:] = b'\0\0\0\0'
|
||||
txlist[0][-TX_TAIL_AFTER_LOCKTIME-4:-TX_TAIL_AFTER_LOCKTIME] = reallocktime
|
||||
txlist[0][seq_off:seq_off+4] = realseq
|
||||
|
||||
# Test 7: Bad tx count
|
||||
txlist.append(b'')
|
||||
@@ -171,7 +234,9 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
|
||||
tmpl['curtime'] = 0x7fffffff
|
||||
assert_template(node, tmpl, txlist, 'time-too-new')
|
||||
tmpl['curtime'] = 0
|
||||
assert_template(node, tmpl, txlist, 'time-too-old')
|
||||
# DragonX split Bitcoin's 'time-too-old' into 'time-too-old-median'
|
||||
# (block time <= prev MedianTimePast) and 'time-too-old-prevblock'.
|
||||
assert_template(node, tmpl, txlist, 'time-too-old-median')
|
||||
tmpl['curtime'] = realtime
|
||||
|
||||
# Test 11: Valid block
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014 The Bitcoin Core developers
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Released under the GPLv3
|
||||
@@ -28,7 +28,7 @@ class ReindexTest(BitcoinTestFramework):
|
||||
wait_bitcoinds()
|
||||
self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug", "-reindex", "-checkblockindex=1"])
|
||||
assert_equal(self.nodes[0].getblockcount(), 3)
|
||||
print "Success"
|
||||
print("Success")
|
||||
|
||||
if __name__ == '__main__':
|
||||
ReindexTest().main()
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# and for constructing a getheaders message
|
||||
#
|
||||
|
||||
from mininode import CBlock, CBlockHeader, CBlockLocator, CTransaction, msg_block, msg_headers, msg_tx
|
||||
from .mininode import CBlock, CBlockHeader, CBlockLocator, CTransaction, msg_block, msg_headers, msg_tx
|
||||
|
||||
import sys
|
||||
import cStringIO
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
# blocktools.py - utilities for manipulating blocks and transactions
|
||||
from mininode import CBlock, CTransaction, CTxIn, CTxOut, COutPoint
|
||||
from script import CScript, OP_0, OP_EQUAL, OP_HASH160
|
||||
from .mininode import CBlock, CTransaction, CTxIn, CTxOut, COutPoint
|
||||
from .script import CScript, OP_0, OP_EQUAL, OP_HASH160
|
||||
|
||||
# Create a block (with regtest difficulty)
|
||||
def create_block(hashprev, coinbase, nTime=None, nBits=None):
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
from mininode import CBlock, CTransaction, CInv, NodeConn, NodeConnCB, \
|
||||
from .mininode import CBlock, CTransaction, CInv, NodeConn, NodeConnCB, \
|
||||
msg_inv, msg_getheaders, msg_ping, msg_mempool, mininode_lock, MAX_INV_SZ
|
||||
from blockstore import BlockStore, TxStore
|
||||
from util import p2p_port
|
||||
from .blockstore import BlockStore, TxStore
|
||||
from .util import p2p_port
|
||||
|
||||
import time
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import shutil
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
from authproxy import JSONRPCException
|
||||
from util import assert_equal, check_json_precision, \
|
||||
from .authproxy import JSONRPCException
|
||||
from .util import assert_equal, check_json_precision, \
|
||||
initialize_chain, initialize_chain_clean, \
|
||||
start_nodes, connect_nodes_bi, stop_nodes, \
|
||||
sync_blocks, sync_mempools, wait_bitcoinds
|
||||
@@ -91,7 +91,7 @@ class BitcoinTestFramework(object):
|
||||
parser.add_option("--noshutdown", dest="noshutdown", default=False, action="store_true",
|
||||
help="Don't stop nodes after the test execution")
|
||||
parser.add_option("--srcdir", dest="srcdir", default="../../src",
|
||||
help="Source directory containing hushd/hush-cli (default: %default)")
|
||||
help="Source directory containing dragonxd/dragonx-cli (default: %default)")
|
||||
parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
|
||||
help="Root directory for datadirs")
|
||||
parser.add_option("--tracerpc", dest="trace_rpc", default=False, action="store_true",
|
||||
|
||||
@@ -18,7 +18,7 @@ import subprocess
|
||||
import time
|
||||
import re
|
||||
|
||||
from authproxy import AuthServiceProxy
|
||||
from .authproxy import AuthServiceProxy
|
||||
|
||||
def p2p_port(n):
|
||||
return 11000 + n + os.getpid()%999
|
||||
@@ -97,8 +97,8 @@ def initialize_datadir(dirname, n):
|
||||
print("Creating dirs %s" % datadir)
|
||||
os.makedirs(datadir)
|
||||
|
||||
print("Writing to " + os.path.join(datadir,"ZZZ.conf"))
|
||||
with open(os.path.join(datadir, "ZZZ.conf"), 'w') as f:
|
||||
print("Writing to " + os.path.join(datadir,"DRAGONX.conf"))
|
||||
with open(os.path.join(datadir, "DRAGONX.conf"), 'w') as f:
|
||||
f.write("regtest=1\n");
|
||||
f.write("txindex=1\n");
|
||||
#f.write("testnode=1\n");
|
||||
@@ -116,7 +116,19 @@ def initialize_datadir(dirname, n):
|
||||
f.write("spentindex=1\n");
|
||||
f.write("timestampindex=1\n");
|
||||
#f.write("zindex=1\n");
|
||||
print("Done writing to %s" % os.path.join(datadir,"ZZZ.conf") )
|
||||
print("Done writing to %s" % os.path.join(datadir,"DRAGONX.conf") )
|
||||
|
||||
# dragonxd refuses to start without an asmap file ("Could not find any asmap file!"),
|
||||
# so every regtest datadir needs one. Link the tree's copy rather than duplicating it.
|
||||
for src in ("../../../asmap.dat", "../../../src/asmap.dat",
|
||||
os.path.expanduser("~/.hush/DRAGONX/asmap.dat")):
|
||||
cand = src if os.path.isabs(src) else os.path.join(os.path.dirname(os.path.abspath(__file__)), src)
|
||||
if os.path.exists(cand):
|
||||
dst = os.path.join(datadir, "asmap.dat")
|
||||
if not os.path.exists(dst):
|
||||
try: os.symlink(os.path.realpath(cand), dst)
|
||||
except OSError: shutil.copyfile(cand, dst)
|
||||
break
|
||||
|
||||
return datadir
|
||||
|
||||
@@ -133,11 +145,23 @@ def initialize_chain(test_dir):
|
||||
# Create cache directories, run hushds:
|
||||
for i in range(4):
|
||||
datadir=initialize_datadir("cache", i)
|
||||
args = [ os.getenv("BITCOIND", "hushd"), "-keypool=1", "-datadir="+datadir, "-discover=0" ]
|
||||
# Same two requirements as start_node(): -regtest must be a command-line flag (the
|
||||
# conf key is ignored, and without it this cache node runs on MAINNET), and -asmap
|
||||
# must be absolute or dragonxd refuses to start.
|
||||
# -connect=<anything> makes init.cpp soft-set -listen=0 ("parameter interaction: -connect
|
||||
# set -> setting -listen=0"), so cache node0 never opened its p2p port and nodes 1-3 could
|
||||
# never sync to it -- initialize_chain() then hung forever in sync_blocks(). Pass -listen
|
||||
# and -bind explicitly (an explicit arg beats SoftSetBoolArg) and keep the loopback bind so
|
||||
# the cache nodes stay off the public network.
|
||||
args = [ os.getenv("BITCOIND", "src/dragonxd"), "-regtest", "-connect=0", "-keypool=1", "-datadir="+datadir, "-discover=0",
|
||||
"-listen=1", "-bind=127.0.0.1", "-dnsseed=0" ]
|
||||
_am = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../asmap.dat")
|
||||
if os.path.exists(_am):
|
||||
args.append("-asmap=" + os.path.realpath(_am))
|
||||
if i > 0:
|
||||
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
|
||||
bitcoind_processes[i] = subprocess.Popen(args)
|
||||
cmd = os.getenv("BITCOINCLI", "hush-cli")
|
||||
cmd = os.getenv("BITCOINCLI", "src/dragonx-cli")
|
||||
cmd_args = cmd + " -datadir="+datadir + " -rpcwait getblockcount"
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
print("initialize_chain: hushd started, calling: " + cmd_args)
|
||||
@@ -180,10 +204,17 @@ def initialize_chain(test_dir):
|
||||
wait_bitcoinds()
|
||||
for i in range(4):
|
||||
print("Cleaning up cache dir files")
|
||||
os.remove(log_filename("cache", i, "debug.log"))
|
||||
os.remove(log_filename("cache", i, "db.log"))
|
||||
os.remove(log_filename("cache", i, "peers.dat"))
|
||||
os.remove(log_filename("cache", i, "fee_estimates.dat"))
|
||||
# log_filename() points at <cache>/node<i>/regtest, but that IS the -datadir we passed;
|
||||
# dragonxd writes its logs/peers.dat one level deeper, into the net-specific
|
||||
# <datadir>/regtest subdir (same datadir-vs-netdir split that forced -asmap to be
|
||||
# absolute in start_node). Try both, and tolerate files a node never created.
|
||||
for name in ("debug.log", "db.log", "peers.dat", "fee_estimates.dat"):
|
||||
for cand in (log_filename("cache", i, os.path.join("regtest", name)),
|
||||
log_filename("cache", i, name)):
|
||||
try:
|
||||
os.remove(cand)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
for i in range(4):
|
||||
from_dir = os.path.join("cache", "node"+str(i))
|
||||
@@ -227,9 +258,10 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
|
||||
"""
|
||||
print("Starting node " + str(i) + " in dir " + dirname)
|
||||
datadir = os.path.join(dirname, "node"+str(i), "regtest")
|
||||
if extra_args is None: extra_args = []
|
||||
# creating special config
|
||||
if len(extra_args) > 0 and extra_args[0] == '-ac_name=ZZZ':
|
||||
configpath = datadir + "/ZZZ.conf"
|
||||
configpath = datadir + "/DRAGONX.conf"
|
||||
with open(configpath, "w+") as config:
|
||||
config.write("rpcuser=hush\n")
|
||||
config.write("rpcpassword=puppy\n")
|
||||
@@ -247,16 +279,30 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
|
||||
print("Done writing to %s" % configpath)
|
||||
|
||||
if binary is None:
|
||||
binary = os.getenv("BITCOIND", "src/hushd")
|
||||
args = [ binary, "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ]
|
||||
binary = os.getenv("BITCOIND", "src/dragonxd")
|
||||
# -regtest MUST be a command-line flag. DragonX ignores "regtest=1" in the conf file, so
|
||||
# without this the node silently runs on MAINNET: it loads the real genesis, dials the real
|
||||
# seeds and starts syncing the live chain into the test datadir (observed: 196k blocks and
|
||||
# 679MB before a test timed out). -connect=0 keeps the regtest node off the public network.
|
||||
args = [ binary, "-regtest", "-connect=0", "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ]
|
||||
# -asmap relative paths are resolved against the NET-SPECIFIC datadir (init.cpp), which for
|
||||
# regtest is <datadir>/regtest -- so a copy sitting in <datadir> is never found. Pass an
|
||||
# absolute path; without it dragonxd exits with "Could not find any asmap file!".
|
||||
_asmap = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../asmap.dat")
|
||||
if os.path.exists(_asmap):
|
||||
args.append("-asmap=" + os.path.realpath(_asmap))
|
||||
if extra_args is not None: args.extend(extra_args)
|
||||
print("args=" + ' '.join(args))
|
||||
bitcoind_processes[i] = subprocess.Popen(args)
|
||||
devnull = open("/dev/null", "w+")
|
||||
|
||||
cmd = os.getenv("BITCOINCLI", "src/hush-cli")
|
||||
cmd = os.getenv("BITCOINCLI", "src/dragonx-cli")
|
||||
print("cmd=" + cmd)
|
||||
args = [ extra_args[0], "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ]
|
||||
# The CLI only needs the datadir: initialize_datadir() already wrote DRAGONX.conf there
|
||||
# with the right rpcport/user/password. The old form passed extra_args[0] as argv[0] and
|
||||
# replayed daemon-only flags at the CLI, which only worked for the -ac_name=ZZZ assetchain
|
||||
# tests and broke every test that passes no extra_args.
|
||||
args = [ "-regtest", "-datadir="+datadir ]
|
||||
cmd_args = ' '.join(args) + " -rpcwait getblockcount "
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
print("start_node: hushd started, calling : " + cmd + " " + cmd_args)
|
||||
@@ -266,18 +312,22 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
|
||||
import time
|
||||
time.sleep(2)
|
||||
subprocess.check_call(strcmd, shell=True);
|
||||
#subprocess.check_call([ os.getenv("BITCOINCLI", "hush-cli"), "-datadir="+datadir] +
|
||||
#subprocess.check_call([ os.getenv("BITCOINCLI", "dragonx-cli"), "-datadir="+datadir] +
|
||||
# _rpchost_to_args(rpchost) +
|
||||
# ["-rpcwait", "-rpcport=6438", "getblockcount"], stdout=devnull)
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
print("start_node: calling hush-cli -rpcwait getblockcount returned")
|
||||
devnull.close()
|
||||
port = extra_args[3]
|
||||
#port = rpc_port(i)
|
||||
# Port comes from the same helper initialize_datadir() used, except for the assetchain
|
||||
# tests which pass it positionally as extra_args[3] == "-rpcport=NNNN".
|
||||
if len(extra_args) > 3 and str(extra_args[0]) == '-ac_name=ZZZ':
|
||||
port = extra_args[3][9:]
|
||||
else:
|
||||
port = str(rpc_port(i))
|
||||
#print("port=%s" % port)
|
||||
username = rpc_username()
|
||||
password = rpc_password()
|
||||
url = "http://%s:%s@%s:%s" % (username, password, rpchost or '127.0.0.1', port[9:])
|
||||
url = "http://%s:%s@%s:%s" % (username, password, rpchost or '127.0.0.1', port)
|
||||
print("connecting to " + url)
|
||||
if timewait is not None:
|
||||
proxy = AuthServiceProxy(url, timeout=timewait)
|
||||
@@ -315,7 +365,7 @@ def stop_nodes(nodes):
|
||||
del nodes[:] # Emptying array closes connections as a side effect
|
||||
|
||||
def set_node_times(nodes, t):
|
||||
print("Setting nodes time to " + t)
|
||||
print("Setting nodes time to " + str(t))
|
||||
for node in nodes:
|
||||
node.setmocktime(t)
|
||||
|
||||
|
||||
+35
-23
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2014 The Bitcoin Core developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -20,8 +20,20 @@ class WalletTest (BitcoinTestFramework):
|
||||
print("Initializing test directory "+self.options.tmpdir)
|
||||
initialize_chain_clean(self.options.tmpdir, 4)
|
||||
|
||||
# PORT NOTE (DragonX/regtest, not a change of test intent):
|
||||
# test_framework.start_node() hardcodes "-connect=0", and init.cpp turns that into
|
||||
# "-connect set -> setting -listen=0". With listening off the nodes never bind their
|
||||
# p2p port, so connect_nodes_bi() connects nothing at all (its version==0 poll loop
|
||||
# exits immediately because there are no local peers) and the first sync_all() hangs
|
||||
# forever. Passing -bind forces -listen back to 1. -dnsseed=0 keeps these regtest
|
||||
# nodes from dialing the live DragonX network.
|
||||
NET_ARGS = ["-listen=1", "-bind=127.0.0.1", "-dnsseed=0"]
|
||||
|
||||
def net_args(self, n, extra=None):
|
||||
return [list(self.NET_ARGS) + list(extra or []) for _ in range(n)]
|
||||
|
||||
def setup_network(self, split=False):
|
||||
self.nodes = start_nodes(3, self.options.tmpdir)
|
||||
self.nodes = start_nodes(3, self.options.tmpdir, self.net_args(3))
|
||||
connect_nodes_bi(self.nodes,0,1)
|
||||
connect_nodes_bi(self.nodes,1,2)
|
||||
connect_nodes_bi(self.nodes,0,2)
|
||||
@@ -29,7 +41,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
self.sync_all()
|
||||
|
||||
def run_test (self):
|
||||
print "Mining blocks..."
|
||||
print("Mining blocks...")
|
||||
|
||||
self.nodes[0].generate(4)
|
||||
self.sync_all()
|
||||
@@ -106,7 +118,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
signed_tx = self.nodes[2].signrawtransaction(raw_tx)
|
||||
try:
|
||||
self.nodes[2].sendrawtransaction(signed_tx["hex"])
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert("absurdly high fees" in errorString)
|
||||
assert("900000000 > 190000" in errorString)
|
||||
@@ -186,7 +198,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
|
||||
sync_mempools(self.nodes)
|
||||
|
||||
self.nodes.append(start_node(3, self.options.tmpdir))
|
||||
self.nodes.append(start_node(3, self.options.tmpdir, list(self.NET_ARGS)))
|
||||
connect_nodes_bi(self.nodes, 0, 3)
|
||||
sync_blocks(self.nodes)
|
||||
|
||||
@@ -227,7 +239,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
#do some -walletbroadcast tests
|
||||
stop_nodes(self.nodes)
|
||||
wait_bitcoinds()
|
||||
self.nodes = start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"],["-walletbroadcast=0"],["-walletbroadcast=0"]])
|
||||
self.nodes = start_nodes(3, self.options.tmpdir, self.net_args(3, ["-walletbroadcast=0"]))
|
||||
connect_nodes_bi(self.nodes,0,1)
|
||||
connect_nodes_bi(self.nodes,1,2)
|
||||
connect_nodes_bi(self.nodes,0,2)
|
||||
@@ -256,7 +268,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
#restart the nodes with -walletbroadcast=1
|
||||
stop_nodes(self.nodes)
|
||||
wait_bitcoinds()
|
||||
self.nodes = start_nodes(3, self.options.tmpdir)
|
||||
self.nodes = start_nodes(3, self.options.tmpdir, self.net_args(3))
|
||||
connect_nodes_bi(self.nodes,0,1)
|
||||
connect_nodes_bi(self.nodes,1,2)
|
||||
connect_nodes_bi(self.nodes,0,2)
|
||||
@@ -290,7 +302,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
num_t_recipients = 3000
|
||||
amount_per_recipient = Decimal('0.00000001')
|
||||
errorString = ''
|
||||
for i in xrange(0,num_t_recipients):
|
||||
for i in range(0,num_t_recipients):
|
||||
newtaddr = self.nodes[2].getnewaddress()
|
||||
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
||||
|
||||
@@ -305,7 +317,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
|
||||
try:
|
||||
self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert("Too many outputs, size of raw transaction" in errorString)
|
||||
|
||||
@@ -314,10 +326,10 @@ class WalletTest (BitcoinTestFramework):
|
||||
num_z_recipients = 50
|
||||
amount_per_recipient = Decimal('0.00000001')
|
||||
errorString = ''
|
||||
for i in xrange(0,num_t_recipients):
|
||||
for i in range(0,num_t_recipients):
|
||||
newtaddr = self.nodes[2].getnewaddress()
|
||||
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
||||
for i in xrange(0,num_z_recipients):
|
||||
for i in range(0,num_z_recipients):
|
||||
newzaddr = self.nodes[2].z_getnewaddress()
|
||||
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
||||
|
||||
@@ -327,7 +339,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
|
||||
try:
|
||||
self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert("size of raw transaction would be larger than limit" in errorString)
|
||||
|
||||
@@ -335,12 +347,12 @@ class WalletTest (BitcoinTestFramework):
|
||||
num_z_recipients = 100
|
||||
amount_per_recipient = Decimal('0.00000001')
|
||||
errorString = ''
|
||||
for i in xrange(0,num_z_recipients):
|
||||
for i in range(0,num_z_recipients):
|
||||
newzaddr = self.nodes[2].z_getnewaddress()
|
||||
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
||||
try:
|
||||
self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert("Invalid parameter, too many zaddr outputs" in errorString)
|
||||
|
||||
@@ -426,7 +438,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
errorString = ""
|
||||
try:
|
||||
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1f-4")
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
|
||||
assert_equal("Invalid amount" in errorString, True)
|
||||
@@ -434,7 +446,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
errorString = ""
|
||||
try:
|
||||
self.nodes[0].generate("2") #use a string to as block amount parameter must fail because it's not interpreted as amount
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
|
||||
assert_equal("not an integer" in errorString, True)
|
||||
@@ -448,9 +460,9 @@ class WalletTest (BitcoinTestFramework):
|
||||
try:
|
||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||
assert(myopid)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
print errorString
|
||||
print(errorString)
|
||||
assert(False)
|
||||
|
||||
# This fee is larger than the default fee and since amount=0
|
||||
@@ -462,7 +474,7 @@ class WalletTest (BitcoinTestFramework):
|
||||
|
||||
try:
|
||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert('Small transaction amount' in errorString)
|
||||
|
||||
@@ -475,9 +487,9 @@ class WalletTest (BitcoinTestFramework):
|
||||
try:
|
||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
|
||||
assert(myopid)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
print errorString
|
||||
print(errorString)
|
||||
assert(False)
|
||||
|
||||
# Make sure amount=0, fee=0 transaction are valid to add to mempool
|
||||
@@ -490,9 +502,9 @@ class WalletTest (BitcoinTestFramework):
|
||||
try:
|
||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
|
||||
assert(myopid)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
print errorString
|
||||
print(errorString)
|
||||
assert(False)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2018 The Zcash developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2017 The Zcash developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -32,7 +32,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
self.sync_all()
|
||||
|
||||
def run_test (self):
|
||||
print "Mining blocks..."
|
||||
print("Mining blocks...")
|
||||
|
||||
self.nodes[0].generate(1)
|
||||
do_not_shield_taddr = self.nodes[0].getnewaddress()
|
||||
@@ -81,7 +81,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
try:
|
||||
self.nodes[0].z_mergetoaddress("*", myzaddr)
|
||||
assert(False)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("JSON value is not an array as expected" in errorString, True)
|
||||
|
||||
@@ -90,7 +90,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
try:
|
||||
self.nodes[2].z_mergetoaddress([mytaddr], myzaddr)
|
||||
assert(False)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Could not find any funds to merge" in errorString, True)
|
||||
|
||||
@@ -98,7 +98,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
try:
|
||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, -1)
|
||||
assert(False)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Amount out of range" in errorString, True)
|
||||
|
||||
@@ -106,7 +106,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
try:
|
||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('21000000.00000001'))
|
||||
assert(False)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Amount out of range" in errorString, True)
|
||||
|
||||
@@ -114,7 +114,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
try:
|
||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, 999)
|
||||
assert(False)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Insufficient funds" in errorString, True)
|
||||
|
||||
@@ -122,7 +122,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
try:
|
||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), -1)
|
||||
assert(False)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Limit on maximum number of UTXOs cannot be negative" in errorString, True)
|
||||
|
||||
@@ -130,7 +130,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
try:
|
||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 99999999999999)
|
||||
assert(False)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("JSON integer out of range" in errorString, True)
|
||||
|
||||
@@ -138,7 +138,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
try:
|
||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, -1)
|
||||
assert(False)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Limit on maximum number of notes cannot be negative" in errorString, True)
|
||||
|
||||
@@ -146,7 +146,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
try:
|
||||
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, 99999999999999)
|
||||
assert(False)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("JSON integer out of range" in errorString, True)
|
||||
|
||||
@@ -154,7 +154,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
|
||||
try:
|
||||
self.nodes[0].z_mergetoaddress([mytaddr], mytaddr)
|
||||
assert(False)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Destination address is also the only source address, and all its funds are already merged" in errorString, True)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2016 The Zcash developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -7,15 +7,71 @@
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal, assert_true, bitcoind_processes, \
|
||||
connect_nodes_bi, start_node, start_nodes, wait_and_assert_operationid_status
|
||||
connect_nodes_bi, initialize_chain_clean, p2p_port, start_node, start_nodes, \
|
||||
sync_blocks, wait_and_assert_operationid_status
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
class WalletNullifiersTest (BitcoinTestFramework):
|
||||
|
||||
# The framework default setup_chain() calls initialize_chain(), which pre-builds a
|
||||
# 200-block chain in a *relative* "cache/" directory shared by every test process
|
||||
# running out of this tree. That directory is guarded only by the datadir lock, so two
|
||||
# qa/rpc-tests running at once collide on it and the second one hangs forever inside
|
||||
# "dragonx-cli -rpcwait" (observed here while a sibling test held cache/node0..3).
|
||||
# Build the identical pre-condition -- 4 nodes, two rounds of 25 blocks each, i.e. 25
|
||||
# mature + 25 immature coinbases per node -- directly in this test's private tmpdir.
|
||||
# This is a setup change only: no assertion below is relaxed, removed or reordered.
|
||||
def setup_chain(self):
|
||||
print("Initializing test directory "+self.options.tmpdir)
|
||||
initialize_chain_clean(self.options.tmpdir, 4)
|
||||
|
||||
# Three networking facts about this daemon force extra flags here. None of them
|
||||
# change what the test exercises; without them the 4 nodes either never peer with
|
||||
# each other, or peer with the LIVE DragonX network instead.
|
||||
#
|
||||
# 1. start_node() hardcodes "-connect=0", which also soft-sets -listen=0, so nothing
|
||||
# binds p2p_port(i) and connect_nodes_bi() can never form the regtest mesh --
|
||||
# sync_blocks() then spins forever (observed: node0 at 25 blocks, nodes 1-3 stuck
|
||||
# at 0, nothing listening on 11005-11008). -listen=1 -bind=127.0.0.1 restores the
|
||||
# mesh and keeps it on loopback.
|
||||
# 2. hush_args() appends node1..node10.dragonx.is to -addnode unconditionally, -regtest
|
||||
# included, and regtest reuses mainnet's network magic. A "regtest" node therefore
|
||||
# joins the live network: node0 of an earlier run handshook 8 production peers
|
||||
# ("receive version message: /DragonX:1.0.3/ ... blocks=3254266") and ingested their
|
||||
# headers. -dns=0 stops those hostname -addnode entries from resolving; RPC addnode
|
||||
# with a numeric 127.0.0.1:port is unaffected.
|
||||
# 3. hush_args() runs BEFORE the config file is read, so its GetArg("-port",0) never
|
||||
# sees the "port=" line initialize_datadir() wrote and GetDefaultPort() stays at the
|
||||
# mainnet p2p port. "-connect=0" is then parsed as the address 0.0.0.0:<mainnet
|
||||
# port>, i.e. the production dragonxd listening on this box -- every node in runs 2
|
||||
# and 3 picked up exactly one peer reporting blocks=3254269. Repeating -port on the
|
||||
# command line points GetDefaultPort() at this node's own regtest port instead.
|
||||
#
|
||||
# -autoshield is on by default on DragonX and is not part of what this test measures:
|
||||
# a background thread sweeps each node's matured coinbase into a seed-derived zaddr
|
||||
# (8 "autoshield operation finished" ops per node while the chain is being mined). That
|
||||
# empties the very taddr this test spends from, and the resulting transactions do not
|
||||
# settle identically on every node ("ERROR: AcceptToMemoryPool: ContextualCheckTransaction
|
||||
# failed" on node1), so sync_mempools() never converges and the run wedges until the
|
||||
# timeout. Turn the background sweeper off; the test does its own shielding explicitly.
|
||||
def net_args(self, i):
|
||||
return ['-listen=1', '-bind=127.0.0.1', '-dns=0', '-autoshield=0',
|
||||
'-port=%d' % p2p_port(i)]
|
||||
|
||||
def setup_nodes(self):
|
||||
return start_nodes(4, self.options.tmpdir,
|
||||
extra_args=[['-experimentalfeatures', '-developerencryptwallet']] * 4)
|
||||
extra_args=[['-experimentalfeatures', '-developerencryptwallet']
|
||||
+ self.net_args(i) for i in range(4)])
|
||||
|
||||
def setup_network(self, split = False):
|
||||
super().setup_network(split)
|
||||
# Same block layout initialize_chain() would have handed us.
|
||||
for _ in range(2):
|
||||
for peer in range(4):
|
||||
self.nodes[peer].generate(25)
|
||||
sync_blocks(self.nodes)
|
||||
self.sync_all()
|
||||
|
||||
def run_test (self):
|
||||
# add zaddr to node 0
|
||||
@@ -25,7 +81,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
|
||||
mytaddr = self.nodes[0].getnewaddress()
|
||||
recipients = []
|
||||
recipients.append({"address":myzaddr0, "amount":Decimal('10.0')-Decimal('0.0001')}) # utxo amount less fee
|
||||
|
||||
|
||||
wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(mytaddr, recipients), timeout=120)
|
||||
|
||||
self.sync_all()
|
||||
@@ -44,7 +100,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
|
||||
bitcoind_processes[1].wait()
|
||||
|
||||
# restart node 1
|
||||
self.nodes[1] = start_node(1, self.options.tmpdir)
|
||||
self.nodes[1] = start_node(1, self.options.tmpdir, self.net_args(1))
|
||||
connect_nodes_bi(self.nodes, 0, 1)
|
||||
connect_nodes_bi(self.nodes, 1, 2)
|
||||
self.sync_all()
|
||||
@@ -52,7 +108,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
|
||||
# send node 0 zaddr to note 2 zaddr
|
||||
recipients = []
|
||||
recipients.append({"address":myzaddr, "amount":7.0})
|
||||
|
||||
|
||||
wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(myzaddr0, recipients), timeout=120)
|
||||
|
||||
self.sync_all()
|
||||
@@ -97,7 +153,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
|
||||
mytaddr1 = self.nodes[1].getnewaddress()
|
||||
recipients = []
|
||||
recipients.append({"address":mytaddr1, "amount":1.0})
|
||||
|
||||
|
||||
wait_and_assert_operationid_status(self.nodes[1], self.nodes[1].z_sendmany(myzaddr, recipients), timeout=120)
|
||||
|
||||
self.sync_all()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2018 The Zcash developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -13,6 +13,29 @@ from test_framework.util import (
|
||||
)
|
||||
from decimal import Decimal
|
||||
|
||||
def get_value_pool(pools, pool_id):
|
||||
"""
|
||||
Return the valuePools entry with the given id, or None if this chain does
|
||||
not have that pool. DragonX's getblockchaininfo only reports the Sapling
|
||||
pool (no Sprout history exists on this chain), so the pools list can no
|
||||
longer be indexed positionally the way the upstream test did.
|
||||
"""
|
||||
for pool in pools:
|
||||
if pool['id'] == pool_id:
|
||||
return pool
|
||||
return None
|
||||
|
||||
def assert_pool_values(pools, sprout_value, sapling_value):
|
||||
sprout = get_value_pool(pools, 'sprout')
|
||||
if sprout is not None:
|
||||
assert_equal(sprout['chainValue'], sprout_value)
|
||||
else:
|
||||
# No Sprout pool at all is the same statement as "the Sprout pool holds nothing"
|
||||
assert_equal(sprout_value, Decimal('0'))
|
||||
sapling = get_value_pool(pools, 'sapling')
|
||||
assert_true(sapling is not None, "Sapling value pool missing from getblockchaininfo")
|
||||
assert_equal(sapling['chainValue'], sapling_value)
|
||||
|
||||
class WalletPersistenceTest (BitcoinTestFramework):
|
||||
|
||||
def setup_chain(self):
|
||||
@@ -20,8 +43,20 @@ class WalletPersistenceTest (BitcoinTestFramework):
|
||||
initialize_chain_clean(self.options.tmpdir, 3)
|
||||
|
||||
def setup_network(self, split=False):
|
||||
# -listen=1/-bind: the framework's start_node() passes -connect=0, and DragonX (like
|
||||
# Bitcoin) reacts to -connect by soft-setting -listen=0. A non-listening node can never
|
||||
# accept the "addnode 127.0.0.1:<port>" that connect_nodes_bi() issues, so without this
|
||||
# the three nodes stay isolated and sync_all() spins forever. -bind keeps the listener on
|
||||
# loopback so a regtest node never becomes reachable from the public internet.
|
||||
self.nodes = start_nodes(3, self.options.tmpdir,
|
||||
extra_args=[[
|
||||
'-listen=1',
|
||||
'-bind=127.0.0.1',
|
||||
# -dns=0: DragonX appends node1..node10.dragonx.is to -addnode for every chain
|
||||
# named DRAGONX (hush_utils.h), and -connect=0 does not suppress -addnode. Without
|
||||
# this a regtest node dials the LIVE DragonX network and is fed mainnet headers.
|
||||
# The addnode calls connect_nodes_bi() makes use literal IPs, so they still work.
|
||||
'-dns=0',
|
||||
'-nuparams=5ba81b19:100', # Overwinter
|
||||
'-nuparams=76b809bb:201', # Sapling
|
||||
]] * 3)
|
||||
@@ -69,12 +104,11 @@ class WalletPersistenceTest (BitcoinTestFramework):
|
||||
|
||||
# Verify shielded balance
|
||||
assert_equal(self.nodes[0].z_getbalance(sapling_addr), Decimal('20'))
|
||||
|
||||
|
||||
# Verify size of shielded pools
|
||||
pools = self.nodes[0].getblockchaininfo()['valuePools']
|
||||
assert_equal(pools[0]['chainValue'], Decimal('0')) # Sprout
|
||||
assert_equal(pools[1]['chainValue'], Decimal('20')) # Sapling
|
||||
|
||||
assert_pool_values(pools, Decimal('0'), Decimal('20'))
|
||||
|
||||
# Restart the nodes
|
||||
stop_nodes(self.nodes)
|
||||
wait_bitcoinds()
|
||||
@@ -82,8 +116,7 @@ class WalletPersistenceTest (BitcoinTestFramework):
|
||||
|
||||
# Verify size of shielded pools
|
||||
pools = self.nodes[0].getblockchaininfo()['valuePools']
|
||||
assert_equal(pools[0]['chainValue'], Decimal('0')) # Sprout
|
||||
assert_equal(pools[1]['chainValue'], Decimal('20')) # Sapling
|
||||
assert_pool_values(pools, Decimal('0'), Decimal('20'))
|
||||
|
||||
# Node 0 sends some shielded funds to Node 1
|
||||
dest_addr = self.nodes[1].z_getnewaddress('sapling')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2016 The Zcash developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.authproxy import JSONRPCException
|
||||
from test_framework.mininode import COIN
|
||||
from test_framework.util import assert_equal, initialize_chain_clean, \
|
||||
start_nodes, connect_nodes_bi, wait_and_assert_operationid_status
|
||||
|
||||
@@ -14,6 +13,20 @@ import sys
|
||||
import timeit
|
||||
from decimal import Decimal
|
||||
|
||||
# Upstream imported this from test_framework.mininode, which is still python2 and
|
||||
# fails to even parse under python3 (0x100000000L literals). mininode is a p2p
|
||||
# serialisation module this test does not otherwise use, so rather than drag a
|
||||
# large unrelated port into test_framework/ the one constant needed is inlined.
|
||||
# Same value as test_framework/mininode.py:52.
|
||||
COIN = 100000000 # 1 DRGX in puposhis
|
||||
|
||||
# DragonX has no Sprout pool: getblockchaininfo/getblock only ever emit a
|
||||
# "sapling" entry in valuePools (see rpc/blockchain.cpp), and z_getnewaddress
|
||||
# only makes Sapling addresses. The shielded value this test moves therefore
|
||||
# lands in the Sapling pool, so every check that upstream made against 'sprout'
|
||||
# is made against 'sapling' here. The assertion itself is unchanged.
|
||||
SHIELDED_POOL = 'sapling'
|
||||
|
||||
def check_value_pool(node, name, total):
|
||||
value_pools = node.getblockchaininfo()['valuePools']
|
||||
found = False
|
||||
@@ -42,7 +55,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
self.sync_all()
|
||||
|
||||
def run_test (self):
|
||||
print "Mining blocks..."
|
||||
print("Mining blocks...")
|
||||
|
||||
self.nodes[0].generate(4)
|
||||
|
||||
@@ -59,17 +72,17 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
assert_equal(self.nodes[2].getbalance(), 0)
|
||||
assert_equal(self.nodes[3].getbalance(), 0)
|
||||
|
||||
check_value_pool(self.nodes[0], 'sprout', 0)
|
||||
check_value_pool(self.nodes[1], 'sprout', 0)
|
||||
check_value_pool(self.nodes[2], 'sprout', 0)
|
||||
check_value_pool(self.nodes[3], 'sprout', 0)
|
||||
check_value_pool(self.nodes[0], SHIELDED_POOL, 0)
|
||||
check_value_pool(self.nodes[1], SHIELDED_POOL, 0)
|
||||
check_value_pool(self.nodes[2], SHIELDED_POOL, 0)
|
||||
check_value_pool(self.nodes[3], SHIELDED_POOL, 0)
|
||||
|
||||
# Send will fail because we are enforcing the consensus rule that
|
||||
# coinbase utxos can only be sent to a zaddr.
|
||||
errorString = ""
|
||||
try:
|
||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Coinbase funds can only be sent to a zaddr" in errorString, True)
|
||||
|
||||
@@ -88,18 +101,18 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
# as it's currently not possible to specify a change address in z_sendmany.
|
||||
recipients = []
|
||||
recipients.append({"address":myzaddr, "amount":Decimal('1.23456789')})
|
||||
|
||||
|
||||
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
|
||||
error_result = wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "wallet does not allow any change", 10)
|
||||
|
||||
# Test that the returned status object contains a params field with the operation's input parameters
|
||||
assert_equal(error_result["method"], "z_sendmany")
|
||||
params = error_result["params"]
|
||||
assert_equal(params["fee"], Decimal('0.0001')) # default
|
||||
assert_equal(params["minconf"], Decimal('1')) # default
|
||||
assert_equal(Decimal(params["fee"]), Decimal('0.0001')) # default
|
||||
assert_equal(Decimal(params["minconf"]), Decimal('1')) # default
|
||||
assert_equal(params["fromaddress"], mytaddr)
|
||||
assert_equal(params["amounts"][0]["address"], myzaddr)
|
||||
assert_equal(params["amounts"][0]["amount"], Decimal('1.23456789'))
|
||||
assert_equal(Decimal(params["amounts"][0]["amount"]), Decimal('1.23456789'))
|
||||
|
||||
# Add viewing key for myzaddr to Node 3
|
||||
myviewingkey = self.nodes[0].z_exportviewingkey(myzaddr)
|
||||
@@ -169,14 +182,17 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
|
||||
assert_equal(Decimal(resp["total"]), Decimal('39.9999'))
|
||||
|
||||
# The Sprout value pool should reflect the send
|
||||
sproutvalue = shieldvalue
|
||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
||||
# The shielded value pool should reflect the send
|
||||
shieldedvalue = shieldvalue
|
||||
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
|
||||
|
||||
# A custom fee of 0 is okay. Here the node will send the note value back to itself.
|
||||
recipients = []
|
||||
recipients.append({"address":myzaddr, "amount": Decimal('19.9999')})
|
||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('0.0'))
|
||||
# NB: the fee is passed as a JSON number, not a Decimal. authproxy serialises
|
||||
# Decimal as a JSON *string* and z_sendmany reads the fee with params[3].get_real(),
|
||||
# which only accepts VNUM -- see port notes. The value is unchanged.
|
||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, 0.0)
|
||||
mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
|
||||
self.sync_all()
|
||||
self.nodes[1].generate(1)
|
||||
@@ -186,8 +202,8 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
|
||||
assert_equal(Decimal(resp["total"]), Decimal('39.9999'))
|
||||
|
||||
# The Sprout value pool should be unchanged
|
||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
||||
# The shielded value pool should be unchanged
|
||||
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
|
||||
|
||||
# convert note to transparent funds
|
||||
unshieldvalue = Decimal('10.0')
|
||||
@@ -206,12 +222,12 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
self.sync_all()
|
||||
|
||||
# check balances
|
||||
sproutvalue -= unshieldvalue + Decimal('0.0001')
|
||||
shieldedvalue -= unshieldvalue + Decimal('0.0001')
|
||||
resp = self.nodes[0].z_gettotalbalance()
|
||||
assert_equal(Decimal(resp["transparent"]), Decimal('30.0'))
|
||||
assert_equal(Decimal(resp["private"]), Decimal('9.9998'))
|
||||
assert_equal(Decimal(resp["total"]), Decimal('39.9998'))
|
||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
||||
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
|
||||
|
||||
# z_sendmany will return an error if there is transparent change output considered dust.
|
||||
# UTXO selection in z_sendmany sorts in ascending order, so smallest utxos are consumed first.
|
||||
@@ -226,7 +242,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
errorString = ""
|
||||
try:
|
||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 99999)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Insufficient funds" in errorString, True)
|
||||
|
||||
@@ -241,7 +257,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
# Send will fail because of insufficient funds unless sender uses coinbase utxos
|
||||
try:
|
||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 21)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Insufficient funds, coinbase funds can only be spent after they have been sent to a zaddr" in errorString, True)
|
||||
|
||||
@@ -256,7 +272,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
# Note that regtest chainparams does not require standard tx, so setting the amount to be
|
||||
# less than the dust threshold, e.g. 0.00000001 will not result in mempool rejection.
|
||||
start_time = timeit.default_timer()
|
||||
for i in xrange(0,num_t_recipients):
|
||||
for i in range(0,num_t_recipients):
|
||||
newtaddr = self.nodes[2].getnewaddress()
|
||||
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
||||
elapsed = timeit.default_timer() - start_time
|
||||
@@ -287,28 +303,30 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
|
||||
# check balance
|
||||
node2balance = amount_per_recipient * num_t_recipients
|
||||
sproutvalue -= node2balance + Decimal('0.0001')
|
||||
shieldedvalue -= node2balance + Decimal('0.0001')
|
||||
assert_equal(self.nodes[2].getbalance(), node2balance)
|
||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
||||
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
|
||||
|
||||
# Send will fail because fee is negative
|
||||
try:
|
||||
self.nodes[0].z_sendmany(myzaddr, recipients, 1, -1)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Amount out of range" in errorString, True)
|
||||
|
||||
# Send will fail because fee is larger than MAX_MONEY
|
||||
errorString = ""
|
||||
try:
|
||||
self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('21000000.00000001'))
|
||||
except JSONRPCException,e:
|
||||
self.nodes[0].z_sendmany(myzaddr, recipients, 1, float(Decimal('21000000.00000001')))
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Amount out of range" in errorString, True)
|
||||
|
||||
# Send will fail because fee is larger than sum of outputs
|
||||
errorString = ""
|
||||
try:
|
||||
self.nodes[0].z_sendmany(myzaddr, recipients, 1, (amount_per_recipient * num_t_recipients) + Decimal('0.00000001'))
|
||||
except JSONRPCException,e:
|
||||
self.nodes[0].z_sendmany(myzaddr, recipients, 1, float((amount_per_recipient * num_t_recipients) + Decimal('0.00000001')))
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("is greater than the sum of outputs" in errorString, True)
|
||||
|
||||
@@ -334,10 +352,10 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
send_amount = num_recipients * amount_per_recipient
|
||||
custom_fee = Decimal('0.00012345')
|
||||
zbalance = self.nodes[0].z_getbalance(myzaddr)
|
||||
for i in xrange(0,num_recipients):
|
||||
for i in range(0,num_recipients):
|
||||
newzaddr = self.nodes[2].z_getnewaddress()
|
||||
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, custom_fee)
|
||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, float(custom_fee))
|
||||
wait_and_assert_operationid_status(self.nodes[0], myopid)
|
||||
self.sync_all()
|
||||
self.nodes[1].generate(1)
|
||||
@@ -353,8 +371,8 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
||||
|
||||
resp = self.nodes[0].z_getbalance(myzaddr)
|
||||
assert_equal(Decimal(resp), zbalance - custom_fee - send_amount)
|
||||
sproutvalue -= custom_fee
|
||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
||||
shieldedvalue -= custom_fee
|
||||
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
|
||||
|
||||
notes = self.nodes[0].z_listunspent(1, 99999, False, [myzaddr])
|
||||
sum_of_notes = sum([note["amount"] for note in notes])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2018 The Zcash developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -8,21 +8,99 @@ from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.authproxy import JSONRPCException
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
start_nodes,
|
||||
initialize_chain_clean,
|
||||
p2p_port,
|
||||
set_node_times,
|
||||
start_node,
|
||||
sync_blocks,
|
||||
wait_and_assert_operationid_status,
|
||||
)
|
||||
|
||||
import os
|
||||
import stat
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
# Test wallet behaviour with Sapling addresses
|
||||
class WalletSaplingTest(BitcoinTestFramework):
|
||||
|
||||
def setup_chain(self):
|
||||
# The shared initialize_chain() cache builder in test_framework/util.py has not been
|
||||
# repaired for DragonX (it spawns the cache nodes by bare name off PATH, drives the
|
||||
# CLI without -regtest so it dials the assetchain RPC port, and deletes debug.log
|
||||
# from the non-net-specific datadir). Build the same starting state here instead --
|
||||
# see _generate_starting_chain() -- so this test does not depend on it.
|
||||
print("Initializing test directory " + self.options.tmpdir)
|
||||
initialize_chain_clean(self.options.tmpdir, 4)
|
||||
|
||||
# !!! test_framework/util.py:start_node() hardcodes "-connect=0" into every regtest
|
||||
# node's argv. Modern Bitcoin Core special-cases that value to mean "make no automatic
|
||||
# connections", but THIS codebase does not (net.cpp ThreadOpenConnections just iterates
|
||||
# mapMultiArgs["-connect"]), so "0" is dialled as a hostname: it resolves to 0.0.0.0,
|
||||
# which on Linux connects to localhost on Params().GetDefaultPort() -- 21768, the live
|
||||
# DRAGONX p2p port. Observed directly: a regtest node started by the unmodified
|
||||
# framework peered with the production dragonxd on this host and with seven public
|
||||
# mainnet nodes (heights ~3.25M) and began ingesting mainnet headers. "-connect" also
|
||||
# soft-sets "-listen=0", so the framework's own connect_nodes_bi() can never establish
|
||||
# the local links a multi-node test needs.
|
||||
#
|
||||
# Both problems are in the shared framework, which this port is not allowed to touch, so
|
||||
# they are worked around per-node here: the daemon is launched through a tiny wrapper
|
||||
# that strips the "-connect=0" argument, and the real topology/listening flags are passed
|
||||
# as extra_args (which start_node appends after its own).
|
||||
def _daemon_wrapper(self):
|
||||
srcdir = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), "..", "..", "src", "dragonxd")
|
||||
path = os.path.join(self.options.tmpdir, "dragonxd-no-connect0")
|
||||
with open(path, "w") as f:
|
||||
f.write("#!/usr/bin/env bash\n")
|
||||
f.write("args=()\n")
|
||||
f.write('for a in "$@"; do\n')
|
||||
f.write(' if [ "$a" = "-connect=0" ]; then continue; fi\n')
|
||||
f.write(' args+=("$a")\n')
|
||||
f.write("done\n")
|
||||
f.write('exec %s "${args[@]}"\n' % os.path.realpath(srcdir))
|
||||
os.chmod(path, os.stat(path).st_mode | stat.S_IXUSR)
|
||||
return path
|
||||
|
||||
def setup_nodes(self):
|
||||
return start_nodes(4, self.options.tmpdir, [[
|
||||
#'-nuparams=5ba81b19:201', # Overwinter
|
||||
#'-nuparams=76b809bb:203', # Sapling
|
||||
#'-experimentalfeatures', '-zmergetoaddress',
|
||||
]] * 4)
|
||||
binary = self._daemon_wrapper()
|
||||
nodes = []
|
||||
for i in range(4):
|
||||
extra_args = [
|
||||
#'-nuparams=5ba81b19:201', # Overwinter
|
||||
#'-nuparams=76b809bb:203', # Sapling
|
||||
#'-experimentalfeatures', '-zmergetoaddress',
|
||||
# Listen on this test's PID-keyed port so the nodes can actually peer with
|
||||
# each other, and only ever dial each other -- never the public network.
|
||||
'-listen=1',
|
||||
'-bind=127.0.0.1',
|
||||
'-port=%d' % p2p_port(i),
|
||||
] + ['-connect=127.0.0.1:%d' % p2p_port(j) for j in range(4) if j != i]
|
||||
nodes.append(start_node(i, self.options.tmpdir, extra_args, binary=binary))
|
||||
return nodes
|
||||
|
||||
def setup_network(self, split=False):
|
||||
super(WalletSaplingTest, self).setup_network(split)
|
||||
self._generate_starting_chain()
|
||||
|
||||
def _generate_starting_chain(self):
|
||||
# Equivalent of test_framework.util.initialize_chain(): a 200-block chain where each
|
||||
# of the 4 nodes mined 25 blocks twice, so every node holds 25 mature and 25 immature
|
||||
# coinbases. Block timestamps are 10 minutes apart starting 1 Jan 2014, as there.
|
||||
block_time = 1388534400
|
||||
for _round in range(2):
|
||||
for peer in range(4):
|
||||
for _j in range(25):
|
||||
set_node_times(self.nodes, block_time)
|
||||
self.nodes[peer].generate(1)
|
||||
block_time += 10 * 75
|
||||
# Must sync before next peer starts generating blocks
|
||||
sync_blocks(self.nodes)
|
||||
# Drop back to wall-clock time: initialize_chain() stops the cache nodes and the test
|
||||
# then runs against freshly started nodes that have no mocktime set.
|
||||
set_node_times(self.nodes, 0)
|
||||
self.sync_all()
|
||||
|
||||
def run_test(self):
|
||||
# Sanity-check the test harness
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2017 The Zcash developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -22,8 +22,24 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
|
||||
initialize_chain_clean(self.options.tmpdir, 4)
|
||||
|
||||
def setup_network(self, split=False):
|
||||
args = ['-regtestprotectcoinbase', '-debug=zrpcunsafe']
|
||||
args2 = ['-regtestprotectcoinbase', '-debug=zrpcunsafe', "-mempooltxinputlimit=7"]
|
||||
# DragonX-specific environment flags. None of these change what the test
|
||||
# asserts; without them the test cannot run at all on this daemon:
|
||||
# -listen=1/-bind=127.0.0.1: the framework's start_node() always passes
|
||||
# -connect=0, and AppInit2 soft-sets -listen=0 whenever -connect is
|
||||
# present, so the nodes never listen and connect_nodes_bi() silently
|
||||
# builds an empty topology (sync_all() then spins forever). An explicit
|
||||
# -listen=1 beats the SoftSetBoolArg.
|
||||
# -dnsseed=0: chainparams_commandline() keeps DRAGONX's DNS seeds and
|
||||
# overwrites pchMessageStart with the DRAGONX chain magic on *every*
|
||||
# network including regtest, so a regtest node otherwise dials and
|
||||
# handshakes with live mainnet peers (observed: 8 mainnet peers,
|
||||
# blocks=3254237, feeding mainnet headers into the regtest node).
|
||||
# -autoshield=0: DragonX auto-shields matured coinbase every 25 blocks
|
||||
# by default, which would race the manual z_shieldcoinbase calls under
|
||||
# test and move the balances this test checks.
|
||||
isolate = ['-listen=1', '-bind=127.0.0.1', '-dnsseed=0', '-autoshield=0']
|
||||
args = ['-regtestprotectcoinbase', '-debug=zrpcunsafe'] + isolate
|
||||
args2 = ['-regtestprotectcoinbase', '-debug=zrpcunsafe', "-mempooltxinputlimit=7"] + isolate
|
||||
if self.addr_type != 'sprout':
|
||||
nu = [
|
||||
'-nuparams=5ba81b19:0', # Overwinter
|
||||
@@ -42,7 +58,7 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
|
||||
self.sync_all()
|
||||
|
||||
def run_test (self):
|
||||
print "Mining blocks..."
|
||||
print("Mining blocks...")
|
||||
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
@@ -73,42 +89,42 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
|
||||
self.nodes[2].importaddress(mytaddr)
|
||||
try:
|
||||
self.nodes[2].z_shieldcoinbase(mytaddr, myzaddr)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Could not find any coinbase funds to shield" in errorString, True)
|
||||
|
||||
# Shielding will fail because fee is negative
|
||||
try:
|
||||
self.nodes[0].z_shieldcoinbase("*", myzaddr, -1)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Amount out of range" in errorString, True)
|
||||
|
||||
# Shielding will fail because fee is larger than MAX_MONEY
|
||||
try:
|
||||
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('21000000.00000001'))
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Amount out of range" in errorString, True)
|
||||
|
||||
# Shielding will fail because fee is larger than sum of utxos
|
||||
try:
|
||||
self.nodes[0].z_shieldcoinbase("*", myzaddr, 999)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Insufficient coinbase funds" in errorString, True)
|
||||
|
||||
# Shielding will fail because limit parameter must be at least 0
|
||||
try:
|
||||
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), -1)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Limit on maximum number of utxos cannot be negative" in errorString, True)
|
||||
|
||||
# Shielding will fail because limit parameter is absurdly large
|
||||
try:
|
||||
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), 99999999999999)
|
||||
except JSONRPCException,e:
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("JSON integer out of range" in errorString, True)
|
||||
|
||||
@@ -214,3 +230,13 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
|
||||
sync_mempools(self.nodes[:2])
|
||||
self.nodes[1].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Upstream (Zcash/Hush) ran this test twice: once with Sprout zaddrs and once
|
||||
# with Sapling. DragonX has no Sprout support at all -- z_getnewaddress only
|
||||
# accepts "sapling" or "amnesia" (src/wallet/rpcwallet.cpp z_getnewaddress),
|
||||
# so WalletShieldCoinbaseTest('sprout') cannot even allocate its target
|
||||
# address. The sprout-only branches inside run_test() are kept intact for
|
||||
# reference but only the sapling variant is executed.
|
||||
print("Running for sapling...")
|
||||
WalletShieldCoinbaseTest('sapling').main()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2016 The Zcash developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -20,7 +20,16 @@ class WalletTreeStateTest (BitcoinTestFramework):
|
||||
|
||||
# Start nodes with -regtestprotectcoinbase to set fCoinbaseMustBeProtected to true.
|
||||
def setup_network(self, split=False):
|
||||
self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[['-regtestprotectcoinbase','-debug=zrpc']] * 3 )
|
||||
# -listen=1 and -dns=0 are DragonX-specific harness requirements, not part of the
|
||||
# original test: start_node() passes -connect=0, which trips the "-connect set ->
|
||||
# setting -listen=0" parameter interaction, so without -listen=1 the three nodes
|
||||
# cannot open the p2p links connect_nodes_bi() asks for (node1/node2 stay at height
|
||||
# 0 forever and sync_all() can never converge). -dns=0 blocks the unconditional
|
||||
# node1..node10.dragonx.is -addnode injection in hush_args(), which otherwise dials
|
||||
# the real DragonX seed nodes from regtest and floods these nodes with mainnet headers.
|
||||
self.nodes = start_nodes(3, self.options.tmpdir,
|
||||
extra_args=[['-regtestprotectcoinbase','-debug=zrpc',
|
||||
'-listen=1','-dns=0']] * 3 )
|
||||
connect_nodes_bi(self.nodes,0,1)
|
||||
connect_nodes_bi(self.nodes,1,2)
|
||||
connect_nodes_bi(self.nodes,0,2)
|
||||
@@ -28,7 +37,7 @@ class WalletTreeStateTest (BitcoinTestFramework):
|
||||
self.sync_all()
|
||||
|
||||
def run_test (self):
|
||||
print "Mining blocks..."
|
||||
print("Mining blocks...")
|
||||
|
||||
self.nodes[0].generate(100)
|
||||
self.sync_all()
|
||||
@@ -79,7 +88,7 @@ class WalletTreeStateTest (BitcoinTestFramework):
|
||||
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||
|
||||
# Wait for Tx 2 to begin executing...
|
||||
for x in xrange(1, 60):
|
||||
for x in range(1, 60):
|
||||
results = self.nodes[0].z_getoperationstatus([myopid])
|
||||
status = results[0]["status"]
|
||||
if status == "executing":
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2024 The Hush developers
|
||||
# Copyright (c) 2014 The Bitcoin Core developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
@@ -37,8 +37,8 @@ and confirm again balances are correct.
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.authproxy import JSONRPCException
|
||||
from test_framework.util import assert_equal, initialize_chain_clean, \
|
||||
start_nodes, start_node, connect_nodes, stop_node, \
|
||||
sync_blocks, sync_mempools
|
||||
start_nodes, start_node, connect_nodes, \
|
||||
sync_blocks, sync_mempools, bitcoind_processes
|
||||
|
||||
import os
|
||||
import shutil
|
||||
@@ -48,6 +48,46 @@ import logging
|
||||
|
||||
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
|
||||
|
||||
# Three node flags this test has to supply for itself, because test_framework.util.start_node()
|
||||
# cannot give it a usable isolated regtest network on DragonX:
|
||||
#
|
||||
# -listen=1: start_node() passes "-connect=0" to every node. In DragonX (as in Bitcoin) the
|
||||
# presence of a -connect argument SoftSetBoolArg()s -listen to false, so no test node ever
|
||||
# binds its p2p port and connect_nodes() cannot build the loopback topology this test needs.
|
||||
# Verified on a live run: only the RPC ports were listening and getpeerinfo showed zero
|
||||
# 127.0.0.1 peers on all four nodes. SoftSetBoolArg does not override an explicit value.
|
||||
#
|
||||
# -dns=0: hush_args() unconditionally appends node1..node10.dragonx.is to -addnode whenever the
|
||||
# chain name is DRAGONX, regardless of network, and -connect=0 does not suppress it. On a live
|
||||
# run every "regtest" node ended up with 7-8 established connections to production mainnet
|
||||
# nodes on port 21768, was flooded with mainnet headers ("AcceptBlockHeader: hashPrevBlock ...
|
||||
# not found"), and one of them aborted on CheckBlockIndex(). -dns=0 stops the hostnames from
|
||||
# resolving while leaving the literal 127.0.0.1:PORT addnodes connect_nodes() uses intact.
|
||||
#
|
||||
# -allowlist=127.0.0.1: "-connect=0" does not mean "no connections". The daemon resolves the
|
||||
# literal "0" to 0.0.0.0 and dials it on the chain's default p2p port, i.e. 127.0.0.1:21768 --
|
||||
# which on a machine that also runs a real node is the PRODUCTION daemon (observed:
|
||||
# ESTAB 127.0.0.1:41282 -> 127.0.0.1:21768 from every test node, peer subver /DragonX:1.0.3/,
|
||||
# startingheight 3254254). That peer is outbound, so it is the node's only preferred-download
|
||||
# peer; main.cpp:8398 then computes fFetch=false for every inbound peer, and node3 -- which in
|
||||
# this test's topology is dialed by everyone and dials no one -- never downloads an announced
|
||||
# block, so sync_blocks() hangs forever. Allowlisting loopback makes inbound test peers
|
||||
# preferred-download too (main.cpp:381), which restores block propagation.
|
||||
LISTEN = "-listen=1"
|
||||
NODNS = "-dns=0"
|
||||
ALLOWLIST = "-allowlist=127.0.0.1"
|
||||
|
||||
|
||||
def stop_node_and_reap(node, i):
|
||||
# Equivalent to test_framework.util.stop_node(), which cannot be called: it does
|
||||
# print("Stopping node " + i) with the int index that every caller passes, which raises
|
||||
# TypeError. Reimplemented here rather than editing shared framework code other tests use.
|
||||
print("Stopping node %d" % i)
|
||||
node.stop()
|
||||
bitcoind_processes[i].wait()
|
||||
del bitcoind_processes[i]
|
||||
|
||||
|
||||
class WalletBackupTest(BitcoinTestFramework):
|
||||
|
||||
def setup_chain(self):
|
||||
@@ -62,7 +102,10 @@ class WalletBackupTest(BitcoinTestFramework):
|
||||
ed2 = "-exportdir=" + self.options.tmpdir + "/node2"
|
||||
|
||||
# nodes 1, 2,3 are spenders, let's give them a keypool=100
|
||||
extra_args = [["-keypool=100", ed0], ["-keypool=100", ed1], ["-keypool=100", ed2], []]
|
||||
extra_args = [["-keypool=100", ed0, LISTEN, NODNS, ALLOWLIST],
|
||||
["-keypool=100", ed1, LISTEN, NODNS, ALLOWLIST],
|
||||
["-keypool=100", ed2, LISTEN, NODNS, ALLOWLIST],
|
||||
[LISTEN, NODNS, ALLOWLIST]]
|
||||
self.nodes = start_nodes(4, self.options.tmpdir, extra_args)
|
||||
connect_nodes(self.nodes[0], 3)
|
||||
connect_nodes(self.nodes[1], 3)
|
||||
@@ -95,18 +138,18 @@ class WalletBackupTest(BitcoinTestFramework):
|
||||
|
||||
# As above, this mirrors the original bash test.
|
||||
def start_three(self):
|
||||
self.nodes[0] = start_node(0, self.options.tmpdir)
|
||||
self.nodes[1] = start_node(1, self.options.tmpdir)
|
||||
self.nodes[2] = start_node(2, self.options.tmpdir)
|
||||
self.nodes[0] = start_node(0, self.options.tmpdir, [LISTEN, NODNS, ALLOWLIST])
|
||||
self.nodes[1] = start_node(1, self.options.tmpdir, [LISTEN, NODNS, ALLOWLIST])
|
||||
self.nodes[2] = start_node(2, self.options.tmpdir, [LISTEN, NODNS, ALLOWLIST])
|
||||
connect_nodes(self.nodes[0], 3)
|
||||
connect_nodes(self.nodes[1], 3)
|
||||
connect_nodes(self.nodes[2], 3)
|
||||
connect_nodes(self.nodes[2], 0)
|
||||
|
||||
def stop_three(self):
|
||||
stop_node(self.nodes[0], 0)
|
||||
stop_node(self.nodes[1], 1)
|
||||
stop_node(self.nodes[2], 2)
|
||||
stop_node_and_reap(self.nodes[0], 0)
|
||||
stop_node_and_reap(self.nodes[1], 1)
|
||||
stop_node_and_reap(self.nodes[2], 2)
|
||||
|
||||
def erase_three(self):
|
||||
os.remove(self.options.tmpdir + "/node0/regtest/wallet.dat")
|
||||
|
||||
+12
-5
@@ -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 \
|
||||
@@ -487,7 +489,7 @@ if TARGET_WINDOWS
|
||||
dragonxd_SOURCES += bitcoind-res.rc
|
||||
endif
|
||||
|
||||
dragonxd_LDADD = \
|
||||
dragonxd_LDADD = $(LINK_GROUP_START) \
|
||||
$(LIBBITCOIN_SERVER) \
|
||||
$(LIBCURL) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
@@ -525,6 +527,8 @@ if TARGET_LINUX
|
||||
dragonxd_LDADD += libcc.so $(LIBSECP256K1)
|
||||
endif
|
||||
|
||||
dragonxd_LDADD += $(LINK_GROUP_END)
|
||||
|
||||
# [+] Decker: use static linking for libstdc++.6.dylib, libgomp.1.dylib, libgcc_s.1.dylib
|
||||
if TARGET_DARWIN
|
||||
dragonxd_LDFLAGS += -static-libgcc
|
||||
@@ -551,7 +555,7 @@ if TARGET_WINDOWS
|
||||
dragonx_cli_SOURCES += bitcoin-cli-res.rc
|
||||
endif
|
||||
|
||||
dragonx_cli_LDADD = \
|
||||
dragonx_cli_LDADD = $(LINK_GROUP_START) \
|
||||
$(LIBBITCOIN_CLI) \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
@@ -564,8 +568,10 @@ dragonx_cli_LDADD = \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBZCASH_LIBS)
|
||||
|
||||
dragonx_cli_LDADD += $(LINK_GROUP_END)
|
||||
|
||||
if ENABLE_WALLET
|
||||
wallet_utility_LDADD = \
|
||||
wallet_utility_LDADD = $(LINK_GROUP_START) \
|
||||
libbitcoin_wallet.a \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
@@ -577,6 +583,7 @@ wallet_utility_LDADD = \
|
||||
$(LIBZCASH) \
|
||||
$(LIBZCASH_LIBS)\
|
||||
$(LIBRANDOMX)
|
||||
wallet_utility_LDADD += $(LINK_GROUP_END)
|
||||
endif
|
||||
|
||||
# hush-tx binary #
|
||||
@@ -589,7 +596,7 @@ if TARGET_WINDOWS
|
||||
dragonx_tx_SOURCES += bitcoin-tx-res.rc
|
||||
endif
|
||||
|
||||
dragonx_tx_LDADD = \
|
||||
dragonx_tx_LDADD = $(LINK_GROUP_START) \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
@@ -600,7 +607,7 @@ dragonx_tx_LDADD = \
|
||||
$(LIBZCASH_LIBS) \
|
||||
$(LIBRANDOMX)
|
||||
|
||||
dragonx_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
|
||||
dragonx_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(LINK_GROUP_END)
|
||||
|
||||
# Zcash Protocol Primitives
|
||||
libzcash_a_SOURCES = \
|
||||
|
||||
@@ -13,7 +13,12 @@ hush_gtest_SOURCES = \
|
||||
gtest/utils.cpp \
|
||||
gtest/test_randomx_preverify.cpp \
|
||||
gtest/test_hdtransparent.cpp \
|
||||
gtest/test_mnemonic_compat.cpp
|
||||
gtest/test_mnemonic_compat.cpp \
|
||||
gtest/test_stratum_jobid.cpp \
|
||||
gtest/test_wallet_notedata.cpp \
|
||||
gtest/test_net_closesocket.cpp \
|
||||
gtest/test_fd_defense.cpp \
|
||||
gtest/test_wallet_dbenv.cpp
|
||||
|
||||
hush_gtest_CPPFLAGS = $(AM_CPPFLAGS) -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC $(BITCOIN_INCLUDES)
|
||||
hush_gtest_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
|
||||
+59
-81
@@ -473,11 +473,6 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
|
||||
if (size() == 0)
|
||||
return CAddrInfo();
|
||||
|
||||
// Track number of attempts to find a table entry, before giving up to avoid infinite loop
|
||||
const int kMaxRetries = 200000; // magic number so unit tests can pass
|
||||
const int kRetriesBetweenSleep = 1000;
|
||||
const int kRetrySleepInterval = 100; // milliseconds
|
||||
|
||||
if (newOnly && nNew == 0)
|
||||
return CAddrInfo();
|
||||
|
||||
@@ -485,89 +480,72 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
|
||||
if (!newOnly &&
|
||||
(nTried > 0 && (nNew == 0 || RandomInt(2) == 0))) {
|
||||
// use a tried node
|
||||
double fChanceFactor = 1.0;
|
||||
double fReachableFactor = 1.0;
|
||||
double fJustTried = 1.0;
|
||||
while (1) {
|
||||
if (ShutdownRequested()) //break loop on shutdown request
|
||||
return CAddrInfo();
|
||||
|
||||
int i = 0;
|
||||
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
|
||||
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
|
||||
while (vvTried[nKBucket][nKBucketPos] == -1) {
|
||||
nKBucket = (nKBucket + insecure_rand()) % ADDRMAN_TRIED_BUCKET_COUNT;
|
||||
nKBucketPos = (nKBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE;
|
||||
if (i++ > kMaxRetries)
|
||||
return CAddrInfo();
|
||||
if (i % kRetriesBetweenSleep == 0 && !nKey.IsNull())
|
||||
MilliSleep(kRetrySleepInterval);
|
||||
}
|
||||
int nId = vvTried[nKBucket][nKBucketPos];
|
||||
// assert(mapInfo.count(nId) == 1);
|
||||
if(mapInfo.count(nId) != 1) {
|
||||
fprintf(stderr,"%s: Could not find tried node with nId=%d=vvTried[%d][%d], mapInfo.count(%d)=%lu\n", __func__, nId, nKBucket, nKBucketPos, nId, mapInfo.count(nId) );
|
||||
continue;
|
||||
}
|
||||
|
||||
CAddrInfo& info = mapInfo[nId];
|
||||
if (info.IsReachableNetwork()) {
|
||||
//deprioritize unreachable networks
|
||||
fReachableFactor = 0.25;
|
||||
}
|
||||
if (info.IsJustTried()) {
|
||||
//deprioritize entries just tried
|
||||
fJustTried = 0.10;
|
||||
}
|
||||
if (RandomInt(1 << 30) < fChanceFactor * fReachableFactor * fJustTried * info.GetChance() * (1 << 30))
|
||||
return info;
|
||||
fChanceFactor *= 1.2;
|
||||
}
|
||||
return SelectFromTable_(vvTried, ADDRMAN_TRIED_BUCKET_COUNT, "tried");
|
||||
} else {
|
||||
// use a new node
|
||||
double fChanceFactor = 1.0;
|
||||
double fReachableFactor = 1.0;
|
||||
double fJustTried = 1.0;
|
||||
while (1) {
|
||||
if (ShutdownRequested()) //break loop on shutdown request
|
||||
return CAddrInfo();
|
||||
|
||||
int i = 0;
|
||||
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
|
||||
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
|
||||
while (vvNew[nUBucket][nUBucketPos] == -1) {
|
||||
nUBucket = (nUBucket + insecure_rand()) % ADDRMAN_NEW_BUCKET_COUNT;
|
||||
nUBucketPos = (nUBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE;
|
||||
if (i++ > kMaxRetries)
|
||||
return CAddrInfo();
|
||||
if (i % kRetriesBetweenSleep == 0 && !nKey.IsNull())
|
||||
MilliSleep(kRetrySleepInterval);
|
||||
}
|
||||
int nId = vvNew[nUBucket][nUBucketPos];
|
||||
|
||||
if(mapInfo.count(nId) != 1) {
|
||||
fprintf(stderr,"%s: Could not find new node with nId=%d=vvNew[%d][%d], mapInfo.count(%d)=%lu\n", __func__, nId, nUBucket, nUBucketPos, nId, mapInfo.count(nId) );
|
||||
continue;
|
||||
}
|
||||
// assert(mapInfo.count(nId) == 1);
|
||||
CAddrInfo& info = mapInfo[nId];
|
||||
if (info.IsReachableNetwork()) {
|
||||
//deprioritize unreachable networks
|
||||
fReachableFactor = 0.25;
|
||||
}
|
||||
if (info.IsJustTried()) {
|
||||
//deprioritize entries just tried
|
||||
fJustTried = 0.10;
|
||||
}
|
||||
if (RandomInt(1 << 30) < fChanceFactor * fReachableFactor * fJustTried * info.GetChance() * (1 << 30))
|
||||
return info;
|
||||
fChanceFactor *= 1.2;
|
||||
}
|
||||
return SelectFromTable_(vvNew, ADDRMAN_NEW_BUCKET_COUNT, "new");
|
||||
}
|
||||
|
||||
return CAddrInfo();
|
||||
}
|
||||
|
||||
// Random-walk one addrman bucket table (tried or new) and return an accepted peer,
|
||||
// applying the reachable/just-tried deprioritization and the growing chance factor.
|
||||
// Extracted verbatim from Select_'s two previously copy-pasted branches; the only
|
||||
// differences were the table (vvTried/vvNew), its bucket count, and the log label.
|
||||
CAddrInfo CAddrMan::SelectFromTable_(int (*vvTable)[ADDRMAN_BUCKET_SIZE], int nBucketCount, const char *tableName)
|
||||
{
|
||||
// Track number of attempts to find a table entry, before giving up to avoid infinite loop
|
||||
const int kMaxRetries = 200000; // magic number so unit tests can pass
|
||||
const int kRetriesBetweenSleep = 1000;
|
||||
const int kRetrySleepInterval = 100; // milliseconds
|
||||
|
||||
// Peer-selection tuning factors (networking heuristics, not consensus).
|
||||
const double kChanceFactorGrowth = 1.2;
|
||||
const double kUnreachableDeprioritize = 0.25;
|
||||
const double kJustTriedDeprioritize = 0.10;
|
||||
const int kChanceScale = 1 << 30;
|
||||
|
||||
double fChanceFactor = 1.0;
|
||||
double fReachableFactor = 1.0;
|
||||
double fJustTried = 1.0;
|
||||
while (1) {
|
||||
if (ShutdownRequested()) //break loop on shutdown request
|
||||
return CAddrInfo();
|
||||
|
||||
int i = 0;
|
||||
int nKBucket = RandomInt(nBucketCount);
|
||||
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
|
||||
while (vvTable[nKBucket][nKBucketPos] == -1) {
|
||||
nKBucket = (nKBucket + insecure_rand()) % nBucketCount;
|
||||
nKBucketPos = (nKBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE;
|
||||
if (i++ > kMaxRetries)
|
||||
return CAddrInfo();
|
||||
if (i % kRetriesBetweenSleep == 0 && !nKey.IsNull())
|
||||
MilliSleep(kRetrySleepInterval);
|
||||
}
|
||||
int nId = vvTable[nKBucket][nKBucketPos];
|
||||
// assert(mapInfo.count(nId) == 1);
|
||||
if(mapInfo.count(nId) != 1) {
|
||||
fprintf(stderr,"%s: Could not find %s node with nId=%d=vvTable[%d][%d], mapInfo.count(%d)=%lu\n", __func__, tableName, nId, nKBucket, nKBucketPos, nId, mapInfo.count(nId) );
|
||||
continue;
|
||||
}
|
||||
|
||||
CAddrInfo& info = mapInfo[nId];
|
||||
if (info.IsReachableNetwork()) {
|
||||
//deprioritize unreachable networks
|
||||
fReachableFactor = kUnreachableDeprioritize;
|
||||
}
|
||||
if (info.IsJustTried()) {
|
||||
//deprioritize entries just tried
|
||||
fJustTried = kJustTriedDeprioritize;
|
||||
}
|
||||
if (RandomInt(kChanceScale) < fChanceFactor * fReachableFactor * fJustTried * info.GetChance() * kChanceScale)
|
||||
return info;
|
||||
fChanceFactor *= kChanceFactorGrowth;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ADDRMAN
|
||||
int CAddrMan::Check_()
|
||||
{
|
||||
|
||||
@@ -300,6 +300,10 @@ protected:
|
||||
//! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
|
||||
CAddrInfo Select_(bool newOnly);
|
||||
|
||||
//! Random-walk one bucket table (tried or new) and return an accepted peer.
|
||||
//! Shared implementation for Select_'s two (previously copy-pasted) branches.
|
||||
CAddrInfo SelectFromTable_(int (*vvTable)[ADDRMAN_BUCKET_SIZE], int nBucketCount, const char *tableName);
|
||||
|
||||
//! Wraps GetRandInt to allow tests to override RandomInt and make it deterministic.
|
||||
virtual int RandomInt(int nMax);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include "asyncrpcoperation.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
@@ -58,6 +59,31 @@ AsyncRPCOperation::AsyncRPCOperation(const AsyncRPCOperation& o) :
|
||||
{
|
||||
}
|
||||
|
||||
// Shared error mapping for every async op's main(): rethrow the in-flight
|
||||
// exception and translate it to this operation's error code/message. Keeping
|
||||
// it here means the mapping is edited in one place, not copy-pasted into six.
|
||||
void AsyncRPCOperation::set_error_from_current_exception()
|
||||
{
|
||||
try {
|
||||
throw;
|
||||
} catch (const UniValue& objError) {
|
||||
set_error_code(find_value(objError, "code").get_int());
|
||||
set_error_message(find_value(objError, "message").get_str());
|
||||
} 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");
|
||||
}
|
||||
}
|
||||
|
||||
AsyncRPCOperation& AsyncRPCOperation::operator=( const AsyncRPCOperation& other ) {
|
||||
this->id_ = other.id_;
|
||||
this->creation_time_ = other.creation_time_;
|
||||
|
||||
@@ -147,6 +147,11 @@ protected:
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
this->error_message_ = errorMessage;
|
||||
}
|
||||
|
||||
// Map the in-flight (rethrown) exception to error_code_/error_message_. Called from
|
||||
// every async op's main() catch(...) so the UniValue/runtime/logic/exception mapping
|
||||
// lives in one place instead of being copy-pasted into all six operations.
|
||||
void set_error_from_current_exception();
|
||||
|
||||
void set_result(UniValue v) {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-1
@@ -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:
|
||||
|
||||
+6
-2
@@ -257,14 +257,18 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params)
|
||||
event_base_dispatch(base.get());
|
||||
|
||||
if (response.status == 0) {
|
||||
// Report the port we ACTUALLY dialled. ASSETCHAINS_RPCPORT is a separate global that is
|
||||
// initialised to the mainnet default at the top of this file and never assigned here, so
|
||||
// using it made every failure claim port 21769 no matter what -rpcport was given -- which
|
||||
// reads as "your -rpcport was ignored" and sends you chasing a config bug that isn't there.
|
||||
throw CConnectionFailed(strprintf("couldn't connect to server at port %d : %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)",
|
||||
ASSETCHAINS_RPCPORT, http_errorstring(response.error), response.error));
|
||||
port, http_errorstring(response.error), response.error));
|
||||
} else if (response.status == HTTP_UNAUTHORIZED) {
|
||||
throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
||||
} else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR) {
|
||||
throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
|
||||
} else if (response.body.empty()) {
|
||||
throw std::runtime_error(strprintf("no response from server at port %d", ASSETCHAINS_RPCPORT ));
|
||||
throw std::runtime_error(strprintf("no response from server at port %d", port));
|
||||
}
|
||||
|
||||
// Parse reply
|
||||
|
||||
+13
-8
@@ -41,7 +41,11 @@
|
||||
|
||||
/// \cond INTERNAL
|
||||
#define CC_MAXVINS 1024
|
||||
#define CC_REQUIREMENTS_MSG (HUSH_NSPV_SUPERLITE?"to use CC contracts you need to nspv_login first\n":"to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n")
|
||||
// NOTE: CryptoConditions (CC) contracts are inherited from the Komodo/Hush lineage and are
|
||||
// largely vestigial on DragonX (ac_private=1 fully-shielded chain). This user-facing message
|
||||
// still describes the legacy prerequisites for using CC contracts (nspv_login in superlite
|
||||
// mode, or launching dragonxd with a valid -pubkey=).
|
||||
#define CC_REQUIREMENTS_MSG (HUSH_NSPV_SUPERLITE?"to use CC contracts you need to nspv_login first\n":"to use CC contracts, you need to launch dragonxd with valid -pubkey= for an address in your wallet\n")
|
||||
|
||||
#define SMALLVAL 0.000000000000001
|
||||
#define SATOSHIDEN ((uint64_t)100000000L)
|
||||
@@ -58,7 +62,8 @@ struct CC_utxo
|
||||
/// \endcond
|
||||
|
||||
|
||||
/// CC contract (Antara module) info structure that contains data used for signing and validation of cc contract transactions
|
||||
/// CC (CryptoConditions) contract info structure that contains data used for signing and validation of cc contract transactions.
|
||||
/// NOTE: the CC framework (historically called "Antara modules" in the Komodo/Hush lineage) is largely vestigial on DragonX.
|
||||
struct CCcontract_info
|
||||
{
|
||||
uint8_t evalcode; //!< cc contract eval code, set by CCinit function
|
||||
@@ -101,7 +106,7 @@ struct CCcontract_info
|
||||
bool(*validate)(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn);
|
||||
|
||||
/// checks if the value of evalcode in cp object is present in the scriptSig parameter,
|
||||
/// that is, the vin for this scriptSig will be validated by the cc contract (Antara module) defined by the eval code in this CCcontract_info object
|
||||
/// that is, the vin for this scriptSig will be validated by the cc contract defined by the eval code in this CCcontract_info object
|
||||
/// @param scriptSig scriptSig to check\n
|
||||
/// Example:
|
||||
/// \code
|
||||
@@ -283,7 +288,7 @@ bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector<CPubKey> &vin
|
||||
/// cp = CCinit(&C, EVAL_ASSETS);
|
||||
/// CPubKey ccAssetsPk = GetUnspendable(cp, ccAssetsPriv);
|
||||
/// \endcode
|
||||
/// Now ccAssetsPk has Antara 'Assets' module global pubkey and ccAssetsPriv has its publicly available private key
|
||||
/// Now ccAssetsPk has the 'Assets' CC module global pubkey and ccAssetsPriv has its publicly available private key
|
||||
CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv);
|
||||
|
||||
// CCutils
|
||||
@@ -373,7 +378,7 @@ int64_t CCfullsupply(uint256 tokenid);
|
||||
/// @returns true if success
|
||||
bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey);
|
||||
|
||||
/// Returns my pubkey, that is set by -pubkey hushd parameter
|
||||
/// Returns my pubkey, that is set by the -pubkey dragonxd parameter
|
||||
/// @returns public key as byte array
|
||||
std::vector<uint8_t> Mypubkey();
|
||||
|
||||
@@ -404,8 +409,8 @@ extern std::vector<CPubKey> NULL_pubkeys; //!< constant value for use in functio
|
||||
std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret,std::vector<CPubKey> pubkeys = NULL_pubkeys);
|
||||
|
||||
/// FinalizeCCTx is a very useful function that will properly sign both CC and normal inputs, adds normal change and might add an opreturn output.
|
||||
/// This allows for Antara module transaction creation rpc functions to create an CMutableTransaction object, add the appropriate vins and vouts to it and use FinalizeCCTx to properly sign the transaction.
|
||||
/// By using -addressindex=1 of hushd daemon, it allows tracking of all the CC addresses.
|
||||
/// This allows for CC module transaction creation rpc functions to create an CMutableTransaction object, add the appropriate vins and vouts to it and use FinalizeCCTx to properly sign the transaction.
|
||||
/// By using -addressindex=1 of the dragonxd daemon, it allows tracking of all the CC addresses.
|
||||
///
|
||||
/// For signing the vins the function builds several default probe scriptPubKeys and checks them against the referred previous transactions (vintx) vouts.
|
||||
/// For cryptocondition vins the function creates a basic set of probe cryptconditions with mypk and module global pubkey, both for coins and tokens cases.
|
||||
@@ -473,7 +478,7 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3
|
||||
int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs);
|
||||
|
||||
/// AddNormalinputs2 adds normal (not cc) inputs to the transaction object vin array for the specified total amount using utxos on my pubkey's TX_PUBKEY address (my pubkey is set by -pubkey command line parameter), to fund the transaction.
|
||||
/// 'My pubkey' is the -pubkey parameter of hushd.
|
||||
/// 'My pubkey' is the -pubkey parameter of dragonxd.
|
||||
/// @param mtx mutable transaction object
|
||||
/// @param total amount of inputs to add. If total equals to 0 the function does not add inputs but returns amount of all available normal inputs in the wallet
|
||||
/// @param maxinputs maximum number of inputs to add
|
||||
|
||||
@@ -72,11 +72,6 @@ int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *
|
||||
abovei = belowi = -1;
|
||||
for (above=below=i=0; i<numunspents; i++)
|
||||
{
|
||||
// Filter to randomly pick utxo to avoid conflicts, and having multiple CC choose the same ones.
|
||||
//if ( numunspents > 200 ) {
|
||||
// if ( (rand() % 100) < 90 )
|
||||
// continue;
|
||||
//}
|
||||
if ( (atx_value= utxos[i].nValue) <= 0 )
|
||||
continue;
|
||||
if ( atx_value == value )
|
||||
@@ -103,13 +98,11 @@ int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *
|
||||
belowi = i;
|
||||
}
|
||||
}
|
||||
//printf("value %.8f gap %.8f abovei.%d %.8f belowi.%d %.8f\n",dstr(value),dstr(gap),abovei,dstr(above),belowi,dstr(below));
|
||||
}
|
||||
*aboveip = abovei;
|
||||
*abovep = above;
|
||||
*belowip = belowi;
|
||||
*belowp = below;
|
||||
//printf("above.%d below.%d\n",abovei,belowi);
|
||||
if ( abovei >= 0 && belowi >= 0 )
|
||||
{
|
||||
if ( above < (below >> 1) )
|
||||
@@ -127,8 +120,6 @@ int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total
|
||||
if ( HUSH_NSPV_SUPERLITE )
|
||||
return(NSPV_AddNormalinputs(mtx,mypk,total,maxinputs,&NSPV_U));
|
||||
|
||||
// if (mypk != pubkey2pk(Mypubkey())) //remote superlite mypk, do not use wallet since it is not locked for non-equal pks (see rpcs with nspv support)!
|
||||
// return(AddNormalinputs3(mtx, mypk, total, maxinputs));
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
assert(pwalletMain != NULL);
|
||||
@@ -150,7 +141,6 @@ int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total
|
||||
vout = out.i;
|
||||
if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
{
|
||||
//fprintf(stderr,"check %.8f to vins array.%d of %d %s/v%d\n",(double)out.tx->vout[out.i].nValue/COIN,n,maxutxos,txid.GetHex().c_str(),(int32_t)vout);
|
||||
if ( mtx.vin.size() > 0 )
|
||||
{
|
||||
for (i=0; i<mtx.vin.size(); i++)
|
||||
@@ -174,7 +164,6 @@ int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total
|
||||
up->nValue = out.tx->vout[out.i].nValue;
|
||||
up->vout = vout;
|
||||
sum += up->nValue;
|
||||
//fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos);
|
||||
if ( n >= maxinputs || sum >= total )
|
||||
break;
|
||||
}
|
||||
@@ -207,14 +196,12 @@ int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total
|
||||
remains -= up->nValue;
|
||||
utxos[ind] = utxos[--n];
|
||||
memset(&utxos[n],0,sizeof(utxos[n]));
|
||||
//fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs);
|
||||
if ( totalinputs >= total || (i+1) >= maxinputs )
|
||||
break;
|
||||
}
|
||||
free(utxos);
|
||||
if ( totalinputs >= total )
|
||||
{
|
||||
//fprintf(stderr,"return totalinputs %.8f\n",(double)totalinputs/COIN);
|
||||
return(totalinputs);
|
||||
}
|
||||
#endif
|
||||
@@ -252,7 +239,6 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to
|
||||
continue;
|
||||
if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
{
|
||||
//fprintf(stderr,"check %.8f to vins array.%d of %d %s/v%d\n",(double)out.tx->vout[out.i].nValue/COIN,n,maxutxos,txid.GetHex().c_str(),(int32_t)vout);
|
||||
if ( mtx.vin.size() > 0 )
|
||||
{
|
||||
for (i=0; i<mtx.vin.size(); i++)
|
||||
@@ -276,7 +262,6 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to
|
||||
up->nValue = it->second.satoshis;
|
||||
up->vout = vout;
|
||||
sum += up->nValue;
|
||||
//fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos);
|
||||
if ( n >= maxinputs || sum >= total )
|
||||
break;
|
||||
}
|
||||
@@ -308,14 +293,12 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to
|
||||
remains -= up->nValue;
|
||||
utxos[ind] = utxos[--n];
|
||||
memset(&utxos[n],0,sizeof(utxos[n]));
|
||||
//fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs);
|
||||
if ( totalinputs >= total || (i+1) >= maxinputs )
|
||||
break;
|
||||
}
|
||||
free(utxos);
|
||||
if ( totalinputs >= total )
|
||||
{
|
||||
//fprintf(stderr,"return totalinputs %.8f\n",(double)totalinputs/COIN);
|
||||
return(totalinputs);
|
||||
}
|
||||
return(0);
|
||||
|
||||
+9
-11
@@ -20,6 +20,7 @@
|
||||
#include "CCinclude.h"
|
||||
#include "hush_structs.h"
|
||||
#include "key_io.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef TESTMODE
|
||||
#define MIN_NON_NOTARIZED_CONFIRMS 2
|
||||
@@ -46,7 +47,6 @@ int32_t has_opret(const CTransaction &tx, uint8_t evalcode)
|
||||
int i = 0;
|
||||
for ( auto vout : tx.vout )
|
||||
{
|
||||
//fprintf(stderr, "[txid.%s] 1.%i 2.%i 3.%i 4.%i\n",tx.GetHash().GetHex().c_str(), vout.scriptPubKey[0], vout.scriptPubKey[1], vout.scriptPubKey[2], vout.scriptPubKey[3]);
|
||||
if ( vout.scriptPubKey.size() > 3 && vout.scriptPubKey[0] == OP_RETURN && vout.scriptPubKey[2] == evalcode )
|
||||
return i;
|
||||
i++;
|
||||
@@ -88,7 +88,6 @@ bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_
|
||||
actualtxfee = valuein-tx.GetValueOut();
|
||||
if ( actualtxfee > txfee )
|
||||
{
|
||||
//fprintf(stderr, "actualtxfee.%li vs txfee.%li\n", actualtxfee, txfee);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -112,7 +111,6 @@ bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey)
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
//fprintf(stderr,"ExtractDestination failed\n");
|
||||
return(false);
|
||||
}
|
||||
|
||||
@@ -202,17 +200,17 @@ bool hush_txnotarizedconfirmed(uint256 txid)
|
||||
{
|
||||
if ( NSPV_myGetTransaction(txid,tx,hashBlock,txheight,currentheight) == 0 )
|
||||
{
|
||||
fprintf(stderr,"hush_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str());
|
||||
LogPrintf("hush_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str());
|
||||
return(0);
|
||||
}
|
||||
else if (txheight<=0)
|
||||
{
|
||||
fprintf(stderr,"hush_txnotarizedconfirmed no txheight.%d for txid %s\n",txheight,txid.ToString().c_str());
|
||||
LogPrintf("hush_txnotarizedconfirmed no txheight.%d for txid %s\n",txheight,txid.ToString().c_str());
|
||||
return(0);
|
||||
}
|
||||
else if (txheight>currentheight)
|
||||
{
|
||||
fprintf(stderr,"hush_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,currentheight);
|
||||
LogPrintf("hush_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,currentheight);
|
||||
return(0);
|
||||
}
|
||||
confirms=1 + currentheight - txheight;
|
||||
@@ -221,24 +219,24 @@ bool hush_txnotarizedconfirmed(uint256 txid)
|
||||
{
|
||||
if ( myGetTransaction(txid,tx,hashBlock) == 0 )
|
||||
{
|
||||
fprintf(stderr,"hush_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str());
|
||||
LogPrintf("hush_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str());
|
||||
return(0);
|
||||
}
|
||||
else if ( hashBlock == zeroid )
|
||||
{
|
||||
fprintf(stderr,"hush_txnotarizedconfirmed no hashBlock for txid %s\n",txid.ToString().c_str());
|
||||
LogPrintf("hush_txnotarizedconfirmed no hashBlock for txid %s\n",txid.ToString().c_str());
|
||||
return(0);
|
||||
}
|
||||
else if ( (pindex= hush_blockindex(hashBlock)) == 0 || (txheight= pindex->GetHeight()) <= 0 )
|
||||
{
|
||||
fprintf(stderr,"hush_txnotarizedconfirmed no txheight.%d %p for txid %s\n",txheight,pindex,txid.ToString().c_str());
|
||||
LogPrintf("hush_txnotarizedconfirmed no txheight.%d %p for txid %s\n",txheight,pindex,txid.ToString().c_str());
|
||||
return(0);
|
||||
}
|
||||
else if ( (pindex= chainActive.LastTip()) == 0 || pindex->GetHeight() < txheight )
|
||||
{
|
||||
fprintf(stderr,"hush_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,(int32_t)pindex->GetHeight());
|
||||
LogPrintf("hush_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,(int32_t)pindex->GetHeight());
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
confirms=1 + pindex->GetHeight() - txheight;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "main.h"
|
||||
#include "chain.h"
|
||||
#include "core_io.h"
|
||||
#define FAUCET2SIZE COIN
|
||||
#define EVAL_FAUCET2 EVAL_FIRSTUSER
|
||||
|
||||
#ifdef BUILD_CUSTOMCC
|
||||
|
||||
Binary file not shown.
@@ -1,20 +0,0 @@
|
||||
# Copyright (c) 2016-2024 The Hush Developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
# Just type make to compile all dat dapp code, fellow cypherpunkz
|
||||
|
||||
# we no longer build zmigrate by default, nobody uses that fucking code
|
||||
all: hushdex
|
||||
|
||||
hushdex:
|
||||
$(CC) hushdex.c -o hushdex -lm
|
||||
|
||||
# Just for historical knowledge, to study how fucking stupid
|
||||
# ZEC+KMD were to still support sprout, to this day!!!!!!!!
|
||||
# Hush leads the entire world into the future, sans Sprout turdz
|
||||
zmigrate:
|
||||
$(CC) zmigrate.c -o zmigrate -lm
|
||||
|
||||
clean:
|
||||
rm zmigrate
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
# CryptoCondition dApps
|
||||
|
||||
## Compiling
|
||||
|
||||
To compile all dapps in this directory:
|
||||
|
||||
make
|
||||
|
||||
## zmigrate - Sprout to Sapling Migration dApp
|
||||
|
||||
This tool converts Sprout zaddress funds into Sapling funds in a new Sapling address.
|
||||
This is not applicable to HUSH3, since we have no Sprout funds, but left for historical
|
||||
purposes.
|
||||
|
||||
### Usage
|
||||
|
||||
./zmigrate COIN zsaplingaddr
|
||||
|
||||
The above command may need to be run multiple times to complete the process.
|
||||
|
||||
This CLI implementation will be called by GUI wallets, average users do not
|
||||
need to worry about using this low-level tool.
|
||||
|
||||
## HushDEX
|
||||
|
||||
HushDEX forked from the Subatomic Decentralized App (dapp) and we focus purely
|
||||
on privacy coin swaps, and specifically, shielded swaps between Zcash Protocol
|
||||
coins. These are called z-swaps.
|
||||
|
||||
### Z-swap example
|
||||
|
||||
Alice has 1 ZEC and wants to trade it for 5 HUSH, since she hears HushChat is
|
||||
pretty awesome and ZEC just goes down in price, always. We represent this in
|
||||
a diagram like this
|
||||
|
||||
Alice (ZEC) <> Bob (HUSH)
|
||||
|
||||
HushDEX is only concerns with Sapling shielded addresses (zaddrs) which start
|
||||
with `zs1`. Even though ZEC supports Sprout addresses (which start with `zc`),
|
||||
they cannot be used on HushDEX. Sprout is unsupported on HushDEX.
|
||||
|
||||
So Alice must make sure her ZEC is in a Sapling zaddr, and then she can use
|
||||
HushDEX on her computer, to z-swap with Bob, in a decentralized way, with
|
||||
no centralized service. The system is not completely trustless, users must
|
||||
trust the developers and miners on the relevant chains to not do nefarious
|
||||
things. There is no central authority to decide who gets to do what, it's
|
||||
peer-to-peer like BitTorrent or Tor.
|
||||
|
||||
### Privacy Features of Z-Swaps
|
||||
|
||||
* No KYC
|
||||
* We will not feed the identity theft industry any more free data
|
||||
* No IP address limiting
|
||||
* It is trivial to pay for an IP address from any country in the world
|
||||
* Alice's address never appears in public data
|
||||
* Bob's address never appears in public data
|
||||
* Consequently, Alice and Bob's address cannot be searched for on an explorer
|
||||
* Since you can't see the address of any transaction, you cannot infer if
|
||||
the same address appears as sender or receiver in many transactions.
|
||||
* The amount of the transaction, how much ZEC and how much HUSH, is unknown
|
||||
* It could be pennies or millions
|
||||
* The exchange rate of the transaction never appears on the blockchain
|
||||
* The exchange rate will be leaked to the network p2p layer, but it is never
|
||||
recorded in blockchain history. If you are not there to record it, it is gone.
|
||||
* Realistically, it's simple to run a malicious node which records all exchange rates
|
||||
and so we assume an adversary does this
|
||||
* Since the exchange rate of ZEC/HUSH is already public data, this is not considered valuable
|
||||
information leakage. We are leaking the differential of CEX ZEC/HUSH exchange ratio to
|
||||
this DEX's ratio.
|
||||
* Adversaries watching all possible public data can infer exchange ratios but no amounts
|
||||
or addresses, which is considered a massive blow against blockchain analysis.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"authorized": [
|
||||
{"dukeleto":"030554bffcf6dfcb34a20c486ff0a5be5546b9cc16fba969216527263f8e98c4af" },
|
||||
{"gilardh":"020554bffcf6dfcb34a20c486ff5a5be5546b9cc06fba9692165272b3f8e98c448" },
|
||||
{"nhdigitalcash":"030554bffcf6dfcb34a20c086ff5a5be5546b9cc16fba9692105272b3f8e98c4a0" },
|
||||
{"miodrag":"02b25de3ee5335518b06f69f4fbabb029cfc737603b100996841d5532b324a5a61" }
|
||||
],
|
||||
"tokens":[
|
||||
],
|
||||
"files":[
|
||||
{"filename":"hushd","prices":[{"HUSH":0.1}, {"ZEC":1}]}
|
||||
],
|
||||
"externalcoins":[
|
||||
{ "BTC":"bitcoin-cli" },
|
||||
{ "HUSH":"hush-cli" },
|
||||
{ "ZEC":"zcash-cli" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
gcc -o oraclefeed cc/dapps/oraclefeed.c -lm
|
||||
gcc -o zmigrate cc/dapps/zmigrate.c -lm
|
||||
File diff suppressed because it is too large
Load Diff
@@ -80,21 +80,6 @@ public:
|
||||
bool Error(std::string s) { return state.Error(s); }
|
||||
bool Valid() { return true; }
|
||||
|
||||
/*
|
||||
* Dispute a payout using a VM
|
||||
*/
|
||||
bool DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn);
|
||||
|
||||
/*
|
||||
* Test an ImportPayout CC Eval condition
|
||||
*/
|
||||
bool ImportPayout(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
|
||||
|
||||
/*
|
||||
* Import coin from another chain with same symbol
|
||||
*/
|
||||
bool ImportCoin(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
|
||||
|
||||
/*
|
||||
* IO functions
|
||||
*/
|
||||
@@ -281,7 +266,6 @@ typedef std::pair<uint256,MerkleBranch> TxProof;
|
||||
|
||||
uint256 GetMerkleRoot(const std::vector<uint256>& vLeaves);
|
||||
struct CCcontract_info *CCinit(struct CCcontract_info *cp,uint8_t evalcode);
|
||||
bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector<uint8_t> paramsNull, const CTransaction &tx, unsigned int nIn);
|
||||
|
||||
|
||||
#endif /* CC_EVAL_H */
|
||||
|
||||
+24
-5
@@ -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 1030050 (v1.3.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
|
||||
@@ -113,10 +113,9 @@ enum BlockStatus: uint32_t {
|
||||
//! Scripts & signatures ok. Implies all parents are also at least SCRIPTS.
|
||||
BLOCK_VALID_SCRIPTS = 5,
|
||||
|
||||
// flag to check if contextual check block has passed in Accept block, if it has not check at connect block.
|
||||
BLOCK_VALID_CONTEXT = 6,
|
||||
|
||||
//! All validity bits.
|
||||
//! NOTE: the levels above are sequential VALUES occupying this 3-bit field, not independent
|
||||
//! bits, so any flag stored in nStatus must live entirely outside this mask.
|
||||
BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS |
|
||||
BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS,
|
||||
|
||||
@@ -129,9 +128,29 @@ enum BlockStatus: uint32_t {
|
||||
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
|
||||
|
||||
BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade
|
||||
BLOCK_IN_TMPFILE = 256
|
||||
BLOCK_IN_TMPFILE = 256,
|
||||
|
||||
//! ContextualCheckBlock already passed in AcceptBlock, so ConnectBlock may skip re-running it.
|
||||
//! Was 6 until v1.3.0, which put it INSIDE BLOCK_VALID_MASK (1|2|3|4|5 == 7): `nStatus |=
|
||||
//! BLOCK_VALID_CONTEXT` then overwrote the validity level rather than setting a flag, so a
|
||||
//! block that was only written to disk read back as BLOCK_VALID_SCRIPTS and every later
|
||||
//! RaiseValidity() silently no-opped. Detected by CheckBlockIndex's "CHAIN valid implies all
|
||||
//! parents are CHAIN valid" assert, which aborts any node doing out-of-order block download
|
||||
//! (regtest only, where fDefaultConsistencyChecks is true). Validity was only ever inflated,
|
||||
//! never deflated, so ConnectBlock's full validation was never skipped -- see git history.
|
||||
//! Legacy block indexes still carry the polluted low bits; they resolve on reindex, and until
|
||||
//! then simply re-run the contextual check they used to skip.
|
||||
BLOCK_VALID_CONTEXT = 512
|
||||
};
|
||||
|
||||
//! The validity level is a small integer packed into BLOCK_VALID_MASK, so every other nStatus flag
|
||||
//! must be disjoint from it. Enforced here so this class of bug cannot be reintroduced silently.
|
||||
static_assert((BLOCK_VALID_CONTEXT & BLOCK_VALID_MASK) == 0, "BLOCK_VALID_CONTEXT overlaps the validity-level field");
|
||||
static_assert((BLOCK_HAVE_MASK & BLOCK_VALID_MASK) == 0, "BLOCK_HAVE_MASK overlaps the validity-level field");
|
||||
static_assert((BLOCK_FAILED_MASK & BLOCK_VALID_MASK) == 0, "BLOCK_FAILED_MASK overlaps the validity-level field");
|
||||
static_assert((BLOCK_ACTIVATES_UPGRADE & BLOCK_VALID_MASK) == 0, "BLOCK_ACTIVATES_UPGRADE overlaps the validity-level field");
|
||||
static_assert((BLOCK_IN_TMPFILE & BLOCK_VALID_MASK) == 0, "BLOCK_IN_TMPFILE overlaps the validity-level field");
|
||||
|
||||
//! Short-hand for the highest consensus validity we implement.
|
||||
//! Blocks with this validity are assumed to satisfy all consensus rules.
|
||||
static const BlockStatus BLOCK_VALID_CONSENSUS = BLOCK_VALID_SCRIPTS;
|
||||
|
||||
+440
-12
@@ -148,6 +148,12 @@ public:
|
||||
nMinerThreads = 0;
|
||||
nMaxTipAge = 24 * 60 * 60;
|
||||
nPruneAfterHeight = 100000;
|
||||
// NOTE: These Equihash parameters and the literal Bitcoin genesis block below are
|
||||
// inherited from the upstream (Zcash/Komodo) CMainParams and are NOT what DragonX
|
||||
// mines under. DragonX is a RandomX CPU-mining chain whose real PoW and chain
|
||||
// parameters are set for its SMART_CHAIN_SYMBOL at runtime (see hush_utils.h and
|
||||
// chainparams_commandline()). They are retained here for upstream-diff hygiene and
|
||||
// genesis fixity; do not "fix" them to RandomX values.
|
||||
const size_t N = 200, K = 9;
|
||||
BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K));
|
||||
nEquihashN = N;
|
||||
@@ -181,11 +187,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);
|
||||
@@ -515,15 +539,27 @@ void hush_setactivation(int32_t height)
|
||||
|
||||
void *chainparams_commandline() {
|
||||
CChainParams::CCheckpointData checkpointData;
|
||||
//if(fDebug) {
|
||||
fprintf(stderr,"chainparams_commandline called with port=%u\n", ASSETCHAINS_P2PPORT);
|
||||
//}
|
||||
LogPrint("net", "chainparams_commandline called with port=%u\n", ASSETCHAINS_P2PPORT);
|
||||
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 (strcmp(SMART_CHAIN_SYMBOL, "DRAGONX") != 0) {
|
||||
pCurrentParams->ClearSeeds();
|
||||
}
|
||||
|
||||
if ( ASSETCHAINS_BLOCKTIME != 60 )
|
||||
{
|
||||
pCurrentParams->consensus.nMaxFutureBlockTime = 7 * ASSETCHAINS_BLOCKTIME; // 7 blocks
|
||||
@@ -547,7 +583,7 @@ void *chainparams_commandline() {
|
||||
pCurrentParams->pchMessageStart[1] = (ASSETCHAINS_MAGIC >> 8) & 0xff;
|
||||
pCurrentParams->pchMessageStart[2] = (ASSETCHAINS_MAGIC >> 16) & 0xff;
|
||||
pCurrentParams->pchMessageStart[3] = (ASSETCHAINS_MAGIC >> 24) & 0xff;
|
||||
fprintf(stderr,">>>>>>>>>> %s: p2p.%u rpc.%u magic.%08x %u %u coins\n",SMART_CHAIN_SYMBOL,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_MAGIC,ASSETCHAINS_MAGIC,(uint32_t)ASSETCHAINS_SUPPLY);
|
||||
LogPrintf("%s: p2p port %u, rpc port %u, magic %08x, supply %u coins\n",SMART_CHAIN_SYMBOL,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_MAGIC,(uint32_t)ASSETCHAINS_SUPPLY);
|
||||
|
||||
pCurrentParams->consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = ASSETCHAINS_SAPLING;
|
||||
pCurrentParams->consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = ASSETCHAINS_OVERWINTER;
|
||||
@@ -5640,9 +5676,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)
|
||||
|
||||
+2
-2
@@ -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 3
|
||||
#define CLIENT_VERSION_REVISION 0
|
||||
#define CLIENT_VERSION_BUILD 50
|
||||
|
||||
//! Set to true for release, false for prerelease or test build
|
||||
|
||||
@@ -214,19 +214,6 @@ void CCoinsViewCache::AbstractPushAnchor(
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: delete
|
||||
/*
|
||||
template<> void CCoinsViewCache::PushAnchor(const SproutMerkleTree &tree)
|
||||
{
|
||||
AbstractPushAnchor<SproutMerkleTree, CAnchorsSproutMap, CAnchorsSproutMap::iterator, CAnchorsSproutCacheEntry>(
|
||||
tree,
|
||||
SPROUT,
|
||||
cacheSproutAnchors,
|
||||
hashSproutAnchor
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
template<> void CCoinsViewCache::PushAnchor(const SaplingMerkleTree &tree)
|
||||
{
|
||||
AbstractPushAnchor<SaplingMerkleTree, CAnchorsSaplingMap, CAnchorsSaplingMap::iterator, CAnchorsSaplingCacheEntry>(
|
||||
@@ -406,8 +393,6 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
|
||||
|
||||
void BatchWriteNullifiers(CNullifiersMap &mapNullifiers, CNullifiersMap &cacheNullifiers)
|
||||
{
|
||||
//if(fZdebug)
|
||||
// LogPrintf("%s\n", __FUNCTION__);
|
||||
for (CNullifiersMap::iterator child_it = mapNullifiers.begin(); child_it != mapNullifiers.end();) {
|
||||
if (child_it->second.flags & CNullifiersCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
CNullifiersMap::iterator parent_it = cacheNullifiers.find(child_it->first);
|
||||
@@ -531,10 +516,7 @@ unsigned int CCoinsViewCache::GetCacheSize() const {
|
||||
const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const
|
||||
{
|
||||
const CCoins* coins = AccessCoins(input.prevout.hash);
|
||||
//fprintf(stderr, "GetOutputFor: input=%s", input.ToString().c_str());
|
||||
//fprintf(stderr, "GetOutputFor: prevout n=%d,txid=%s\n", input.prevout.n, input.prevout.hash.ToString().c_str());
|
||||
assert(coins && coins->IsAvailable(input.prevout.n));
|
||||
//fprintf(stderr, "GetOutputFor: IsAvailable\n");
|
||||
return coins->vout[input.prevout.n];
|
||||
}
|
||||
|
||||
@@ -596,7 +578,6 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
|
||||
const COutPoint &prevout = tx.vin[i].prevout;
|
||||
const CCoins* coins = AccessCoins(prevout.hash);
|
||||
if (!coins || !coins->IsAvailable(prevout.n)) {
|
||||
//fprintf(stderr,"HaveInputs missing input %s/v%d\n",prevout.hash.ToString().c_str(),prevout.n);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,10 +63,6 @@ UpgradeState NetworkUpgradeState(
|
||||
const Consensus::Params& params,
|
||||
Consensus::UpgradeIndex idx)
|
||||
{
|
||||
if (nHeight < 0)
|
||||
{
|
||||
printf("height: %d", nHeight);
|
||||
}
|
||||
assert(nHeight >= 0);
|
||||
assert(idx >= Consensus::BASE_SPROUT && idx < Consensus::MAX_NETWORK_UPGRADES);
|
||||
auto nActivationHeight = params.vUpgrades[idx].nActivationHeight;
|
||||
|
||||
+3
-1
@@ -41,7 +41,9 @@
|
||||
// XXX: There are potential crashes wherever we access chainActive without a lock,
|
||||
// because it might be disconnecting blocks at the same time.
|
||||
// TODO: this assumes a blocktime of 75 seconds for HUSH and 60 seconds for other chains
|
||||
int NOTARISATION_SCAN_LIMIT_BLOCKS = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? 1152 : 1440;
|
||||
// DragonX: the HUSH3 (1152) branch is dead — SMART_CHAIN_SYMBOL is always "DRAGONX", and at
|
||||
// static-init time it is empty, so this already always resolved to 1440. Pinned to 1440.
|
||||
int NOTARISATION_SCAN_LIMIT_BLOCKS = 1440;
|
||||
CBlockIndex *hush_getblockindex(uint256 hash);
|
||||
|
||||
/* On HUSH */
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2016-2024 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <errno.h>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include "chainparams.h"
|
||||
#include "init.h"
|
||||
#include "main.h"
|
||||
#include "noui.h"
|
||||
#include "util.h"
|
||||
#include "validationinterface.h"
|
||||
|
||||
extern std::atomic<bool> fRequestShutdown;
|
||||
|
||||
// A block file opened for reading must not be writable through its fd: a stray write through a reused fd number
|
||||
// (the wolfSSL CloseSocket macro let the wallet's BDB fd be closed behind its back) used to land in it.
|
||||
TEST(FdDefense, ReadOnlyBlockFileIsNotWritable) {
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
const boost::filesystem::path dir = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("fddefense-%%%%%%%%");
|
||||
const bool hadDatadir = mapArgs.count("-datadir") > 0;
|
||||
const std::string oldDatadir = hadDatadir ? mapArgs["-datadir"] : "";
|
||||
boost::filesystem::create_directories(dir);
|
||||
mapArgs["-datadir"] = dir.string();
|
||||
ClearDatadirCache();
|
||||
|
||||
const CDiskBlockPos pos(0, 0);
|
||||
const boost::filesystem::path blk = GetBlockPosFilename(pos, "blk");
|
||||
boost::filesystem::create_directories(blk.parent_path());
|
||||
{
|
||||
FILE* f = fopen(blk.string().c_str(), "wb");
|
||||
ASSERT_TRUE(f != nullptr);
|
||||
fputs("block bytes", f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
FILE* ro = OpenBlockFile(pos, true);
|
||||
ASSERT_TRUE(ro != nullptr);
|
||||
char c = 0;
|
||||
EXPECT_EQ(1u, fread(&c, 1, 1, ro)) << "still readable";
|
||||
errno = 0;
|
||||
EXPECT_EQ(-1, pwrite(fileno(ro), "X", 1, 0)) << "a write through a read-only block file's fd must fail";
|
||||
EXPECT_EQ(EBADF, errno);
|
||||
fclose(ro);
|
||||
|
||||
FILE* rw = OpenBlockFile(pos, false);
|
||||
ASSERT_TRUE(rw != nullptr);
|
||||
EXPECT_EQ(1, pwrite(fileno(rw), "b", 1, 0)) << "the writable open still writes";
|
||||
fclose(rw);
|
||||
|
||||
if (hadDatadir) mapArgs["-datadir"] = oldDatadir; else mapArgs.erase("-datadir");
|
||||
ClearDatadirCache();
|
||||
boost::filesystem::remove_all(dir);
|
||||
}
|
||||
|
||||
// A wallet notification that throws (a wallet DB failure) requests a clean shutdown instead of escaping the
|
||||
// notifier thread into std::terminate; a successful one doesn't; thread interruption still propagates.
|
||||
TEST(FdDefense, WalletNotifyFailureShutsDownCleanly) {
|
||||
noui_connect(); // the daemon connects these UI signals at startup; the error path shows a message box
|
||||
fRequestShutdown = false;
|
||||
EXPECT_TRUE(NotifyWalletsOrShutdown([] {}));
|
||||
EXPECT_FALSE(ShutdownRequested());
|
||||
|
||||
EXPECT_FALSE(NotifyWalletsOrShutdown([] { throw std::runtime_error("CWalletDB::ListAccountCreditDebit(): error scanning DB"); }));
|
||||
EXPECT_TRUE(ShutdownRequested());
|
||||
fRequestShutdown = false;
|
||||
|
||||
EXPECT_FALSE(NotifyWalletsOrShutdown([] { throw 42; }));
|
||||
EXPECT_TRUE(ShutdownRequested());
|
||||
fRequestShutdown = false;
|
||||
|
||||
EXPECT_THROW(NotifyWalletsOrShutdown([] { throw boost::thread_interrupted(); }), boost::thread_interrupted);
|
||||
EXPECT_FALSE(ShutdownRequested());
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2016-2024 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "net.h"
|
||||
#include "util/sock.h"
|
||||
|
||||
// Regression test for the wolfSSL CloseSocket macro (see net.h): closing a CNode's socket must invalidate
|
||||
// hSocket, so neither a repeated CloseSocketDisconnect nor ~CNode can close that fd number again once another
|
||||
// owner (the wallet's BDB files, a block file, an RPC socket) has been handed it.
|
||||
TEST(NetCloseSocket, ClosedNodeNeverClosesAReusedFdNumber) {
|
||||
int pair[2];
|
||||
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
|
||||
const int number = pair[0];
|
||||
|
||||
CNode* node = new CNode(number, CAddress(), "", true); // inbound: sends no version message
|
||||
node->CloseSocketDisconnect();
|
||||
EXPECT_EQ(INVALID_SOCKET, node->hSocket) << "CloseSocketDisconnect left the closed fd number in hSocket";
|
||||
EXPECT_EQ(-1, fcntl(number, F_GETFD)) << "the node's socket wasn't closed";
|
||||
|
||||
// Another owner gets the freed number, as the kernel's lowest-free-fd rule does in a busy node.
|
||||
const int other = open("/dev/null", O_RDONLY);
|
||||
ASSERT_GE(other, 0);
|
||||
ASSERT_EQ(number, dup2(other, number));
|
||||
|
||||
node->CloseSocketDisconnect(); // e.g. a send error after the disconnect was already handled
|
||||
delete node; // ~CNode
|
||||
EXPECT_NE(-1, fcntl(number, F_GETFD)) << "a closed CNode closed the fd number's new owner";
|
||||
|
||||
close(number);
|
||||
close(other);
|
||||
close(pair[1]);
|
||||
}
|
||||
|
||||
// util/sock.cpp's CloseSocket is the one that must be called: it closes once and invalidates.
|
||||
TEST(NetCloseSocket, CloseSocketInvalidates) {
|
||||
int pair[2];
|
||||
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
|
||||
SOCKET s = pair[0];
|
||||
EXPECT_TRUE(CloseSocket(s));
|
||||
EXPECT_EQ(INVALID_SOCKET, s);
|
||||
EXPECT_FALSE(CloseSocket(s)) << "a second close of an invalidated handle must be a no-op";
|
||||
close(pair[1]);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) 2016-2026 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
//
|
||||
// Regression coverage for the b3e81f1ed fix:
|
||||
// "stratum: do not abort the daemon on a malformed 63-character job_id"
|
||||
//
|
||||
// The EWBF "31 bytes job_id" path in stratum_mining_submit() completes a
|
||||
// 63-character job_id with each hex digit in turn and feeds the result to
|
||||
// uint256(). ParseHex() stops at the first non-hex character and returns a
|
||||
// SHORT vector without signalling an error, and base_blob's vector ctor
|
||||
// asserts vch.size() == 32. asserts are live in release builds here, and the
|
||||
// job_id arrives from an unauthenticated client -- so a single mining.submit
|
||||
// whose 63-character job_id contains any non-hex byte (63 spaces will do)
|
||||
// used to abort the node.
|
||||
//
|
||||
// The fix size-checks each candidate before constructing uint256. These tests
|
||||
// pin the exact invariant that guard relies on, using the real ParseHex and
|
||||
// uint256 primitives, without ever constructing a uint256 from a short vector
|
||||
// (which would still abort under the guard we are protecting).
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "uint256.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string HEXDIGITS = "0123456789abcdef";
|
||||
|
||||
// Mirrors the guarded completion loop in stratum_mining_submit(): try every
|
||||
// single-hex-digit completion and report whether ANY of them parses to a
|
||||
// whole 32-byte value. Only then is uint256() construction reached.
|
||||
bool AnyCompletionParsesTo32(const std::string& jobid) {
|
||||
for (char d : HEXDIGITS) {
|
||||
std::vector<unsigned char> vch = ParseHex(jobid + d);
|
||||
if (vch.size() == 32) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// The exact example from the fix commit: a 63-character job_id of spaces.
|
||||
// No completion may reach a 32-byte vector, so the daemon never constructs
|
||||
// uint256() and never aborts.
|
||||
TEST(StratumJobId, AllSpacesNeverYields32Bytes) {
|
||||
std::string spaces(63, ' ');
|
||||
ASSERT_EQ(spaces.size(), 63u);
|
||||
for (char d : HEXDIGITS) {
|
||||
EXPECT_NE(ParseHex(spaces + d).size(), 32u)
|
||||
<< "completion '" << d << "' unexpectedly produced 32 bytes";
|
||||
}
|
||||
EXPECT_FALSE(AnyCompletionParsesTo32(spaces));
|
||||
}
|
||||
|
||||
// A single non-hex byte embedded in an otherwise-hex 63-char job_id is enough:
|
||||
// ParseHex stops at it, so every completion is short.
|
||||
TEST(StratumJobId, SingleNonHexByteInMiddleIsRejected) {
|
||||
std::string jobid(63, 'a');
|
||||
jobid[30] = 'g'; // 'g' is not a hex digit
|
||||
ASSERT_EQ(jobid.size(), 63u);
|
||||
EXPECT_FALSE(AnyCompletionParsesTo32(jobid));
|
||||
}
|
||||
|
||||
// A non-hex byte at the very end (position 62) is likewise rejected: the last
|
||||
// hex pair can never complete to a whole byte.
|
||||
TEST(StratumJobId, NonHexByteAtEndIsRejected) {
|
||||
std::string jobid(62, 'a');
|
||||
jobid.push_back('z'); // length 63, last char non-hex
|
||||
ASSERT_EQ(jobid.size(), 63u);
|
||||
EXPECT_FALSE(AnyCompletionParsesTo32(jobid));
|
||||
}
|
||||
|
||||
// A genuine truncated EWBF job_id -- 63 real hex characters -- must complete
|
||||
// to exactly 32 bytes for every digit, so uint256() construction is safe.
|
||||
TEST(StratumJobId, ValidSixtyThreeHexCompletesToExactly32Bytes) {
|
||||
std::string jobid(63, 'a');
|
||||
ASSERT_EQ(jobid.size(), 63u);
|
||||
for (char d : HEXDIGITS) {
|
||||
std::vector<unsigned char> vch = ParseHex(jobid + d);
|
||||
ASSERT_EQ(vch.size(), 32u) << "completion '" << d << "'";
|
||||
// 32-byte vector: construction must not trip the size assertion.
|
||||
uint256 h(vch);
|
||||
EXPECT_EQ(h.size(), 32u);
|
||||
}
|
||||
EXPECT_TRUE(AnyCompletionParsesTo32(jobid));
|
||||
}
|
||||
|
||||
// Sanity anchors for ParseHex's odd/even handling that the loop depends on:
|
||||
// an odd hex length drops the trailing nibble (63 hex -> 31 bytes), and one
|
||||
// more hex char fills the 32nd byte (64 hex -> 32 bytes).
|
||||
TEST(StratumJobId, ParseHexOddLengthDropsTrailingNibble) {
|
||||
EXPECT_EQ(ParseHex(std::string(63, 'a')).size(), 31u);
|
||||
EXPECT_EQ(ParseHex(std::string(64, 'a')).size(), 32u);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2016-2024 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "wallet/db.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// A fresh environment in a temp dir with one committed record in "wallet.dat", so database/ holds a log file.
|
||||
class WalletDbEnv : public ::testing::Test {
|
||||
protected:
|
||||
boost::filesystem::path dir;
|
||||
CDBEnv env;
|
||||
|
||||
void SetUp() override {
|
||||
dir = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("dbenv-%%%%%%%%");
|
||||
boost::filesystem::create_directories(dir);
|
||||
ASSERT_TRUE(env.Open(dir));
|
||||
Db db(env.dbenv, DB_CXX_NO_EXCEPTIONS);
|
||||
ASSERT_EQ(0, db.open(NULL, "wallet.dat", "main", DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, 0));
|
||||
char k[] = "key", v[] = "value";
|
||||
Dbt key(k, sizeof k), value(v, sizeof v);
|
||||
ASSERT_EQ(0, db.put(NULL, &key, &value, 0));
|
||||
ASSERT_EQ(0, db.close(0));
|
||||
ASSERT_TRUE(boost::filesystem::exists(Log()));
|
||||
}
|
||||
void TearDown() override {
|
||||
env.Close();
|
||||
boost::system::error_code ec;
|
||||
boost::filesystem::remove_all(dir, ec);
|
||||
}
|
||||
boost::filesystem::path Log() const { return dir / "database" / "log.0000000001"; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(WalletDbEnv, CleanShutdownRemovesTheLogs) {
|
||||
env.Flush(true);
|
||||
EXPECT_FALSE(boost::filesystem::exists(dir / "database"));
|
||||
}
|
||||
|
||||
// BDB0060 PANIC (a page write failed, e.g. through an fd closed behind BDB's back): the logs may hold committed
|
||||
// changes wallet.dat never got, and the next start's DB_RECOVER needs them.
|
||||
TEST_F(WalletDbEnv, PanickedEnvironmentKeepsTheLogs) {
|
||||
ASSERT_EQ(0, env.dbenv->set_flags(DB_PANIC_ENVIRONMENT, 1));
|
||||
env.Flush(true);
|
||||
EXPECT_TRUE(boost::filesystem::exists(Log()));
|
||||
}
|
||||
|
||||
TEST_F(WalletDbEnv, FailedCheckpointKeepsTheLogs) {
|
||||
env.CheckpointLSN("no-such-file.dat"); // lsn_reset fails
|
||||
env.Flush(true);
|
||||
EXPECT_TRUE(boost::filesystem::exists(Log()));
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2016-2024 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "primitives/transaction.h"
|
||||
#include "uint256.h"
|
||||
#include "wallet/wallet.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class NoteDataWallet : public CWallet {
|
||||
public:
|
||||
bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx, bool fSameBlock = true) {
|
||||
return CWallet::UpdatedNoteData(wtxIn, wtx, fSameBlock);
|
||||
}
|
||||
};
|
||||
|
||||
libzcash::SaplingIncomingViewingKey TestIvk()
|
||||
{
|
||||
return libzcash::SaplingIncomingViewingKey(uint256S("0101010101010101010101010101010101010101010101010101010101010101"));
|
||||
}
|
||||
|
||||
// A note as the wallet holds it once witnessed: nullifier set, witnesses cached at a height.
|
||||
SaplingNoteData WitnessedNote(const SaplingMerkleTree& tree, int height, const uint256& nullifier)
|
||||
{
|
||||
SaplingNoteData nd(TestIvk(), nullifier);
|
||||
nd.witnesses.push_front(tree.witness());
|
||||
nd.witnessHeight = height;
|
||||
return nd;
|
||||
}
|
||||
|
||||
// A note as FindMySaplingNotes returns it during a replay or rescan: no nullifier, no witnesses.
|
||||
SaplingNoteData FreshNote()
|
||||
{
|
||||
return SaplingNoteData(TestIvk());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// A replayed or rescanned tx must keep the witnesses AND the nullifier it already had, and must not
|
||||
// report an update: dropping the nullifier made the next block clear and reseed every carried witness.
|
||||
TEST(WalletNoteData, ReplayKeepsNullifierAndWitnesses) {
|
||||
NoteDataWallet wallet;
|
||||
CWalletTx wtx(&wallet, CTransaction());
|
||||
uint256 txid = uint256S("aa");
|
||||
SaplingOutPoint op0(txid, 0);
|
||||
|
||||
SaplingMerkleTree tree;
|
||||
tree.append(uint256S("01"));
|
||||
const uint256 nf = uint256S("f00d");
|
||||
wtx.mapSaplingNoteData[op0] = WitnessedNote(tree, 100, nf);
|
||||
wtx.mapSaplingNoteData[op0].witnessRootValidated = true;
|
||||
auto before = wtx.mapSaplingNoteData;
|
||||
|
||||
CWalletTx replay(&wallet, CTransaction());
|
||||
replay.mapSaplingNoteData[op0] = FreshNote();
|
||||
|
||||
EXPECT_FALSE(wallet.UpdatedNoteData(replay, wtx));
|
||||
ASSERT_EQ(1u, wtx.mapSaplingNoteData.size());
|
||||
const SaplingNoteData& nd = wtx.mapSaplingNoteData.at(op0);
|
||||
ASSERT_TRUE(static_cast<bool>(nd.nullifier));
|
||||
EXPECT_EQ(nf, *nd.nullifier);
|
||||
EXPECT_EQ(100, nd.witnessHeight);
|
||||
EXPECT_TRUE(nd.witnesses == before.at(op0).witnesses);
|
||||
// ...but the kept witness is re-checked against the active chain once (in-memory flag only).
|
||||
EXPECT_FALSE(nd.witnessRootValidated);
|
||||
}
|
||||
|
||||
// A tx re-mined in a different block (reorg) has its notes at new tree positions: the old nullifier
|
||||
// must not be carried, so VerifyAndSetInitialWitness reseeds the note at its new position.
|
||||
TEST(WalletNoteData, MovedTxDropsNullifier) {
|
||||
NoteDataWallet wallet;
|
||||
CWalletTx wtx(&wallet, CTransaction());
|
||||
SaplingOutPoint op0(uint256S("ee"), 0);
|
||||
|
||||
SaplingMerkleTree tree;
|
||||
tree.append(uint256S("04"));
|
||||
wtx.mapSaplingNoteData[op0] = WitnessedNote(tree, 500, uint256S("cafe"));
|
||||
|
||||
CWalletTx moved(&wallet, CTransaction());
|
||||
moved.mapSaplingNoteData[op0] = FreshNote();
|
||||
|
||||
EXPECT_TRUE(wallet.UpdatedNoteData(moved, wtx, false));
|
||||
const SaplingNoteData& nd = wtx.mapSaplingNoteData.at(op0);
|
||||
EXPECT_FALSE(static_cast<bool>(nd.nullifier));
|
||||
}
|
||||
|
||||
// When the incoming data really adds something (a note for a newly added key), the tx is updated,
|
||||
// and the notes it already had keep their witnesses, witness height and nullifier together.
|
||||
TEST(WalletNoteData, NewNoteUpdatesAndKeepsExistingNoteState) {
|
||||
NoteDataWallet wallet;
|
||||
CWalletTx wtx(&wallet, CTransaction());
|
||||
uint256 txid = uint256S("bb");
|
||||
SaplingOutPoint op0(txid, 0);
|
||||
SaplingOutPoint op1(txid, 1);
|
||||
|
||||
SaplingMerkleTree tree;
|
||||
tree.append(uint256S("02"));
|
||||
const uint256 nf = uint256S("beef");
|
||||
wtx.mapSaplingNoteData[op0] = WitnessedNote(tree, 200, nf);
|
||||
|
||||
CWalletTx incoming(&wallet, CTransaction());
|
||||
incoming.mapSaplingNoteData[op0] = FreshNote();
|
||||
incoming.mapSaplingNoteData[op1] = FreshNote();
|
||||
|
||||
EXPECT_TRUE(wallet.UpdatedNoteData(incoming, wtx));
|
||||
ASSERT_EQ(2u, wtx.mapSaplingNoteData.size());
|
||||
const SaplingNoteData& nd0 = wtx.mapSaplingNoteData.at(op0);
|
||||
ASSERT_TRUE(static_cast<bool>(nd0.nullifier));
|
||||
EXPECT_EQ(nf, *nd0.nullifier);
|
||||
EXPECT_EQ(200, nd0.witnessHeight);
|
||||
EXPECT_EQ(1u, nd0.witnesses.size());
|
||||
const SaplingNoteData& nd1 = wtx.mapSaplingNoteData.at(op1);
|
||||
EXPECT_FALSE(static_cast<bool>(nd1.nullifier));
|
||||
EXPECT_TRUE(nd1.witnesses.empty());
|
||||
EXPECT_EQ(-1, nd1.witnessHeight);
|
||||
}
|
||||
|
||||
// Nothing is carried for a note that has no witnesses (PART B2): its height and nullifier describe
|
||||
// no witness, so they must not survive onto the incoming data.
|
||||
TEST(WalletNoteData, UnwitnessedNoteCarriesNothing) {
|
||||
NoteDataWallet wallet;
|
||||
CWalletTx wtx(&wallet, CTransaction());
|
||||
SaplingOutPoint op0(uint256S("cc"), 0);
|
||||
|
||||
SaplingNoteData stale(TestIvk(), uint256S("dead"));
|
||||
stale.witnessHeight = 300;
|
||||
wtx.mapSaplingNoteData[op0] = stale;
|
||||
|
||||
CWalletTx incoming(&wallet, CTransaction());
|
||||
incoming.mapSaplingNoteData[op0] = FreshNote();
|
||||
|
||||
EXPECT_TRUE(wallet.UpdatedNoteData(incoming, wtx));
|
||||
const SaplingNoteData& nd = wtx.mapSaplingNoteData.at(op0);
|
||||
EXPECT_FALSE(static_cast<bool>(nd.nullifier));
|
||||
EXPECT_EQ(-1, nd.witnessHeight);
|
||||
EXPECT_TRUE(nd.witnesses.empty());
|
||||
}
|
||||
|
||||
// Incoming data with no Sapling notes never touches the wallet's note data.
|
||||
TEST(WalletNoteData, EmptyIncomingIsNoUpdate) {
|
||||
NoteDataWallet wallet;
|
||||
CWalletTx wtx(&wallet, CTransaction());
|
||||
SaplingOutPoint op0(uint256S("dd"), 0);
|
||||
SaplingMerkleTree tree;
|
||||
tree.append(uint256S("03"));
|
||||
wtx.mapSaplingNoteData[op0] = WitnessedNote(tree, 400, uint256S("abcd"));
|
||||
|
||||
CWalletTx incoming(&wallet, CTransaction());
|
||||
EXPECT_FALSE(wallet.UpdatedNoteData(incoming, wtx));
|
||||
EXPECT_EQ(1u, wtx.mapSaplingNoteData.size());
|
||||
EXPECT_EQ(400, wtx.mapSaplingNoteData.at(op0).witnessHeight);
|
||||
}
|
||||
-33
@@ -95,7 +95,6 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
|
||||
errs++;
|
||||
else
|
||||
{
|
||||
//printf("updated %d pubkeys at %s ht.%d\n",num,symbol,ht);
|
||||
if ( (HUSH_EXTERNAL_NOTARIES != 0 && matched != 0) )
|
||||
hush_eventadd_pubkeys(sp,symbol,ht,num,pubkeys);
|
||||
}
|
||||
@@ -131,7 +130,6 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
|
||||
uint8_t n,nid; uint256 hash; uint64_t mask;
|
||||
n = fgetc(fp);
|
||||
nid = fgetc(fp);
|
||||
//printf("U %d %d\n",n,nid);
|
||||
if ( fread(&mask,1,sizeof(mask),fp) != sizeof(mask) )
|
||||
errs++;
|
||||
if ( fread(&hash,1,sizeof(hash),fp) != sizeof(hash) )
|
||||
@@ -145,7 +143,6 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
|
||||
if ( fread(&kheight,1,sizeof(kheight),fp) != sizeof(kheight) )
|
||||
errs++;
|
||||
//if ( matched != 0 ) global independent states -> inside *sp
|
||||
//printf("%s.%d load[%s] ht.%d\n",SMART_CHAIN_SYMBOL,ht,symbol,kheight);
|
||||
hush_eventadd_hushheight(sp,symbol,ht,kheight,0);
|
||||
}
|
||||
else if ( func == 'T' )
|
||||
@@ -156,7 +153,6 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
|
||||
if ( fread(&ktimestamp,1,sizeof(ktimestamp),fp) != sizeof(ktimestamp) )
|
||||
errs++;
|
||||
//if ( matched != 0 ) global independent states -> inside *sp
|
||||
//printf("%s.%d load[%s] ht.%d t.%u\n",SMART_CHAIN_SYMBOL,ht,symbol,kheight,ktimestamp);
|
||||
hush_eventadd_hushheight(sp,symbol,ht,kheight,ktimestamp);
|
||||
}
|
||||
else if ( func == 'R' )
|
||||
@@ -186,7 +182,6 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
|
||||
int32_t i;
|
||||
for (i=0; i<olen; i++)
|
||||
fgetc(fp);
|
||||
//printf("illegal olen.%u\n",olen);
|
||||
}
|
||||
}
|
||||
else if ( func == 'D' )
|
||||
@@ -200,9 +195,7 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
|
||||
if ( numpvals*sizeof(uint32_t) <= sizeof(pvals) && fread(pvals,sizeof(uint32_t),numpvals,fp) == numpvals )
|
||||
{
|
||||
//if ( matched != 0 ) global shared state -> global PVALS
|
||||
//printf("%s load[%s] prices %d\n",SMART_CHAIN_SYMBOL,symbol,ht);
|
||||
hush_eventadd_pricefeed(sp,symbol,ht,pvals,numpvals);
|
||||
//printf("load pvals ht.%d numpvals.%d\n",ht,numpvals);
|
||||
} else printf("error loading pvals[%d]\n",numpvals);
|
||||
} // else printf("[%s] %s illegal func.(%d %c)\n",SMART_CHAIN_SYMBOL,symbol,func,func);
|
||||
return(func);
|
||||
@@ -239,7 +232,6 @@ int32_t hush_parsestatefiledata(struct hush_state *sp,uint8_t *filedata,long *fp
|
||||
errs++;
|
||||
else
|
||||
{
|
||||
//printf("updated %d pubkeys at %s ht.%d\n",num,symbol,ht);
|
||||
if ( (HUSH_EXTERNAL_NOTARIES != 0 && matched != 0) )
|
||||
hush_eventadd_pubkeys(sp,symbol,ht,num,pubkeys);
|
||||
}
|
||||
@@ -274,7 +266,6 @@ int32_t hush_parsestatefiledata(struct hush_state *sp,uint8_t *filedata,long *fp
|
||||
uint8_t n,nid; uint256 hash; uint64_t mask;
|
||||
n = filedata[fpos++];
|
||||
nid = filedata[fpos++];
|
||||
//printf("U %d %d\n",n,nid);
|
||||
if ( memread(&mask,sizeof(mask),filedata,&fpos,datalen) != sizeof(mask) )
|
||||
errs++;
|
||||
if ( memread(&hash,sizeof(hash),filedata,&fpos,datalen) != sizeof(hash) )
|
||||
@@ -295,7 +286,6 @@ int32_t hush_parsestatefiledata(struct hush_state *sp,uint8_t *filedata,long *fp
|
||||
if ( memread(&ktimestamp,sizeof(ktimestamp),filedata,&fpos,datalen) != sizeof(ktimestamp) )
|
||||
errs++;
|
||||
//if ( matched != 0 ) global independent states -> inside *sp
|
||||
//printf("%s.%d load[%s] ht.%d t.%u\n",SMART_CHAIN_SYMBOL,ht,symbol,kheight,ktimestamp);
|
||||
hush_eventadd_hushheight(sp,symbol,ht,kheight,ktimestamp);
|
||||
}
|
||||
else if ( func == 'R' )
|
||||
@@ -325,7 +315,6 @@ int32_t hush_parsestatefiledata(struct hush_state *sp,uint8_t *filedata,long *fp
|
||||
int32_t i;
|
||||
for (i=0; i<olen; i++)
|
||||
filedata[fpos++];
|
||||
//printf("illegal olen.%u\n",olen);
|
||||
}
|
||||
}
|
||||
else if ( func == 'D' )
|
||||
@@ -339,9 +328,7 @@ int32_t hush_parsestatefiledata(struct hush_state *sp,uint8_t *filedata,long *fp
|
||||
if ( numpvals*sizeof(uint32_t) <= sizeof(pvals) && memread(pvals,(int32_t)(sizeof(uint32_t)*numpvals),filedata,&fpos,datalen) == numpvals*sizeof(uint32_t) )
|
||||
{
|
||||
//if ( matched != 0 ) global shared state -> global PVALS
|
||||
//printf("%s load[%s] prices %d\n",SMART_CHAIN_SYMBOL,symbol,ht);
|
||||
hush_eventadd_pricefeed(sp,symbol,ht,pvals,numpvals);
|
||||
//printf("load pvals ht.%d numpvals.%d\n",ht,numpvals);
|
||||
} else printf("error loading pvals[%d]\n",numpvals);
|
||||
} // else printf("[%s] %s illegal func.(%d %c)\n",SMART_CHAIN_SYMBOL,symbol,func,func);
|
||||
*fposp = fpos;
|
||||
@@ -366,7 +353,6 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
|
||||
printf("[%s] no hush_stateptr\n",SMART_CHAIN_SYMBOL);
|
||||
return;
|
||||
}
|
||||
//printf("[%s] (%s) -> (%s)\n",SMART_CHAIN_SYMBOL,symbol,dest);
|
||||
if ( fp == 0 )
|
||||
{
|
||||
hush_statefname(fname,SMART_CHAIN_SYMBOL,(char *)"hushstate");
|
||||
@@ -385,12 +371,10 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
|
||||
}
|
||||
if ( height <= 0 )
|
||||
{
|
||||
//printf("early return: stateupdate height.%d\n",height);
|
||||
return;
|
||||
}
|
||||
if ( fp != 0 ) // write out funcid, height, other fields, call side effect function
|
||||
{
|
||||
//printf("fpos.%ld ",ftell(fp));
|
||||
if ( HUSHheight != 0 )
|
||||
{
|
||||
if ( HUSHtimestamp != 0 )
|
||||
@@ -425,7 +409,6 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
|
||||
errs++;
|
||||
if ( fwrite(opretbuf,1,olen,fp) != olen )
|
||||
errs++;
|
||||
//printf("create ht.%d R opret[%d] sp.%p\n",height,olen,sp);
|
||||
hush_eventadd_opreturn(sp,symbol,height,txhash,opretvalue,vout,opretbuf,olen);
|
||||
}
|
||||
else if ( notarypubs != 0 && numnotaries > 0 )
|
||||
@@ -441,7 +424,6 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
|
||||
}
|
||||
else if ( voutmask != 0 && numvouts > 0 )
|
||||
{
|
||||
//printf("ht.%d func U %d %d errs.%d hashsize.%ld\n",height,numvouts,notaryid,errs,sizeof(txhash));
|
||||
fputc('U',fp);
|
||||
if ( fwrite(&height,1,sizeof(height),fp) != sizeof(height) )
|
||||
errs++;
|
||||
@@ -468,13 +450,10 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
|
||||
if ( fwrite(pvals,sizeof(uint32_t),numpvals,fp) != numpvals )
|
||||
errs++;
|
||||
hush_eventadd_pricefeed(sp,symbol,height,pvals,numpvals);
|
||||
//printf("ht.%d V numpvals[%d]\n",height,numpvals);
|
||||
}
|
||||
//printf("save pvals height.%d numpvals.%d\n",height,numpvals);
|
||||
}
|
||||
else if ( height != 0 )
|
||||
{
|
||||
//printf("ht.%d func N ht.%d errs.%d\n",height,NOTARIZED_HEIGHT,errs);
|
||||
if ( sp != 0 )
|
||||
{
|
||||
if ( sp->MoMdepth != 0 && sp->MoM != zero )
|
||||
@@ -504,7 +483,6 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
|
||||
|
||||
int32_t hush_validate_chain(uint256 srchash,int32_t notarized_height)
|
||||
{
|
||||
//fprintf(stderr,"%s\n", __func__);
|
||||
static int32_t last_rewind; int32_t rewindtarget; CBlockIndex *pindex; struct hush_state *sp; char symbol[HUSH_SMART_CHAIN_MAXLEN],dest[HUSH_SMART_CHAIN_MAXLEN];
|
||||
if ( (sp= hush_stateptr(symbol,dest)) == 0 )
|
||||
return(0);
|
||||
@@ -555,11 +533,9 @@ int32_t hush_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryi
|
||||
if ( memcmp(crypto555,scriptbuf+1,33) == 0 )
|
||||
{
|
||||
*specialtxp = 1;
|
||||
//printf(">>>>>>>> ");
|
||||
}
|
||||
else if ( hush_chosennotary(&nid,height,scriptbuf + 1,timestamp) >= 0 )
|
||||
{
|
||||
//printf("found notary.k%d\n",k);
|
||||
if ( notaryid < 64 )
|
||||
{
|
||||
if ( notaryid < 0 )
|
||||
@@ -569,9 +545,6 @@ int32_t hush_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryi
|
||||
}
|
||||
else if ( notaryid != nid )
|
||||
{
|
||||
//for (i=0; i<33; i++)
|
||||
// printf("%02x",scriptbuf[i+1]);
|
||||
//printf(" %s mismatch notaryid.%d k.%d\n",SMART_CHAIN_SYMBOL,notaryid,nid);
|
||||
notaryid = 64;
|
||||
*voutmaskp = 0;
|
||||
}
|
||||
@@ -605,7 +578,6 @@ int32_t hush_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryi
|
||||
} else {
|
||||
if ( scriptbuf[len] == 'K' )
|
||||
{
|
||||
//fprintf(stderr,"i.%d j.%d KV OPRET len.%d %.8f\n",i,j,opretlen,dstr(value));
|
||||
hush_stateupdate(height,0,0,0,txhash,0,0,0,0,0,0,value,&scriptbuf[len],opretlen,j,zero,0);
|
||||
return(-1);
|
||||
}
|
||||
@@ -727,9 +699,6 @@ int32_t hush_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryi
|
||||
}
|
||||
else if ( matched != 0 )
|
||||
{
|
||||
//int32_t k; for (k=0; k<scriptlen; k++)
|
||||
// printf("%02x",scriptbuf[k]);
|
||||
//printf(" <- script ht.%d i.%d j.%d value %.8f %s\n",height,i,j,dstr(value),SMART_CHAIN_SYMBOL);
|
||||
if ( opretlen >= 32*2+4 && strcmp(SMART_CHAIN_SYMBOL,(char *)&scriptbuf[len+32*2+4]) == 0 )
|
||||
{
|
||||
for (k=0; k<32; k++)
|
||||
@@ -793,7 +762,6 @@ int32_t hush_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block)
|
||||
fprintf(stderr,"unexpected null stateptr.[%s]\n",SMART_CHAIN_SYMBOL);
|
||||
return(0);
|
||||
}
|
||||
//fprintf(stderr,"%s connect.%d\n",SMART_CHAIN_SYMBOL,pindex->nHeight);
|
||||
// Wallet Filter. Disabled here. Cant be activated by notaries or pools with some changes.
|
||||
numnotaries = hush_notaries(pubkeys,pindex->GetHeight(),pindex->GetBlockTime());
|
||||
calc_rmd160_sha256(rmd160,pubkeys[0],33);
|
||||
@@ -970,7 +938,6 @@ int32_t hush_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block)
|
||||
else
|
||||
{ fprintf(stderr,"hush_connectblock: unexpected null pindex\n"); return(0); }
|
||||
//HUSH_INITDONE = (uint32_t)time(NULL);
|
||||
//fprintf(stderr,"%s end connect.%d\n",SMART_CHAIN_SYMBOL,pindex->GetHeight());
|
||||
if (fJustCheck)
|
||||
{
|
||||
if ( notarizations.size() == 0 )
|
||||
|
||||
+7
-6
@@ -975,8 +975,9 @@ uint64_t hush_commission(int height)
|
||||
INTERVAL = GetArg("-ac_halving1",840000), TRANSITION = 129;
|
||||
uint64_t commission = 0;
|
||||
|
||||
//TODO: Likely a bug hiding here or at the next halving :)
|
||||
//if( height >= HALVING1) {
|
||||
// NB: INTERVAL is consumed only by the debug fprintf at the end of this function;
|
||||
// the commission schedule below uses hardcoded height thresholds, not INTERVAL. So
|
||||
// the > vs >= boundary at HALVING1 has no consensus effect. Left as > for stability.
|
||||
if( height > HALVING1) {
|
||||
// Block time going from 150s to 75s (half) means the interval between halvings
|
||||
// must be twice as often, i.e. 840000*2=1680000
|
||||
@@ -1019,14 +1020,14 @@ uint64_t hush_commission(int height)
|
||||
commission = 61035;
|
||||
} else if (height < 23860000) {
|
||||
commission = 30517;
|
||||
} else if (height < 23860000) {
|
||||
commission = 15258;
|
||||
// removed unreachable duplicate `height < 23860000` (=> 15258); the schedule
|
||||
// intentionally drops straight to 7629 next — this is the deployed behavior.
|
||||
} else if (height < 25540000) {
|
||||
commission = 7629;
|
||||
} else if (height < 27220000) {
|
||||
commission = 3814;
|
||||
} else if (height < 27220000) {
|
||||
commission = 1907;
|
||||
// removed unreachable duplicate `height < 27220000` (=> 1907); the schedule
|
||||
// intentionally drops straight to 953 next — this is the deployed behavior.
|
||||
} else if (height < 28900000) {
|
||||
commission = 953;
|
||||
} else if (height < 30580000) {
|
||||
|
||||
+1
-1
@@ -597,7 +597,7 @@ void hush_netevent(std::vector<uint8_t> payload);
|
||||
int32_t getacseason(uint32_t timestamp);
|
||||
int32_t gethushseason(int32_t height);
|
||||
|
||||
#define DRAGON_MAXSCRIPTSIZE 10001
|
||||
// DRAGON_MAXSCRIPTSIZE is defined once near the top of this header; the duplicate here was removed.
|
||||
#define HUSH_KVDURATION 1440
|
||||
#define HUSH_KVBINARY 2
|
||||
#define PRICES_SMOOTHWIDTH 1
|
||||
|
||||
@@ -568,8 +568,6 @@ uint256 NSPV_opretextract(int32_t *heightp,uint256 *blockhashp,char *symbol,std:
|
||||
((uint8_t *)blockhashp)[i] = opret[i];
|
||||
for (i=0; i<32; i++)
|
||||
((uint8_t *)&desttxid)[i] = opret[4 + 32 + i];
|
||||
if ( 0 && *heightp != 2690 )
|
||||
fprintf(stderr," ntzht.%d %s <- txid.%s size.%d\n",*heightp,(*blockhashp).GetHex().c_str(),(txid).GetHex().c_str(),(int32_t)opret.size());
|
||||
return(desttxid);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ struct NSPV_ntzargs
|
||||
int32_t NSPV_notarization_find(struct NSPV_ntzargs *args,int32_t height,int32_t dir)
|
||||
{
|
||||
int32_t ntzheight = 0; uint256 hashBlock; CTransaction tx; Notarization nota; char *symbol; std::vector<uint8_t> opret;
|
||||
symbol = (SMART_CHAIN_SYMBOL[0] == 0) ? (char *)"HUSH3" : SMART_CHAIN_SYMBOL;
|
||||
// Notarization symbol; the empty-symbol fallback is dead on DragonX (SMART_CHAIN_SYMBOL is always "DRAGONX", never empty)
|
||||
symbol = (SMART_CHAIN_SYMBOL[0] == 0) ? (char *)"DRAGONX" : SMART_CHAIN_SYMBOL;
|
||||
memset(args,0,sizeof(*args));
|
||||
if ( dir > 0 )
|
||||
height += 10;
|
||||
@@ -659,7 +660,7 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
||||
struct NSPV_utxosresp U;
|
||||
if ( len >= 2 && len < 64+5 && request[1] < 64 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
||||
{
|
||||
int32_t skipcount = 0; char coinaddr[64]; uint8_t filter; uint8_t isCC = 0;
|
||||
int32_t skipcount = 0; char coinaddr[64]; uint32_t filter = 0; uint8_t isCC = 0; // only assigned on the len-11 form; the other two passed it on uninitialized
|
||||
memcpy(coinaddr,&request[2],request[1]); // request[1] < 64 bounds the copy + the terminator write
|
||||
coinaddr[request[1]] = 0;
|
||||
if ( request[1] == len-3 )
|
||||
@@ -675,8 +676,6 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
||||
dragon_rwnum(0,&request[len-8],sizeof(skipcount),&skipcount);
|
||||
dragon_rwnum(0,&request[len-4],sizeof(filter),&filter);
|
||||
}
|
||||
if ( 0 && isCC != 0 )
|
||||
fprintf(stderr,"utxos %s isCC.%d skipcount.%d filter.%x\n",coinaddr,isCC,skipcount,filter);
|
||||
memset(&U,0,sizeof(U));
|
||||
if ( (slen= NSPV_getaddressutxos(&U,coinaddr,isCC,skipcount,filter)) > 0 )
|
||||
{
|
||||
@@ -699,7 +698,7 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
||||
struct NSPV_txidsresp T;
|
||||
if ( len >= 2 && len < 64+5 && request[1] < 64 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
||||
{
|
||||
int32_t skipcount = 0; char coinaddr[64]; uint32_t filter; uint8_t isCC = 0;
|
||||
int32_t skipcount = 0; char coinaddr[64]; uint32_t filter = 0; uint8_t isCC = 0; // only assigned on the len-11 form; the other two passed it on uninitialized
|
||||
memcpy(coinaddr,&request[2],request[1]); // request[1] < 64 bounds the copy + the terminator write
|
||||
coinaddr[request[1]] = 0;
|
||||
if ( request[1] == len-3 )
|
||||
@@ -715,8 +714,6 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
||||
dragon_rwnum(0,&request[len-8],sizeof(skipcount),&skipcount);
|
||||
dragon_rwnum(0,&request[len-4],sizeof(filter),&filter);
|
||||
}
|
||||
if ( 0 && isCC != 0 )
|
||||
fprintf(stderr,"txids %s isCC.%d skipcount.%d filter.%d\n",coinaddr,isCC,skipcount,filter);
|
||||
memset(&T,0,sizeof(T));
|
||||
if ( (slen= NSPV_getaddresstxids(&T,coinaddr,isCC,skipcount,filter)) > 0 )
|
||||
{
|
||||
|
||||
+25
-30
@@ -62,7 +62,7 @@ struct NSPV_ntzsresp *NSPV_ntzsresp_add(struct NSPV_ntzsresp *ptr)
|
||||
i = (rand() % (sizeof(NSPV_ntzsresp_cache)/sizeof(*NSPV_ntzsresp_cache)));
|
||||
NSPV_ntzsresp_purge(&NSPV_ntzsresp_cache[i]);
|
||||
NSPV_ntzsresp_copy(&NSPV_ntzsresp_cache[i],ptr);
|
||||
fprintf(stderr,"ADD CACHE ntzsresp req.%d\n",ptr->reqheight);
|
||||
LogPrint("nspv","ADD CACHE ntzsresp req.%d\n",ptr->reqheight);
|
||||
return(&NSPV_ntzsresp_cache[i]);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ struct NSPV_txproof *NSPV_txproof_add(struct NSPV_txproof *ptr)
|
||||
i = (rand() % (sizeof(NSPV_txproof_cache)/sizeof(*NSPV_txproof_cache)));
|
||||
NSPV_txproof_purge(&NSPV_txproof_cache[i]);
|
||||
NSPV_txproof_copy(&NSPV_txproof_cache[i],ptr);
|
||||
fprintf(stderr,"ADD CACHE txproof %s\n",ptr->txid.GetHex().c_str());
|
||||
LogPrint("nspv","ADD CACHE txproof %s\n",ptr->txid.GetHex().c_str());
|
||||
return(&NSPV_txproof_cache[i]);
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ struct NSPV_ntzsproofresp *NSPV_ntzsproof_add(struct NSPV_ntzsproofresp *ptr)
|
||||
i = (rand() % (sizeof(NSPV_ntzsproofresp_cache)/sizeof(*NSPV_ntzsproofresp_cache)));
|
||||
NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresp_cache[i]);
|
||||
NSPV_ntzsproofresp_copy(&NSPV_ntzsproofresp_cache[i],ptr);
|
||||
fprintf(stderr,"ADD CACHE ntzsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str());
|
||||
LogPrint("nspv","ADD CACHE ntzsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str());
|
||||
return(&NSPV_ntzsproofresp_cache[i]);
|
||||
}
|
||||
|
||||
@@ -139,13 +139,13 @@ void hush_nSPVresp(CNode *pfrom,std::vector<uint8_t> response) // received a res
|
||||
switch ( response[0] )
|
||||
{
|
||||
case NSPV_INFORESP:
|
||||
fprintf(stderr,"got version.%d info response %u size.%d height.%d\n",NSPV_inforesult.version,timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status
|
||||
LogPrint("nspv","got version.%d info response %u size.%d height.%d\n",NSPV_inforesult.version,timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status
|
||||
I = NSPV_inforesult;
|
||||
NSPV_inforesp_purge(&NSPV_inforesult);
|
||||
NSPV_rwinforesp(0,&response[1],&NSPV_inforesult);
|
||||
if ( NSPV_inforesult.height < I.height )
|
||||
{
|
||||
fprintf(stderr,"got old info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status
|
||||
LogPrint("nspv","got old info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status
|
||||
NSPV_inforesp_purge(&NSPV_inforesult);
|
||||
NSPV_inforesult = I;
|
||||
}
|
||||
@@ -160,56 +160,56 @@ void hush_nSPVresp(CNode *pfrom,std::vector<uint8_t> response) // received a res
|
||||
case NSPV_UTXOSRESP:
|
||||
NSPV_utxosresp_purge(&NSPV_utxosresult);
|
||||
NSPV_rwutxosresp(0,&response[1],&NSPV_utxosresult);
|
||||
fprintf(stderr,"got utxos response %u size.%d\n",timestamp,(int32_t)response.size());
|
||||
LogPrint("nspv","got utxos response %u size.%d\n",timestamp,(int32_t)response.size());
|
||||
break;
|
||||
case NSPV_TXIDSRESP:
|
||||
NSPV_txidsresp_purge(&NSPV_txidsresult);
|
||||
NSPV_rwtxidsresp(0,&response[1],&NSPV_txidsresult);
|
||||
fprintf(stderr,"got txids response %u size.%d %s CC.%d num.%d\n",timestamp,(int32_t)response.size(),NSPV_txidsresult.coinaddr,NSPV_txidsresult.CCflag,NSPV_txidsresult.numtxids);
|
||||
LogPrint("nspv","got txids response %u size.%d %s CC.%d num.%d\n",timestamp,(int32_t)response.size(),NSPV_txidsresult.coinaddr,NSPV_txidsresult.CCflag,NSPV_txidsresult.numtxids);
|
||||
break;
|
||||
case NSPV_MEMPOOLRESP:
|
||||
NSPV_mempoolresp_purge(&NSPV_mempoolresult);
|
||||
NSPV_rwmempoolresp(0,&response[1],&NSPV_mempoolresult);
|
||||
fprintf(stderr,"got mempool response %u size.%d %s CC.%d num.%d funcid.%d %s/v%d\n",timestamp,(int32_t)response.size(),NSPV_mempoolresult.coinaddr,NSPV_mempoolresult.CCflag,NSPV_mempoolresult.numtxids,NSPV_mempoolresult.funcid,NSPV_mempoolresult.txid.GetHex().c_str(),NSPV_mempoolresult.vout);
|
||||
LogPrint("nspv","got mempool response %u size.%d %s CC.%d num.%d funcid.%d %s/v%d\n",timestamp,(int32_t)response.size(),NSPV_mempoolresult.coinaddr,NSPV_mempoolresult.CCflag,NSPV_mempoolresult.numtxids,NSPV_mempoolresult.funcid,NSPV_mempoolresult.txid.GetHex().c_str(),NSPV_mempoolresult.vout);
|
||||
break;
|
||||
case NSPV_NTZSRESP:
|
||||
NSPV_ntzsresp_purge(&NSPV_ntzsresult);
|
||||
NSPV_rwntzsresp(0,&response[1],&NSPV_ntzsresult);
|
||||
if ( NSPV_ntzsresp_find(NSPV_ntzsresult.reqheight) == 0 )
|
||||
NSPV_ntzsresp_add(&NSPV_ntzsresult);
|
||||
fprintf(stderr,"got ntzs response %u size.%d %s prev.%d, %s next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.prevntz.height,NSPV_ntzsresult.nextntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.height);
|
||||
LogPrint("nspv","got ntzs response %u size.%d %s prev.%d, %s next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.prevntz.height,NSPV_ntzsresult.nextntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.height);
|
||||
break;
|
||||
case NSPV_NTZSPROOFRESP:
|
||||
NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult);
|
||||
NSPV_rwntzsproofresp(0,&response[1],&NSPV_ntzsproofresult);
|
||||
if ( NSPV_ntzsproof_find(NSPV_ntzsproofresult.prevtxid,NSPV_ntzsproofresult.nexttxid) == 0 )
|
||||
NSPV_ntzsproof_add(&NSPV_ntzsproofresult);
|
||||
fprintf(stderr,"got ntzproof response %u size.%d prev.%d next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsproofresult.common.prevht,NSPV_ntzsproofresult.common.nextht);
|
||||
LogPrint("nspv","got ntzproof response %u size.%d prev.%d next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsproofresult.common.prevht,NSPV_ntzsproofresult.common.nextht);
|
||||
break;
|
||||
case NSPV_TXPROOFRESP:
|
||||
NSPV_txproof_purge(&NSPV_txproofresult);
|
||||
NSPV_rwtxproof(0,&response[1],&NSPV_txproofresult);
|
||||
if ( NSPV_txproof_find(NSPV_txproofresult.txid) == 0 )
|
||||
NSPV_txproof_add(&NSPV_txproofresult);
|
||||
fprintf(stderr,"got txproof response %u size.%d %s ht.%d\n",timestamp,(int32_t)response.size(),NSPV_txproofresult.txid.GetHex().c_str(),NSPV_txproofresult.height);
|
||||
LogPrint("nspv","got txproof response %u size.%d %s ht.%d\n",timestamp,(int32_t)response.size(),NSPV_txproofresult.txid.GetHex().c_str(),NSPV_txproofresult.height);
|
||||
break;
|
||||
case NSPV_SPENTINFORESP:
|
||||
NSPV_spentinfo_purge(&NSPV_spentresult);
|
||||
NSPV_rwspentinfo(0,&response[1],&NSPV_spentresult);
|
||||
fprintf(stderr,"got spentinfo response %u size.%d\n",timestamp,(int32_t)response.size());
|
||||
LogPrint("nspv","got spentinfo response %u size.%d\n",timestamp,(int32_t)response.size());
|
||||
break;
|
||||
case NSPV_BROADCASTRESP:
|
||||
NSPV_broadcast_purge(&NSPV_broadcastresult);
|
||||
NSPV_rwbroadcastresp(0,&response[1],&NSPV_broadcastresult);
|
||||
fprintf(stderr,"got broadcast response %u size.%d %s retcode.%d\n",timestamp,(int32_t)response.size(),NSPV_broadcastresult.txid.GetHex().c_str(),NSPV_broadcastresult.retcode);
|
||||
LogPrint("nspv","got broadcast response %u size.%d %s retcode.%d\n",timestamp,(int32_t)response.size(),NSPV_broadcastresult.txid.GetHex().c_str(),NSPV_broadcastresult.retcode);
|
||||
break;
|
||||
case NSPV_CCMODULEUTXOSRESP:
|
||||
NSPV_utxosresp_purge(&NSPV_utxosresult);
|
||||
NSPV_rwutxosresp(0, &response[1], &NSPV_utxosresult);
|
||||
fprintf(stderr, "got cc module utxos response %u size.%d\n", timestamp, (int32_t)response.size());
|
||||
LogPrint("nspv", "got cc module utxos response %u size.%d\n", timestamp, (int32_t)response.size());
|
||||
break;
|
||||
|
||||
default: fprintf(stderr,"unexpected response %02x size.%d at %u\n",response[0],(int32_t)response.size(),timestamp);
|
||||
default: LogPrint("nspv","unexpected response %02x size.%d at %u\n",response[0],(int32_t)response.size(),timestamp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -254,7 +254,7 @@ CNode *NSPV_req(CNode *pnode,uint8_t *msg,int32_t len,uint64_t mask,int32_t ind)
|
||||
pnode->PushMessage("getnSPV",request);
|
||||
pnode->prevtimes[ind] = timestamp;
|
||||
return(pnode);
|
||||
} else fprintf(stderr,"no pnodes\n");
|
||||
} else LogPrint("nspv","no pnodes\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ UniValue NSPV_logout()
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("result","success"));
|
||||
if ( NSPV_logintime != 0 )
|
||||
fprintf(stderr,"scrub wif and privkey from NSPV memory\n");
|
||||
LogPrint("nspv","scrub wif and privkey from NSPV memory\n");
|
||||
else result.push_back(Pair("status","wasnt logged in"));
|
||||
memset(NSPV_ntzsproofresp_cache,0,sizeof(NSPV_ntzsproofresp_cache));
|
||||
memset(NSPV_txproof_cache,0,sizeof(NSPV_txproof_cache));
|
||||
@@ -294,7 +294,6 @@ void hush_nSPV(CNode *pto) // polling loop from SendMessages
|
||||
len = 0;
|
||||
msg[len++] = NSPV_INFO;
|
||||
len += dragon_rwnum(1,&msg[len],sizeof(reqht),&reqht);
|
||||
//fprintf(stderr,"issue getinfo\n");
|
||||
NSPV_req(pto,msg,len,NODE_NSPV,NSPV_INFO>>1);
|
||||
}
|
||||
}
|
||||
@@ -485,7 +484,6 @@ UniValue NSPV_ntzsproof_json(struct NSPV_ntzsproofresp *ptr)
|
||||
result.push_back(Pair("numhdrs",(int64_t)ptr->common.numhdrs));
|
||||
result.push_back(Pair("headers",NSPV_headers_json(ptr->common.hdrs,ptr->common.numhdrs,ptr->common.prevht)));
|
||||
result.push_back(Pair("lastpeer",NSPV_lastpeer));
|
||||
//fprintf(stderr,"ntzs_proof %s %d, %s %d\n",ptr->prevtxid.GetHex().c_str(),ptr->common.prevht,ptr->nexttxid.GetHex().c_str(),ptr->common.nextht);
|
||||
return(result);
|
||||
}
|
||||
|
||||
@@ -577,7 +575,7 @@ uint32_t NSPV_blocktime(int32_t hdrheight)
|
||||
{
|
||||
timestamp = NSPV_inforesult.H.nTime;
|
||||
NSPV_inforesult = old;
|
||||
fprintf(stderr,"NSPV_blocktime ht.%d -> t%u\n",hdrheight,timestamp);
|
||||
LogPrint("nspv","NSPV_blocktime ht.%d -> t%u\n",hdrheight,timestamp);
|
||||
return(timestamp);
|
||||
}
|
||||
}
|
||||
@@ -588,7 +586,6 @@ uint32_t NSPV_blocktime(int32_t hdrheight)
|
||||
UniValue NSPV_addressutxos(char *coinaddr,int32_t CCflag,int32_t skipcount,int32_t filter)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ); uint8_t msg[512]; int32_t i,iter,slen,len = 0;
|
||||
//fprintf(stderr,"utxos %s NSPV addr %s\n",coinaddr,NSPV_address.c_str());
|
||||
//if ( NSPV_utxosresult.nodeheight >= NSPV_inforesult.height && strcmp(coinaddr,NSPV_utxosresult.coinaddr) == 0 && CCflag == NSPV_utxosresult.CCflag && skipcount == NSPV_utxosresult.skipcount && filter == NSPV_utxosresult.filter )
|
||||
// return(NSPV_utxosresp_json(&NSPV_utxosresult));
|
||||
if ( skipcount < 0 )
|
||||
@@ -644,7 +641,6 @@ UniValue NSPV_addresstxids(char *coinaddr,int32_t CCflag,int32_t skipcount,int32
|
||||
msg[len++] = (CCflag != 0);
|
||||
len += dragon_rwnum(1,&msg[len],sizeof(skipcount),&skipcount);
|
||||
len += dragon_rwnum(1,&msg[len],sizeof(filter),&filter);
|
||||
//fprintf(stderr,"skipcount.%d\n",skipcount);
|
||||
for (iter=0; iter<3; iter++)
|
||||
if ( NSPV_req(0,msg,len,NODE_ADDRINDEX,msg[0]>>1) != 0 )
|
||||
{
|
||||
@@ -683,7 +679,7 @@ UniValue NSPV_ccaddresstxids(char *coinaddr,int32_t CCflag,int32_t skipcount,uin
|
||||
slen = (int32_t)strlen(coinaddr);
|
||||
msg[len++] = slen;
|
||||
memcpy(&msg[len],coinaddr,slen), len += slen;
|
||||
fprintf(stderr,"(%s) func.%d CC.%d %s skipcount.%d len.%d\n",coinaddr,NSPV_CC_TXIDS,CCflag,filtertxid.GetHex().c_str(),skipcount,len);
|
||||
LogPrint("nspv","(%s) func.%d CC.%d %s skipcount.%d len.%d\n",coinaddr,NSPV_CC_TXIDS,CCflag,filtertxid.GetHex().c_str(),skipcount,len);
|
||||
for (iter=0; iter<3; iter++)
|
||||
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 )
|
||||
{
|
||||
@@ -721,7 +717,7 @@ UniValue NSPV_mempooltxids(char *coinaddr,int32_t CCflag,uint8_t funcid,uint256
|
||||
slen = (int32_t)strlen(coinaddr);
|
||||
msg[len++] = slen;
|
||||
memcpy(&msg[len],coinaddr,slen), len += slen;
|
||||
fprintf(stderr,"(%s) func.%d CC.%d %s/v%d len.%d\n",coinaddr,funcid,CCflag,txid.GetHex().c_str(),vout,len);
|
||||
LogPrint("nspv","(%s) func.%d CC.%d %s/v%d len.%d\n",coinaddr,funcid,CCflag,txid.GetHex().c_str(),vout,len);
|
||||
for (iter=0; iter<3; iter++)
|
||||
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 )
|
||||
{
|
||||
@@ -782,7 +778,7 @@ UniValue NSPV_notarizations(int32_t reqheight)
|
||||
uint8_t msg[512]; int32_t i,iter,len = 0; struct NSPV_ntzsresp N,*ptr;
|
||||
if ( (ptr= NSPV_ntzsresp_find(reqheight)) != 0 )
|
||||
{
|
||||
fprintf(stderr,"FROM CACHE NSPV_notarizations.%d\n",reqheight);
|
||||
LogPrint("nspv","FROM CACHE NSPV_notarizations.%d\n",reqheight);
|
||||
NSPV_ntzsresp_purge(&NSPV_ntzsresult);
|
||||
NSPV_ntzsresp_copy(&NSPV_ntzsresult,ptr);
|
||||
return(NSPV_ntzsresp_json(ptr));
|
||||
@@ -808,7 +804,7 @@ UniValue NSPV_txidhdrsproof(uint256 prevtxid,uint256 nexttxid)
|
||||
uint8_t msg[512]; int32_t i,iter,len = 0; struct NSPV_ntzsproofresp P,*ptr;
|
||||
if ( (ptr= NSPV_ntzsproof_find(prevtxid,nexttxid)) != 0 )
|
||||
{
|
||||
fprintf(stderr,"FROM CACHE NSPV_txidhdrsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str());
|
||||
LogPrint("nspv","FROM CACHE NSPV_txidhdrsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str());
|
||||
NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult);
|
||||
NSPV_ntzsproofresp_copy(&NSPV_ntzsproofresult,ptr);
|
||||
return(NSPV_ntzsproof_json(ptr));
|
||||
@@ -846,7 +842,7 @@ UniValue NSPV_txproof(int32_t vout,uint256 txid,int32_t height)
|
||||
uint8_t msg[512]; int32_t i,iter,len = 0; struct NSPV_txproof P,*ptr;
|
||||
if ( (ptr= NSPV_txproof_find(txid)) != 0 )
|
||||
{
|
||||
fprintf(stderr,"FROM CACHE NSPV_txproof %s\n",txid.GetHex().c_str());
|
||||
LogPrint("nspv","FROM CACHE NSPV_txproof %s\n",txid.GetHex().c_str());
|
||||
NSPV_txproof_purge(&NSPV_txproofresult);
|
||||
NSPV_txproof_copy(&NSPV_txproofresult,ptr);
|
||||
return(NSPV_txproof_json(ptr));
|
||||
@@ -856,7 +852,7 @@ UniValue NSPV_txproof(int32_t vout,uint256 txid,int32_t height)
|
||||
len += dragon_rwnum(1,&msg[len],sizeof(height),&height);
|
||||
len += dragon_rwnum(1,&msg[len],sizeof(vout),&vout);
|
||||
len += dragon_rwbignum(1,&msg[len],sizeof(txid),(uint8_t *)&txid);
|
||||
fprintf(stderr,"req txproof %s/v%d at height.%d\n",txid.GetHex().c_str(),vout,height);
|
||||
LogPrint("nspv","req txproof %s/v%d at height.%d\n",txid.GetHex().c_str(),vout,height);
|
||||
for (iter=0; iter<3; iter++)
|
||||
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 )
|
||||
{
|
||||
@@ -867,7 +863,7 @@ UniValue NSPV_txproof(int32_t vout,uint256 txid,int32_t height)
|
||||
return(NSPV_txproof_json(&NSPV_txproofresult));
|
||||
}
|
||||
} else sleep(1);
|
||||
fprintf(stderr,"txproof timeout\n");
|
||||
LogPrint("nspv","txproof timeout\n");
|
||||
memset(&P,0,sizeof(P));
|
||||
return(NSPV_txproof_json(&P));
|
||||
}
|
||||
@@ -907,7 +903,6 @@ UniValue NSPV_broadcast(char *hex)
|
||||
len += dragon_rwnum(1,&msg[len],sizeof(n),&n);
|
||||
memcpy(&msg[len],data,n), len += n;
|
||||
free(data);
|
||||
//fprintf(stderr,"send txid.%s\n",txid.GetHex().c_str());
|
||||
for (iter=0; iter<3; iter++)
|
||||
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 )
|
||||
{
|
||||
|
||||
+19
-31
@@ -26,7 +26,7 @@ int32_t NSPV_validatehdrs(struct NSPV_ntzsproofresp *ptr)
|
||||
int32_t i,height,txidht; CTransaction tx; uint256 blockhash,txid,desttxid;
|
||||
if ( (ptr->common.nextht-ptr->common.prevht+1) != ptr->common.numhdrs )
|
||||
{
|
||||
fprintf(stderr,"next.%d prev.%d -> %d vs %d\n",ptr->common.nextht,ptr->common.prevht,ptr->common.nextht-ptr->common.prevht+1,ptr->common.numhdrs);
|
||||
LogPrintf("next.%d prev.%d -> %d vs %d\n",ptr->common.nextht,ptr->common.prevht,ptr->common.nextht-ptr->common.prevht+1,ptr->common.numhdrs);
|
||||
return(-2);
|
||||
}
|
||||
else if ( NSPV_txextract(tx,ptr->nextntz,ptr->nexttxlen) < 0 )
|
||||
@@ -64,7 +64,6 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int
|
||||
struct NSPV_txproof *ptr; int32_t i,offset,retval; int64_t rewards = 0; uint32_t nLockTime; std::vector<uint8_t> proof;
|
||||
retval = skipvalidation != 0 ? 0 : -1;
|
||||
|
||||
//fprintf(stderr,"NSPV_gettx %s/v%d ht.%d\n",txid.GetHex().c_str(),vout,height);
|
||||
if ( (ptr= NSPV_txproof_find(txid)) == 0 )
|
||||
{
|
||||
NSPV_txproof(vout,txid,height);
|
||||
@@ -75,7 +74,7 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int
|
||||
currentheight=NSPV_inforesult.height;
|
||||
if ( ptr->txid != txid )
|
||||
{
|
||||
fprintf(stderr,"txproof error %s != %s\n",ptr->txid.GetHex().c_str(),txid.GetHex().c_str());
|
||||
LogPrintf("txproof error %s != %s\n",ptr->txid.GetHex().c_str(),txid.GetHex().c_str());
|
||||
return(-1);
|
||||
}
|
||||
else if ( NSPV_txextract(tx,ptr->tx,ptr->txlen) < 0 || ptr->txlen <= 0 )
|
||||
@@ -87,8 +86,7 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int
|
||||
|
||||
//char coinaddr[64];
|
||||
//Getscriptaddress(coinaddr,tx.vout[0].scriptPubKey); causes crash??
|
||||
//fprintf(stderr,"%s txid.%s vs hash.%s\n",coinaddr,txid.GetHex().c_str(),tx.GetHash().GetHex().c_str());
|
||||
|
||||
|
||||
if ( skipvalidation == 0 )
|
||||
{
|
||||
if ( ptr->txprooflen > 0 )
|
||||
@@ -99,18 +97,17 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int
|
||||
NSPV_notarizations(height); // gets the prev and next notarizations
|
||||
if ( NSPV_inforesult.notarization.height >= height && (NSPV_ntzsresult.prevntz.height == 0 || NSPV_ntzsresult.prevntz.height >= NSPV_ntzsresult.nextntz.height) )
|
||||
{
|
||||
fprintf(stderr,"issue manual bracket\n");
|
||||
LogPrintf("issue manual bracket\n");
|
||||
NSPV_notarizations(height-1);
|
||||
NSPV_notarizations(height+1);
|
||||
NSPV_notarizations(height); // gets the prev and next notarizations
|
||||
}
|
||||
if ( NSPV_ntzsresult.prevntz.height != 0 && NSPV_ntzsresult.prevntz.height <= NSPV_ntzsresult.nextntz.height )
|
||||
{
|
||||
fprintf(stderr,">>>>> gettx ht.%d prev.%d next.%d\n",height,NSPV_ntzsresult.prevntz.height, NSPV_ntzsresult.nextntz.height);
|
||||
LogPrintf(">>>>> gettx ht.%d prev.%d next.%d\n",height,NSPV_ntzsresult.prevntz.height, NSPV_ntzsresult.nextntz.height);
|
||||
offset = (height - NSPV_ntzsresult.prevntz.height);
|
||||
if ( offset >= 0 && height <= NSPV_ntzsresult.nextntz.height )
|
||||
{
|
||||
//fprintf(stderr,"call NSPV_txidhdrsproof %s %s\n",NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.txid.GetHex().c_str());
|
||||
NSPV_txidhdrsproof(NSPV_ntzsresult.prevntz.txid,NSPV_ntzsresult.nextntz.txid);
|
||||
usleep(10000);
|
||||
if ( (retval= NSPV_validatehdrs(&NSPV_ntzsproofresult)) == 0 )
|
||||
@@ -119,8 +116,8 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int
|
||||
proofroot = BitcoinGetProofMerkleRoot(proof,txids);
|
||||
if ( proofroot != NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot || txids[0] != txid )
|
||||
{
|
||||
fprintf(stderr,"txid.%s vs txids[0] %s\n",txid.GetHex().c_str(),txids[0].GetHex().c_str());
|
||||
fprintf(stderr,"prooflen.%d proofroot.%s vs %s\n",(int32_t)proof.size(),proofroot.GetHex().c_str(),NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot.GetHex().c_str());
|
||||
LogPrintf("txid.%s vs txids[0] %s\n",txid.GetHex().c_str(),txids[0].GetHex().c_str());
|
||||
LogPrintf("prooflen.%d proofroot.%s vs %s\n",(int32_t)proof.size(),proofroot.GetHex().c_str(),NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot.GetHex().c_str());
|
||||
retval = -2003;
|
||||
} else retval = 0;
|
||||
}
|
||||
@@ -162,13 +159,11 @@ int32_t NSPV_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t
|
||||
belowi = i;
|
||||
}
|
||||
}
|
||||
//printf("value %.8f gap %.8f abovei.%d %.8f belowi.%d %.8f\n",dstr(value),dstr(gap),abovei,dstr(above),belowi,dstr(below));
|
||||
}
|
||||
*aboveip = abovei;
|
||||
*abovep = above;
|
||||
*belowip = belowi;
|
||||
*belowp = below;
|
||||
//printf("above.%d below.%d\n",abovei,belowi);
|
||||
if ( abovei >= 0 && belowi >= 0 )
|
||||
{
|
||||
if ( above < (below >> 1) )
|
||||
@@ -195,14 +190,13 @@ int64_t NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64
|
||||
utxos[n++] = ptr[i];
|
||||
}
|
||||
remains = total;
|
||||
//fprintf(stderr,"threshold %.8f n.%d for total %.8f\n",(double)threshold/COIN,n,(double)total/COIN);
|
||||
for (i=0; i<maxinputs && n>0; i++)
|
||||
{
|
||||
below = above = 0;
|
||||
abovei = belowi = -1;
|
||||
if ( NSPV_vinselect(&abovei,&above,&belowi,&below,utxos,n,remains) < 0 )
|
||||
{
|
||||
fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN);
|
||||
LogPrintf("error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN);
|
||||
return(0);
|
||||
}
|
||||
if ( belowi < 0 || abovei >= 0 )
|
||||
@@ -210,10 +204,9 @@ int64_t NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64
|
||||
else ind = belowi;
|
||||
if ( ind < 0 )
|
||||
{
|
||||
fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind);
|
||||
LogPrintf("error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind);
|
||||
return(0);
|
||||
}
|
||||
//fprintf(stderr,"i.%d ind.%d abovei.%d belowi.%d n.%d\n",i,ind,abovei,belowi,n);
|
||||
up = &utxos[ind];
|
||||
mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript()));
|
||||
used[i] = *up;
|
||||
@@ -221,11 +214,9 @@ int64_t NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64
|
||||
remains -= up->satoshis;
|
||||
utxos[ind] = utxos[--n];
|
||||
memset(&utxos[n],0,sizeof(utxos[n]));
|
||||
//fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs);
|
||||
if ( totalinputs >= total || (i+1) >= maxinputs )
|
||||
break;
|
||||
}
|
||||
//fprintf(stderr,"totalinputs %.8f vs total %.8f\n",(double)totalinputs/COIN,(double)total/COIN);
|
||||
if ( totalinputs >= total )
|
||||
return(totalinputs);
|
||||
return(0);
|
||||
@@ -236,21 +227,20 @@ bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const C
|
||||
CTransaction txNewConst(mtx); SignatureData sigdata; CBasicKeyStore keystore; int64_t branchid = NSPV_BRANCHID;
|
||||
if ( NSPV_logintime == 0 || time(NULL) > NSPV_logintime+NSPV_AUTOLOGOUT )
|
||||
{
|
||||
fprintf(stderr,"need to be logged in to get myprivkey\n");
|
||||
LogPrintf("need to be logged in to get myprivkey\n");
|
||||
return false;
|
||||
}
|
||||
keystore.AddKey(NSPV_key);
|
||||
if ( nTime != 0 && nTime < HUSH_SAPING_ACTIVATION )
|
||||
{
|
||||
fprintf(stderr,"use legacy sig validation\n");
|
||||
LogPrintf("use legacy sig validation\n");
|
||||
branchid = 0;
|
||||
}
|
||||
if ( ProduceSignature(TransactionSignatureCreator(&keystore,&txNewConst,vini,utxovalue,SIGHASH_ALL),scriptPubKey,sigdata,branchid) != 0 )
|
||||
{
|
||||
UpdateTransaction(mtx,vini,sigdata);
|
||||
fprintf(stderr,"SIG_TXHASH %s vini.%d %.8f\n",SIG_TXHASH.GetHex().c_str(),vini,(double)utxovalue/COIN);
|
||||
return(true);
|
||||
} //else fprintf(stderr,"sigerr SIG_TXHASH %s vini.%d %.8f\n",SIG_TXHASH.GetHex().c_str(),vini,(double)utxovalue/COIN);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
@@ -285,22 +275,21 @@ std::string NSPV_signtx(int64_t &rewardsum,int64_t &interestsum,UniValue &retcod
|
||||
{
|
||||
if ( vintx.vout[utxovout].nValue != used[i].satoshis )
|
||||
{
|
||||
fprintf(stderr,"vintx mismatch %.8f != %.8f\n",(double)vintx.vout[utxovout].nValue/COIN,(double)used[i].satoshis/COIN);
|
||||
LogPrintf("vintx mismatch %.8f != %.8f\n",(double)vintx.vout[utxovout].nValue/COIN,(double)used[i].satoshis/COIN);
|
||||
return("");
|
||||
}
|
||||
else if ( utxovout != used[i].vout )
|
||||
{
|
||||
fprintf(stderr,"vintx vout mismatch %d != %d\n",utxovout,used[i].vout);
|
||||
LogPrintf("vintx vout mismatch %d != %d\n",utxovout,used[i].vout);
|
||||
return("");
|
||||
}
|
||||
else if ( NSPV_SignTx(mtx,i,vintx.vout[utxovout].nValue,vintx.vout[utxovout].scriptPubKey,0) == 0 )
|
||||
{
|
||||
fprintf(stderr,"signing error for vini.%d\n",i);
|
||||
LogPrintf("signing error for vini.%d\n",i);
|
||||
return("");
|
||||
}
|
||||
} else fprintf(stderr,"couldnt find txid.%s/v%d or it was spent\n",mtx.vin[i].prevout.hash.GetHex().c_str(),utxovout); // of course much better handling is needed
|
||||
} else LogPrintf("couldnt find txid.%s/v%d or it was spent\n",mtx.vin[i].prevout.hash.GetHex().c_str(),utxovout); // of course much better handling is needed
|
||||
}
|
||||
fprintf(stderr,"sign %d inputs %.8f + interest %.8f -> %d outputs %.8f change %.8f\n",(int32_t)mtx.vin.size(),(double)totalinputs/COIN,(double)interest/COIN,(int32_t)mtx.vout.size(),(double)totaloutputs/COIN,(double)change/COIN);
|
||||
return(EncodeHexTx(mtx));
|
||||
}
|
||||
|
||||
@@ -360,7 +349,6 @@ UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis) // what its a
|
||||
result.push_back(Pair("amount",(double)satoshis/COIN));
|
||||
return(result);
|
||||
}
|
||||
printf("%s numutxos.%d balance %.8f\n",NSPV_utxosresult.coinaddr,NSPV_utxosresult.numutxos,(double)NSPV_utxosresult.total/COIN);
|
||||
CScript opret; std::string hex; struct NSPV_utxoresp used[NSPV_MAXVINS]; CMutableTransaction mtx; CTransaction tx; int64_t rewardsum=0,interestsum=0;
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nExpiryHeight = 0;
|
||||
@@ -428,7 +416,7 @@ int64_t NSPV_AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total
|
||||
NSPV_utxosresp_purge(&ptr->U);
|
||||
NSPV_utxosresp_copy(&ptr->U,&NSPV_utxosresult);
|
||||
// }
|
||||
fprintf(stderr,"%s numutxos.%d\n",ptr->U.coinaddr,ptr->U.numutxos);
|
||||
LogPrintf("%s numutxos.%d\n",ptr->U.coinaddr,ptr->U.numutxos);
|
||||
memset(ptr->used,0,sizeof(ptr->used));
|
||||
return(NSPV_addinputs(ptr->used,mtx,total,maxinputs,ptr->U.utxos,ptr->U.numutxos));
|
||||
} else return(0);
|
||||
@@ -442,7 +430,7 @@ void NSPV_utxos2CCunspents(struct NSPV_utxosresp *ptr,std::vector<std::pair<CAdd
|
||||
CBitcoinAddress address(addrstr);
|
||||
if ( address.GetIndexKey(hashBytes, type, ptr->CCflag) == 0 )
|
||||
{
|
||||
fprintf(stderr,"couldnt get indexkey\n");
|
||||
LogPrintf("couldnt get indexkey\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < ptr->numutxos; i ++)
|
||||
@@ -466,7 +454,7 @@ void NSPV_txids2CCtxids(struct NSPV_txidsresp *ptr,std::vector<std::pair<CAddres
|
||||
CBitcoinAddress address(addrstr);
|
||||
if ( address.GetIndexKey(hashBytes, type, ptr->CCflag) == 0 )
|
||||
{
|
||||
fprintf(stderr,"couldnt get indexkey\n");
|
||||
LogPrintf("couldnt get indexkey\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < ptr->numtxids; i ++)
|
||||
|
||||
+111
-114
@@ -770,19 +770,15 @@ int32_t bitcoin_addr2rmd160(uint8_t *addrtypep,uint8_t rmd160[20],char *coinaddr
|
||||
memcpy(rmd160,buf+1,20);
|
||||
if ( (buf[21]&0xff) == hash.bytes[31] && (buf[22]&0xff) == hash.bytes[30] &&(buf[23]&0xff) == hash.bytes[29] && (buf[24]&0xff) == hash.bytes[28] )
|
||||
{
|
||||
//printf("coinaddr.(%s) valid checksum addrtype.%02x\n",coinaddr,*addrtypep);
|
||||
return(20);
|
||||
}
|
||||
else
|
||||
{
|
||||
int32_t i;
|
||||
if ( len > 20 )
|
||||
{
|
||||
hash = bits256_doublesha256(0,buf,len);
|
||||
}
|
||||
for (i=0; i<len; i++)
|
||||
printf("%02x ",buf[i]);
|
||||
printf("\nhex checkhash.(%s) len.%d mismatch %02x %02x %02x %02x vs %02x %02x %02x %02x\n",coinaddr,len,buf[len-1]&0xff,buf[len-2]&0xff,buf[len-3]&0xff,buf[len-4]&0xff,hash.bytes[31],hash.bytes[30],hash.bytes[29],hash.bytes[28]);
|
||||
LogPrintf("\nhex checkhash.(%s) len.%d mismatch %02x %02x %02x %02x vs %02x %02x %02x %02x\n",coinaddr,len,buf[len-1]&0xff,buf[len-2]&0xff,buf[len-3]&0xff,buf[len-4]&0xff,hash.bytes[31],hash.bytes[30],hash.bytes[29],hash.bytes[28]);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
@@ -801,10 +797,6 @@ char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,
|
||||
data[21+i] = hash.bytes[31-i];
|
||||
if ( (coinaddr= bitcoin_base58encode(coinaddr,data,25)) != 0 )
|
||||
{
|
||||
//uint8_t checktype,rmd160[20];
|
||||
//bitcoin_addr2rmd160(&checktype,rmd160,coinaddr);
|
||||
//if ( strcmp(checkaddr,coinaddr) != 0 )
|
||||
// printf("checkaddr.(%s) vs coinaddr.(%s) %02x vs [%02x] memcmp.%d\n",checkaddr,coinaddr,addrtype,checktype,memcmp(rmd160,data+1,20));
|
||||
}
|
||||
return(coinaddr);
|
||||
}
|
||||
@@ -858,7 +850,7 @@ int32_t unhex(char c)
|
||||
int32_t hex;
|
||||
if ( (hex= _unhex(c)) < 0 )
|
||||
{
|
||||
fprintf(stderr,"unhex: illegal hexchar.(%c)\n",c);
|
||||
LogPrintf("unhex: illegal hexchar.(%c)\n",c);
|
||||
}
|
||||
return(hex);
|
||||
}
|
||||
@@ -868,7 +860,6 @@ unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])
|
||||
int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex)
|
||||
{
|
||||
int32_t adjust,i = 0;
|
||||
//printf("decode.(%s)\n",hex);
|
||||
if ( is_hexstr(hex,n) <= 0 )
|
||||
{
|
||||
memset(bytes,0,n);
|
||||
@@ -881,7 +872,7 @@ int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex)
|
||||
if ( n > 0 )
|
||||
{
|
||||
bytes[0] = unhex(hex[0]);
|
||||
printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex));
|
||||
LogPrintf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex));
|
||||
}
|
||||
bytes++;
|
||||
hex++;
|
||||
@@ -918,10 +909,8 @@ int32_t init_hexbytes_noT(char *hexbytes,unsigned char *message,long len)
|
||||
{
|
||||
hexbytes[i*2] = hexbyte((message[i]>>4) & 0xf);
|
||||
hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf);
|
||||
//printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]);
|
||||
}
|
||||
hexbytes[len*2] = 0;
|
||||
//printf("len.%ld\n",len*2+1);
|
||||
return((int32_t)len*2+1);
|
||||
}
|
||||
|
||||
@@ -1087,7 +1076,7 @@ char *clonestr(char *str)
|
||||
char *clone;
|
||||
if ( str == 0 || str[0] == 0 )
|
||||
{
|
||||
printf("warning cloning nullstr.%p\n",str);
|
||||
LogPrintf("warning cloning nullstr.%p\n",str);
|
||||
#ifdef __APPLE__
|
||||
while ( 1 ) sleep(1);
|
||||
#endif
|
||||
@@ -1109,7 +1098,7 @@ int32_t safecopy(char *dest,char *src,long len)
|
||||
dest[i] = src[i];
|
||||
if ( i == len )
|
||||
{
|
||||
printf("safecopy: %s too long %ld\n",src,len);
|
||||
LogPrintf("safecopy: %s too long %ld\n",src,len);
|
||||
#ifdef __APPLE__
|
||||
//getchar();
|
||||
#endif
|
||||
@@ -1131,7 +1120,6 @@ char *parse_conf_line(char *line,char *field)
|
||||
line++;
|
||||
while ( line[strlen(line)-1] == '\r' || line[strlen(line)-1] == '\n' || line[strlen(line)-1] == ' ' )
|
||||
line[strlen(line)-1] = 0;
|
||||
//printf("LINE.(%s)\n",line);
|
||||
_stripwhite(line,0);
|
||||
return(clonestr(line));
|
||||
}
|
||||
@@ -1141,7 +1129,6 @@ double OS_milliseconds()
|
||||
struct timeval tv; double millis;
|
||||
gettimeofday(&tv,NULL);
|
||||
millis = ((double)tv.tv_sec * 1000. + (double)tv.tv_usec / 1000.);
|
||||
//printf("tv_sec.%ld usec.%d %f\n",tv.tv_sec,tv.tv_usec,millis);
|
||||
return(millis);
|
||||
}
|
||||
|
||||
@@ -1193,7 +1180,7 @@ void queue_enqueue(char *name,queue_t *queue,struct queueitem *item)
|
||||
strcpy(queue->name,name);
|
||||
if ( item == 0 )
|
||||
{
|
||||
printf("FATAL type error: queueing empty value\n");
|
||||
LogPrintf("FATAL type error: queueing empty value\n");
|
||||
return;
|
||||
}
|
||||
lock_queue(queue);
|
||||
@@ -1230,7 +1217,7 @@ void *queue_delete(queue_t *queue,struct queueitem *copy,int32_t copysize)
|
||||
{
|
||||
DL_DELETE(queue->list,item);
|
||||
portable_mutex_unlock(&queue->mutex);
|
||||
printf("name.(%s) deleted item.%p list.%p\n",queue->name,item,queue->list);
|
||||
LogPrintf("name.(%s) deleted item.%p list.%p\n",queue->name,item,queue->list);
|
||||
return(item);
|
||||
}
|
||||
}
|
||||
@@ -1250,7 +1237,6 @@ void *queue_free(queue_t *queue)
|
||||
DL_DELETE(queue->list,item);
|
||||
free(item);
|
||||
}
|
||||
//printf("name.(%s) dequeue.%p list.%p\n",queue->name,item,queue->list);
|
||||
}
|
||||
portable_mutex_unlock(&queue->mutex);
|
||||
return(0);
|
||||
@@ -1268,7 +1254,6 @@ void *queue_clone(queue_t *clone,queue_t *queue,int32_t size)
|
||||
memcpy(ptr,item,size);
|
||||
queue_enqueue(queue->name,clone,ptr);
|
||||
}
|
||||
//printf("name.(%s) dequeue.%p list.%p\n",queue->name,item,queue->list);
|
||||
}
|
||||
portable_mutex_unlock(&queue->mutex);
|
||||
return(0);
|
||||
@@ -1304,7 +1289,6 @@ uint16_t _hush_userpass(char *username,char *password,FILE *fp)
|
||||
{
|
||||
if ( line[0] == '#' )
|
||||
continue;
|
||||
//printf("line.(%s) %p %p\n",line,strstr(line,(char *)"rpcuser"),strstr(line,(char *)"rpcpassword"));
|
||||
if ( (str= strstr(line,(char *)"rpcuser")) != 0 )
|
||||
rpcuser = parse_conf_line(str,(char *)"rpcuser");
|
||||
else if ( (str= strstr(line,(char *)"rpcpassword")) != 0 )
|
||||
@@ -1312,7 +1296,6 @@ uint16_t _hush_userpass(char *username,char *password,FILE *fp)
|
||||
else if ( (str= strstr(line,(char *)"rpcport")) != 0 )
|
||||
{
|
||||
port = atoi(parse_conf_line(str,(char *)"rpcport"));
|
||||
//fprintf(stderr,"rpcport.%u in file\n",port);
|
||||
}
|
||||
}
|
||||
if ( rpcuser != 0 && rpcpassword != 0 )
|
||||
@@ -1320,7 +1303,6 @@ uint16_t _hush_userpass(char *username,char *password,FILE *fp)
|
||||
strcpy(username,rpcuser);
|
||||
strcpy(password,rpcpassword);
|
||||
}
|
||||
//printf("rpcuser.(%s) rpcpassword.(%s) HUSHUSERPASS.(%s) %u\n",rpcuser,rpcpassword,HUSHUSERPASS,port);
|
||||
if ( rpcuser != 0 )
|
||||
free(rpcuser);
|
||||
if ( rpcpassword != 0 )
|
||||
@@ -1340,7 +1322,7 @@ void hush_statefname(char *fname,char *symbol,char *str)
|
||||
else
|
||||
{
|
||||
if ( strcmp(symbol,"ZZZ") != 0 )
|
||||
printf("unexpected fname.(%s) vs %s [%s] n.%d len.%d (%s)\n",fname,symbol,SMART_CHAIN_SYMBOL,n,len,&fname[len - n]);
|
||||
LogPrintf("unexpected fname.(%s) vs %s [%s] n.%d len.%d (%s)\n",fname,symbol,SMART_CHAIN_SYMBOL,n,len,&fname[len - n]);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -1353,7 +1335,6 @@ void hush_statefname(char *fname,char *symbol,char *str)
|
||||
if ( symbol != 0 && symbol[0] != 0)
|
||||
{
|
||||
strcat(fname,symbol);
|
||||
//printf("statefname.(%s) -> (%s)\n",symbol,fname);
|
||||
#ifdef _WIN32
|
||||
strcat(fname,"\\");
|
||||
#else
|
||||
@@ -1361,7 +1342,6 @@ void hush_statefname(char *fname,char *symbol,char *str)
|
||||
#endif
|
||||
}
|
||||
strcat(fname,str);
|
||||
//printf("test.(%s) -> [%s] statename.(%s) %s\n",test,SMART_CHAIN_SYMBOL,symbol,fname);
|
||||
}
|
||||
|
||||
void hush_configfile(char *symbol,uint16_t rpcport)
|
||||
@@ -1398,14 +1378,19 @@ void hush_configfile(char *symbol,uint16_t rpcport)
|
||||
{
|
||||
fprintf(fp,"rpcuser=user%u\nrpcpassword=pass%s\nrpcport=%u\nserver=1\ntxindex=1\nrpcworkqueue=4096\nrpcallowip=127.0.0.1\nrpcbind=127.0.0.1\n",crc,password,rpcport);
|
||||
fclose(fp);
|
||||
printf("Created (%s)\n",fname);
|
||||
} else printf("Couldnt create (%s)\n",fname);
|
||||
LogPrintf("Created (%s)\n",fname);
|
||||
} else LogPrintf("Couldnt create (%s)\n",fname);
|
||||
#endif
|
||||
} else {
|
||||
_hush_userpass(myusername,mypassword,fp);
|
||||
mapArgs["-rpcpassword"] = mypassword;
|
||||
mapArgs["-rpcusername"] = myusername;
|
||||
//fprintf(stderr,"myusername.(%s)\n",myusername);
|
||||
// Feed the credentials read by InitRPCAuthentication (httprpc.cpp) and the
|
||||
// CLI (bitcoin-cli.cpp) -- both read "-rpcuser"/"-rpcpassword". Use SoftSetArg
|
||||
// so a value passed on the command line (or an explicit -rpcuser/-rpcpassword)
|
||||
// still wins: the old direct assignment silently overwrote a command-line
|
||||
// -rpcpassword on every restart once this conf existed, and the username was
|
||||
// written to a misspelled "-rpcusername" key that nothing ever reads.
|
||||
SoftSetArg("-rpcpassword", mypassword);
|
||||
SoftSetArg("-rpcuser", myusername);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
@@ -1429,9 +1414,8 @@ void hush_configfile(char *symbol,uint16_t rpcport)
|
||||
DRAGONX_PORT = hushport;
|
||||
sprintf(HUSHUSERPASS,"%s:%s",username,password);
|
||||
fclose(fp);
|
||||
//printf("HUSH.(%s) -> userpass.(%s)\n",fname,HUSHUSERPASS);
|
||||
} else {
|
||||
printf("could not open.(%s)\n",fname);
|
||||
LogPrintf("could not open.(%s)\n",fname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1466,13 +1450,13 @@ uint32_t hush_smartmagic(char *symbol,uint64_t supply,uint8_t *extraptr,int32_t
|
||||
{
|
||||
vcalc_sha256(0,hash.bytes,extraptr,extralen);
|
||||
crc0 = hash.uints[0];
|
||||
fprintf(stderr,"DragonX raw magic=");
|
||||
int32_t i; for (i=0; i<extralen; i++)
|
||||
fprintf(stderr,"%02x",extraptr[i]);
|
||||
fprintf(stderr," extralen=%d crc0=%x\n",extralen,crc0);
|
||||
LogPrintf("DragonX raw magic extralen=%d crc0=%x\n",extralen,crc0);
|
||||
}
|
||||
|
||||
//TODO: why is this needed?
|
||||
// Legacy special case: HUSH3 mainnet had a hardcoded network magic (HUSH_MAGIC)
|
||||
// rather than the crc32-derived value used by every other chain. This branch is
|
||||
// dead on DragonX (SMART_CHAIN_SYMBOL is "DRAGONX", never "HUSH3"); it is kept only
|
||||
// so the function still reproduces HUSH3's magic if ever run with that symbol.
|
||||
const bool ishush3 = strncmp(symbol, "HUSH3",5) == 0 ? true : false;
|
||||
if(ishush3) {
|
||||
return HUSH_MAGIC;
|
||||
@@ -1497,8 +1481,7 @@ uint16_t hush_port(char *symbol,uint64_t supply,uint32_t *magicp,uint8_t *extrap
|
||||
fprintf(stderr,"%s: extralen=%d\n",__func__,extralen);
|
||||
|
||||
*magicp = hush_smartmagic(symbol,supply,extraptr,extralen);
|
||||
//if(fDebug)
|
||||
fprintf(stderr,"%s: extralen=%d, supply=%lu\n",__func__,extralen, supply);
|
||||
LogPrintf("%s: extralen=%d, supply=%lu\n",__func__,extralen, supply);
|
||||
|
||||
return(hush_smartport(*magicp,extralen));
|
||||
}
|
||||
@@ -1519,16 +1502,19 @@ uint64_t hush_max_money()
|
||||
return hush_current_supply(10000000);
|
||||
}
|
||||
|
||||
// This implements the Hush Emission Curve, the miner subsidy part,
|
||||
// and must be kept in sync with hush_commision() in hush_bitcoind.h!
|
||||
// Changing these functions are consensus changes!
|
||||
// Here Be Dragons! -- Duke Leto
|
||||
// This implements the emission curve (miner subsidy part) and must be kept in
|
||||
// sync with hush_commission() in hush_bitcoind.h! Changing these functions,
|
||||
// including the height literals below, is a CONSENSUS change.
|
||||
// NOTE: this TRANSITION boundary is 128 here, while hush_commission() uses 129.
|
||||
// This off-by-one between the two curves is a historical consensus quirk and is
|
||||
// deliberately left as-is: changing either value would be a consensus change.
|
||||
uint64_t hush_block_subsidy(int height)
|
||||
{
|
||||
uint64_t subsidy = 0;
|
||||
int32_t HALVING1 = GetArg("-z2zheight",340000);
|
||||
//TODO: support INTERVAL :(
|
||||
//int32_t INTERVAL = GetArg("-ac_halving1",840000);
|
||||
// Consensus: TRANSITION is 128 here vs 129 in hush_commission(); do not change (see note above).
|
||||
int32_t TRANSITION = 128;
|
||||
|
||||
if (height < TRANSITION) {
|
||||
@@ -1564,14 +1550,14 @@ uint64_t hush_block_subsidy(int height)
|
||||
subsidy = 549316;
|
||||
} else if (height < 23860000) {
|
||||
subsidy = 274658;
|
||||
} else if (height < 23860000) {
|
||||
subsidy = 137329;
|
||||
// removed unreachable duplicate `height < 23860000` (=> 137329); kept in sync
|
||||
// with hush_commission() — the schedule drops straight to 68664 next.
|
||||
} else if (height < 25540000) {
|
||||
subsidy = 68664;
|
||||
} else if (height < 27220000) {
|
||||
subsidy = 34332;
|
||||
} else if (height < 27220000) {
|
||||
subsidy = 17166;
|
||||
// removed unreachable duplicate `height < 27220000` (=> 17166); kept in sync
|
||||
// with hush_commission() — the schedule drops straight to 8583 next.
|
||||
} else if (height < 28900000) {
|
||||
subsidy = 8583;
|
||||
} else if (height < 30580000) {
|
||||
@@ -1611,7 +1597,9 @@ uint64_t hush_block_subsidy(int height)
|
||||
return subsidy;
|
||||
}
|
||||
|
||||
// wrapper for more general supply curves of Hush Arrakis Chains
|
||||
// Wrapper for the more general supply curves used by assetchains (era/halving/decay driven).
|
||||
// On DragonX the reward comes from the -ac_reward/-ac_halving parameters set in hush_args();
|
||||
// the ishush3 branch below is a legacy special case that is dead on DragonX.
|
||||
uint64_t hush_sc_block_subsidy(int nHeight)
|
||||
{
|
||||
// Find current era, start from beginning reward, and determine current subsidy
|
||||
@@ -1619,12 +1607,13 @@ uint64_t hush_sc_block_subsidy(int nHeight)
|
||||
int64_t subsidyDifference;
|
||||
int32_t numhalvings = 0, curEra = 0, sign = 1;
|
||||
static uint64_t cached_subsidy; static int32_t cached_numhalvings; static int cached_era;
|
||||
// Legacy-HUSH3 detection: dead on DragonX (SMART_CHAIN_SYMBOL is "DRAGONX"), used only
|
||||
// to route HUSH3 mainnet through its bespoke hush_block_subsidy() emission curve below.
|
||||
const bool ishush3 = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? true : false;
|
||||
// fprintf(stderr,"%s: ht=%d ishush3=%d\n", __func__, nHeight, ishush3);
|
||||
|
||||
// check for backwards compat, older chains with no explicit rewards had 0.0001 block reward
|
||||
if ( ASSETCHAINS_ENDSUBSIDY[0] == 0 && ASSETCHAINS_REWARD[0] == 0 ) {
|
||||
fprintf(stderr,"%s: defaulting to 0.0001 subsidy\n",__func__);
|
||||
LogPrintf("%s: defaulting to 0.0001 subsidy\n",__func__);
|
||||
subsidy = 10000;
|
||||
} else if ( (ASSETCHAINS_ENDSUBSIDY[0] == 0 && ASSETCHAINS_REWARD[0] != 0) || ASSETCHAINS_ENDSUBSIDY[0] != 0 ) {
|
||||
// if we have an end block in the first era, find our current era
|
||||
@@ -1656,10 +1645,11 @@ uint64_t hush_sc_block_subsidy(int nHeight)
|
||||
if(fDebug)
|
||||
fprintf(stderr,"%s: HUSH3 subsidy=%ld at height=%d\n",__func__,subsidy,nHeight);
|
||||
} else if ( (numhalvings = ((nHeight - nStart) / ASSETCHAINS_HALVING[curEra])) > 0 ) {
|
||||
// The code below is not compatible with HUSH3 mainnet
|
||||
// Generic halving/decay path used by DragonX and other assetchains.
|
||||
// (Legacy HUSH3 mainnet did NOT use this path; it took the ishush3
|
||||
// branch above, which reproduces its bespoke emission curve.)
|
||||
if ( ASSETCHAINS_DECAY[curEra] == 0 ) {
|
||||
subsidy >>= numhalvings;
|
||||
// fprintf(stderr,"%s: no decay, numhalvings.%d curEra.%d subsidy.%ld nStart.%ld\n",__func__, numhalvings, curEra, subsidy, nStart);
|
||||
} else if ( ASSETCHAINS_DECAY[curEra] == 100000000 && ASSETCHAINS_ENDSUBSIDY[curEra] != 0 ) {
|
||||
if ( curEra == ASSETCHAINS_LASTERA )
|
||||
{
|
||||
@@ -1675,12 +1665,11 @@ uint64_t hush_sc_block_subsidy(int nHeight)
|
||||
}
|
||||
denominator = ASSETCHAINS_ENDSUBSIDY[curEra] - nStart;
|
||||
numerator = denominator - ((ASSETCHAINS_ENDSUBSIDY[curEra] - nHeight) + ((nHeight - nStart) % ASSETCHAINS_HALVING[curEra]));
|
||||
// fprintf(stderr,"%s: numerator=%ld , denominator=%ld at height=%d\n",__func__,numerator, denominator,nHeight);
|
||||
if( denominator ) {
|
||||
subsidy = subsidy - sign * ((subsidyDifference * numerator) / denominator);
|
||||
} else {
|
||||
fprintf(stderr,"%s: invalid denominator=%ld !\n", __func__, denominator);
|
||||
fprintf(stderr,"%s: defaulting to 0.0001 subsidy\n",__func__);
|
||||
LogPrintf("%s: invalid denominator=%ld !\n", __func__, denominator);
|
||||
LogPrintf("%s: defaulting to 0.0001 subsidy\n",__func__);
|
||||
subsidy = 10000;
|
||||
}
|
||||
} else {
|
||||
@@ -1698,13 +1687,13 @@ uint64_t hush_sc_block_subsidy(int nHeight)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,"%s: curEra.%d > lastEra.%lu\n", __func__, curEra, ASSETCHAINS_LASTERA);
|
||||
LogPrintf("%s: curEra.%d > lastEra.%lu\n", __func__, curEra, ASSETCHAINS_LASTERA);
|
||||
}
|
||||
}
|
||||
uint32_t magicExtra = ASSETCHAINS_STAKED ? ASSETCHAINS_MAGIC : (ASSETCHAINS_MAGIC & 0xffffff);
|
||||
if ( ASSETCHAINS_SUPPLY > 10000000000 ) // over 10 billion?
|
||||
{
|
||||
fprintf(stderr,"%s: Detected supply over 10 billion, danger zone!\n",__func__);
|
||||
LogPrintf("%s: Detected supply over 10 billion, danger zone!\n",__func__);
|
||||
if ( nHeight <= ASSETCHAINS_SUPPLY/1000000000 )
|
||||
{
|
||||
subsidy += (uint64_t)1000000000 * COIN;
|
||||
@@ -1782,7 +1771,7 @@ void hush_args(char *argv0)
|
||||
IS_HUSH_NOTARY = 1;
|
||||
HUSH_MININGTHREADS = 1;
|
||||
mapArgs ["-genproclimit"] = itostr(HUSH_MININGTHREADS);
|
||||
fprintf(stderr,"running as notary.%d %s\n",i,notaries_list[hush_season-1][i][0]);
|
||||
LogPrintf("running as notary.%d %s\n",i,notaries_list[hush_season-1][i][0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1794,19 +1783,38 @@ void hush_args(char *argv0)
|
||||
fprintf(stderr,".oO Starting %s Full Node (Extreme Privacy!) with genproc=%d notary=%d\n",name.c_str(),HUSH_MININGTHREADS, IS_HUSH_NOTARY);
|
||||
|
||||
vector<string> DRAGONX_nodes = {};
|
||||
// Only DRAGONX connects to these by default, other chains must opt-in via -connect/-addnode
|
||||
// Only DRAGONX connects to these by default, other chains must opt-in via -connect/-addnode.
|
||||
// Never on regtest or testnet: regtest reuses mainnet's network magic, so injecting the mainnet
|
||||
// seeds here makes a supposedly isolated node handshake production peers and pull their headers
|
||||
// into its own index -- which is exactly what it did, and why multi-node rpc-tests were talking
|
||||
// to the live chain. NOTE: hush_args() runs between ParseParameters() and ReadConfigFile()
|
||||
// (bitcoind.cpp:115/144/158), so this sees a command-line -regtest (how qa/rpc-tests starts
|
||||
// nodes) but NOT a bare "regtest=1" in the config file.
|
||||
const bool isdragonx = strncmp(name.c_str(), "DRAGONX",7) == 0 ? true : false;
|
||||
|
||||
LogPrint("net", "%s: isdragonx=%d\n", __func__, isdragonx);
|
||||
if (isdragonx) {
|
||||
// Seed injection only -- isdragonx itself must stay true here, because it also selects
|
||||
// ac_private, ac_algo, blocktime and the reward/halving schedule below.
|
||||
const bool isnotmainnet = GetBoolArg("-regtest", false) || GetBoolArg("-testnet", false);
|
||||
|
||||
LogPrint("net", "%s: isdragonx=%d isnotmainnet=%d\n", __func__, isdragonx, isnotmainnet);
|
||||
if (isdragonx && !isnotmainnet) {
|
||||
// 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"
|
||||
};
|
||||
}
|
||||
|
||||
vector<string> more_nodes = mapMultiArgs["-addnode"];
|
||||
if (more_nodes.size() > 0) {
|
||||
fprintf(stderr,"%s: Adding %lu more nodes via custom -addnode arguments\n", __func__, more_nodes.size() );
|
||||
LogPrint("net", "%s: Adding %lu more nodes via custom -addnode arguments\n", __func__, more_nodes.size() );
|
||||
}
|
||||
// Add default DRAGONX nodes after custom addnodes, if applicable
|
||||
if(DRAGONX_nodes.size() > 0) {
|
||||
@@ -1848,19 +1856,19 @@ void hush_args(char *argv0)
|
||||
if ( i > 1 && ccEnablesHeight[i-2] == ecode )
|
||||
break;
|
||||
if ( ecode > 255 || ecode < 0 )
|
||||
fprintf(stderr, "ac_ccactivateht: invalid evalcode.%i must be between 0 and 256.\n", ecode);
|
||||
LogPrintf("ac_ccactivateht: invalid evalcode.%i must be between 0 and 256.\n", ecode);
|
||||
else if ( ht > 0 )
|
||||
{
|
||||
// update global map.
|
||||
mapHeightEvalActivate[ecode] = ht;
|
||||
fprintf(stderr, "ac_ccactivateht: ecode.%i activates at height.%i\n", ecode, mapHeightEvalActivate[ecode]);
|
||||
LogPrintf("ac_ccactivateht: ecode.%i activates at height.%i\n", ecode, mapHeightEvalActivate[ecode]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if ( (HUSH_REWIND= GetArg("-rewind",0)) != 0 )
|
||||
{
|
||||
printf("HUSH_REWIND %d\n",HUSH_REWIND);
|
||||
LogPrintf("HUSH_REWIND %d\n",HUSH_REWIND);
|
||||
}
|
||||
HUSH_EARLYTXID = Parseuint256(GetArg("-earlytxid","0").c_str());
|
||||
ASSETCHAINS_EARLYTXIDCONTRACT = GetArg("-ac_earlytxidcontract",0);
|
||||
@@ -1878,7 +1886,7 @@ void hush_args(char *argv0)
|
||||
STAKING_MIN_DIFF = ASSETCHAINS_MINDIFF[i];
|
||||
// only worth mentioning if it's not equihash
|
||||
if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH)
|
||||
printf("ASSETCHAINS_ALGO, algorithm set to %s\n", selectedAlgo.c_str());
|
||||
LogPrintf("ASSETCHAINS_ALGO, algorithm set to %s\n", selectedAlgo.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1888,11 +1896,11 @@ void hush_args(char *argv0)
|
||||
{
|
||||
printf("equihash values N.%li and K.%li are not currently available\n", ASSETCHAINS_NK[0], ASSETCHAINS_NK[1]);
|
||||
exit(0);
|
||||
} else printf("ASSETCHAINS_ALGO, algorithm set to equihash with N.%li and K.%li\n", ASSETCHAINS_NK[0], ASSETCHAINS_NK[1]);
|
||||
} else LogPrintf("ASSETCHAINS_ALGO, algorithm set to equihash with N.%li and K.%li\n", ASSETCHAINS_NK[0], ASSETCHAINS_NK[1]);
|
||||
}
|
||||
if (i == ASSETCHAINS_NUMALGOS)
|
||||
{
|
||||
printf("ASSETCHAINS_ALGO, %s not supported. using equihash\n", selectedAlgo.c_str());
|
||||
LogPrintf("ASSETCHAINS_ALGO, %s not supported. using equihash\n", selectedAlgo.c_str());
|
||||
}
|
||||
|
||||
// Set our symbol from -ac_name value
|
||||
@@ -1907,14 +1915,14 @@ void hush_args(char *argv0)
|
||||
} else {
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = 1; // all other RandomX HACs: enforce from height 1
|
||||
}
|
||||
printf("ASSETCHAINS_RANDOMX_VALIDATION set to %d for %s\n", ASSETCHAINS_RANDOMX_VALIDATION, SMART_CHAIN_SYMBOL);
|
||||
LogPrintf("ASSETCHAINS_RANDOMX_VALIDATION set to %d for %s\n", ASSETCHAINS_RANDOMX_VALIDATION, SMART_CHAIN_SYMBOL);
|
||||
}
|
||||
|
||||
ASSETCHAINS_LASTERA = GetArg("-ac_eras", 1);
|
||||
if ( ASSETCHAINS_LASTERA < 1 || ASSETCHAINS_LASTERA > ASSETCHAINS_MAX_ERAS )
|
||||
{
|
||||
ASSETCHAINS_LASTERA = 1;
|
||||
printf("ASSETCHAINS_LASTERA, if specified, must be between 1 and %u. ASSETCHAINS_LASTERA set to %lu\n", ASSETCHAINS_MAX_ERAS, ASSETCHAINS_LASTERA);
|
||||
LogPrintf("ASSETCHAINS_LASTERA, if specified, must be between 1 and %u. ASSETCHAINS_LASTERA set to %lu\n", ASSETCHAINS_MAX_ERAS, ASSETCHAINS_LASTERA);
|
||||
}
|
||||
ASSETCHAINS_LASTERA -= 1;
|
||||
if(fDebug)
|
||||
@@ -1925,7 +1933,7 @@ void hush_args(char *argv0)
|
||||
ASSETCHAINS_TIMEUNLOCKTO = GetArg("-ac_timeunlockto", 0);
|
||||
if ( ASSETCHAINS_TIMEUNLOCKFROM > ASSETCHAINS_TIMEUNLOCKTO )
|
||||
{
|
||||
printf("ASSETCHAINS_TIMELOCKGTE - must specify valid ac_timeunlockfrom and ac_timeunlockto\n");
|
||||
LogPrintf("ASSETCHAINS_TIMELOCKGTE - must specify valid ac_timeunlockfrom and ac_timeunlockto\n");
|
||||
ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
|
||||
ASSETCHAINS_TIMEUNLOCKFROM = ASSETCHAINS_TIMEUNLOCKTO = 0;
|
||||
}
|
||||
@@ -1944,7 +1952,7 @@ void hush_args(char *argv0)
|
||||
ASSETCHAINS_SCRIPTPUB = GetArg("-ac_script","");
|
||||
|
||||
|
||||
fprintf(stderr,"%s: Setting custom %s reward isdragonx=%d reward,halving,subsidy chain values...\n",__func__, SMART_CHAIN_SYMBOL, isdragonx);
|
||||
LogPrintf("%s: Setting custom %s reward isdragonx=%d reward,halving,subsidy chain values...\n",__func__, SMART_CHAIN_SYMBOL, isdragonx);
|
||||
if(isdragonx) {
|
||||
// DragonX chain parameters (previously set via wrapper script)
|
||||
// -ac_name=DRAGONX -ac_algo=randomx -ac_halving=3500000 -ac_reward=300000000 -ac_blocktime=36 -ac_private=1
|
||||
@@ -1960,12 +1968,12 @@ void hush_args(char *argv0)
|
||||
if ( ASSETCHAINS_DECAY[i] == 100000000 && ASSETCHAINS_ENDSUBSIDY == 0 )
|
||||
{
|
||||
ASSETCHAINS_DECAY[i] = 0;
|
||||
printf("ERA%u: ASSETCHAINS_DECAY of 100000000 means linear and that needs ASSETCHAINS_ENDSUBSIDY\n", i);
|
||||
LogPrintf("ERA%u: ASSETCHAINS_DECAY of 100000000 means linear and that needs ASSETCHAINS_ENDSUBSIDY\n", i);
|
||||
}
|
||||
else if ( ASSETCHAINS_DECAY[i] > 100000000 )
|
||||
{
|
||||
ASSETCHAINS_DECAY[i] = 0;
|
||||
printf("ERA%u: ASSETCHAINS_DECAY cant be more than 100000000\n", i);
|
||||
LogPrintf("ERA%u: ASSETCHAINS_DECAY cant be more than 100000000\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1991,21 +1999,15 @@ void hush_args(char *argv0)
|
||||
SplitStr(GetArg("-ac_stocks",""), ASSETCHAINS_STOCKS);
|
||||
if ( ASSETCHAINS_STOCKS.size() > 0 )
|
||||
ASSETCHAINS_CBOPRET |= 8;
|
||||
for (i=0; i<ASSETCHAINS_PRICES.size(); i++)
|
||||
fprintf(stderr,"%s ",ASSETCHAINS_PRICES[i].c_str());
|
||||
fprintf(stderr,"%d -ac_prices\n",(int32_t)ASSETCHAINS_PRICES.size());
|
||||
for (i=0; i<ASSETCHAINS_STOCKS.size(); i++)
|
||||
fprintf(stderr,"%s ",ASSETCHAINS_STOCKS[i].c_str());
|
||||
fprintf(stderr,"%d -ac_stocks\n",(int32_t)ASSETCHAINS_STOCKS.size());
|
||||
LogPrintf("%d -ac_prices\n",(int32_t)ASSETCHAINS_PRICES.size());
|
||||
LogPrintf("%d -ac_stocks\n",(int32_t)ASSETCHAINS_STOCKS.size());
|
||||
}
|
||||
hexstr = GetArg("-ac_mineropret","");
|
||||
if ( hexstr.size() != 0 )
|
||||
{
|
||||
Mineropret.resize(hexstr.size()/2);
|
||||
decode_hex(Mineropret.data(),hexstr.size()/2,(char *)hexstr.c_str());
|
||||
for (i=0; i<Mineropret.size(); i++)
|
||||
fprintf(stderr,"%02x",Mineropret[i]);
|
||||
fprintf(stderr," Mineropret\n");
|
||||
LogPrintf(" Mineropret\n");
|
||||
}
|
||||
if ( ASSETCHAINS_COMMISSION != 0 && ASSETCHAINS_FOUNDERS_REWARD != 0 )
|
||||
{
|
||||
@@ -2017,7 +2019,8 @@ void hush_args(char *argv0)
|
||||
uint8_t prevCCi = 0;
|
||||
ASSETCHAINS_CCLIB = GetArg("-ac_cclib","hush3");
|
||||
|
||||
// these are the enabled CCs on HUSH3 mainnet
|
||||
// Default CC set inherited from legacy HUSH3 mainnet; only used when a chain
|
||||
// enables CryptoConditions and does not override -ac_ccenable.
|
||||
Split(GetArg("-ac_ccenable","228,234,235,236,241"), sizeof(ccenables)/sizeof(*ccenables), ccenables, 0);
|
||||
for (i=nonz=0; i<0x100; i++)
|
||||
{
|
||||
@@ -2025,10 +2028,9 @@ void hush_args(char *argv0)
|
||||
{
|
||||
nonz++;
|
||||
prevCCi = ccenables[i];
|
||||
fprintf(stderr,"%d ",(uint8_t)(ccenables[i] & 0xff));
|
||||
}
|
||||
}
|
||||
fprintf(stderr,"nonz.%d ccenables[]\n",nonz);
|
||||
LogPrintf("nonz.%d ccenables[]\n",nonz);
|
||||
if ( nonz > 0 )
|
||||
{
|
||||
for (i=0; i<256; i++)
|
||||
@@ -2128,9 +2130,9 @@ void hush_args(char *argv0)
|
||||
if ( ASSETCHAINS_FOUNDERS_REWARD == 0 )
|
||||
{
|
||||
ASSETCHAINS_COMMISSION = 53846154; // maps to 35%
|
||||
printf("ASSETCHAINS_COMMISSION defaulted to 35%% when founders reward active\n");
|
||||
LogPrintf("ASSETCHAINS_COMMISSION defaulted to 35%% when founders reward active\n");
|
||||
} else {
|
||||
printf("ASSETCHAINS_FOUNDERS_REWARD set to %ld\n", ASSETCHAINS_FOUNDERS_REWARD);
|
||||
LogPrintf("ASSETCHAINS_FOUNDERS_REWARD set to %ld\n", ASSETCHAINS_FOUNDERS_REWARD);
|
||||
}
|
||||
/*else if ( ASSETCHAINS_SELFIMPORT.size() == 0 )
|
||||
{
|
||||
@@ -2142,12 +2144,12 @@ void hush_args(char *argv0)
|
||||
if ( ASSETCHAINS_COMMISSION != 0 )
|
||||
{
|
||||
ASSETCHAINS_COMMISSION = 0;
|
||||
printf("ASSETCHAINS_COMMISSION needs an ASSETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n");
|
||||
LogPrintf("ASSETCHAINS_COMMISSION needs an ASSETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n");
|
||||
}
|
||||
if ( ASSETCHAINS_FOUNDERS != 0 )
|
||||
{
|
||||
ASSETCHAINS_FOUNDERS = 0;
|
||||
printf("ASSETCHAINS_FOUNDERS needs an ASSETCHAINS_OVERRIDE_PUBKEY or ASSETCHAINS_SCRIPTPUB\n");
|
||||
LogPrintf("ASSETCHAINS_FOUNDERS needs an ASSETCHAINS_OVERRIDE_PUBKEY or ASSETCHAINS_SCRIPTPUB\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2215,7 +2217,7 @@ void hush_args(char *argv0)
|
||||
// NOTE: Hush does not use this, we use -ac_script to implement our FR -- Duke
|
||||
if ( ASSETCHAINS_FOUNDERS_REWARD != 0 )
|
||||
{
|
||||
fprintf(stderr, "set founders reward.%lld\n",(long long)ASSETCHAINS_FOUNDERS_REWARD);
|
||||
LogPrintf("set founders reward.%lld\n",(long long)ASSETCHAINS_FOUNDERS_REWARD);
|
||||
extralen += dragon_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS_REWARD),(void *)&ASSETCHAINS_FOUNDERS_REWARD);
|
||||
}
|
||||
}
|
||||
@@ -2224,14 +2226,12 @@ void hush_args(char *argv0)
|
||||
decode_hex(&extraptr[extralen],ASSETCHAINS_SCRIPTPUB.size()/2,(char *)ASSETCHAINS_SCRIPTPUB.c_str());
|
||||
extralen += ASSETCHAINS_SCRIPTPUB.size()/2;
|
||||
//extralen += dragon_rwnum(1,&extraptr[extralen],(int32_t)ASSETCHAINS_SCRIPTPUB.size(),(void *)ASSETCHAINS_SCRIPTPUB.c_str());
|
||||
fprintf(stderr,"append ac_script %s\n",ASSETCHAINS_SCRIPTPUB.c_str());
|
||||
LogPrintf("append ac_script %s\n",ASSETCHAINS_SCRIPTPUB.c_str());
|
||||
}
|
||||
if ( ASSETCHAINS_SELFIMPORT.size() > 0 )
|
||||
{
|
||||
memcpy(&extraptr[extralen],(char *)ASSETCHAINS_SELFIMPORT.c_str(),ASSETCHAINS_SELFIMPORT.size());
|
||||
for (i=0; i<ASSETCHAINS_SELFIMPORT.size(); i++)
|
||||
fprintf(stderr,"%c",extraptr[extralen+i]);
|
||||
fprintf(stderr," selfimport\n");
|
||||
LogPrintf(" selfimport\n");
|
||||
extralen += ASSETCHAINS_SELFIMPORT.size();
|
||||
}
|
||||
if ( ASSETCHAINS_BEAMPORT != 0 )
|
||||
@@ -2241,7 +2241,7 @@ void hush_args(char *argv0)
|
||||
if ( ASSETCHAINS_MARMARA != 0 )
|
||||
extraptr[extralen++] = ASSETCHAINS_MARMARA;
|
||||
|
||||
fprintf(stderr,"extralen.%d before disable bits\n",extralen);
|
||||
LogPrintf("extralen.%d before disable bits\n",extralen);
|
||||
|
||||
if ( nonz > 0 ) {
|
||||
memcpy(&extraptr[extralen],disablebits,sizeof(disablebits));
|
||||
@@ -2252,14 +2252,13 @@ void hush_args(char *argv0)
|
||||
for (i=0; i<ASSETCHAINS_CCLIB.size(); i++)
|
||||
{
|
||||
extraptr[extralen++] = ASSETCHAINS_CCLIB[i];
|
||||
fprintf(stderr,"%c",ASSETCHAINS_CCLIB[i]);
|
||||
}
|
||||
fprintf(stderr," <- CCLIB name\n");
|
||||
LogPrintf(" <- CCLIB name\n");
|
||||
}
|
||||
|
||||
if ( ASSETCHAINS_BLOCKTIME != 60 ) {
|
||||
extralen += dragon_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_BLOCKTIME),(void *)&ASSETCHAINS_BLOCKTIME);
|
||||
fprintf(stderr,"%s: ASSETCHAINS_BLOCKTIME=%d, extralen=%d\n", __func__, ASSETCHAINS_BLOCKTIME, extralen);
|
||||
LogPrintf("%s: ASSETCHAINS_BLOCKTIME=%d, extralen=%d\n", __func__, ASSETCHAINS_BLOCKTIME, extralen);
|
||||
}
|
||||
|
||||
if ( Mineropret.size() != 0 )
|
||||
@@ -2290,7 +2289,7 @@ void hush_args(char *argv0)
|
||||
}
|
||||
//hush_pricesinit();
|
||||
hush_cbopretupdate(1); // will set Mineropret
|
||||
fprintf(stderr,"This blockchain uses data produced from CoinDesk Bitcoin Price Index\n");
|
||||
LogPrintf("This blockchain uses data produced from CoinDesk Bitcoin Price Index\n");
|
||||
}
|
||||
if ( ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0 )
|
||||
{
|
||||
@@ -2346,13 +2345,12 @@ void hush_args(char *argv0)
|
||||
MAX_MONEY = HUSH_MAXNVALUE;
|
||||
if(fDebug)
|
||||
fprintf(stderr,"MAX_MONEY %llu %.8f\n",(long long)MAX_MONEY,(double)MAX_MONEY/SATOSHIDEN);
|
||||
//printf("baseid.%d MAX_MONEY.%s %.8f\n",baseid,SMART_CHAIN_SYMBOL,(double)MAX_MONEY/SATOSHIDEN);
|
||||
uint16_t tmpport = hush_port(SMART_CHAIN_SYMBOL,ASSETCHAINS_SUPPLY,&ASSETCHAINS_MAGIC,extraptr,extralen);
|
||||
if ( GetArg("-port",0) != 0 )
|
||||
{
|
||||
ASSETCHAINS_P2PPORT = GetArg("-port",0);
|
||||
if(ishush3) {
|
||||
fprintf(stderr,"set HUSH3 p2pport.%u\n",ASSETCHAINS_P2PPORT);
|
||||
LogPrintf("set HUSH3 p2pport.%u\n",ASSETCHAINS_P2PPORT);
|
||||
ASSETCHAINS_P2PPORT = 18030;
|
||||
}
|
||||
if(fDebug)
|
||||
@@ -2368,7 +2366,6 @@ void hush_args(char *argv0)
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(3000));
|
||||
#endif
|
||||
}
|
||||
//fprintf(stderr,"Got datadir.(%s)\n",dirname);
|
||||
if ( SMART_CHAIN_SYMBOL[0] != 0 )
|
||||
{
|
||||
int32_t hush_baseid(char *origbase);
|
||||
@@ -2386,7 +2383,6 @@ void hush_args(char *argv0)
|
||||
fprintf(stderr,"ac_cbmaturity must be >0, shutting down\n");
|
||||
StartShutdown();
|
||||
}
|
||||
//fprintf(stderr,"ASSETCHAINS_RPCPORT (%s) %u\n",SMART_CHAIN_SYMBOL,ASSETCHAINS_RPCPORT);
|
||||
}
|
||||
if ( ASSETCHAINS_RPCPORT == 0 )
|
||||
ASSETCHAINS_RPCPORT = ASSETCHAINS_P2PPORT + 1;
|
||||
@@ -2402,13 +2398,16 @@ void hush_args(char *argv0)
|
||||
if ( HUSH_CCACTIVATE != 0 )
|
||||
{
|
||||
ASSETCHAINS_CC = 2;
|
||||
fprintf(stderr,"smart utxo CC contracts will activate at height.%d\n",HUSH_CCACTIVATE);
|
||||
LogPrintf("smart utxo CC contracts will activate at height.%d\n",HUSH_CCACTIVATE);
|
||||
} else if ( ccEnablesHeight[0] != 0 ) {
|
||||
ASSETCHAINS_CC = 2;
|
||||
fprintf(stderr,"smart utxo CC contract %d will activate at height.%d\n",(int32_t)ccEnablesHeight[0],(int32_t)ccEnablesHeight[1]);
|
||||
LogPrintf("smart utxo CC contract %d will activate at height.%d\n",(int32_t)ccEnablesHeight[0],(int32_t)ccEnablesHeight[1]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Legacy fallback path taken only when no -ac_name is set (SMART_CHAIN_SYMBOL empty).
|
||||
// Dead on DragonX, which always runs with -ac_name=DRAGONX. The HUSH3/Bitcoin conf
|
||||
// paths and default ports below are historical and left as-is for backwards compat.
|
||||
char fname[512],username[512],password[4096]; int32_t iter; FILE *fp;
|
||||
ASSETCHAINS_P2PPORT = 7770;
|
||||
ASSETCHAINS_RPCPORT = 7771;
|
||||
@@ -2439,8 +2438,7 @@ void hush_args(char *argv0)
|
||||
_hush_userpass(username,password,fp);
|
||||
sprintf(iter == 0 ? HUSHUSERPASS : BTCUSERPASS,"%s:%s",username,password);
|
||||
fclose(fp);
|
||||
//printf("HUSH.(%s) -> userpass.(%s)\n",fname,HUSHUSERPASS);
|
||||
} //else printf("couldnt open.(%s)\n",fname);
|
||||
}
|
||||
if ( IS_HUSH_NOTARY == 0 )
|
||||
break;
|
||||
}
|
||||
@@ -2449,7 +2447,6 @@ void hush_args(char *argv0)
|
||||
if ( SMART_CHAIN_SYMBOL[0] != 0 )
|
||||
{
|
||||
BITCOIND_RPCPORT = GetArg("-rpcport", ASSETCHAINS_RPCPORT);
|
||||
//fprintf(stderr,"(%s) port.%u chain params initialized\n",SMART_CHAIN_SYMBOL,BITCOIND_RPCPORT);
|
||||
|
||||
// Set custom cc rulse for chains here
|
||||
if ( strcmp("HUSH3",SMART_CHAIN_SYMBOL) == 0 ) {
|
||||
@@ -2505,7 +2502,7 @@ void hush_prefetch(FILE *fp)
|
||||
{
|
||||
rewind(fp);
|
||||
while ( fread(ignore,1,incr,fp) == incr ) // prefetch
|
||||
fprintf(stderr,".");
|
||||
;
|
||||
free(ignore);
|
||||
}
|
||||
}
|
||||
|
||||
+260
-84
@@ -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>
|
||||
@@ -120,7 +121,11 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
|
||||
|
||||
static const char* DEFAULT_ASMAP_FILENAME="asmap.dat";
|
||||
|
||||
CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h
|
||||
// LevelDB read-cache size (bytes) for the notarizations (dPoW) DB. Non-consensus: just the
|
||||
// in-memory cache the DB is opened with; the value here does not affect validation.
|
||||
static const size_t NOTARIZATION_DB_CACHE_BYTES = 100 * 1024 * 1024; // 100 MiB
|
||||
|
||||
CClientUIInterface uiInterface; // global UI callback dispatcher (declared extern in ui_interface.h)
|
||||
|
||||
// Shutdown
|
||||
//
|
||||
@@ -148,7 +153,7 @@ std::atomic<bool> fRequestShutdown(false);
|
||||
void StartShutdown()
|
||||
{
|
||||
if(fDebug) {
|
||||
fprintf(stderr,"%s: fRequestShudown=true\n", __FUNCTION__);
|
||||
fprintf(stderr,"%s: fRequestShutdown=true\n", __FUNCTION__);
|
||||
}
|
||||
fRequestShutdown = true;
|
||||
}
|
||||
@@ -471,7 +476,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 +494,14 @@ 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 %i)"), DEFAULT_AUTOSHIELD_INTERVAL, MIN_AUTOSHIELD_INTERVAL));
|
||||
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)"), DEFAULT_AUTOSHIELD_FEE));
|
||||
|
||||
strUsage += HelpMessageOpt("-sietch-min-zouts=<n>", strprintf(_("Minimum number of shielded (Sapling) outputs Sietch adds to each z_sendmany transaction as decoys, strengthening amount/linkability privacy. Higher values add privacy at the cost of larger transactions (default: %u, clamped to the range 3-50)"), 7));
|
||||
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));
|
||||
@@ -608,12 +621,13 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
|
||||
strUsage += HelpMessageGroup(_("Stratum server options:"));
|
||||
strUsage += HelpMessageOpt("-stratum", _("Enable stratum server (default: off)"));
|
||||
strUsage += HelpMessageOpt("-stratumtarget=<hex>", _("Pool share target (64-hex, big-endian; larger = easier). Default is the diff-1 target. Useful for solo/low-difficulty mining."));
|
||||
strUsage += HelpMessageOpt("-stratumaddress=<address>", _("Mining address to use when special address of 'x' is sent by miner (default: none)"));
|
||||
strUsage += HelpMessageOpt("-stratumbind=<ipaddr>", _("Bind to given address to listen for Stratum work requests. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces)"));
|
||||
strUsage += HelpMessageOpt("-stratumport=<port>", strprintf(_("Listen for Stratum work requests on <port> (default: %u or testnet: %u)"), BaseParams().StratumPort(), BaseParams().StratumPort()));
|
||||
strUsage += HelpMessageOpt("-stratumallowip=<ip>", _("Allow Stratum work requests from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"));
|
||||
|
||||
// "ac" stands for "affects consensus" or Arrakis Chain
|
||||
// "ac" prefix is inherited from the Komodo asset-chain lineage ("asset chain"/"affects consensus")
|
||||
strUsage += HelpMessageGroup(_("DragonX Chain options:"));
|
||||
strUsage += HelpMessageOpt("-ac_algo", _("Choose PoW mining algorithm, either 'equihash' or 'randomx'. default is Equihash (200,9)"));
|
||||
strUsage += HelpMessageOpt("-ac_blocktime", _("Block time in seconds, default is 60"));
|
||||
@@ -780,7 +794,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
|
||||
}
|
||||
|
||||
/** Sanity checks
|
||||
* Ensure that Hush is running in a usable environment with all
|
||||
* Ensure that DragonX is running in a usable environment with all
|
||||
* necessary library support.
|
||||
*/
|
||||
bool InitSanityCheck(void)
|
||||
@@ -901,7 +915,7 @@ static void ZC_LoadParams(const CChainParams& chainparams)
|
||||
|
||||
if (!found) {
|
||||
// The traditional place Zcash params are stored, should not hit this case in normal circumstances,
|
||||
// as Hush packages sapling params now
|
||||
// as DragonX packages sapling params now
|
||||
sapling_spend = ZC_GetParamsDir() / "sapling-spend.params";
|
||||
sapling_output = ZC_GetParamsDir() / "sapling-output.params";
|
||||
if (files_exist(sapling_spend, sapling_output)) {
|
||||
@@ -920,7 +934,7 @@ static void ZC_LoadParams(const CChainParams& chainparams)
|
||||
boost::system::error_code ec1, ec2;
|
||||
boost::uintmax_t spend_size = file_size(sapling_spend, ec1);
|
||||
boost::uintmax_t output_size = file_size(sapling_output, ec2);
|
||||
fprintf(stderr,"Sapling spend: %d bytes, output: %d bytes\n", (int)spend_size, (int)output_size);
|
||||
LogPrintf("Sapling spend: %d bytes, output: %d bytes\n", (int)spend_size, (int)output_size);
|
||||
|
||||
// We could check sha hashes, but we mostly want to detect on-disk file corruption
|
||||
// or people having a full harddrive. Full validation happens in librustzcash_init_zksnark_params
|
||||
@@ -973,11 +987,15 @@ static void ZC_LoadParams(const CChainParams& chainparams)
|
||||
|
||||
bool AppInitServers(boost::thread_group& threadGroup)
|
||||
{
|
||||
fprintf(stderr,"%s: start\n",__func__);
|
||||
LogPrintf("%s: start\n",__func__);
|
||||
RPCServer::OnStopped(&OnRPCStopped);
|
||||
RPCServer::OnPreCommand(&OnRPCPreCommand);
|
||||
if (!InitHTTPServer())
|
||||
return false;
|
||||
// Stratum server (stratum.cpp) supports DragonX's RandomX PoW (32-byte solution + per-height
|
||||
// RandomX key conveyed to the miner) as well as legacy Equihash, branched on ASSETCHAINS_ALGO.
|
||||
// Off by default (DEFAULT_STRATUM_ENABLE=false); only -stratum turns it on. Needs a RandomX-aware
|
||||
// stratum miner (see contrib/ reference miner) — stock Equihash/Monero miners won't work.
|
||||
if (GetBoolArg("-stratum", DEFAULT_STRATUM_ENABLE) && !InitStratumServer())
|
||||
return false;
|
||||
if (!StartRPC())
|
||||
@@ -991,14 +1009,15 @@ bool AppInitServers(boost::thread_group& threadGroup)
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Initialize Hush.
|
||||
/** Initialize DragonX.
|
||||
* @pre Parameters should be parsed and config file should be read.
|
||||
*/
|
||||
extern int32_t HUSH_REWIND;
|
||||
|
||||
// --- Adaptive coins-cache sizing -------------------------------------------------------------
|
||||
// The in-memory UTXO/coins cache (nCoinCacheUsage) is the biggest lever on IBD speed: a bigger
|
||||
// cache means far fewer chainstate flushes to disk. We size it to use most of RAM, but a scheduled
|
||||
// cache means fewer size-triggered chainstate flushes to disk (the chainstate is also flushed at
|
||||
// least every DATABASE_FLUSH_INTERVAL / DATABASE_FLUSH_BLOCKS, see main.h). We size it to use most of RAM, but a scheduled
|
||||
// background task (AdjustCoinCacheForMemoryPressure, registered in AppInit2) shrinks the target when
|
||||
// free system memory runs low — e.g. the user opens other apps — and grows it back when memory frees
|
||||
// up, always leaving a reserve free for the rest of the system. The existing per-block flush
|
||||
@@ -1115,7 +1134,6 @@ static void AdjustCoinCacheForMemoryPressure()
|
||||
|
||||
bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
{
|
||||
//fprintf(stderr,"%s start\n", __FUNCTION__);
|
||||
// ********************************************************* Step 1: setup
|
||||
#ifdef _MSC_VER
|
||||
// Turn off Microsoft heap dump noise
|
||||
@@ -1152,7 +1170,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
return InitError("Error: -sysperms is not allowed in combination with enabled wallet functionality");
|
||||
#endif
|
||||
} else {
|
||||
//fprintf(stderr,"%s setting umask\n", __FUNCTION__);
|
||||
umask(077);
|
||||
}
|
||||
|
||||
@@ -1170,12 +1187,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
|
||||
std::set_new_handler(new_handler_terminate);
|
||||
|
||||
//fprintf(stderr,"%s: set signal handlers\n", __FUNCTION__);
|
||||
|
||||
// ********************************************************* Step 2: parameter interactions
|
||||
const CChainParams& chainparams = Params();
|
||||
|
||||
//fprintf(stderr,"%s: got chain params\n", __FUNCTION__);
|
||||
|
||||
// Set this early so that experimental features are correctly enabled/disabled
|
||||
fExperimentalMode = GetBoolArg("-experimentalfeatures", true);
|
||||
@@ -1186,11 +1201,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
// Fail early if user has set experimental options without the global flag
|
||||
if (!fExperimentalMode) {
|
||||
if (mapArgs.count("-developerencryptwallet")) {
|
||||
fprintf(stderr,"%s wallet encryption error\n", __FUNCTION__);
|
||||
LogPrintf("%s wallet encryption error\n", __FUNCTION__);
|
||||
return InitError(_("Wallet encryption requires -experimentalfeatures."));
|
||||
}
|
||||
}
|
||||
//fprintf(stderr,"%s tik2\n", __FUNCTION__);
|
||||
|
||||
// Set this early so that parameter interactions go to console
|
||||
fPrintToConsole = GetBoolArg("-printtoconsole", false);
|
||||
@@ -1199,7 +1213,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
|
||||
|
||||
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
|
||||
LogPrintf("Hush version %s (%s)\n", FormatFullVersion());
|
||||
LogPrintf("DragonX version %s (%s)\n", FormatFullVersion());
|
||||
|
||||
|
||||
#ifdef DEBUG_LOCKORDER
|
||||
@@ -1219,7 +1233,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
LogPrintf("%s: parameter interaction: -allowbind set -> setting -listen=1\n", __func__);
|
||||
}
|
||||
|
||||
//fprintf(stderr,"%s tik3\n", __FUNCTION__);
|
||||
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
|
||||
// when only connecting to trusted nodes, do not seed via DNS, or listen by default
|
||||
if (SoftSetBoolArg("-dnsseed", false))
|
||||
@@ -1251,7 +1264,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__);
|
||||
}
|
||||
|
||||
// Read asmap file by default for HUSH3 and all Hush Arrakis Chains
|
||||
// Read asmap file by default on DragonX
|
||||
if (GetArg("-asmap",1)) {
|
||||
fs::path asmap_path = fs::path(GetArg("-asmap", ""));
|
||||
|
||||
@@ -1269,36 +1282,36 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (asmap_path.empty()) {
|
||||
// Most binaries will have it in PWD
|
||||
asmap_path = pwd / DEFAULT_ASMAP_FILENAME;
|
||||
printf("%s: looking for asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: looking for asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
if(fs::exists(asmap_path)) {
|
||||
printf("%s: found asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: found asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
} else {
|
||||
// Debian Packages
|
||||
asmap_path = fs::path("/usr/share/hush") / DEFAULT_ASMAP_FILENAME;
|
||||
printf("%s: looking for asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: looking for asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
if(fs::exists(asmap_path)) {
|
||||
printf("%s: found asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: found asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
} else {
|
||||
// Source code
|
||||
asmap_path = contrib / DEFAULT_ASMAP_FILENAME;
|
||||
printf("%s: looking for asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: looking for asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
if(fs::exists(asmap_path)) {
|
||||
printf("%s: found asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: found asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
} else {
|
||||
// Last Resort: Check the parent directory
|
||||
asmap_path = pwd / ".." / DEFAULT_ASMAP_FILENAME;
|
||||
printf("%s: looking for asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: looking for asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
if(fs::exists(asmap_path)) {
|
||||
printf("%s: found asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: found asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
} else {
|
||||
// Mac SD
|
||||
asmap_path = fs::path("/Applications/SilentDragon.app/Contents/MacOS/") / DEFAULT_ASMAP_FILENAME;
|
||||
printf("%s: looking for asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: looking for asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
if(fs::exists(asmap_path)) {
|
||||
printf("%s: found asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: found asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
} else {
|
||||
// Shit is fucked up, die an honorable death
|
||||
InitError(strprintf(_("Could not find any asmap file! Please report this bug to Hush Developers")));
|
||||
// No asmap file found in any known location; abort startup.
|
||||
InitError(strprintf(_("Could not find any asmap file! Please report this bug to DragonX Developers")));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1309,7 +1322,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (!asmap_path.is_absolute()) {
|
||||
asmap_path = GetDataDir() / asmap_path;
|
||||
}
|
||||
printf("%s: looking for custom asmap file at %s\n", __func__, asmap_path.c_str() );
|
||||
LogPrint("net", "%s: looking for custom asmap file at %s\n", __func__, asmap_path.string().c_str() );
|
||||
}
|
||||
|
||||
//TODO: verify asmap_path is not a directory
|
||||
@@ -1323,7 +1336,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
return false;
|
||||
}
|
||||
const uint256 asmap_version = SerializeHash(asmap);
|
||||
printf("%s: asmap version=%s with %lu mappings\n", __func__, asmap_version.ToString().c_str(), asmap.size());
|
||||
LogPrint("net", "%s: asmap version=%s with %lu mappings\n", __func__, asmap_version.ToString().c_str(), asmap.size());
|
||||
LogPrintf("Using asmap version %s for IP bucketing with %lu mappings\n", asmap_version.ToString(), asmap.size());
|
||||
addrman.m_asmap = std::move(asmap); // //node.connman->SetAsmap(std::move(asmap));
|
||||
|
||||
@@ -1342,20 +1355,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (SoftSetBoolArg("-rescan", true))
|
||||
LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__);
|
||||
}
|
||||
//fprintf(stderr,"%s tik4\n", __FUNCTION__);
|
||||
|
||||
// Make sure enough file descriptors are available
|
||||
int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-allowbind"), 1);
|
||||
nMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
|
||||
//fprintf(stderr,"nMaxConnections %d\n",nMaxConnections);
|
||||
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
|
||||
int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
|
||||
fprintf(stderr,"nMaxConnections %d FD_SETSIZE.%d nBind.%d expr.%d \n",nMaxConnections,FD_SETSIZE,nBind,(int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS));
|
||||
LogPrintf("nMaxConnections %d FD_SETSIZE.%d nBind.%d expr.%d \n",nMaxConnections,FD_SETSIZE,nBind,(int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS));
|
||||
if (nFD < MIN_CORE_FILEDESCRIPTORS)
|
||||
return InitError(_("Not enough file descriptors available."));
|
||||
if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections)
|
||||
nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS;
|
||||
//fprintf(stderr,"nMaxConnections %d\n",nMaxConnections);
|
||||
// if using block pruning, then disable txindex
|
||||
// also disable the wallet (for now, until SPV support is implemented in wallet)
|
||||
if (GetArg("-prune", 0)) {
|
||||
@@ -1401,10 +1411,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
}
|
||||
if (find(categories.begin(), categories.end(), string("randomx")) != categories.end()) {
|
||||
fRandomXDebug = true;
|
||||
fprintf(stderr,"%s: enabled randomx debug\n", __func__);
|
||||
LogPrintf("%s: enabled randomx debug\n", __func__);
|
||||
}
|
||||
|
||||
//fprintf(stderr,"%s tik5\n", __FUNCTION__);
|
||||
// Check for -debugnet
|
||||
if (GetBoolArg("-debugnet", false))
|
||||
InitWarning(_("Warning: Unsupported argument -debugnet ignored, use -debug=net."));
|
||||
@@ -1424,7 +1433,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
mempool.setSanityCheck(1.0 / ratio);
|
||||
}
|
||||
fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
|
||||
fCheckpointsEnabled = GetBoolArg("-checkpoints", true);
|
||||
// Regtest inherits the DRAGONX *mainnet* checkpoint set (chainparams_commandline applies it
|
||||
// for every SMART_CHAIN_SYMBOL=="DRAGONX" network, regardless of -regtest), whose top height
|
||||
// ~3.2M would otherwise pin an isolated regtest chain in IsInitialBlockDownload() forever --
|
||||
// disabling the ChainTip auto-ops (autoshield/sweep/consolidation) and the below-checkpoint
|
||||
// script-check skip. Default checkpoints OFF on regtest so a fresh regtest node leaves IBD
|
||||
// normally; still overridable with -checkpoints=1.
|
||||
fCheckpointsEnabled = GetBoolArg("-checkpoints", chainparams.NetworkIDString() != "regtest");
|
||||
|
||||
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
|
||||
nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
|
||||
@@ -1463,7 +1478,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
LogPrintf("Bulk block streaming: %s\n", fBulkBlockSync ? "enabled" : "disabled");
|
||||
|
||||
fServer = GetBoolArg("-server", false);
|
||||
//fprintf(stderr,"%s tik6\n", __FUNCTION__);
|
||||
|
||||
// block pruning; get the amount of disk space (in MB) to allot for block & undo files
|
||||
int64_t nSignedPruneTarget = GetArg("-prune", 0) * 1024 * 1024;
|
||||
@@ -1551,7 +1565,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
expiryDelta = GetArg("-txexpirydelta", DEFAULT_TX_EXPIRY_DELTA);
|
||||
bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", true);
|
||||
fSendFreeTransactions = GetBoolArg("-sendfreetransactions", false);
|
||||
//fprintf(stderr,"%s tik7\n", __FUNCTION__);
|
||||
|
||||
std::string strWalletFile = GetArg("-wallet", "wallet.dat");
|
||||
#endif // ENABLE_WALLET
|
||||
@@ -1568,7 +1581,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
nLocalServices |= NODE_BLOOM;
|
||||
}
|
||||
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
|
||||
//fprintf(stderr,"%s tik8\n", __FUNCTION__);
|
||||
|
||||
#ifdef ENABLE_MINING
|
||||
if (mapArgs.count("-mineraddress")) {
|
||||
@@ -1591,7 +1603,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
}
|
||||
}
|
||||
|
||||
//fprintf(stderr,"%s tik9\n", __FUNCTION__);
|
||||
if (!mapMultiArgs["-nuparams"].empty()) {
|
||||
// Allow overriding network upgrade parameters for testing
|
||||
if (Params().NetworkIDString() != "regtest") {
|
||||
@@ -1640,10 +1651,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
std::string sha256_algo = SHA256AutoDetect();
|
||||
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
|
||||
|
||||
//fprintf(stderr,"%s tik10\n", __FUNCTION__);
|
||||
// Sanity check
|
||||
if (!InitSanityCheck())
|
||||
return InitError(_("Initialization sanity check failed. Please check for insanity. Hush is shutting down!"));
|
||||
return InitError(_("Initialization sanity check failed. Please check for insanity. DragonX is shutting down!"));
|
||||
|
||||
std::string strDataDir = GetDataDir().string();
|
||||
#ifdef ENABLE_WALLET
|
||||
@@ -1651,14 +1661,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (strWalletFile != boost::filesystem::basename(strWalletFile) + boost::filesystem::extension(strWalletFile))
|
||||
return InitError(strprintf(_("Wallet %s resides outside data directory %s"), strWalletFile, strDataDir));
|
||||
#endif
|
||||
// Make sure only a single Hush process is using the data directory.
|
||||
// Make sure only a single DragonX process is using the data directory.
|
||||
boost::filesystem::path pathLockFile = GetDataDir() / ".lock";
|
||||
FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist.
|
||||
if (file) fclose(file);
|
||||
|
||||
|
||||
//fprintf(stderr,"%s tik11\n", __FUNCTION__);
|
||||
fprintf(stderr,"Attempting to obtain lock %s\n", pathLockFile.string().c_str());
|
||||
LogPrintf("Attempting to obtain lock %s\n", pathLockFile.string().c_str());
|
||||
try {
|
||||
static boost::interprocess::file_lock lock(pathLockFile.string().c_str());
|
||||
if (!lock.try_lock())
|
||||
@@ -1673,10 +1682,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (GetBoolArg("-shrinkdebugfile", !fDebug))
|
||||
ShrinkDebugFile();
|
||||
|
||||
//fprintf(stderr,"%s tik12\n", __FUNCTION__);
|
||||
|
||||
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
|
||||
LogPrintf("Hush version %s\n", FormatFullVersion());
|
||||
LogPrintf("DragonX version %s\n", FormatFullVersion());
|
||||
|
||||
if (fPrintToDebugLog)
|
||||
OpenDebugLog();
|
||||
@@ -1706,7 +1714,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
threadGroup.create_thread(&ThreadRandomXVerify);
|
||||
}
|
||||
|
||||
//fprintf(stderr,"%s tik13\n", __FUNCTION__);
|
||||
|
||||
// Start the lightweight task scheduler thread
|
||||
CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler);
|
||||
@@ -1714,7 +1721,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
|
||||
// Count uptime
|
||||
MarkStartTime();
|
||||
//fprintf(stderr,"%s tik14\n", __FUNCTION__);
|
||||
|
||||
if ((chainparams.NetworkIDString() != "regtest") &&
|
||||
GetBoolArg("-showmetrics", 0) &&
|
||||
@@ -1724,7 +1730,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
threadGroup.create_thread(&ThreadShowMetricsScreen);
|
||||
}
|
||||
|
||||
//fprintf(stderr,"%s tik15\n", __FUNCTION__);
|
||||
|
||||
if ( HUSH_NSPV_FULLNODE )
|
||||
{
|
||||
@@ -1742,7 +1747,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (!AppInitServers(threadGroup))
|
||||
return InitError(_("Unable to start HTTP server. See debug log for details."));
|
||||
}
|
||||
//fprintf(stderr,"%s tik16\n", __FUNCTION__);
|
||||
|
||||
int64_t nStart;
|
||||
|
||||
@@ -1769,7 +1773,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
#endif // ENABLE_WALLET
|
||||
// ********************************************************* Step 6: network initialization
|
||||
|
||||
//fprintf(stderr,"%s tik17\n", __FUNCTION__);
|
||||
RegisterNodeSignals(GetNodeSignals());
|
||||
|
||||
// sanitize comments per BIP-0014, format user agent and check total size
|
||||
@@ -1785,7 +1788,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
return InitError(strprintf("Total length of network version string %i exceeds maximum of %i characters. Reduce the number and/or size of uacomments.",
|
||||
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
|
||||
}
|
||||
//fprintf(stderr,"%s tik18\n", __FUNCTION__);
|
||||
|
||||
// Disable clearnet peers if -clearnet=0 for this node or -ac_clearnet=0 for this chain
|
||||
if (ASSETCHAINS_CLEARNET == 0 || !GetBoolArg("-clearnet", DEFAULT_CLEARNET)) {
|
||||
@@ -1844,7 +1846,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
SetReachable(NET_IPV4, false);
|
||||
}
|
||||
|
||||
//fprintf(stderr,"%s tik19\n", __FUNCTION__);
|
||||
if (mapArgs.count("-allowlist")) {
|
||||
BOOST_FOREACH(const std::string& net, mapMultiArgs["-allowlist"]) {
|
||||
CSubNet subnet;
|
||||
@@ -1907,7 +1908,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
fDiscover = GetBoolArg("-discover", true);
|
||||
fNameLookup = GetBoolArg("-dns", true);
|
||||
|
||||
//fprintf(stderr,"%s tik22\n", __FUNCTION__);
|
||||
bool fBound = false;
|
||||
if (fListen) {
|
||||
if (mapArgs.count("-bind") || mapArgs.count("-allowbind")) {
|
||||
@@ -1946,7 +1946,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
}
|
||||
}
|
||||
|
||||
//fprintf(stderr,"%s tik23\n", __FUNCTION__);
|
||||
|
||||
BOOST_FOREACH(const std::string& strDest, mapMultiArgs["-seednode"])
|
||||
AddOneShot(strDest);
|
||||
@@ -1982,7 +1981,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
return !fRequestShutdown;
|
||||
}
|
||||
// ********************************************************* Step 7: load block chain
|
||||
//fprintf(stderr,"%s tik24\n", __FUNCTION__);
|
||||
|
||||
fReindex = GetBoolArg("-reindex", false);
|
||||
|
||||
@@ -2047,7 +2045,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if ( checkval != fAddressIndex && fAddressIndex != 0 )
|
||||
{
|
||||
pblocktree->WriteFlag("addressindex", fAddressIndex);
|
||||
fprintf(stderr,"set addressindex, will reindex. could take a while.\n");
|
||||
LogPrintf("set addressindex, will reindex. could take a while.\n");
|
||||
fReindex = true;
|
||||
}
|
||||
fSpentIndex = GetBoolArg("-spentindex", DEFAULT_SPENTINDEX);
|
||||
@@ -2055,7 +2053,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if ( checkval != fSpentIndex && fSpentIndex != 0 )
|
||||
{
|
||||
pblocktree->WriteFlag("spentindex", fSpentIndex);
|
||||
fprintf(stderr,"set spentindex, will reindex. could take a while.\n");
|
||||
LogPrintf("set spentindex, will reindex. could take a while.\n");
|
||||
fReindex = true;
|
||||
}
|
||||
}
|
||||
@@ -2084,7 +2082,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
||||
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
||||
try {
|
||||
pnotarizations = new NotarizationDB(100*1024*1024, false, fReindex);
|
||||
pnotarizations = new NotarizationDB(NOTARIZATION_DB_CACHE_BYTES, false, fReindex);
|
||||
} catch (const std::exception& e) {
|
||||
// The notarizations (dPoW) DB is non-essential and node-regenerable. It has been seen to
|
||||
// snapshot/flush torn (0-byte log -> leveldb "Database I/O error" on reopen), which
|
||||
@@ -2100,7 +2098,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
boost::filesystem::remove_all(ndir.string() + ".corrupt");
|
||||
boost::filesystem::rename(ndir, ndir.string() + ".corrupt");
|
||||
}
|
||||
pnotarizations = new NotarizationDB(100*1024*1024, false, true);
|
||||
pnotarizations = new NotarizationDB(NOTARIZATION_DB_CACHE_BYTES, false, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -2108,14 +2106,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
boost::filesystem::remove(GetDataDir() / "hushstate");
|
||||
boost::filesystem::remove(GetDataDir() / "hushsignedmasks");
|
||||
pblocktree->WriteReindexing(true);
|
||||
fprintf(stderr, "%s: Deleted hushstate and hushsignedmasks...\n", __FUNCTION__);
|
||||
LogPrintf("%s: Deleted hushstate and hushsignedmasks...\n", __FUNCTION__);
|
||||
|
||||
//If we're reindexing in prune mode, wipe away unusable block files and all undo data files
|
||||
if (fPruneMode)
|
||||
CleanupBlockRevFiles();
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: Loading block index...\n", __FUNCTION__);
|
||||
LogPrintf("%s: Loading block index...\n", __FUNCTION__);
|
||||
if (!LoadBlockIndex()) {
|
||||
strLoadError = _("Error loading block database");
|
||||
break;
|
||||
@@ -2139,7 +2137,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "zindex=%s in block index\n", fZindex ? "enabled" : "disabled");
|
||||
LogPrintf("zindex=%s in block index\n", fZindex ? "enabled" : "disabled");
|
||||
if (fZindex != GetBoolArg("-zindex", false)) {
|
||||
strLoadError = _("You need to rebuild the database using -reindex to change -zindex");
|
||||
break;
|
||||
@@ -2194,7 +2192,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (!fLoaded) {
|
||||
// first suggest a reindex
|
||||
if (!fReset) {
|
||||
fprintf(stderr,"%s: error in hd data\n", __FUNCTION__);
|
||||
LogPrintf("%s: error in hd data\n", __FUNCTION__);
|
||||
bool fRet = uiInterface.ThreadSafeMessageBox(
|
||||
strLoadError + ".\n\n" + _("error in HDD data, might just need to update to latest, if that doesnt work, then you need to resync"),
|
||||
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
|
||||
@@ -2229,10 +2227,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
mempool.ReadFeeEstimates(est_filein);
|
||||
fFeeEstimatesInitialized = true;
|
||||
|
||||
//fprintf(stderr,"%s tik25\n", __FUNCTION__);
|
||||
|
||||
// ********************************************************* Step 8: load wallet
|
||||
#ifdef ENABLE_WALLET
|
||||
// Set when the startup rescan below is interrupted: the wallet's state then ends at the scan's
|
||||
// own checkpoint, not at the tip the notifier starts from.
|
||||
bool fWalletScanIncomplete = false;
|
||||
if (fDisableWallet) {
|
||||
pwalletMain = NULL;
|
||||
LogPrintf("Wallet disabled!\n");
|
||||
@@ -2264,7 +2264,22 @@ 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";
|
||||
{
|
||||
// Abort HERE, as the DB_NEED_REWRITE branch below already does. Falling through
|
||||
// runs several hundred more lines of initialisation against a wallet we have just
|
||||
// declared corrupt -- including SetHDSeedOrigin(), which WRITES to it, and the
|
||||
// rescan and SetBestChain that follow.
|
||||
//
|
||||
// The old text advised restoring with -mnemonic. That is wrong twice over:
|
||||
// -usemnemonic defaulted to 0 in v1.0.3 so many such wallets never had a phrase,
|
||||
// and SetHDSeedFromMnemonic refuses a non-empty wallet, so "move wallet.dat aside"
|
||||
// would discard every non-HD key the salvage preserved.
|
||||
strErrors << _("Error loading wallet.dat: the wallet database is corrupt. Your keys may "
|
||||
"still be intact -- do NOT delete or replace wallet.dat. Back it up now, "
|
||||
"and see debug.log for the specific record at fault.") << "\n";
|
||||
LogPrintf("%s", strErrors.str());
|
||||
return InitError(strErrors.str());
|
||||
}
|
||||
else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
|
||||
{
|
||||
string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data"
|
||||
@@ -2272,10 +2287,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
InitWarning(msg);
|
||||
}
|
||||
else if (nLoadWalletRet == DB_TOO_NEW)
|
||||
strErrors << _("Error loading wallet.dat: Wallet requires newer version of Hush") << "\n";
|
||||
strErrors << _("Error loading wallet.dat: Wallet requires newer version of DragonX") << "\n";
|
||||
else if (nLoadWalletRet == DB_NEED_REWRITE)
|
||||
{
|
||||
strErrors << _("Wallet needed to be rewritten: restart Hush to complete") << "\n";
|
||||
strErrors << _("Wallet needed to be rewritten: restart DragonX to complete") << "\n";
|
||||
LogPrintf("%s", strErrors.str());
|
||||
return InitError(strErrors.str());
|
||||
}
|
||||
@@ -2301,6 +2316,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 +2364,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 +2395,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);
|
||||
@@ -2359,7 +2413,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
|
||||
int consolidationInterval = GetArg("-consolidationinterval", 25);
|
||||
if (consolidationInterval < 5) {
|
||||
fprintf(stderr,"%s: Invalid consolidation interval of %d < 5, setting to default of 25\n", __func__, consolidationInterval);
|
||||
LogPrintf("%s: Invalid consolidation interval of %d < 5, setting to default of 25\n", __func__, consolidationInterval);
|
||||
consolidationInterval = 25;
|
||||
}
|
||||
|
||||
@@ -2384,7 +2438,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (pwalletMain->fSweepEnabled) {
|
||||
int sweepInterval = GetArg("-zsweepinterval", 10);
|
||||
if (sweepInterval < 5) {
|
||||
fprintf(stderr,"%s: Invalid sweep interval of %d, setting to default of 10\n", __func__, sweepInterval);
|
||||
LogPrintf("%s: Invalid sweep interval of %d, setting to default of 10\n", __func__, sweepInterval);
|
||||
sweepInterval = 10;
|
||||
}
|
||||
pwalletMain->sweepInterval = sweepInterval;
|
||||
@@ -2401,7 +2455,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
}
|
||||
|
||||
for (int i = 0; i < vSweep.size(); i++) {
|
||||
// LogPrintf("Sweep Address: %s\n", vSweep[i]);
|
||||
auto zSweep = DecodePaymentAddress(vSweep[i]);
|
||||
if (!IsValidPaymentAddress(zSweep)) {
|
||||
return InitError("Invalid zsweep address");
|
||||
@@ -2451,6 +2504,87 @@ 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", DEFAULT_AUTOSHIELD_INTERVAL);
|
||||
if (autoShieldInterval < MIN_AUTOSHIELD_INTERVAL) {
|
||||
InitWarning(strprintf(_("autoshield interval %d below the minimum, clamping to %d"),
|
||||
autoShieldInterval, MIN_AUTOSHIELD_INTERVAL));
|
||||
autoShieldInterval = MIN_AUTOSHIELD_INTERVAL;
|
||||
}
|
||||
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", DEFAULT_AUTOSHIELD_FEE);
|
||||
if (autoShieldFee < AUTOSHIELD_MIN_FEE || autoShieldFee > AUTOSHIELD_MAX_FEE) {
|
||||
InitWarning(strprintf(_("-autoshieldfee=%lld out of range [%lld,%lld], using default %lld"),
|
||||
(long long)autoShieldFee, (long long)AUTOSHIELD_MIN_FEE, (long long)AUTOSHIELD_MAX_FEE, (long long)DEFAULT_AUTOSHIELD_FEE));
|
||||
autoShieldFee = DEFAULT_AUTOSHIELD_FEE;
|
||||
}
|
||||
pwalletMain->autoShieldFee = autoShieldFee;
|
||||
pwalletMain->autoShieldMinUtxos = GetArg("-autoshieldminutxos", DEFAULT_AUTOSHIELD_MIN_UTXOS);
|
||||
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);
|
||||
@@ -2524,8 +2658,20 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
nStart = GetTimeMillis();
|
||||
pwalletMain->ScanForWalletTransactions(pindexRescan, true);
|
||||
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
|
||||
pwalletMain->SetBestChain(chainActive.GetLocator());
|
||||
nWalletDBUpdated++;
|
||||
// ONLY record "scanned to the tip" if the scan actually reached it. An aborted or
|
||||
// shutdown-interrupted scan that stamps a tip locator tells the next startup there is
|
||||
// nothing left to scan (the chainActive.Tip() != pindexRescan guard above then skips
|
||||
// the rescan entirely), so every transaction in the unscanned range stays out of
|
||||
// mapWallet permanently: invisible to getbalance and unspendable. The scan writes its
|
||||
// own locator at the interrupt point instead.
|
||||
if (pwalletMain->fLastRescanCompleted) {
|
||||
pwalletMain->SetBestChain(chainActive.GetLocator());
|
||||
nWalletDBUpdated++;
|
||||
} else {
|
||||
LogPrintf("Rescan did not complete; leaving the best-block locator at the scan's own "
|
||||
"checkpoint so the remaining range is rescanned on the next start\n");
|
||||
fWalletScanIncomplete = true;
|
||||
}
|
||||
|
||||
// Restore wallet transaction metadata after -zapwallettxes=1
|
||||
if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2")
|
||||
@@ -2561,10 +2707,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
#ifdef ENABLE_MINING
|
||||
#ifndef ENABLE_WALLET
|
||||
if (GetBoolArg("-minetolocalwallet", false)) {
|
||||
return InitError(_("Hush was not built with wallet support. Set -minetolocalwallet=0 to use -mineraddress, or rebuild Hush with wallet support."));
|
||||
return InitError(_("DragonX was not built with wallet support. Set -minetolocalwallet=0 to use -mineraddress, or rebuild DragonX with wallet support."));
|
||||
}
|
||||
if (GetArg("-mineraddress", "").empty() && GetBoolArg("-gen", false)) {
|
||||
return InitError(_("Hush was not built with wallet support. Set -mineraddress, or rebuild Hush with wallet support."));
|
||||
return InitError(_("DragonX was not built with wallet support. Set -mineraddress, or rebuild DragonX with wallet support."));
|
||||
}
|
||||
#endif // !ENABLE_WALLET
|
||||
|
||||
@@ -2595,6 +2741,21 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
{
|
||||
LOCK(cs_main);
|
||||
pindexLastTip = chainActive.Tip();
|
||||
#ifdef ENABLE_WALLET
|
||||
// The wallet's state reflects the block the notifier starts from, unless the startup
|
||||
// rescan was interrupted: then it ends at the scan's checkpoint, and the hourly
|
||||
// SetBestChain must keep stamping that so the next start rescans the rest.
|
||||
if (pwalletMain) {
|
||||
const CBlockIndex* pindexWallet = pindexLastTip;
|
||||
if (fWalletScanIncomplete) {
|
||||
CWalletDB walletdb(pwalletMain->strWalletFile);
|
||||
CBlockLocator locator;
|
||||
if (walletdb.ReadBestBlock(locator))
|
||||
pindexWallet = FindForkInGlobalIndex(chainActive, locator);
|
||||
}
|
||||
pwalletMain->SetWalletTip(pindexWallet);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
boost::function<void()> threadnotifywallets = boost::bind(&ThreadNotifyWallets, pindexLastTip);
|
||||
threadGroup.create_thread(
|
||||
@@ -2623,7 +2784,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
// Advertise willingness to SERVE bulk block streams (full nodes only) when opted in.
|
||||
if ( fBulkBlockSync )
|
||||
nLocalServices |= NODE_BULKBLOCKS;
|
||||
fprintf(stderr,"nLocalServices %llx %d, %d\n",(long long)nLocalServices,GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX),GetBoolArg("-spentindex", DEFAULT_SPENTINDEX));
|
||||
LogPrintf("nLocalServices %llx %d, %d\n",(long long)nLocalServices,GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX),GetBoolArg("-spentindex", DEFAULT_SPENTINDEX));
|
||||
}
|
||||
// ********************************************************* Step 10: import blocks
|
||||
|
||||
@@ -2636,10 +2797,26 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
uiInterface.InitMessage(_("Activating best chain..."));
|
||||
// scan for better chains in the block chain database, that are not yet connected in the active best chain
|
||||
CValidationState state;
|
||||
const CBlockIndex* pindexBeforeActivate = NULL;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
pindexBeforeActivate = chainActive.Tip();
|
||||
}
|
||||
if ( !ActivateBestChain(true,state))
|
||||
strErrors << "Failed to connect best block";
|
||||
else {
|
||||
// Persist a startup replay now: the wallet's witness rebuild runs next and can hold
|
||||
// cs_main for a long time, and a node killed before any later flush would replay it all again.
|
||||
bool fTipMoved;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
fTipMoved = chainActive.Tip() != pindexBeforeActivate;
|
||||
}
|
||||
if (fTipMoved)
|
||||
FlushStateToDisk();
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,"HUSH_REWIND < 0\n");
|
||||
LogPrintf("HUSH_REWIND < 0\n");
|
||||
}
|
||||
std::vector<boost::filesystem::path> vImportFiles;
|
||||
if (mapArgs.count("-loadblock"))
|
||||
@@ -2668,7 +2845,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
|
||||
// ********************************************************* Step 11: start node
|
||||
|
||||
//fprintf(stderr,"Checking disk space...\n");
|
||||
if (!CheckDiskSpace())
|
||||
return false;
|
||||
|
||||
@@ -2706,7 +2882,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
|
||||
SetRPCWarmupFinished();
|
||||
if(fDebug)
|
||||
fprintf(stderr,"RPC warmump finished\n");
|
||||
fprintf(stderr,"RPC warmup finished\n");
|
||||
uiInterface.InitMessage(_("Full Node Done Loading! :)"));
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user