Node flags (cmd/node/main.go):
--max-cpu / --max-ram-mb — Go runtime caps (GOMAXPROCS / GOMEMLIMIT)
--feed-disk-limit-mb — hard 507 refusal for new post bodies over quota
--chain-disk-limit-mb — advisory watcher (can't reject blocks without
breaking consensus; logs WARN every minute)
Client — Saved Messages (self-chat):
- Auto-created on sign-in, pinned top of chat list, blue bookmark avatar
- Send short-circuits the relay (no encrypt, no fee, no mailbox hop)
- Empty state rendered outside inverted FlatList — fixes the mirrored
"say hi…" on Android RTL-aware layout builds
- PostCard shows "You" for own posts instead of the self-contact alias
Client — user walls:
- New route /(app)/feed/author/[pub] with infinite-scroll via
`created_at` cursor and pull-to-refresh
- Profile screen gains "View posts" button (universal) next to
"Open chat" (contact-only)
Feed pipeline:
- Bump client JPEG quality 0.5 → 0.75 to match server scrubber (Q=75),
so a 60 KiB compose doesn't balloon past 256 KiB after server re-encode
- ErrPostTooLarge now wraps with the actual size vs cap, errors.Is
preserved in the HTTP layer
- FeedMailbox quota + DiskUsage surface — supports new CLI flag
README:
- Step-by-step "first node / joiner" section on the landing page,
full flag tables incl. the new resource-cap group, minimal
checklists for open/private/low-end deployments
446 lines
24 KiB
Markdown
446 lines
24 KiB
Markdown
# 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
|
||
(1:1 чаты через NaCl box; посты в ленте — plaintext-публичные)
|
||
- **Социальная лента v2.0.0** (заменила каналы): публичные посты
|
||
с оплатой за размер (автор платит, хостящая релей-нода получает);
|
||
on-chain граф подписок + лайки; off-chain просмотры + хэштеги;
|
||
мандаторный server-side scrubber метаданных (EXIF/GPS-стрип + FFmpeg
|
||
sidecar для видео); share-to-chat c embedded post-карточкой
|
||
- **Система обновлений:** build-time версия → `/api/well-known-version`,
|
||
peer-version gossip, `/api/update-check` против Gitea releases,
|
||
`update.sh` с semver guard
|
||
- **Prometheus `/metrics`**, Caddy auto-HTTPS, observer mode, load-test
|
||
|
||
## Содержание
|
||
|
||
- [Быстрый старт](#быстрый-старт)
|
||
- [Поднятие ноды — пошагово](#поднятие-ноды--пошагово)
|
||
- [Продакшен деплой](#продакшен-деплой)
|
||
- [Архитектура](#архитектура)
|
||
- [REST / WebSocket API](#rest--websocket-api)
|
||
- [CLI](#cli)
|
||
- [Мониторинг](#мониторинг)
|
||
- [Тесты](#тесты)
|
||
- [Документация](#документация)
|
||
|
||
---
|
||
|
||
## Быстрый старт
|
||
|
||
Одна нода в Docker, HTTP API на `localhost:8080`, Explorer UI и Swagger
|
||
открыты:
|
||
|
||
```bash
|
||
# 1. Собираем prod-образ
|
||
docker build -t dchain-node-slim -f deploy/prod/Dockerfile.slim .
|
||
|
||
# 2. Ключ ноды (один раз)
|
||
mkdir -p keys
|
||
docker run --rm --entrypoint /usr/local/bin/client \
|
||
-v "$PWD/keys:/out" dchain-node-slim \
|
||
keygen --out /out/node.json
|
||
|
||
# 3. Запуск (genesis-валидатор)
|
||
docker run -d --name dchain --restart unless-stopped \
|
||
-p 4001:4001 -p 8080:8080 \
|
||
-v dchain_data:/data \
|
||
-v "$PWD/keys:/keys:ro" \
|
||
-e DCHAIN_GENESIS=true \
|
||
-e DCHAIN_ANNOUNCE=/ip4/127.0.0.1/tcp/4001 \
|
||
dchain-node-slim \
|
||
--db=/data/chain --mailbox-db=/data/mailbox --key=/keys/node.json \
|
||
--relay-key=/data/relay.json --listen=/ip4/0.0.0.0/tcp/4001 --stats-addr=:8080
|
||
|
||
# 4. Проверка
|
||
open http://localhost:8080/ # Explorer
|
||
open http://localhost:8080/swagger # Swagger UI
|
||
curl -s http://localhost:8080/api/well-known-version | jq .
|
||
```
|
||
|
||
3-node dev-кластер (для тестов PBFT кворума, slashing, federation): `docker compose up --build -d` — см. [`docs/quickstart.md`](docs/quickstart.md).
|
||
|
||
## Поднятие ноды — пошагово
|
||
|
||
Ниже — полный минимум для двух сценариев, которые покрывают 99% случаев:
|
||
**первая нода сети** (genesis) и **присоединение к существующей сети**.
|
||
Все флаги читаются также из соответствующего `DCHAIN_*` env-var (CLI > env > default).
|
||
|
||
### Шаг 1. Ключи
|
||
|
||
```bash
|
||
# Ключ identity ноды (Ed25519 — подпись блоков + tx)
|
||
./client keygen --out keys/node.json
|
||
# relay-ключ (X25519 — E2E-mailbox) создаётся нодой сам при первом старте,
|
||
# но можно задать путь заранее через --relay-key.
|
||
```
|
||
|
||
### Шаг 2a. Первая нода (genesis)
|
||
|
||
Поднимает новую сеть с одним валидатором. `--genesis=true` **только** для самой первой ноды и **только один раз** — если блок 0 уже есть в `--db`, флаг игнорируется.
|
||
|
||
```bash
|
||
./node \
|
||
--genesis=true \
|
||
--key=keys/node.json \
|
||
--db=./chaindata \
|
||
--mailbox-db=./mailboxdata \
|
||
--feed-db=./feeddata \
|
||
--listen=/ip4/0.0.0.0/tcp/4001 \
|
||
--announce=/ip4/<ВАШ-ПУБЛИЧНЫЙ-IP>/tcp/4001 \
|
||
--stats-addr=:8080 \
|
||
--register-relay=true \
|
||
--relay-fee=1000
|
||
```
|
||
|
||
`--announce` **обязателен** для любой ноды смотрящей в интернет (VPS / внешний IP / Docker с проброшенным портом). Без него libp2p пытается UPnP/NAT-PMP и чаще всего промахивается.
|
||
|
||
### Шаг 2b. Вторая и последующие ноды
|
||
|
||
Нужен **один** из двух способов узнать первую ноду. Второй удобнее.
|
||
|
||
**Через HTTP URL живой ноды** (рекомендуется — нода сама заберёт multiaddr через `/api/network-info`, проверит genesis_hash и синхронизирует цепь):
|
||
|
||
```bash
|
||
./node \
|
||
--join=https://first-node.example.com \
|
||
--key=keys/node.json \
|
||
--db=./chaindata \
|
||
--mailbox-db=./mailboxdata \
|
||
--feed-db=./feeddata \
|
||
--listen=/ip4/0.0.0.0/tcp/4001 \
|
||
--announce=/ip4/<ВАШ-ПУБЛИЧНЫЙ-IP>/tcp/4001 \
|
||
--stats-addr=:8080 \
|
||
--register-relay=true \
|
||
--relay-fee=1000
|
||
```
|
||
|
||
**Через libp2p multiaddr** (если есть прямой мульти-адрес):
|
||
|
||
```bash
|
||
./node \
|
||
--peers=/ip4/1.2.3.4/tcp/4001/p2p/12D3KooW... \
|
||
# остальные флаги как выше
|
||
```
|
||
|
||
**Автоприсоединение к validator set** происходит не само: после того как нода синхронизируется, действующий validator должен вызвать `client add-validator --target <your-pub> --cosigs ...` (multi-sig admit). До этого новая нода живёт как **observer** — читает и гоняет tx, но не голосует. Запустить ноду **явно** как observer (никогда не проситься в validator set): `--observer=true`.
|
||
|
||
### Все флаги `node`
|
||
|
||
CLI / env / default. Группы:
|
||
|
||
**Storage**
|
||
| Флаг | Env | Default | Назначение |
|
||
|------|-----|---------|-----------|
|
||
| `--db` | `DCHAIN_DB` | `./chaindata` | BadgerDB блокчейна |
|
||
| `--mailbox-db` | `DCHAIN_MAILBOX_DB` | `./mailboxdata` | E2E-конверты 1:1 чатов |
|
||
| `--feed-db` | `DCHAIN_FEED_DB` | `./feeddata` | Тела постов ленты (off-chain) |
|
||
| `--feed-ttl-days` | `DCHAIN_FEED_TTL_DAYS` | `30` | Через сколько дней тела постов auto-evict'ятся (метаданные on-chain остаются вечно) |
|
||
|
||
**Identity**
|
||
| Флаг | Env | Default | Назначение |
|
||
|------|-----|---------|-----------|
|
||
| `--key` | `DCHAIN_KEY` | `./node.json` | Ed25519 ключ ноды |
|
||
| `--relay-key` | `DCHAIN_RELAY_KEY` | `./relay.json` | X25519 ключ для relay-mailbox (создастся сам) |
|
||
| `--wallet` | `DCHAIN_WALLET` | — | Отдельный payout-кошелёк (опционально) |
|
||
| `--wallet-pass` | `DCHAIN_WALLET_PASS` | — | Парольная фраза для wallet-файла |
|
||
|
||
**Network**
|
||
| Флаг | Env | Default | Назначение |
|
||
|------|-----|---------|-----------|
|
||
| `--listen` | `DCHAIN_LISTEN` | `/ip4/0.0.0.0/tcp/4001` | libp2p listen multiaddr |
|
||
| `--announce` | `DCHAIN_ANNOUNCE` | — | Multiaddr который нода рассказывает пирам (обязателен на VPS/внешнем IP) |
|
||
| `--peers` | `DCHAIN_PEERS` | — | Bootstrap multiaddrs, comma-separated |
|
||
| `--join` | `DCHAIN_JOIN` | — | HTTP URL живой ноды для авто-дискавери — получает peers и genesis_hash |
|
||
| `--allow-genesis-mismatch` | — | `false` | Отключить защиту, падающую при расхождении локального и seed'ового genesis (только для явной миграции) |
|
||
|
||
**Consensus & role**
|
||
| Флаг | Env | Default | Назначение |
|
||
|------|-----|---------|-----------|
|
||
| `--genesis` | `DCHAIN_GENESIS` | `false` | Создать блок 0 (только для самой первой ноды сети) |
|
||
| `--validators` | `DCHAIN_VALIDATORS` | — | Исходный validator set (CSV pub-keys) — применяется только при genesis |
|
||
| `--observer` | `DCHAIN_OBSERVER` | `false` | Observer-режим: синхронизируется и отдаёт API, но не голосует и не предлагает блоки |
|
||
| `--heartbeat` | `DCHAIN_HEARTBEAT` | `true` | Периодический HEARTBEAT-tx (нужен для liveness-детекции валидаторов) |
|
||
|
||
**Relay / mailbox**
|
||
| Флаг | Env | Default | Назначение |
|
||
|------|-----|---------|-----------|
|
||
| `--register-relay` | `DCHAIN_REGISTER_RELAY` | `false` | Отправить `REGISTER_RELAY` tx на старте (объявить ноду публичным relay'ем) |
|
||
| `--relay-fee` | `DCHAIN_RELAY_FEE` | `1000` | Плата за доставку одного сообщения в µT (1000 = 0.001 T). `0` = бесплатный relay |
|
||
|
||
**Media scrubber (feed)**
|
||
| Флаг | Env | Default | Назначение |
|
||
|------|-----|---------|-----------|
|
||
| `--media-sidecar-url` | `DCHAIN_MEDIA_SIDECAR_URL` | — | URL FFmpeg-сайдкара для видео-скраба. Пустой = только картинки |
|
||
| `--allow-unscrubbed-video` | `DCHAIN_ALLOW_UNSCRUBBED_VIDEO` | `false` | Принимать видео **без** серверного скраба (опасно — EXIF/GPS/автор-теги останутся) |
|
||
|
||
**HTTP API**
|
||
| Флаг | Env | Default | Назначение |
|
||
|------|-----|---------|-----------|
|
||
| `--stats-addr` | `DCHAIN_STATS_ADDR` | `:8080` | Адрес HTTP/WS сервера |
|
||
| `--api-token` | `DCHAIN_API_TOKEN` | — | Bearer-токен для submit tx. Пустой = публичная нода |
|
||
| `--api-private` | `DCHAIN_API_PRIVATE` | `false` | Требовать токен также на чтение |
|
||
| `--disable-ui` | `DCHAIN_DISABLE_UI` | `false` | Отключить HTML-explorer (JSON API остаётся) |
|
||
| `--disable-swagger` | `DCHAIN_DISABLE_SWAGGER` | `false` | Отключить `/swagger*` |
|
||
|
||
**Resource caps** (новое в v2.1.0)
|
||
| Флаг | Env | Default | Назначение |
|
||
|------|-----|---------|-----------|
|
||
| `--max-cpu` | `DCHAIN_MAX_CPU` | `0` | Сколько CPU-ядер Go-runtime'у (`GOMAXPROCS`). `0` = все |
|
||
| `--max-ram-mb` | `DCHAIN_MAX_RAM_MB` | `0` | Soft-лимит Go-хипа в MiB (`GOMEMLIMIT`). `0` = без лимита. *Не OOM-kill'ит — усиливает GC при приближении* |
|
||
| `--feed-disk-limit-mb` | `DCHAIN_FEED_DISK_LIMIT_MB` | `0` | Жёсткая квота на feed-БД. При превышении `/feed/publish` отвечает 507. Существующие посты продолжают отдаваться |
|
||
| `--chain-disk-limit-mb` | `DCHAIN_CHAIN_DISK_LIMIT_MB` | `0` | Advisory-квота на блокчейн-БД. Превышение → `WARN` в лог раз в минуту (жёстко не отказываем — сломали бы консенсус) |
|
||
|
||
Для реального sandboxing (hard-kill при OOM, hard CPU throttling) используйте `docker run --cpus --memory` или systemd `CPUQuota` / `MemoryMax` поверх этих флагов.
|
||
|
||
**Update / versioning**
|
||
| Флаг | Env | Default | Назначение |
|
||
|------|-----|---------|-----------|
|
||
| `--update-source-url` | `DCHAIN_UPDATE_SOURCE_URL` | — | Gitea `/api/v1/repos/{owner}/{repo}/releases/latest` для `/api/update-check` |
|
||
| `--update-source-token` | `DCHAIN_UPDATE_SOURCE_TOKEN` | — | PAT для приватного репо |
|
||
| `--log-format` | `DCHAIN_LOG_FORMAT` | `text` | `text` (human) или `json` (Loki/ELK) |
|
||
| `--governance-contract` | `DCHAIN_GOVERNANCE_CONTRACT` | — | ID governance-контракта для динамических параметров |
|
||
| `--version` | — | — | Печатает версию и выходит |
|
||
|
||
### Минимальные чек-листы
|
||
|
||
**Первая нода (открытая):** `--genesis=true` + `--key` + `--announce` на внешний IP + `--stats-addr` + опционально `--register-relay=true --relay-fee=...` чтобы сразу монетизировать relay-трафик.
|
||
|
||
**Joiner:** `--join=<url-любой-живой-ноды>` + `--key` + `--announce` + `--stats-addr`. После синка попросите действующего валидатора поднять `add-validator` (иначе остаётесь observer'ом до принятия — это нормально и безопасно).
|
||
|
||
**Приватная/домашняя нода** без публичного эксплорера: добавьте `--api-token=<random>`, `--api-private=true`, `--disable-ui=true`, `--disable-swagger=true`. Clients передают `Authorization: Bearer <token>`.
|
||
|
||
**Слабое железо:** `--max-cpu=2 --max-ram-mb=1024 --feed-disk-limit-mb=2048 --chain-disk-limit-mb=10240`.
|
||
|
||
Docker-обёртка с теми же флагами — в [`deploy/single/README.md`](deploy/single/README.md).
|
||
|
||
## Продакшен деплой
|
||
|
||
Два варианта, по масштабу.
|
||
|
||
### 🔸 Single-node (`deploy/single/`)
|
||
|
||
**Рекомендуется для личного/первого узла.** Один узел + Caddy TLS +
|
||
опциональный Prometheus. Полный runbook с 6 сценариями (публичная с UI,
|
||
headless, полностью приватная, genesis, joiner, auto-update) — в
|
||
[`deploy/single/README.md`](deploy/single/README.md).
|
||
|
||
#### Модели доступа
|
||
|
||
| Режим | `DCHAIN_API_TOKEN` | `DCHAIN_API_PRIVATE` | Поведение |
|
||
|-------|:------------------:|:--------------------:|-----------|
|
||
| Public (default) | не задан | — | Все могут читать и писать |
|
||
| Token writes | задан | `false` | Читать — любой; submit tx — только с токеном |
|
||
| Fully private | задан | `true` | Всё требует `Authorization: Bearer <token>` |
|
||
|
||
#### UI / Swagger — включать или нет?
|
||
|
||
| Нужно | `DCHAIN_DISABLE_UI` | `DCHAIN_DISABLE_SWAGGER` | Открыто |
|
||
|-------|:-------------------:|:------------------------:|---------|
|
||
| Публичная с эксплорером + docs | не задано | не задано | `/` Explorer, `/swagger`, `/api/*`, `/metrics` |
|
||
| Headless API-нода с OpenAPI | `true` | не задано | `/swagger`, `/api/*`, `/metrics` |
|
||
| Личная 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
|
||
```
|
||
|
||
Подробно: [`docs/update-system.md`](docs/update-system.md) + [`deploy/UPDATE_STRATEGY.md`](deploy/UPDATE_STRATEGY.md).
|
||
|
||
### 🔹 Multi-validator (`deploy/prod/`)
|
||
|
||
3 validator'а в PBFT-кворуме, Caddy с `ip_hash` для WS-стикинесса и
|
||
`least-conn` для REST. Для федераций / консорциумов — см.
|
||
[`deploy/prod/README.md`](deploy/prod/README.md) и
|
||
[`docs/node/multi-server.md`](docs/node/multi-server.md).
|
||
|
||
## Архитектура
|
||
|
||
```
|
||
┌────────────┐ libp2p ┌────────────┐ libp2p ┌────────────┐
|
||
│ node-A │◄─────pubsub──►│ node-B │◄─────pubsub──►│ node-C │
|
||
│ validator │ │ validator │ │ validator │
|
||
│ + relay │ │ + relay │ │ + relay │
|
||
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘
|
||
│ │ │
|
||
│ HTTPS / wss (via Caddy) │ │
|
||
▼ ▼ ▼
|
||
mobile / web / CLI clients (ip_hash для WS, least-conn для REST)
|
||
```
|
||
|
||
Четыре слоя: network → chain → transport → app. Детали — в
|
||
[`docs/architecture.md`](docs/architecture.md).
|
||
|
||
Основные пакеты:
|
||
|
||
| Путь | Роль |
|
||
|------|------|
|
||
| `blockchain/` | Блочная машина, applyTx, native-контракты, schema-migrations |
|
||
| `consensus/` | PBFT (Pre-prepare → Prepare → Commit), мемпул, equivocation |
|
||
| `p2p/` | libp2p host, gossipsub, sync протокол, peer-version gossip |
|
||
| `node/` | HTTP + WS API, SSE, metrics, access control |
|
||
| `node/version/` | Build-time version metadata (ldflags-инжектимый) |
|
||
| `vm/` | wazero runtime для WASM-контрактов + gas model |
|
||
| `relay/` | E2E mailbox (1:1 envelopes) + public feed-mailbox (post bodies, view counter, hashtag index) |
|
||
| `media/` | Server-side metadata scrubber (EXIF strip + FFmpeg sidecar client) |
|
||
| `identity/` | Ed25519 + X25519 keypair, tx signing |
|
||
| `economy/` | Fee model, rewards |
|
||
| `wallet/` | Optional payout wallet (отдельный ключ) |
|
||
|
||
## REST / WebSocket API
|
||
|
||
Обзор (полный reference — [`docs/api/README.md`](docs/api/README.md)):
|
||
|
||
### Chain + stats
|
||
| Endpoint | Описание |
|
||
|----------|----------|
|
||
| `GET /api/netstats` | height, total_txs, supply, validator_count |
|
||
| `GET /api/network-info` | chain_id, genesis, peers, validators, contracts |
|
||
| `GET /api/blocks?limit=N` / `/api/block/{index}` | Блоки |
|
||
| `GET /api/tx/{id}` / `/api/txs/recent?limit=N` | Транзакции |
|
||
| `GET /api/address/{pub_or_addr}` | Баланс + история |
|
||
| `GET /api/validators` | Validator set |
|
||
| `GET /api/peers` | Connected peers + их версии |
|
||
| `POST /api/tx` | Submit signed tx (rate-limited, size-capped) |
|
||
|
||
### Discovery + update
|
||
| Endpoint | Описание |
|
||
|----------|----------|
|
||
| `GET /api/well-known-version` | `{node_version, build{}, protocol_version, features[], chain_id}` |
|
||
| `GET /api/well-known-contracts` | `{name → contract_id}` map |
|
||
| `GET /api/update-check` | Diff с Gitea release (см. [`docs/update-system.md`](docs/update-system.md)) |
|
||
|
||
### Real-time
|
||
- `GET /api/ws` — **WebSocket** (recommended). Ops: `auth`, `subscribe`,
|
||
`unsubscribe`, `submit_tx`, `typing`, `ping`. Push: `block`, `tx`,
|
||
`inbox`, `typing`, `submit_ack`.
|
||
- `GET /api/events` — SSE legacy one-way.
|
||
|
||
Scoped WS-топики (`addr:`, `inbox:`, `typing:`) требуют auth через
|
||
Ed25519-nonce; публичные (`blocks`, `tx`, `contract_log`) — без.
|
||
|
||
### Relay (E2E messaging)
|
||
| Endpoint | Описание |
|
||
|----------|----------|
|
||
| `POST /relay/broadcast` | Опубликовать pre-sealed envelope (E2E-путь, рекомендован) |
|
||
| `GET /relay/inbox?pub=<x25519>` | Прочитать входящие конверты |
|
||
| `DELETE /relay/inbox/{id}` | Удалить envelope (требует Ed25519-подписи владельца) |
|
||
|
||
Детали — [`docs/api/relay.md`](docs/api/relay.md). `/relay/send` оставлен
|
||
для backward-compat, но ломает E2E (nod-релей запечатывает своим ключом)
|
||
и помечен как non-recommended.
|
||
|
||
### Social feed (v2.0.0)
|
||
| Endpoint | Описание |
|
||
|----------|----------|
|
||
| `POST /feed/publish` | Загрузить тело поста + EXIF-скраб + вернуть fee |
|
||
| `GET /feed/post/{id}` | Тело поста |
|
||
| `GET /feed/post/{id}/attachment` | Сырые байты картинки/видео (cache'able) |
|
||
| `GET /feed/post/{id}/stats?me=<pub>` | `{views, likes, liked_by_me?}` |
|
||
| `POST /feed/post/{id}/view` | Бамп off-chain счётчика просмотров |
|
||
| `GET /feed/author/{pub}?before=<ts>&limit=N` | Посты автора (пагинация `before`) |
|
||
| `GET /feed/timeline?follower=<pub>&before=<ts>&limit=N` | Merged лента подписок |
|
||
| `GET /feed/trending?window=24&limit=N` | Топ по `likes × 3 + views` за окно |
|
||
| `GET /feed/foryou?pub=<pub>&limit=N` | Рекомендации (неподписанные авторы) |
|
||
| `GET /feed/hashtag/{tag}?limit=N` | Посты по хэштегу |
|
||
|
||
Детали + спецификация — [`docs/api/feed.md`](docs/api/feed.md).
|
||
|
||
### Docs / UI
|
||
- `GET /swagger` — **Swagger UI** (рендерится через swagger-ui-dist).
|
||
- `GET /swagger/openapi.json` — сырая OpenAPI 3.0 спека.
|
||
- `GET /` — block-explorer HTML (выключается `DCHAIN_DISABLE_UI=true`).
|
||
|
||
## CLI
|
||
|
||
```bash
|
||
client keygen --out key.json
|
||
client --version # → dchain-client vX.Y.Z (commit=... date=...)
|
||
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 --arg 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 deploy-contract --key key.json --wasm ./my.wasm --abi ./my_abi.json --node URL
|
||
# полный список — `client` без аргументов, или в docs/cli/README.md
|
||
```
|
||
|
||
Все флаги `node` — в [`docs/node/README.md`](docs/node/README.md), либо
|
||
`node --help`.
|
||
|
||
## Мониторинг
|
||
|
||
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 + Prometheus поднимаются вместе с нодой через
|
||
`docker compose --profile monitor up -d` (см. `deploy/single/docker-compose.yml`).
|
||
|
||
## Тесты
|
||
|
||
```bash
|
||
# Unit + integration
|
||
go test ./... # blockchain + consensus + identity + relay + vm
|
||
go vet ./... # static checks
|
||
|
||
# End-to-end load (3-node dev cluster должен быть поднят):
|
||
docker compose up --build -d
|
||
go run ./cmd/loadtest \
|
||
--node http://localhost:8081 \
|
||
--funder testdata/node1.json \
|
||
--clients 50 --duration 60s
|
||
```
|
||
|
||
## Документация
|
||
|
||
Полный справочник в `docs/` — см. [`docs/README.md`](docs/README.md) как
|
||
оглавление. Ключевые документы:
|
||
|
||
| Документ | О чём |
|
||
|----------|-------|
|
||
| [`docs/quickstart.md`](docs/quickstart.md) | Три пути: локально, single-node prod, федерация |
|
||
| [`docs/architecture.md`](docs/architecture.md) | 4 слоя, consensus, storage, gas model |
|
||
| [`docs/update-system.md`](docs/update-system.md) | Versioning + update-check + semver guard |
|
||
| [`docs/api/README.md`](docs/api/README.md) | REST + WebSocket reference |
|
||
| [`docs/cli/README.md`](docs/cli/README.md) | CLI команды и флаги |
|
||
| [`docs/node/README.md`](docs/node/README.md) | Запуск ноды (native + Docker) |
|
||
| [`docs/contracts/README.md`](docs/contracts/README.md) | Системные + WASM контракты |
|
||
| [`docs/development/README.md`](docs/development/README.md) | SDK для своих контрактов |
|
||
| [`deploy/single/README.md`](deploy/single/README.md) | Single-node operator runbook |
|
||
| [`deploy/prod/README.md`](deploy/prod/README.md) | Multi-validator federation |
|
||
| [`deploy/UPDATE_STRATEGY.md`](deploy/UPDATE_STRATEGY.md) | Forward-compat design (4 слоя) |
|