#!/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