Files
dchain/docs/node/multi-server.md
vsecoder 7e7393e4f8 chore: initial commit for v0.0.1
DChain single-node blockchain + React Native messenger client.

Core:
- PBFT consensus with multi-sig validator admission + equivocation slashing
- BadgerDB + schema migration scaffold (CurrentSchemaVersion=0)
- libp2p gossipsub (tx/v1, blocks/v1, relay/v1, version/v1)
- Native Go contracts (username_registry) alongside WASM (wazero)
- WebSocket gateway with topic-based fanout + Ed25519-nonce auth
- Relay mailbox with NaCl envelope encryption (X25519 + Ed25519)
- Prometheus /metrics, per-IP rate limit, body-size cap

Deployment:
- Single-node compose (deploy/single/) with Caddy TLS + optional Prometheus
- 3-node dev compose (docker-compose.yml) with mocked internet topology
- 3-validator prod compose (deploy/prod/) for federation
- Auto-update from Gitea via /api/update-check + systemd timer
- Build-time version injection (ldflags → node --version)
- UI / Swagger toggle flags (DCHAIN_DISABLE_UI, DCHAIN_DISABLE_SWAGGER)

Client (client-app/):
- Expo / React Native / NativeWind
- E2E NaCl encryption, typing indicator, contact requests
- Auto-discovery of canonical contracts, chain_id aware, WS reconnect on node switch

