Montana/Node/: инструкция развёртывания валидаторского узла
Полная пошаговая документация (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) <noreply@anthropic.com>
This commit is contained in:
parent
8764f7ac74
commit
310ce21e17
162
Node/README.md
Normal file
162
Node/README.md
Normal file
@ -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 <new-server>:/usr/local/bin/montana-node
|
||||
ssh <new-server> "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 <new-server>:/etc/montana/genesis-manifest.json
|
||||
```
|
||||
|
||||
### 4. Сгенерировать identity нового узла
|
||||
|
||||
```bash
|
||||
ssh <new-server> "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 <new-server>:/etc/systemd/system/
|
||||
ssh <new-server> "systemctl daemon-reload && systemctl enable --now montana-node"
|
||||
```
|
||||
|
||||
### 7. Проверка
|
||||
|
||||
```bash
|
||||
ssh <new-server> "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
|
||||
# например: <alias>.montana.quest → <new-ip>, DNS-only (не proxied, Reality не работает за CF)
|
||||
```
|
||||
|
||||
#### 9.2. Добавить алиас на Moscow (orchestrator)
|
||||
|
||||
```bash
|
||||
# /root/.ssh/config на Moscow:
|
||||
Host montana-<alias>-stats
|
||||
HostName <alias>.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` — добавить `[<id>]="..."` в SRC/LABEL/REGION/COUNTRY/HOSTING + `for NODE in msk fra fin us <id>;`
|
||||
- `/usr/local/bin/montana-cities-build` — добавить `<id>: {label, country, hosting, coords, role, peers}` в CITIES
|
||||
|
||||
#### 9.4. Добавить в `peer-health.py` на каждом узле
|
||||
|
||||
```python
|
||||
PEERS = [
|
||||
...
|
||||
{"name": "<id>", "ip": "<alias>.montana.quest", "country": "<CC>"},
|
||||
]
|
||||
```
|
||||
|
||||
Распространить через 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 в публичных файлах запрещены** — везде использовать `<alias>.montana.quest` через CF DNS-only (см. `feedback_no_ip_in_public.md`).
|
||||
|
||||
## Удаление узла
|
||||
|
||||
```bash
|
||||
ssh <server> "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 |
|
||||
29
Node/genesis-manifest.json
Normal file
29
Node/genesis-manifest.json
Normal file
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
37
Node/montana-node.service
Normal file
37
Node/montana-node.service
Normal file
@ -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
|
||||
Loading…
Reference in New Issue
Block a user