Genesis 2026-05-09 00:00 UTC: bootstrap-узла.sh (signed manifest e4eb11c9d198ab80)
This commit is contained in:
parent
6b0f93c7da
commit
837badf8fb
129
Russian/Genesis/ГЕНЕЗИС_2026-05-09_00-00_UTC/bootstrap-узла.sh
Normal file
129
Russian/Genesis/ГЕНЕЗИС_2026-05-09_00-00_UTC/bootstrap-узла.sh
Normal 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"
|
||||||
Loading…
Reference in New Issue
Block a user