From 310ce21e177093a301374fa520b566408df4dfc1 Mon Sep 17 00:00:00 2001 From: Montana Moscow Node Date: Thu, 14 May 2026 20:45:14 +0300 Subject: [PATCH] =?UTF-8?q?Montana/Node/:=20=D0=B8=D0=BD=D1=81=D1=82=D1=80?= =?UTF-8?q?=D1=83=D0=BA=D1=86=D0=B8=D1=8F=20=D1=80=D0=B0=D0=B7=D0=B2=D1=91?= =?UTF-8?q?=D1=80=D1=82=D1=8B=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B2=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B4=D0=B0=D1=82=D0=BE=D1=80=D1=81=D0=BA=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=83=D0=B7=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Полная пошаговая документация (README + systemd unit + genesis-manifest) для добавления нового p2p-узла к существующей сети Montana без перегенерации genesis. Новый узел подключается к bootstrap-peers через M8, скачивает state, проходит Bootstrap → CandidateVdf → Registered → Active. Появляется в NodeTable автоматически. Co-Authored-By: Claude Opus 4.7 (1M context) --- Node/README.md | 162 +++++++++++++++++++++++++++++++++++++ Node/genesis-manifest.json | 29 +++++++ Node/montana-node.service | 37 +++++++++ 3 files changed, 228 insertions(+) create mode 100644 Node/README.md create mode 100644 Node/genesis-manifest.json create mode 100644 Node/montana-node.service diff --git a/Node/README.md b/Node/README.md new file mode 100644 index 00000000..013f0acc --- /dev/null +++ b/Node/README.md @@ -0,0 +1,162 @@ +# Montana Node — установка валидаторского узла + +Полная инструкция запуска нового p2p-узла сети Montana с присоединением к существующей сети без перегенерации genesis. + +## Требования к серверу + +- Ubuntu 22.04 / 24.04 (или совместимый Linux x86_64) +- Минимум 2 CPU / 2 GB RAM / 20 GB диск +- Публичный IP, открытый порт `:8444` (TCP) — M8 cross-machine +- Открытый порт `:443` (TCP) — опционально, для xray VLESS+Reality VPN-фронта/бэкенда + +## Шаги установки + +### 1. Подготовка сервера + +```bash +apt-get update +apt-get install -y curl ca-certificates ufw +useradd -r -m -d /var/lib/montana -s /usr/sbin/nologin montana +mkdir -p /var/lib/montana && chown montana:montana /var/lib/montana && chmod 700 /var/lib/montana +mkdir -p /etc/montana +``` + +### 2. Скопировать `montana-node` бинарь + +С любого существующего узла Montana (`montana-moscow`, `montana-frankfurt`, `montana-finland`, `montana-us`): + +```bash +# на macOS: +scp montana-moscow:/usr/local/bin/montana-node /tmp/ +scp /tmp/montana-node :/usr/local/bin/montana-node +ssh "chmod +x /usr/local/bin/montana-node" +``` + +### 3. Скопировать `genesis-manifest.json` + +`genesis-manifest.json` определяет **bootstrap-peers** (с кем новый узел синхронизируется при первом запуске). Manifest **одинаков на всех узлах сети** — берём с любого существующего. + +```bash +scp montana-moscow:/etc/montana/genesis-manifest.json /tmp/ +scp /tmp/genesis-manifest.json :/etc/montana/genesis-manifest.json +``` + +### 4. Сгенерировать identity нового узла + +```bash +ssh "sudo -u montana /usr/local/bin/montana-node init --data-dir /var/lib/montana" +``` + +Команда выведет **24 слова мнемоники** — **сохрани их в надёжном месте** (это единственный способ восстановить ключи узла). Также покажет: +- `account_id` (hex 64 символа) +- `node_id` (hex 64 символа) +- `master_seed_fp` (8-байтный отпечаток) + +### 5. Открыть порты + +```bash +ufw default deny incoming +ufw default allow outgoing +ufw allow 22/tcp comment 'SSH' +ufw allow 8444/tcp comment 'Montana M8 p2p' +ufw allow 443/tcp comment 'optional xray VPN' # если узел работает как VPN +echo 'y' | ufw enable +``` + +### 6. systemd unit + +Скопировать `montana-node.service` из этой папки: + +```bash +scp Montana/Node/montana-node.service :/etc/systemd/system/ +ssh "systemctl daemon-reload && systemctl enable --now montana-node" +``` + +### 7. Проверка + +```bash +ssh "systemctl status montana-node; journalctl -u montana-node --no-pager -n 30" +``` + +В логах должно появиться: + +``` +[network] CONNECTION ESTABLISHED peer=12D3Koo... label=moscow +[network] CONNECTION ESTABLISHED peer=12D3Koo... label=frankfurt +[network] CONNECTION ESTABLISHED peer=12D3Koo... label=helsinki +[network] heartbeat OK peer=... +``` + +### 8. Lifecycle нового узла + +После запуска узел проходит фазы автоматически: + +1. **Bootstrap** (несколько секунд) — подключение к peer-ам manifest, дискавери. +2. **CandidateVdf** — тикает VDF (Verifiable Delay Function), накапливая `vdf_chain_length` до `τ₂ = 20160` окон (≈10 часов wall-clock на M-class CPU). +3. **Registered** — формирует NodeRegistration через `apply_noderegistrations_batch`, попадает в `CandidatePool`. +4. **Active** — на ближайшем selection-окне (`window % 336 == 0`) проходит `apply_selection_event` и появляется в `NodeTable`. С этого момента — полноправный валидатор: per-window VdfReveal + BundledConfirmation + ProposalHeader + эмиссия `13 Ɉ`. + +### 9. Регистрация на карте (опционально) + +Чтобы новый узел отображался на `/net/` карте `montana.quest`: + +#### 9.1. Добавить DNS A-запись через Cloudflare + +```bash +# например: .montana.quest → , DNS-only (не proxied, Reality не работает за CF) +``` + +#### 9.2. Добавить алиас на Moscow (orchestrator) + +```bash +# /root/.ssh/config на Moscow: +Host montana--stats + HostName .montana.quest + User root + IdentityFile /root/.ssh/montana_stats + IdentitiesOnly yes + StrictHostKeyChecking accept-new + ConnectTimeout 6 + BatchMode yes +``` + +#### 9.3. Добавить в `montana-net-pull` arrays + `montana-cities-build` CITIES dict + +Файлы: +- `/usr/local/bin/montana-net-pull` — добавить `[]="..."` в SRC/LABEL/REGION/COUNTRY/HOSTING + `for NODE in msk fra fin us ;` +- `/usr/local/bin/montana-cities-build` — добавить `: {label, country, hosting, coords, role, peers}` в CITIES + +#### 9.4. Добавить в `peer-health.py` на каждом узле + +```python +PEERS = [ + ... + {"name": "", "ip": ".montana.quest", "country": ""}, +] +``` + +Распространить через scp на все узлы и перезапустить `montana-net-aggregator.timer`. + +## Архитектурные принципы + +- **Никакого rebuild genesis при добавлении узла**. `genesis-manifest.json` определяет bootstrap-peers, state узла живёт в `/var/lib/montana/`. +- **Manifest одинаков на всех узлах** — peer_id хэши участвуют в M8 handshake. +- **Identity рандомная** — каждый новый узел генерирует свою (мнемоника + ML-DSA-65 ключи + ML-KEM-768 для PQ-шифрования). +- **IP в публичных файлах запрещены** — везде использовать `.montana.quest` через CF DNS-only (см. `feedback_no_ip_in_public.md`). + +## Удаление узла + +```bash +ssh "systemctl stop montana-node && systemctl disable montana-node && rm -rf /var/lib/montana /etc/montana /etc/systemd/system/montana-node.service && systemctl daemon-reload" +``` + +После остановки узел перестанет получать NodeRegistration heartbeat, существующие peer-ы через `τ₂` окон уберут его из активного `NodeTable` (cooldown logic). + +## Текущая сеть (на момент написания) + +| ID | Город | Страна | Хостинг | Роль | +|-----|-----------|--------|--------------|---------------------| +| msk | Москва | RU | Timeweb | genesis, vpn origin | +| fra | Frankfurt | DE | Timeweb | genesis, vpn backend| +| fin | Helsinki | FI | THE.Hosting | genesis, vpn frontend| +| us | New York | US | WorkTitans | non-genesis joined 2026-05-14, vpn backend | diff --git a/Node/genesis-manifest.json b/Node/genesis-manifest.json new file mode 100644 index 00000000..f7c91bf7 --- /dev/null +++ b/Node/genesis-manifest.json @@ -0,0 +1,29 @@ +{ + "network_name": "montana", + "peers": [ + { + "label": "moscow", + "multiaddr": "/ip4/176.124.208.93/tcp/8444", + "peer_id": "12D3KooWE6knqrBMdVR32P9TowDtHDrsBzCHH8weq8fjeyEgFdL3", + "account_id_hex": "4c290c3d5d63e84b99c30c83fb4d172e04102af4492b4d56d0642711b09e2072", + "node_id_hex": "75bfaf9026405c12ef36437f08cc63c040cfe1924773dedcba0abadf8c6928a1", + "bootstrap": true + }, + { + "label": "frankfurt", + "multiaddr": "/ip4/89.19.208.158/tcp/8444", + "peer_id": "12D3KooWMzPBiJWvFfRX3pvcjF5VNtj6eyxMm4u4fhtZgcP9zQrn", + "account_id_hex": "53560626aff44b5f0a88d7b235ef2028a3cf0517fd6fd2aa20b5566345a91e29", + "node_id_hex": "5509211b179d69698913e47605d2b0ed24a91702fb6e9d0fbcd3c3c626270aab", + "bootstrap": false + }, + { + "label": "helsinki", + "multiaddr": "/ip4/91.132.142.42/tcp/8444", + "peer_id": "12D3KooWEzWH5x655cZpw7Dnw96q1bUVKYBHhrfrMC2X48JQG3P7", + "account_id_hex": "19edd79c0c13b7164ed5fb00d571ba1fa26726adf1e6ef61a3f21b20fa1b42c4", + "node_id_hex": "d63cc60c8367ba6be903e50bc0190d7e2e60f89f30f24d3a10dceb92613a5901", + "bootstrap": false + } + ] +} diff --git a/Node/montana-node.service b/Node/montana-node.service new file mode 100644 index 00000000..08477c07 --- /dev/null +++ b/Node/montana-node.service @@ -0,0 +1,37 @@ +[Unit] +Description=Montana Local Node (M8 cross-machine, Proof-of-Time) +Documentation=https://github.com/efir369999/Montana +After=network.target +Wants=network-online.target + +[Service] +Type=simple +User=montana +Group=montana +ExecStart=/usr/local/bin/montana-node start --data-dir /var/lib/montana --listen /ip4/0.0.0.0/tcp/8444 --genesis-manifest /etc/montana/genesis-manifest.json +Restart=on-failure +RestartSec=10 +StandardOutput=journal +StandardError=journal + +# Hardening (per systemd security best-practice) +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=/var/lib/montana +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectControlGroups=yes +RestrictRealtime=yes +RestrictSUIDSGID=yes +LockPersonality=yes +MemoryDenyWriteExecute=no +SystemCallArchitectures=native + +# Resource limits — узел single-thread + libp2p network thread (1 узел = ~110% CPU max) +CPUQuota=200% +LimitNOFILE=8192 + +[Install] +WantedBy=multi-user.target