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
This commit is contained in:
vsecoder
2026-04-17 14:16:44 +03:00
commit 7e7393e4f8
196 changed files with 55947 additions and 0 deletions

310
README.md Normal file
View File

@@ -0,0 +1,310 @@
# 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=<public ip>, DOMAIN=<fqdn>, ACME_EMAIL=<your>
# 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 <token>` |
#### 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:<x25519>`) |
| Chat | E2E NaCl, typing-индикатор, day-separators |
| Contact Requests | Входящие запросы (push через `addr:<pub>`) |
| 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": "<x25519>" }
{ "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 <pub> --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 <pub> --cosigs pub:sig,pub:sig
client admit-sign --key validator.json --target <candidate-pub>
client remove-validator --key key.json --target <pub>
# ... полный список — `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`.