Second phase of the code-hygiene remediation, covering the items the
DragonX team introduced or the rebrand missed. No consensus behavior
changes; validated on a local self-mining node.
Log cruft:
- chainparams: route the startup ">>>>>>>>>>" banner and the port line
through LogPrintf/LogPrint("net") instead of fprintf(stderr).
- pow: drop the fprintf(stderr) hash-mismatch dump that duplicated the
LogPrintf copy verbatim.
- miner: gate the RandomXDatasetManager per-alloc/per-VM address dumps and
MemDiag /proc reads behind LogPrint("randomx"); keep a one-line
"allocated shared dataset (N GB)" summary at default verbosity.
Named constants (single source of truth in wallet.h):
- DEFAULT_AUTOSHIELD_FEE/INTERVAL, MIN_AUTOSHIELD_INTERVAL,
DEFAULT_AUTOSHIELD_MIN_UTXOS, AUTOSHIELD_MIN/MAX_FEE for the autoshield
option parsing and help text (were bare 10000/25/5 literals repeated
across init.cpp and wallet.h).
- AUTO_OP_TARGET_HEIGHT_OFFSET replaces the three copy-pasted
`blockHeight + 5` async-op scheduling offsets.
- AUTO_OP_EXPIRY_DELTA replaces the three per-file *_EXPIRY_DELTA=15
constants (sweep/consolidation/autoshield) with one shared value.
- Move DEFAULT_AUTOSHIELD_FEE out of the op header into wallet.h so it no
longer collides when both headers are included.
Error surfacing:
- init: report clamped/out-of-range -autoshieldinterval/-autoshieldfee via
InitWarning() (surfaces to GUI/log) instead of fprintf(stderr).
Rebrand / dead foreign-chain code (approved removals):
- server: drop the HUSH3 special-cases in stop() and HelpExampleCli; the
cli example now shows "dragonx-cli" instead of "hush-cli".
- getinfo (misc): remove the stale dPoW notarization block (notarized,
prevMoMheight, notarizedhash, notarizedtxid, notarizedtxid_height,
HUSHnotarized_height, notarized_confirms) — DragonX is a private chain
from genesis with no active dPoW. Also fixes the hardcoded "HUSH3" that
made getinfo query a foreign chain's notarization.
- delete the dead Komodo notary RPCs getera/getdragonjson/
getnotarysendmany/geterablockheights (getera returned 0;
getnotarysendmany was marked "this is broke") and their registrations.
- remove the dead ASSETCHAINS_EQUIHASH reporting branches in getinfo and
getmininginfo — DragonX is RandomX-only.
chainparams: document why the upstream Equihash params and the literal
Bitcoin genesis are retained under RandomX (do not "fix" them).
Deferred: the hdSeedOrigin int->string switch in z_autoshieldstatus
(no shared helper exists to reuse; low value).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up nits surfaced by the multi-agent review of the dragonx..dev delta;
none are correctness/consensus bugs, all are defensive/consistency tidy-ups.
Builds clean; the diff was reviewed across concurrency, scheduler, and
tx-building lenses.
- wallet: default CWallet::fAutoShieldEnabled to false. init.cpp always
recomputes it (ON only for CREATED/RESTORED seed provenance) before any
ChainTip round, so this is behaviour-neutral in the normal path and stops a
CWallet that skips that init from auto-enabling for provenance the gate would
reject.
- wallet: clamp a loaded hdSeedOrigin to UNKNOWN when out of enum range, so a
corrupt/hand-edited wallet.dat cannot claim a known-recoverable seed and flip
autoshield ON.
- wallet: key the sweep and consolidation ops' NU-straddle guard, transaction
builder height, and expiry off execution-time tipHeight instead of the stale
enqueue-time targetHeight_ -- matching the autoshield op (65130c312) so the
builder's consensus-branch selection agrees with the height the tx is signed
for. (Sweep previously built at targetHeight_ but expired at the live tip.)
- wallet: on the sweep NU-straddle skip, set sweepComplete_ so the round backs
nextSweep off one interval instead of re-dispatching a fresh sweep op every
block through the activation window.
- init: clamp -autoshieldinterval below 5 up to the documented minimum of 5,
rather than silently resetting it to the default 25.
- chainparams: make the ClearSeeds guard an exact "DRAGONX" match instead of a
7-char prefix, so DRAGONX-prefixed assetchains (e.g. DRAGONX2) no longer
inherit DragonX's seeds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UtvyqQQSqR64DNEjUEuTmb
A smart chain builds its params by copying a base network and
overriding pieces, but nothing ever touched vSeeds/vFixedSeeds. That is
how DRAGONX ran on Hush's seeds -- seed1.hush.is and friends, long
since gone from DNS -- for as long as it did, and it means any other
assetchain started from this binary now inherits DragonX's.
Seeds are per-chain by nature: an address serving one chain is useless
to another, and dialling it is at best wasted effort and at worst a
peer speaking 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 an assetchain operator has to
configure regardless.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU87LdsJZiZkfq1eXubpeo
seed.dragonx.is is now an A-record set over the five seed nodes (DNS-only, TTL 300,
created in Cloudflare alongside this change). One lookup returns all of them, and
the set can change -- a node added, a node retired -- with a DNS edit rather than a
release.
That is the actual point. Before this the network's entry points were hardcoded
into the binary twice over: here in vSeeds, and again in the -addnode injection in
hush_utils.h. Adding a sixth node meant shipping a new version and waiting for
users to upgrade.
node1 and node5 stay as static fallbacks against the round-robin record being
mistyped or deleted. They resolve to the same hosts, so that is insurance against a
DNS mistake rather than real redundancy.
Verified end to end on a fresh datadir (empty addrman, real node untouched, and
crucially with NO custom -port -- see below):
before: 0 addresses found from DNS seeds, 0 handshakes, 1 block (genesis)
after: 7 addresses found, connection attempts to all five seeds on :21768,
3 version handshakes, 3296 blocks connected and syncing
The earlier run of this test appeared to fail with 0 handshakes. That was the
harness, not the code: -port overrides ASSETCHAINS_P2PPORT, and net.cpp builds
DNS-seeded addresses as CAddress(CService(ip, ASSETCHAINS_P2PPORT)), so a test node
with a custom port dials every seeded peer on its own port and reaches nothing.
That is the mechanism behind the long-standing "never use a custom -port on a test
node" rule; use -listen=0 instead.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A DRAGONX node had no working peer discovery. Both mechanisms were broken, and a
third bug hid the fact.
1. DNS seeds were Hush's, and all three are dead. chainparams_commandline() sets an
assetchain's port, magic, blocktime, upgrade heights and checkpoints but never
touches vSeeds or vFixedSeeds, so DRAGONX silently inherited CMainParams':
seed1.hush.is, seed2.hush.is and dns.leto.net. None of the three has an A record
any more -- verified against 1.1.1.1 and 8.8.8.8, with google.com and
node1..node5.dragonx.is resolving fine from the same host as a control. Replaced
with the five DragonX node hostnames, which do resolve and do listen.
2. Every fixed seed carried port 0. contrib/seeds/generate-seeds.py documents its
input as <ip>:<port>, but contrib/seeds/nodes_main.txt held bare IPs, so
parse_spec() took the port as empty and emitted 0x00,0x00 for all five entries.
The fixed-seed fallback -- which exists precisely for when DNS seeding yields
nothing -- was therefore handing out unconnectable addresses. Added the port to
nodes_main.txt and regenerated; entries now end 0x55,0x08 (21768).
3. ThreadDNSAddressSeed never incremented `found`, so "%d addresses found from DNS
seeds" printed 0 unconditionally, whether seeding worked or not. That is almost
certainly why nobody noticed the seeds had gone dead: the one diagnostic that
would have shown it was hardcoded to say zero.
Verified on a fresh datadir (empty addrman, separate ports, real node untouched):
DNS seeding now reports "5 addresses found from DNS seeds" where it previously
reported 0, and the fixed-seed path adds 5 entries carrying the correct port.
Note on scope: the five hostnames are single-A-record hosts, so each contributes one
address rather than the spread a real seeder returns. A dedicated DNS seeder, or
simply a round-robin A record over the seed set, would be the proper fix and needs
only a DNS change rather than a release. This restores a working discovery path;
it does not make it a good one.
Also corrected the generated header's #endif comment, which said
HUSH_CHAINPARAMSSEEDS_H while the guard is DRAGONX_CHAINPARAMSSEEDS_H.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The RandomX skip in RandomXValidationRequired() has never fired in production.
It skips verification below the last in-index checkpoint, but the DRAGONX
checkpoint table ended at 2,838,000 while ASSETCHAINS_RANDOMX_VALIDATION is
2,838,976 -- the window was empty by 976 blocks. Every block since the RandomX
activation has been fully verified, at ~65ms per hash on the fastest x86 core
available and ~180ms on a typical user machine.
Extends the table by 388 entries at stride 1000, from 2,839,000 to 3,226,000
(tip - ~5,600, far beyond any reorg this chain has produced; max observed depth
is 3-4). Blocks requiring a RandomX verify drop from 391,447 to ~5,500. The
same extension carries the existing script/zk-proof skip (fScriptChecks,
fExpensiveChecks) over the same range.
Checkpoint data verification, before it went anywhere near source:
- generated on a continuously-synced node
- all 388 hashes identical on 4 other full nodes (388/388 on each)
- reverse-verified hash -> height, all on the active chain
- re-extracted from the patched file and diffed against the verified set
- 2,838 pre-existing entries unchanged; all 3,226 ascending and unique
Trailer fields computed from RPC, not util/checkpoints.pl, which greps a
rotating debug.log and assumes 1440 blk/day (DragonX is 2400).
Also switches all three build scripts from -DARCH=native to -DARCH=default.
RandomX's CMakeLists maps ARCH=native to -march=native, tuning the binary to
the build host: a Zen4 build emitted 746 AVX-512 instructions into
librandomx.a, and every seed reports avx512f=no, so that binary would SIGILL
inside RandomX fleet-wide -- and on any user CPU older than the build machine.
build-win.sh had the same flag, so shipped Windows binaries inherited it.
ARCH=default keeps -maes and per-file -mssse3/-mavx2, so the portable baseline
costs essentially nothing. Note build.sh skips cmake entirely when
src/RandomX/build/ exists, so a stale dir silently preserves the old ARCH.
Validated on an isolated datadir on an EPYC seed, bootstrap -> tip:
- below 3,226,000 (RandomX skipped): 91.3 blk/s (22,823 blocks / 250s)
- at/above 3,226,000 (verified): 4.7 blk/s
~19x at the boundary. The 4.7 blk/s baseline matches a same-day restore on
the old binary, corroborating it independently.
- gettxoutsetinfo at height 3,231,951 BYTE-IDENTICAL to a live node
(hash_serialized 4885c2374ef8b84c648b97d560a57cfcc99bb979142dc89c2a5ccbc90a1f1692,
222,635 txs/txouts, 15,826,352 bytes, total 667909.93689180)
- synced through all 388 new checkpoints with zero rejections
- verifychain 4 (500) and 3 (2000) both true
Binary: v1.0.3-bf3c33c53-dirty, stripped md5 fe83d70fec5b50c38bf65ea6c733ffa9
Checkpoints decay at 2,400 blocks/day; regenerating them belongs on the
release checklist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up to the doc rebrand, addressing the previously out-of-scope legacy:
- Currency unit: strCurrencyUnits (chainparams.cpp) and CURRENCY_UNIT
(amount.cpp) "HUSH" -> "DRAGONX". Both are display-only (RPC help + metrics);
no logic comparisons, verified.
- depends mirrors: libsodium/boost/utfcpp fetched from git.hush.is/attachments;
repointed to canonical upstream (GitHub release / archives.boost.io / GitHub
tag) with the existing sha256 hashes verified to match those sources.
- Seeds: nodes_main.txt now lists the five node[1-5].dragonx.is IPs (DNS-resolved)
instead of Hush nodes; regenerated src/chainparamsseeds.h (was compiling Hush
seed IPs as the fixed fallback); generate-seeds.py header now says DragonX;
hush_seed_nodes.txt updated to DragonX seeds.
- Deleted Hush-history / wrong-for-DragonX files: contrib/snapshot/ (block-500000
Hush airdrop, ~10MB), notary_seeds.txt (Hush notaries; DragonX isn't notarized),
and the Hush emission scripts hush_supply, hush_supply_old, hush_halvings,
hush_block_subsidy_per_halving (hardcode Hush's 340000/12.5 economics).
Kept: hush_scanner (engine invoked by dragonx_scanner) and the "The Hush
developers" copyright headers (lineage credit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nMinimumChainWork was defined in chainparams but never checked, and IsInitialBlockDownload
decided "synced" from tip timestamp/height alone -- so an eclipsed or bootstrapping node
could be fed a cheap low-work fake chain with recent timestamps and trust it. Reset the
stale mainnet floor (0x281b32ff3198a1 was ABOVE the live chain, would have bricked mainnet)
to the real chainwork at height ~3,100,000, and hold a node in IBD until its tip reaches the
floor. Gated to the DRAGONX symbol so ephemeral assetchains from the same binary are not
trapped in IBD; the check can only keep a node in IBD, never force it out (no false-sync risk).
Complements the header-flood fix (b9fdc7981): that stops invalid-PoW headers off the real tip;
this stops valid-but-cheap fake chains from a fake genesis.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Minimal rebrand (see compliant-rebrand branch for full rebrand):
- Rename binaries: hushd/hush-cli/hush-tx → dragonxd/dragonx-cli/dragonx-tx
- Default to DRAGONX chain params without -ac_* flags (randomx, blocktime=36, private=1)
- Update configure.ac: AC_INIT([DragonX],[1.0.0])
- Update client version string and user-agent to /DragonX:1.0.0/
- Add chainparams.cpp with DRAGONX network parameters
- Update build.sh, miner.cpp, pow.cpp for DragonX
- Add bootstrap-dragonx.sh utility script
- Update .gitignore for release directory
Share single RandomX dataset across all mining threads:
- Add RandomXDatasetManager with readers-writer lock, reducing RAM from
~2GB per thread to ~2GB total plus ~2MB per thread for the VM scratchpad
- Add LogProcessMemory() diagnostic helper for Linux and Windows
In 3.10.4 release, Hush checkpoints were added as DragonX checkpoints, causing nodes to not sync and ban seeds and other nodes that updated. These are DragonX checkpoints from a synced node but need to be verified with others that have synced nodes.
Our code requires sapling activation at height 1 so we can simplify this function
which should lead to some performance improvements since it is called from many
places, including mining code.