# DChain Блокчейн-стек для децентрализованного мессенджера: - **PBFT** консенсус с multi-sig validator governance и equivocation slashing - **Native Go контракты** рядом с WASM (wazero) — нулевая задержка для системных сервисов типа username registry - **WebSocket push API** — клиент не опрашивает, все события прилетают на соединение - **E2E-шифрованный relay mailbox** на libp2p gossipsub с TTL live-detection - **Prometheus `/metrics`**, Caddy auto-HTTPS, observer mode, load-test - React Native / Expo мессенджер (`client-app/`) ## Содержание - [Быстрый старт (dev)](#быстрый-старт-dev) - [Продакшен деплой](#продакшен-деплой) - [Клиент](#клиент) - [Архитектура](#архитектура) - [Контракты](#контракты) - [REST / WebSocket API](#rest--websocket-api) - [CLI](#cli) - [Мониторинг](#мониторинг) - [Тесты](#тесты) - [История изменений](#история-изменений) --- ## Быстрый старт (dev) 3 валидатора в docker-compose с замоканной «интернет» топологией: ```bash docker compose up --build -d open http://localhost:8081 # Explorer главной ноды curl -s http://localhost:8081/api/netstats # синхронность ноды ``` После поднятия нативный `username_registry` уже доступен — отдельный `--profile deploy` больше не нужен. Системные контракты регистрируются в Go-коде при запуске (см. `blockchain/native.go`). ## Продакшен деплой Два варианта, по масштабу: ### 🔸 Single-node (`deploy/single/`) **Рекомендуется для личного/первого узла.** Один узел + Caddy TLS + опциональный Prometheus. ```bash cd deploy/single # 1. Ключ ноды (один раз, сохранить в безопасном месте) docker build -t dchain-node-slim -f ../prod/Dockerfile.slim ../.. mkdir -p keys docker run --rm --entrypoint /usr/local/bin/client \ -v "$PWD/keys:/out" dchain-node-slim \ keygen --out /out/node.json # 2. Конфиг cp node.env.example node.env && $EDITOR node.env # минимум: DCHAIN_ANNOUNCE=, DOMAIN=, ACME_EMAIL= # 3. Вверх docker compose up -d # 4. Проверка curl -s https://$DOMAIN/api/netstats curl -s https://$DOMAIN/api/well-known-version ``` #### Модели доступа | Режим | `DCHAIN_API_TOKEN` | `DCHAIN_API_PRIVATE` | Поведение | |-------|:------------------:|:--------------------:|-----------| | Public (default) | не задан | — | Все могут читать и писать | | Public reads / token writes | задан | `false` | Читать — любой; submit tx — только с токеном | | Fully private | задан | `true` | Всё требует `Authorization: Bearer ` | #### UI / Swagger — включать или нет? | Нужно | `DCHAIN_DISABLE_UI` | `DCHAIN_DISABLE_SWAGGER` | Где что открыто | |-------|:-------------------:|:------------------------:|-----------------| | Публичная с эксплорером + живой docs | не задано | не задано | `/` Explorer, `/swagger` UI, `/api/*`, `/metrics` | | Headless API-нода с открытой OpenAPI | `true` | не задано | `/swagger` UI, `/api/*`, `/metrics` (Explorer выкл.) | | Личная hardened | `true` | `true` | только `/api/*` + `/metrics` | Флаги читаются и из CLI (`--disable-ui`, `--disable-swagger`), и из env. `/api/*` JSON-поверхность регистрируется всегда — отключить её можно только на уровне Caddy / firewall. #### Auto-update ```ini # node.env — когда проект у вас в Gitea DCHAIN_UPDATE_SOURCE_URL=https://gitea.example.com/api/v1/repos/OWNER/REPO/releases/latest UPDATE_ALLOW_MAJOR=false ``` ```bash # Hourly systemd timer с 15-мин jitter sudo cp deploy/single/systemd/dchain-update.{service,timer} /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now dchain-update.timer ``` Скрипт `deploy/single/update.sh` читает `/api/update-check`, делает semver-guarded `git checkout tag`, ребилдит образ с injected версией, smoke-test `node --version`, recreate, health poll. Подробные сценарии (первая нода, joiner, приватная, headless) + полный API reference + systemd-интеграция + troubleshooting — в **[`deploy/single/README.md`](deploy/single/README.md)**. ### 🔹 Multi-validator (`deploy/prod/`) 3 validator'а в PBFT-кворуме, Caddy с ip_hash для WS-стикинесса и least-conn для REST. Для федераций / консорциумов — см. `deploy/prod/README.md`. ## Клиент ```bash cd client-app npm install npx expo start # Expo Dev Tools — iOS / Android / Web ``` React Native + Expo + NativeWind. Ключевые экраны: | Экран | Описание | |-------|----------| | Welcome / Create Account | Генерация Ed25519 + X25519 keypair | | Chat List | Диалоги + real-time via WS (`inbox:`) | | Chat | E2E NaCl, typing-индикатор, day-separators | | Contact Requests | Входящие запросы (push через `addr:`) | | Add Contact | Поиск по `@username` (native registry) или hex | | Wallet | Баланс, история (push), кошельковые action buttons | | Settings | Нода, `@username` покупка, экспорт ключа | Клиент подсоединяется к одной ноде через HTTP + WebSocket: - **WS для всего real-time** (balance, inbox, contacts, tx commit). - **HTTP fallback** через 15 s после обрыва, автоматически. - **Auto-discovery** канонического `username_registry` через `/api/well-known-contracts`. ## Архитектура ``` ┌────────────┐ libp2p ┌────────────┐ libp2p ┌────────────┐ │ node1 │◄─────pubsub──►│ node2 │◄─────pubsub──►│ node3 │ │ validator │ │ validator │ │ validator │ │ + relay │ │ + relay │ │ + relay │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │ │ │ │ HTTPS / wss (via Caddy) │ │ ▼ ▼ ▼ mobile / web / CLI clients (load-balanced ip_hash for WS) ``` Слои (`blockchain/`): - `chain.go` — блочная машина (applyTx, AddBlock, BadgerDB) - `native.go` — системные Go-контракты (интерфейс + registry) - `native_username.go` — реализация username_registry - `equivocation.go` — проверка evidence для SLASH - `types.go` — транзакции, payloads Сети (`p2p/`): - gossipsub topics `dchain/tx/v1`, `dchain/blocks/v1` - стрим-протокол `/dchain/sync/1.0.0` для catch-up - mDNS + DHT для peer discovery Консенсус (`consensus/pbft.go`): - Pre-prepare → Prepare → Commit, 2/3 quorum - liveness tracking, equivocation detection (`recordVote`, `TakeEvidence`) - per-sender FIFO мемпул + round-robin drain до `MaxTxsPerBlock` Node service layer (`node/`): - HTTP API + SSE + **WebSocket hub** с auth и topic-based fanout - Prometheus `/metrics` (zero-dep) - event bus (`events.go`) — SSE + WS + future consumers с одного emit'а - rate-limiter + body-size cap на tx submit Relay (`relay/`): - encrypted envelope routing через gossipsub - BadgerDB mailbox для offline-получателей (TTL 7 дней) - `SetOnStore` hook push'ит новые envelopes в WS ## Контракты **Системные (native Go)** — зарегистрированы при запуске ноды: | ID | Назначение | ABI | |----|-----------|-----| | `native:username_registry` | `@username` → адрес | `register`, `resolve`, `lookup`, `transfer`, `release` | **Пользовательские (WASM)** — деплоятся через `DEPLOY_CONTRACT` tx. TinyGo SDK + host functions (get_state, transfer, get_caller, log, call_contract, …). Ручной деплой из `scripts/deploy_contracts.sh` или CLI `client deploy-contract --wasm ...`. Обзор ABI / примеры — `docs/contracts/` (отдельные файлы на auction, escrow, governance, username_registry). ## REST / WebSocket API ### Chain | Endpoint | Описание | |----------|----------| | `GET /api/netstats` | total_blocks, tx_count, supply, peer count | | `GET /api/blocks?limit=N` | Последние блоки | | `GET /api/block/{index}` | Конкретный блок | | `GET /api/tx/{id}` | Транзакция по ID | | `GET /api/txs/recent?limit=N` | Последние tx (O(limit)) | | `GET /api/address/{pubkey}` | Баланс + история tx | | `GET /api/validators` | Активный validator set | | `GET /api/network-info` | One-shot bootstrap payload (chain_id, genesis, peers, validators, contracts) | | `GET /api/peers` | Живые libp2p peer'ы | | `GET /api/well-known-contracts` | Канонические контракты (native + WASM) | | `GET /api/contracts/{id}` | Метаданные контракта, `"native": true/false` | | `GET /api/contracts/{id}/state/{key}` | Прямое чтение state | | `GET /api/relays` | Список relay-нод (filtered TTL) | | `POST /api/tx` | Submit signed tx (rate-limited, size-capped) | ### Real-time - `GET /api/events` — Server-Sent Events (classic, 1-way) - `GET /api/ws` — **WebSocket** (bidirectional, recommended) WS protocol — см. `node/ws.go`: ```json { "op": "auth", "pubkey": "...", "sig": "..." } { "op": "subscribe", "topic": "addr:..." | "inbox:..." | "typing:..." | "blocks" | "tx" } { "op": "unsubscribe", "topic": "..." } { "op": "submit_tx", "tx": {...}, "id": "client-req-id" } { "op": "typing", "to": "" } { "op": "ping" } ``` Server push: ```json { "event": "hello", "chain_id": "...", "auth_nonce": "..." } { "event": "block", "data": {...} } { "event": "tx", "data": {...} } { "event": "inbox", "data": { id, recipient_pub, sender_pub, sent_at } } { "event": "typing", "data": { from, to } } { "event": "submit_ack", "id": "...", "status": "accepted|rejected", "reason": "..." } ``` Scoped топики (`addr:`, `inbox:`, `typing:`) требуют auth. Без auth доступны только публичные (`blocks`, `tx`, `contract_log`). ## CLI ```bash client keygen --out key.json client balance --key key.json --node URL client transfer --key key.json --to --amount <µT> --node URL client call-contract --key key.json --contract native:username_registry \ --method register --args '["alice"]' --amount 10000 \ --node URL client add-validator --key key.json --target --cosigs pub:sig,pub:sig client admit-sign --key validator.json --target client remove-validator --key key.json --target # ... полный список — `client` без аргументов ``` Node flags (все читают `DCHAIN_*` env fallbacks): - `--db`, `--listen`, `--announce`, `--peers`, `--validators` - `--join http://seed1:8080,http://seed2:8080` (multi-seed, persisted) - `--genesis`, `--observer`, `--register-relay` - `--log-format text|json`, `--allow-genesis-mismatch` ## Мониторинг Prometheus endpoint `/metrics` на каждой ноде. Ключевые метрики: ``` dchain_blocks_total # committed blocks count dchain_txs_total # tx count dchain_tx_submit_accepted_total dchain_tx_submit_rejected_total dchain_ws_connections # current WS sockets dchain_peer_count_live # live libp2p peer count dchain_max_missed_blocks # worst validator liveness gap dchain_block_commit_seconds # histogram of AddBlock time ``` Grafana dashboard + provisioning — `deploy/prod/grafana/` (create as needed; Prometheus data source is auto-wired in compose profile `monitor`). ## Тесты ```bash go test ./... # blockchain + consensus + relay + identity + vm go run ./cmd/loadtest \ --node http://localhost:8081 \ --funder testdata/node1.json \ --clients 50 --duration 60s # end-to-end WS + submit_tx + native contract path ``` ## История изменений Подробный список того, что сделано за последнюю итерацию (стабилизация чейна, governance, WS gateway, observability, native contracts, deployment) — `CHANGELOG.md`.