montana/Node/README.md

205 lines
9.1 KiB
Markdown
Raw Normal View History

# 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 |
---
## Альтернатива: одной командой через `join.sh`
Не хочешь читать 9 шагов — запусти автоматический скрипт:
```bash
# на новом сервере (под root):
scp <existing-server>:/usr/local/bin/montana-node /usr/local/bin/
scp <existing-server>:/etc/montana/genesis-manifest.json /etc/montana/
ALIAS=amsterdam \
LABEL=Amsterdam \
COUNTRY=NL \
HOSTING=DigitalOcean \
COORDS="52.37,4.89" \
TOKEN=<orchestrator_token> \
bash <(curl -sk https://hub.montana.quest/efir369999/montana/raw/branch/main/Node/join.sh)
```
Скрипт автоматически:
1. Поставит xray + ufw
2. Сгенерирует Reality keypair, UUID, shortId
3. Создаст systemd unit для montana-node и xray
4. Откроет порты :443 и :8444
5. Сделает identity через `montana-node init`
6. Зарегистрирует узел в Moscow **orchestrator** (`POST /vpn/node/register`)
7. Orchestrator автоматически обновит на **всех** узлах:
- `peer-health.py` (PEERS)
- `montana-net-pull` (SRC/LABEL/HOSTING/COUNTRY)
- `montana-cities-build` (CITIES dict)
- Helsinki `xray/config.json` (добавит outbound + расширит cascade balancer selector)
8. Через ~30 секунд новый узел появится на `montana.quest/net/` и `/vpn/`
`TOKEN` — секрет orchestrator, лежит на Moscow в `/etc/montana-orchestrator.token`. Запроси у admin Montana.
## Orchestrator API
- `GET https://montana.quest/vpn/node/health``{"ok":true,"nodes":<count>}`
- `GET https://montana.quest/vpn/node/nodes` → список всех известных узлов сети
- `POST https://montana.quest/vpn/node/register` → регистрация нового узла (требует `secret` в body)