Genesis 2026-05-09 00:00 UTC: bootstrap-узла.sh (signed manifest e4eb11c9d198ab80)

This commit is contained in:
efir369999 2026-05-10 04:54:59 +03:00
parent 6b0f93c7da
commit 837badf8fb

View File

@ -0,0 +1,129 @@
#!/usr/bin/env bash
# Bootstrap нового узла Montana из Genesis 2026-05-09 00:00 UTC.
#
# Скрипт делает четыре вещи:
# 1. Находит живой источник Genesis (перебор IP-пула, далее TON DNS).
# 2. Клонирует репозиторий `Ничто` локально.
# 3. Проверяет МАНИФЕСТ.md по ed25519-подписи Genesis root pubkey.
# 4. Сверяет SHA-256 каждого файла с тем, что в манифесте.
#
# Использование:
# curl -sfL https://<любой-mirror>/montana.git/raw/main/.../bootstrap-узла.sh | bash
# или
# bash ./bootstrap-узла.sh [целевая_папка]
#
# Запуск без root. Зависимости: git, python3, curl, openssl.
set -euo pipefail
# --- Genesis-параметры (вшиты, не должны меняться) ---
ROOT_PUBKEY_B64="MtE485L/O87feImV8XjSdPPskgestVd7mqJvKo9MRas="
ROOT_FP="e4eb11c9d198ab80"
GENESIS_FOLDER="Montana/Russian/Genesis/ГЕНЕЗИС_2026-05-09_00-00_UTC"
# IP-пул реплицируемых узлов (обновляется только при новом Genesis)
IP_POOL=(
"91.132.142.42" # Helsinki (Hetzner AS24940)
"89.19.208.158" # Frankfurt (Timeweb AS9123)
"176.124.208.93" # Moscow (Timeweb AS9123, master/Gitea)
)
# Доменные источники (DNS + TON DNS)
DOMAINS=(
"hub.montana.quest/efir369999/montana"
"junomoneta.ton/montana"
)
TARGET="${1:-$HOME/montana-node}"
log() { echo "[bootstrap] $*" >&2; }
fail() { echo "[bootstrap] FATAL: $*" >&2; exit 1; }
# --- 1. Найти живой источник ---
SOURCE=""
log "Поиск живого Genesis-источника..."
for dom in "${DOMAINS[@]}"; do
if curl -fsI --max-time 5 "https://${dom}.git/info/refs?service=git-upload-pack" >/dev/null 2>&1; then
SOURCE="https://${dom}.git"
log "Найден: $SOURCE"
break
fi
done
if [[ -z "$SOURCE" ]]; then
for ip in "${IP_POOL[@]}"; do
if curl -fsI --max-time 5 -k "https://${ip}/montana.git/info/refs?service=git-upload-pack" >/dev/null 2>&1; then
SOURCE="https://${ip}/montana.git"
log "Найден IP-fallback: $SOURCE"
break
fi
done
fi
[[ -z "$SOURCE" ]] && fail "ни один Genesis-источник не отвечает (домены и IP-пул)"
# --- 2. Клонирование ---
log "Клонирование $SOURCE$TARGET"
mkdir -p "$(dirname "$TARGET")"
GIT_LFS_SKIP_SMUDGE=1 git clone --depth=1 --no-tags "$SOURCE" "$TARGET"
# --- 3. Проверка подписи манифеста ---
GDIR="$TARGET/$GENESIS_FOLDER"
[[ -d "$GDIR" ]] || fail "Genesis-папка не найдена в репозитории: $GDIR"
MANIFEST="$GDIR/МАНИФЕСТ.md"
[[ -f "$MANIFEST" ]] || fail "МАНИФЕСТ.md отсутствует"
log "Проверка ed25519-подписи манифеста против root pubkey $ROOT_FP"
python3 - "$MANIFEST" "$ROOT_PUBKEY_B64" "$GDIR" <<'PY'
import sys, base64, re, hashlib, pathlib
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from cryptography.exceptions import InvalidSignature
manifest_path, pubkey_b64, gdir = sys.argv[1], sys.argv[2], sys.argv[3]
text = pathlib.Path(manifest_path).read_text()
m_files = re.search(r'## Файлы\n\n```\n(.*?)\n```', text, re.S)
m_sig = re.search(r'## Подпись.*?```\n(\S+)\n```', text, re.S)
m_pk = re.search(r'## Корневой ключ.*?```\n(\S+)\n```', text, re.S)
if not (m_files and m_sig and m_pk):
sys.exit("[bootstrap] FATAL: манифест повреждён, не удалось распарсить блоки")
if m_pk.group(1) != pubkey_b64:
sys.exit(f"[bootstrap] FATAL: pubkey в манифесте ({m_pk.group(1)[:20]}…) не совпадает со вшитым корнем")
body = m_files.group(1) + '\n'
sig = base64.b64decode(m_sig.group(1))
pk = Ed25519PublicKey.from_public_bytes(base64.b64decode(pubkey_b64))
try:
pk.verify(sig, body.encode())
except InvalidSignature:
sys.exit("[bootstrap] FATAL: подпись манифеста НЕ ПРОВЕРЕНА — источник возможно подменён")
print("[bootstrap] ✓ подпись манифеста валидна")
bad = []
for line in body.strip().split('\n'):
sha, fname = line.split(' ', 1)
p = pathlib.Path(gdir) / fname
if not p.exists():
bad.append(f" отсутствует: {fname}")
continue
actual = hashlib.sha256(p.read_bytes()).hexdigest()
if actual != sha:
bad.append(f" hash mismatch {fname}: expected {sha[:12]}…, got {actual[:12]}…")
if bad:
print("[bootstrap] FATAL: содержимое не совпадает с манифестом")
for b in bad: print(b)
sys.exit(1)
print(f"[bootstrap] ✓ {len(body.strip().splitlines())} файлов прошли SHA-256-сверку")
PY
log "Bootstrap завершён. Genesis: $GDIR"
log "Дальше: montana-node init --from-folder $TARGET --genesis $GENESIS_FOLDER"