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:
310
README.md
Normal file
310
README.md
Normal 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`.
|
||||
Reference in New Issue
Block a user