Documentation:
- README.md, CHANGELOG.md, CONTEXT.md
- deploy/single/README.md with 6 operator scenarios
- deploy/UPDATE_STRATEGY.md with 4-layer forward-compat design
- docs/contracts/*.md per contract
2026-04-17 14:16:44 +03:00

9.8 KiB
Raw Permalink Blame History

Деплой на реальные серверы

Руководство по запуску трёх полноценных нод на разных VPS/серверах в интернете.

Концепция

  Server A (1.2.3.11)          Server B (1.2.3.12)          Server C (1.2.3.13)
  ┌─────────────────┐          ┌─────────────────┐          ┌─────────────────┐
  │  node1          │          │  node2          │          │  node3          │
  │  validator      │◀────────▶│  validator      │◀────────▶│  validator      │
  │  relay (2000µT) │          │  relay (1500µT) │          │  relay (1000µT) │
  │  :4001 :8080    │          │  :4001 :8080    │          │  :4001 :8080    │
  └────────┬────────┘          └────────┬────────┘          └────────┬────────┘
           │                            │                             │
           └────────────────────────────┴─────────────────────────────┘
                                  libp2p P2P
                          gossipsub: tx + blocks
                          streams: PBFT consensus, sync

Каждая нода — полноценный валидатор (участвует в PBFT-консенсусе) и relay-провайдер (хранит и доставляет зашифрованные сообщения).

Требования

  • Ubuntu 22.04 / Debian 12 (или любой Linux)
  • Открытый TCP-порт 4001 (P2P) и опционально 8080 (HTTP API)
  • Go 1.21+ только для сборки (на сервере нужен только бинарник)

1. Подготовка ключей

На любой машине (ноутбук / CI):

# Сгенерировать 3 ключа
./client keygen --out keys/node1.json
./client keygen --out keys/node2.json
./client keygen --out keys/node3.json

# Получить pubkey и peer ID для каждого
./peerid --key keys/node1.json --ip <SERVER_A_IP> --port 4001
./peerid --key keys/node2.json --ip <SERVER_B_IP> --port 4001
./peerid --key keys/node3.json --ip <SERVER_C_IP> --port 4001

Вывод peerid:

pub_key:   26018d40...
peer_id:   12D3KooW...
multiaddr: /ip4/1.2.3.11/tcp/4001/p2p/12D3KooW...

Запишите pub_key и peer_id для каждой ноды — они нужны в конфигурации.

2. Сборка бинарника

git clone https://github.com/your/go-blockchain
cd go-blockchain

CGO_ENABLED=0 GOOS=linux go build -trimpath -o node ./cmd/node
CGO_ENABLED=0 GOOS=linux go build -trimpath -o client ./cmd/client

# Скопировать на серверы
scp node client root@1.2.3.11:/usr/local/bin/
scp node client root@1.2.3.12:/usr/local/bin/
scp node client root@1.2.3.13:/usr/local/bin/

3. Копирование ключей

# Каждый сервер получает ТОЛЬКО свой ключ
scp keys/node1.json root@1.2.3.11:/etc/dchain/node.json
scp keys/node2.json root@1.2.3.12:/etc/dchain/node.json
scp keys/node3.json root@1.2.3.13:/etc/dchain/node.json

chmod 600 /etc/dchain/node.json  # на каждом сервере

4. Переменные конфигурации

Подставьте реальные значения из шага 1:

# Validators — все три pubkey через запятую
VALIDATORS="<NODE1_PUB>,<NODE2_PUB>,<NODE3_PUB>"

# Bootstrap multiaddrs для node2 и node3
NODE1_PEER="/ip4/1.2.3.11/tcp/4001/p2p/<NODE1_PEER_ID>"
NODE2_PEER="/ip4/1.2.3.12/tcp/4001/p2p/<NODE2_PEER_ID>"

5. Запуск нод

Server A — node1 (genesis + validator + relay)

node \
  --genesis \
  --key        /etc/dchain/node.json \
  --db         /var/lib/dchain/chain \
  --mailbox-db /var/lib/dchain/mailbox \
  --listen     /ip4/0.0.0.0/tcp/4001 \
  --announce   /ip4/1.2.3.11/tcp/4001 \
  --stats-addr :8080 \
  --validators "$VALIDATORS" \
  --heartbeat=true \
  --register-relay \
  --relay-fee  2000

Server B — node2 (validator + relay)

node \
  --key        /etc/dchain/node.json \
  --db         /var/lib/dchain/chain \
  --mailbox-db /var/lib/dchain/mailbox \
  --listen     /ip4/0.0.0.0/tcp/4001 \
  --announce   /ip4/1.2.3.12/tcp/4001 \
  --stats-addr :8080 \
  --validators "$VALIDATORS" \
  --peers      "$NODE1_PEER" \
  --heartbeat=true \
  --register-relay \
  --relay-fee  1500

Server C — node3 (validator + relay)

node \
  --key        /etc/dchain/node.json \
  --db         /var/lib/dchain/chain \
  --mailbox-db /var/lib/dchain/mailbox \
  --listen     /ip4/0.0.0.0/tcp/4001 \
  --announce   /ip4/1.2.3.13/tcp/4001 \
  --stats-addr :8080 \
  --validators "$VALIDATORS" \
  --peers      "$NODE1_PEER,$NODE2_PEER" \
  --heartbeat=true \
  --register-relay \
  --relay-fee  1000

6. systemd unit

Создайте /etc/systemd/system/dchain.service на каждом сервере:

[Unit]
Description=DChain Node
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=dchain
ExecStart=/usr/local/bin/node \
    --key        /etc/dchain/node.json \
    --db         /var/lib/dchain/chain \
    --mailbox-db /var/lib/dchain/mailbox \
    --listen     /ip4/0.0.0.0/tcp/4001 \
    --announce   /ip4/YOUR_PUBLIC_IP/tcp/4001 \
    --stats-addr :8080 \
    --validators "V1_PUB,V2_PUB,V3_PUB" \
    --peers      "/ip4/SEED_IP/tcp/4001/p2p/SEED_PEER_ID" \
    --heartbeat=true \
    --register-relay \
    --relay-fee  1000
Restart=on-failure
RestartSec=5s
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
# Применить
sudo systemctl daemon-reload
sudo systemctl enable --now dchain

# Логи
sudo journalctl -u dchain -f

7. Деплой контрактов

После запуска всех трёх нод:

# С любой машины имеющей client и ключ node1
export NODE_URL=http://1.2.3.11:8080
export KEY=/etc/dchain/node.json  # или локальная копия

# Задеплоить username_registry
CONTRACT_ID=$(client deploy-contract \
  --key $KEY \
  --wasm contracts/username_registry/username_registry.wasm \
  --abi  contracts/username_registry/username_registry_abi.json \
  --node $NODE_URL | grep contract_id | awk '{print $2}')

echo "username_registry: $CONTRACT_ID"

# Залинковать governance на всех трёх нодах
for NODE in http://1.2.3.11:8080 http://1.2.3.12:8080 http://1.2.3.13:8080; do
  curl -sX POST "$NODE/api/governance/link" \
    -H "Content-Type: application/json" \
    -d "{\"governance\":\"$GOV_ID\"}"
done

8. Как работает сеть

Bootstrap и discovery

node2 старт:
  1. --peers → connectWithRetry("/ip4/1.2.3.11/tcp/4001/p2p/...")
     Подключается к node1 (бесконечный retry с backoff 1→30s)

  2. При connect → syncOnConnect()
     Запрашивает все блоки которых нет у node2

  3. Kademlia DHT bootstrap (через node1)
     DHT узнаёт о node3 → DiscoverPeers() подключается

  4. mDNS (только LAN/Docker) — игнорируется в интернете

  5. connectWithRetry keep-alive: каждые 30s проверяет связь,
     автоматически переподключает при обрыве

Консенсус

PBFT 3-of-3, fault tolerance f=1:

  • Для коммита блока нужны подписи 2 из 3 валидаторов
  • Если одна нода упала → сеть продолжает работать
  • Если упали две → сеть ждёт (не производит блоки)

--announce и адреса

Без --announce libp2p рекламирует все адреса интерфейсов:

  • 0.0.0.0 → раскрывается в loopback и внутренние адреса
  • Другие ноды получают 127.0.0.1:4001 → не могут подключиться

С --announce /ip4/1.2.3.11/tcp/4001:

  • Единственный рекламируемый адрес — публичный IP сервера
  • AddrStrings() возвращает только этот адрес
  • DHT propagates этот адрес другим нодам в сети

9. Firewall

# UFW
ufw allow 4001/tcp  # P2P — обязательно
ufw allow 8080/tcp  # HTTP API — опционально (для Explorer, деплоя контрактов)

# iptables
iptables -A INPUT -p tcp --dport 4001 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT

10. Добавление новой ноды в сеть

Новый участник (не валидатор, только синхронизация и relay):

node \
  --key        /etc/dchain/node.json \
  --db         /var/lib/dchain/chain \
  --mailbox-db /var/lib/dchain/mailbox \
  --listen     /ip4/0.0.0.0/tcp/4001 \
  --announce   /ip4/YOUR_IP/tcp/4001 \
  --stats-addr :8080 \
  --validators "$VALIDATORS" \
  --peers      "$NODE1_PEER" \
  --heartbeat=false \
  --register-relay \
  --relay-fee  500

Нода автоматически:

  1. Подключится к node1 через --peers
  2. Синхронизирует всю историю блоков
  3. Через DHT найдёт node2 и node3
  4. Начнёт получать и пересылать relay-сообщения

Чтобы сделать её валидатором — нужна ADD_VALIDATOR транзакция от существующего валидатора.