vsecoder 32eec62ba4 fix(chain): RELAY_PROOF dedup by envelopeID + sticky BlockContact
RELAY_PROOF previously had no per-envelope dedup — every relay that
saw the gossipsub re-broadcast could extract the sender's FeeSig from
the envelope and submit its own RELAY_PROOF claim with its own
RelayPubKey. The tx-ID uniqueness check didn't help because tx.ID =
sha256(relayPubKey||envelopeID)[:16], which is unique per (relay,
envelope) pair. A malicious mesh of N relays could drain N× the fee
from the sender's balance for a single message.

Fix: record prefixRelayProof:<envelopeID> on first successful apply
and reject subsequent claims for the same envelope.

CONTACT_REQUEST previously overwrote any prior record (including a
blocked one) back to pending, letting spammers unblock themselves by
paying another MinContactFee. Now the handler reads the existing
record first and rejects the tx with "recipient has blocked sender"
when prev.Status == ContactBlocked. Block becomes sticky.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:51:14 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00
2026-04-17 14:16:44 +03:00

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
  • Система обновлений: 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

Содержание


Быстрый старт

Одна нода в Docker, HTTP API на localhost:8080, Explorer UI и Swagger открыты:

# 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.

Продакшен деплой

Два варианта, по масштабу.

🔸 Single-node (deploy/single/)

Рекомендуется для личного/первого узла. Один узел + Caddy TLS + опциональный Prometheus. Полный runbook с 6 сценариями (публичная с UI, headless, полностью приватная, genesis, joiner, auto-update) — в 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

# node.env — когда проект в Gitea
DCHAIN_UPDATE_SOURCE_URL=https://gitea.example.com/api/v1/repos/OWNER/REPO/releases/latest
UPDATE_ALLOW_MAJOR=false
# 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 + deploy/UPDATE_STRATEGY.md.

🔹 Multi-validator (deploy/prod/)

3 validator'а в PBFT-кворуме, Caddy с ip_hash для WS-стикинесса и least-conn для REST. Для федераций / консорциумов — см. deploy/prod/README.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.

Основные пакеты:

Путь Роль
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 с NaCl-envelopes
identity/ Ed25519 + X25519 keypair, tx signing
economy/ Fee model, rewards
wallet/ Optional payout wallet (отдельный ключ)

REST / WebSocket API

Обзор (полный reference — 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)

Real-time

  • GET /api/wsWebSocket (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) — без.

Docs / UI

  • GET /swaggerSwagger UI (рендерится через swagger-ui-dist).
  • GET /swagger/openapi.json — сырая OpenAPI 3.0 спека.
  • GET / — block-explorer HTML (выключается DCHAIN_DISABLE_UI=true).

CLI

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, либо 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).

Тесты

# 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/quickstart.md Три пути: локально, single-node prod, федерация
docs/architecture.md 4 слоя, consensus, storage, gas model
docs/update-system.md Versioning + update-check + semver guard
docs/api/README.md REST + WebSocket reference
docs/cli/README.md CLI команды и флаги
docs/node/README.md Запуск ноды (native + Docker)
docs/contracts/README.md Системные + WASM контракты
docs/development/README.md SDK для своих контрактов
deploy/single/README.md Single-node operator runbook
deploy/prod/README.md Multi-validator federation
deploy/UPDATE_STRATEGY.md Forward-compat design (4 слоя)
Description
No description provided
Readme 1.8 MiB
Languages
Go 57.7%
TypeScript 28.1%
JavaScript 5.8%
HTML 3.1%
CSS 2.7%
Other 2.6%