aristotle @ rpc-de-32 crash-looped after its build was fixed:
priv_validator_state.json found in /root/.0g. Continuing!
Already initialized, continuing!
panic: ProvideBlsSigner (node-core/components/signer.go:46):
key file does not exist at path: /root/.0g/config/priv_validator_key.json
$DATA_DIR and $CONFIG_DIR are SEPARATE docker volumes (<node>_zerog and
<node>_config), but the guard tested only $DATA_DIR/priv_validator_state.json —
inferring the state of the config volume from a file on the data volume. Worse, the
copy order wrote that guard file BEFORE the two key files, so an init interrupted
between them left the data volume with the guard and the config volume without the
keys. Every later start then took the 'Already initialized' branch, copied nothing,
and panicked. Unrecoverable by restarting: the node wedges permanently. aristotle
got there via the repeated build failures earlier today.
Fix, two parts:
- guard on everything 0gchaind needs to boot (both config keys AND the data state
file), not one proxy file on the wrong volume;
- write the keys FIRST and the guard file LAST, so an interrupted init re-runs on
the next start instead of latching into the wedged state.
Tested locally against the real 0gchaind binary (aristotle v1.0.6), sandboxed HOME:
scenario before after
fresh volumes keys created keys created (no regression)
wedged (state, no keys) keys MISSING keys created (unwedges)
partial (state+node_key) keys MISSING keys created
healthy (all present) no re-init no re-init, and a SENTINEL written
into priv_validator_key.json survives
-> never clobbers an initialized node
The 'wedged' row reproduces aristotle's production failure exactly.
Note this regenerates node identity on an affected node (node_key.json,
priv_validator_key.json). These are RPC nodes that never sign, so a fresh identity is
harmless; chain data on the _zerog volume is untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
102 lines
3.5 KiB
Bash
102 lines
3.5 KiB
Bash
#!/bin/sh
|
|
|
|
set -e # Exit on failure
|
|
|
|
if [ $# -lt 1 ]; then
|
|
echo "Error: No argument provided. Please specify '0gchaind' or 'geth' as the first argument."
|
|
exit 1
|
|
fi
|
|
|
|
MODE="$1"
|
|
shift
|
|
|
|
case "$MODE" in
|
|
0gchaind)
|
|
# Continue with 0gchaind-specific logic (rest of script)
|
|
;;
|
|
geth)
|
|
GETH_DATA_DIR="/root/.ethereum/"
|
|
|
|
if [ -z "$(ls -A "$GETH_DATA_DIR")" ]; then
|
|
if [ -f /0g/genesis.json ]; then
|
|
/0g/bin/geth init --datadir $GETH_DATA_DIR /0g/genesis.json
|
|
elif [ -f /0g/geth-genesis.json ]; then
|
|
/0g/bin/geth init --datadir $GETH_DATA_DIR /0g/geth-genesis.json
|
|
else
|
|
echo "No genesis file found at /0g/genesis.json or /0g/geth-genesis.json" >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "Datadir not empty, continuing!" >&2
|
|
fi
|
|
|
|
exec /0g/bin/geth $@
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Error: Unknown argument '$MODE'. Please specify '0gchaind' or 'geth'."
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
echo "MONIKER: $MONIKER"
|
|
|
|
AUTH_RPC=${AUTH_RPC:-http://0g-$CHAIN_NAME:8551} # just as example
|
|
|
|
HOME_DIR="/root/.0g"
|
|
mkdir -p $HOME_DIR
|
|
|
|
CONFIG_DIR="$HOME_DIR/config"
|
|
DATA_DIR="$HOME_DIR/data"
|
|
|
|
mkdir -p $CONFIG_DIR
|
|
mkdir -p $DATA_DIR
|
|
|
|
if [ "$CHAIN_NAME" = "galileo" ]; then
|
|
CHAIN_SPEC=testnet
|
|
else
|
|
CHAIN_SPEC=mainnet
|
|
fi
|
|
env
|
|
|
|
# seems to be the same for all the 0g chains
|
|
|
|
# $DATA_DIR and $CONFIG_DIR are SEPARATE docker volumes (<node>_zerog and <node>_config),
|
|
# so the presence of a file in one says nothing about the other. Guard on everything
|
|
# 0gchaind actually needs to boot, and write the copies so the LAST file written is the
|
|
# one that would make a re-run skip.
|
|
#
|
|
# The previous version guarded solely on $DATA_DIR/priv_validator_state.json and copied it
|
|
# BEFORE the two key files. An init interrupted between those copies therefore left the
|
|
# data volume with the guard file and the config volume without the keys, so every later
|
|
# start took the "Already initialized" branch, copied nothing, and 0gchaind panicked:
|
|
#
|
|
# panic: error calling provider ProvideBlsSigner (node-core/components/signer.go:46):
|
|
# key file does not exist at path: /root/.0g/config/priv_validator_key.json
|
|
#
|
|
# That state is unrecoverable by restarting - the node wedges permanently. Hit on
|
|
# aristotle @ rpc-de-32 on 2026-08-12 after repeated build failures interrupted its init.
|
|
if [ ! -f "$CONFIG_DIR/priv_validator_key.json" ] || \
|
|
[ ! -f "$CONFIG_DIR/node_key.json" ] || \
|
|
[ ! -f "$DATA_DIR/priv_validator_state.json" ]; then
|
|
echo "0g: node identity incomplete (config keys and/or priv_validator_state.json). Initializing..."
|
|
TMP_DIR=$(mktemp -d)
|
|
# You can add any additional initialization logic here if needed
|
|
if /0g/bin/0gchaind init ${MONIKER} --chaincfg.chain-spec ${CHAIN_SPEC} --home $TMP_DIR; then
|
|
cp -r /0g/0g-home/0gchaind-home/config/* $CONFIG_DIR
|
|
# Keys first, guard file last: if this is interrupted the next start re-runs
|
|
# initialization instead of latching into the wedged state described above.
|
|
cp $TMP_DIR/config/node_key.json $CONFIG_DIR
|
|
cp $TMP_DIR/config/priv_validator_key.json $CONFIG_DIR
|
|
cp $TMP_DIR/data/priv_validator_state.json $DATA_DIR
|
|
else
|
|
echo "Already initialized, continuing!" >&2
|
|
fi
|
|
rm -rf $TMP_DIR # delete tmp dir
|
|
|
|
else
|
|
echo "0g: node identity complete (config keys + priv_validator_state.json). Continuing!" >&2
|
|
fi
|
|
|
|
exec /0g/bin/0gchaind $@
|