#!/bin/bash # Monad Labs — restore a Monad mainnet full node from the official snapshot, fetched from the Labs mirror. # # curl -fsSL https://labs.monad.exploreme.pro/install.sh | sudo bash -s -- --yes # # For a node that is already installed and configured the official way (the `monad` package, # keys, node.toml, /home/monad/.env, /dev/triedb). This script does not install Monad and does # not touch your keys. It runs the Monad Foundation's own scripts, unchanged except one line: # # 1. stops monad-bft, monad-execution, monad-rpc # 2. /opt/monad/scripts/reset-workspace.sh (package script: ledger, forkpoint, TrieDB truncate) # 3. restore-from-snapshot.sh from bucket.monadinfra.com, with HOST pointed at this mirror # — it downloads, checks the official sha256, picks the TrieDB mode (MIP-8) and imports # 4. download-forkpoint.sh from bucket.monadinfra.com (a fresh forkpoint, as Monad requires) # 5. starts the services and waits until the node follows the head of the chain # # --check download the snapshot from the mirror and verify sha256 only; the node is not touched # --yes required for a real restore: it ERASES the node's TrieDB and ledger set -euo pipefail LABS=${LABS:-https://labs.monad.exploreme.pro} NETWORK=${NETWORK:-mainnet} BUCKET=https://bucket.monadinfra.com YES=0; CHECK=0 for a in "$@"; do case "$a" in --yes) YES=1 ;; --check) CHECK=1 ;; -h|--help) sed -n '2,20p' "$0" 2>/dev/null || true; exit 0 ;; *) echo "unknown option: $a" >&2; exit 2 ;; esac; done c_dim=$'\033[2m'; c_b=$'\033[1m'; c_ok=$'\033[32m'; c_err=$'\033[31m'; c_ac=$'\033[38;2;131;110;249m'; c_off=$'\033[0m' [ -t 1 ] || [ -n "${CLICOLOR_FORCE:-}" ] || { c_dim=""; c_b=""; c_ok=""; c_err=""; c_ac=""; c_off=""; } say(){ printf '%s\n' "$*"; } step(){ printf '\n%s%s%s\n' "$c_ac$c_b" "$*" "$c_off"; } die(){ printf '%s%s%s\n' "$c_err" "$*" "$c_off" >&2; exit 1; } T0=$(date +%s) since(){ local s=$(( $(date +%s) - T0 )); printf '%dm %02ds' $((s/60)) $((s%60)); } rpc_height(){ curl -s -m5 -X POST -H 'content-type: application/json' \ --data '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' "$1" 2>/dev/null | jq -r '.result // empty' 2>/dev/null; } [ "$(id -u)" = 0 ] || die "run as root: curl -fsSL $LABS/install.sh | sudo bash -s -- --yes" for b in curl wget jq sha256sum unzstd tar; do command -v "$b" >/dev/null || NEED="${NEED:-} $b"; done command -v aria2c >/dev/null || NEED="${NEED:-} aria2" if [ -n "${NEED:-}" ]; then step "Installing:${NEED}" apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ${NEED/unzstd/zstd} >/dev/null || die "apt-get install failed" fi step "1/5 Labs manifest" M=$(curl -fsS -m 30 "$LABS/manifests/$NETWORK/latest.json") || die "cannot read $LABS/manifests/$NETWORK/latest.json" NAME=$(jq -r .name <<<"$M"); BLOCK=$(jq -r .block <<<"$M"); BYTES=$(jq -r .bytes <<<"$M"); SHA=$(jq -r .sha256 <<<"$M") say " official snapshot $NAME — block $BLOCK, $(( BYTES / 1000000 )) MB, sha256 ${SHA:0:16}…" say " published by $(jq -r .source.publisher <<<"$M") at $(jq -r .source.published_at <<<"$M"), mirrored $(jq -r .mirrored_at <<<"$M")" if [ "$CHECK" = 1 ]; then step "2/2 Download from the mirror and verify (node untouched)" D=$(mktemp -d /tmp/monad-labs-check.XXXX); cd "$D" aria2c -x 8 -s 8 --summary-interval=10 --console-log-level=warn "$LABS/snapshots/$NETWORK/$NAME.tar.zst" "$LABS/snapshots/$NETWORK/$NAME.checksum" -Z curl -fsS -m 30 "$BUCKET/snapshots/$NETWORK/$NAME.checksum" -o official.checksum || die "cannot read the official checksum" cmp -s official.checksum "$NAME.checksum" || die "the mirror's .checksum differs from the official one" sha256sum -c "$NAME.checksum" || die "sha256 mismatch" say "${c_ok}✓ $(( BYTES / 1000000 )) MB from the mirror match the official sha256 — in $(since)${c_off}" rm -rf "$D"; exit 0 fi [ "$YES" = 1 ] || die "This ERASES the node's TrieDB and ledger and restores block $BLOCK. Re-run with --yes (or --check to only verify the download)." for f in /usr/local/bin/monad-cli /usr/local/bin/monad-mpt /home/monad/.env /dev/triedb; do [ -e "$f" ] || die "$f is missing — set the node up the official way first (docs.monad.xyz), then run this" done step "2/5 Stop the node and reset the workspace" systemctl stop monad-bft monad-execution monad-rpc 2>/dev/null || true if [ -x /opt/monad/scripts/reset-workspace.sh ]; then say " ${c_dim}/opt/monad/scripts/reset-workspace.sh${c_off}" /opt/monad/scripts/reset-workspace.sh >/tmp/monad-labs-reset.log 2>&1 || die "reset-workspace.sh failed — see /tmp/monad-labs-reset.log" else say " /opt/monad/scripts/reset-workspace.sh not found — the official restore truncates TrieDB itself" fi step "3/5 Official restore-from-snapshot.sh, downloading from the Labs mirror" R=$(mktemp /tmp/restore-from-snapshot.XXXX.sh) curl -fsS -m 30 "$BUCKET/scripts/$NETWORK/restore-from-snapshot.sh" -o "$R" || die "cannot fetch the official restore script" say " $BUCKET/scripts/$NETWORK/restore-from-snapshot.sh sha256 $(sha256sum "$R" | cut -c1-16)…" grep -q '^HOST="https://bucket.monadinfra.com/snapshots/$NETWORK"' "$R" || die "the official script changed (no HOST line) — not patching it blindly" sed -i "s|^HOST=\"https://bucket.monadinfra.com/snapshots/\$NETWORK\"|HOST=\"$LABS/snapshots/\$NETWORK\"|" "$R" say " the only change: HOST=\"$LABS/snapshots/\$NETWORK\"" NETWORK=$NETWORK bash "$R" || die "restore-from-snapshot.sh failed" rm -f "$R" step "4/5 Fresh forkpoint (official download-forkpoint.sh)" F=$(mktemp /tmp/download-forkpoint.XXXX.sh) curl -fsS -m 30 "$BUCKET/scripts/$NETWORK/download-forkpoint.sh" -o "$F" || die "cannot fetch download-forkpoint.sh" ok=0; for i in 1 2 3 4 5 6; do bash "$F" && { ok=1; break; }; sleep 20; done # файл раз в минуту [ "$ok" = 1 ] || die "could not fetch a forkpoint" rm -f "$F" step "5/5 Start the node and wait for the head" systemctl start monad-bft monad-execution monad-rpc START=$(date +%s); last="" for i in $(seq 1 360); do L=$(rpc_height http://127.0.0.1:8080); N=$(rpc_height https://rpc.monad.xyz) if [ -n "$L" ] && [ -n "$N" ]; then lag=$(( N - L )) [ "$L" != "$last" ] && say " $(( $(date +%s) - START ))s node $((L)), chain $((N)), behind $lag" last=$L if [ "$lag" -lt 10 ] && [ $((L)) -gt "$BLOCK" ]; then say ""; say "${c_ok}${c_b}✓ Monad node at the head, block $((L)) — total $(since)${c_off}"; exit 0 fi fi sleep 10 done die "the node did not reach the head in an hour — journalctl -u monad-bft -u monad-execution -n 100"