|
| 1 | +#!/usr/bin/env bash |
| 2 | +# dgb_regtest_won_block_soak.sh -- DGB #82 won-block dual-path soak (regtest substrate). |
| 3 | +# |
| 4 | +# Closes the DGB broadcaster dual-path gate (#82): a real won block must reach the |
| 5 | +# network down BOTH arms before #281 (the faithful reconstruct-closure install at |
| 6 | +# main_dgb.cpp:317) can merge. Per integrator (2026-06-21, UID1708) two properties |
| 7 | +# must be PROVEN on the wire, not merely "not-errored": |
| 8 | +# |
| 9 | +# PROP 1 (COINBASE-ONLY IS CORRECT, NOT FAIL-OPEN) -- template_other_txs_fn is |
| 10 | +# empty today ("correct-and-empty"). The soak must reconstruct + get a |
| 11 | +# COINBASE-ONLY block ACCEPTED by a peer, proving empty is valid rather |
| 12 | +# than a silent malformed-block drop (the original #82 root cause). |
| 13 | +# PROP 2 (BOTH ARMS REACH THE NETWORK) -- |
| 14 | +# ARM A (P2P PRIMARY) : on_block_found -> make_reconstruct_closure_from_template |
| 15 | +# (#280) -> submit_block_p2p_raw relays to a real peer |
| 16 | +# (main_dgb.cpp:317/333). Assert peer node B ingests it. |
| 17 | +# ARM B (RPC FALLBACK): the independent external submitblock RPC fallback (the |
| 18 | +# &coin_node arm at main_dgb.cpp:317) lands the SAME block. |
| 19 | +# |
| 20 | +# WON-SHARE TRIGGER: UpdateTip-driven, no test-only seam. Unlike NMC (no --regtest |
| 21 | +# CLI -> forced-won-share KAT seam, PR #276), c2pool-dgb already targets an external |
| 22 | +# regtest daemon directly (main_dgb.cpp:393 "dev regtest daemon"; :433 fresh-regtest |
| 23 | +# locator), and at regtest difficulty the embedded work loop finds a winning share |
| 24 | +# naturally -- a genuine live UpdateTip won-block, not a synthetic inject. |
| 25 | +# |
| 26 | +# CLI is real (main_dgb.cpp:534-560): --run --coin-daemon H:P --coin-rpc H:P |
| 27 | +# --coin-rpc-auth <digibyte.conf> --coin-magic <hex> --coin-genesis <hash>. |
| 28 | +# |
| 29 | +# TWO OPEN LIVE-LEG ITEMS (surfaced to integrator, do NOT read as covered): |
| 30 | +# (1) digibyted regtest binary self-provisioned -- not yet on the fleet (only |
| 31 | +# namecoind exists). Until provisioned this is a substrate validator + harness, |
| 32 | +# not a green gate. |
| 33 | +# (2) ARM B isolation MECHANISM. There is NO --no-p2p-relay flag; both arms land on |
| 34 | +# node A. Cleanly proving the submitblock fallback INDEPENDENTLY (P2P arm |
| 35 | +# suppressed) is a design decision for integrator -- mirrors NMC's |
| 36 | +# forced-seam-vs-CLI call. Current draft asserts ARM B against node A only as a |
| 37 | +# placeholder and is marked GATED below until that mechanism is chosen. |
| 38 | +# |
| 39 | +# PER-COIN ISOLATION: DGB only. Localhost-only. Self-service creds (no secret on any |
| 40 | +# coordination card). Ports sit outside the bitcoin-family 1844x regtest band. |
| 41 | +set -euo pipefail |
| 42 | + |
| 43 | +CLI_BIN="${DGB_CLI:-digibyte-cli}" |
| 44 | +DAEMON_BIN="${DGB_DAEMON:-digibyted}" |
| 45 | +C2POOL_DGB="${C2POOL_DGB:-c2pool-dgb}" |
| 46 | + |
| 47 | +DATADIR_A="${DGB_REGTEST_DATADIR:-$HOME/.c2pool-dgb-regtest}" |
| 48 | +DATADIR_B="${DATADIR_A}-peer" |
| 49 | +RPCPORT_A=${DGB_RPCPORT_A:-18843} |
| 50 | +RPCPORT_B=${DGB_RPCPORT_B:-18853} |
| 51 | +P2PPORT_A=${DGB_P2PPORT_A:-18844} |
| 52 | +P2PPORT_B=${DGB_P2PPORT_B:-18854} |
| 53 | +PAYOUT_LABEL="c2pool-soak" |
| 54 | + |
| 55 | +log() { echo "[dgb-won-soak $(printf "%(%H:%M:%S)T")] $*" >&2; } |
| 56 | +die() { echo "[dgb-won-soak FAIL] $*" >&2; exit 1; } |
| 57 | +need() { command -v "$1" >/dev/null 2>&1 || die "missing binary: $1 (set DGB_DAEMON/DGB_CLI or self-provision DigiByte Core)"; } |
| 58 | + |
| 59 | +cli_a() { "$CLI_BIN" -regtest -datadir="$DATADIR_A" -rpcport=$RPCPORT_A "$@"; } |
| 60 | +cli_b() { "$CLI_BIN" -regtest -datadir="$DATADIR_B" -rpcport=$RPCPORT_B "$@"; } |
| 61 | + |
| 62 | +C2POOL_PID="" |
| 63 | +cleanup() { |
| 64 | + [ -n "$C2POOL_PID" ] && kill "$C2POOL_PID" >/dev/null 2>&1 || true |
| 65 | + cli_a stop >/dev/null 2>&1 || true |
| 66 | + cli_b stop >/dev/null 2>&1 || true |
| 67 | +} |
| 68 | +trap cleanup EXIT |
| 69 | + |
| 70 | +need "$DAEMON_BIN"; need "$CLI_BIN"; need "$C2POOL_DGB"; need openssl |
| 71 | + |
| 72 | +provision() { |
| 73 | + local dd="$1" rpcport="$2" |
| 74 | + mkdir -p "$dd"; chmod 700 "$dd" |
| 75 | + if [ ! -f "$dd/digibyte.conf" ]; then |
| 76 | + local pass; pass="$(openssl rand -hex 24)" |
| 77 | + cat > "$dd/digibyte.conf" <<CONF |
| 78 | +regtest=1 |
| 79 | +server=1 |
| 80 | +rpcuser=c2pool |
| 81 | +rpcpassword=$pass |
| 82 | +fallbackfee=0.0002 |
| 83 | +[regtest] |
| 84 | +rpcport=$rpcport |
| 85 | +CONF |
| 86 | + chmod 600 "$dd/digibyte.conf" |
| 87 | + fi |
| 88 | +} |
| 89 | +provision "$DATADIR_A" "$RPCPORT_A" |
| 90 | +provision "$DATADIR_B" "$RPCPORT_B" |
| 91 | + |
| 92 | +wait_rpc() { local n=0; until "$@" getblockcount >/dev/null 2>&1; do n=$((n+1)); [ $n -gt 60 ] && die "rpc never came up: $*"; sleep 0.5; done; } |
| 93 | +tip_b() { cli_b getblockcount; } |
| 94 | + |
| 95 | +# --- substrate: two peered regtest nodes -------------------------------------- |
| 96 | +log "starting node A (regtest RPC $RPCPORT_A / P2P $P2PPORT_A)" |
| 97 | +"$DAEMON_BIN" -regtest -datadir="$DATADIR_A" -rpcport=$RPCPORT_A -port=$P2PPORT_A -daemon >/dev/null |
| 98 | +wait_rpc cli_a |
| 99 | +log "starting node B (regtest RPC $RPCPORT_B / P2P $P2PPORT_B), peering to A" |
| 100 | +"$DAEMON_BIN" -regtest -datadir="$DATADIR_B" -rpcport=$RPCPORT_B -port=$P2PPORT_B \ |
| 101 | + -connect=127.0.0.1:$P2PPORT_A -daemon >/dev/null |
| 102 | +wait_rpc cli_b |
| 103 | + |
| 104 | +cli_a createwallet "$PAYOUT_LABEL" >/dev/null 2>&1 || cli_a loadwallet "$PAYOUT_LABEL" >/dev/null 2>&1 || true |
| 105 | +ADDR_A="$(cli_a getnewaddress "$PAYOUT_LABEL")" |
| 106 | +log "mining 101 maturity blocks to $ADDR_A" |
| 107 | +cli_a generatetoaddress 101 "$ADDR_A" >/dev/null |
| 108 | +BASE_B="$(tip_b)" |
| 109 | + |
| 110 | +# regtest magic + genesis are read from the running daemon's chainparams so the |
| 111 | +# embedded backend's getheaders bootstrap matches node A exactly (no hand-typed |
| 112 | +# consensus literal). DGB regtest magic is fixed in DigiByte Core chainparams. |
| 113 | +DGB_REGTEST_GENESIS="$(cli_a getblockhash 0)" |
| 114 | +: "${DGB_REGTEST_MAGIC:?set DGB_REGTEST_MAGIC to the DigiByte regtest pchMessageStart hex (from chainparams)}" |
| 115 | +log "substrate OK: node A height $(cli_a getblockcount), peer B $BASE_B, genesis $DGB_REGTEST_GENESIS" |
| 116 | + |
| 117 | +# --- ARM A (P2P PRIMARY): live UpdateTip won block relayed to node B ---------- |
| 118 | +log "ARM A: launching embedded c2pool-dgb against node A (regtest diff -> natural won share)" |
| 119 | +"$C2POOL_DGB" --run \ |
| 120 | + --coin-daemon 127.0.0.1:$P2PPORT_A \ |
| 121 | + --coin-rpc 127.0.0.1:$RPCPORT_A \ |
| 122 | + --coin-rpc-auth "$DATADIR_A/digibyte.conf" \ |
| 123 | + --coin-magic "$DGB_REGTEST_MAGIC" \ |
| 124 | + --coin-genesis "$DGB_REGTEST_GENESIS" >/tmp/c2pool-dgb-soak.log 2>&1 & |
| 125 | +C2POOL_PID=$! |
| 126 | +log "c2pool-dgb PID $C2POOL_PID; waiting for a won share to relay a coinbase-only block to node B" |
| 127 | +n=0 |
| 128 | +until [ "$(tip_b)" -gt "$BASE_B" ]; do |
| 129 | + n=$((n+1)); [ $n -gt 240 ] && die "ARM A: no won block reached node B within 120s (P2P relay arm)" |
| 130 | + sleep 0.5 |
| 131 | +done |
| 132 | +WON_HASH="$(cli_b getblockhash "$(tip_b)")" |
| 133 | +NTX="$(cli_b getblock "$WON_HASH" | grep -o '"tx"[^]]*]' | grep -o '"[0-9a-f]\{64\}"' | wc -l)" |
| 134 | +[ "$NTX" -eq 1 ] || die "ARM A: won block $WON_HASH has $NTX txs, expected 1 (coinbase-only, PROP 1)" |
| 135 | +log "ARM A OK: P2P-relayed coinbase-only won block $WON_HASH accepted by peer node B (PROP 1 + ARM A)" |
| 136 | +kill "$C2POOL_PID" >/dev/null 2>&1; C2POOL_PID="" |
| 137 | + |
| 138 | +# --- ARM B (RPC FALLBACK): GATED on integrator isolation-mechanism decision ---- |
| 139 | +# No --no-p2p-relay flag exists; isolating the submitblock fallback from the P2P arm |
| 140 | +# is an open design call (see header item 2). Until integrator picks the mechanism, |
| 141 | +# ARM B is NOT asserted -- shipping a placeholder assert would be the silent-gap the |
| 142 | +# gate exists to prevent. |
| 143 | +log "ARM B: GATED -- submitblock-fallback isolation mechanism pending integrator decision (header item 2)" |
| 144 | + |
| 145 | +log "ARM A proven; ARM B + digibyted provisioning are the two remaining live-leg items for the #82 gate." |
0 commit comments