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
10 KiB
Архитектура DChain
Обзор
DChain — это L1-блокчейн для децентрализованного мессенджера. Архитектура разделена на четыре слоя:
┌─────────────────────────────────────────────────────────────┐
│ L3 Application — messaging, usernames, auctions, escrow │
│ (Smart contracts: username_registry, governance, auction, │
│ escrow; deployed on-chain via DEPLOY_CONTRACT) │
├─────────────────────────────────────────────────────────────┤
│ L2 Transport — relay mailbox, E2E NaCl encryption │
│ (relay/, mailbox, GossipSub envelopes, RELAY_PROOF tx) │
├─────────────────────────────────────────────────────────────┤
│ L1 Chain — PBFT consensus, WASM VM, BadgerDB │
│ (blockchain/, consensus/, vm/, identity/) │
├─────────────────────────────────────────────────────────────┤
│ L0 Network — libp2p, GossipSub, DHT, mDNS │
│ (p2p/) │
└─────────────────────────────────────────────────────────────┘
Консенсус (PBFT)
Алгоритм: Practical Byzantine Fault Tolerance, кворум 2/3.
Фазы:
Leader Validator-2 Validator-3
│── PRE-PREPARE ──▶│ │
│── PRE-PREPARE ────────────────────────▶ │
│◀─ PREPARE ───────│ │
│◀─ PREPARE ────────────────────────────── │
│── COMMIT ────────▶│ │
│── COMMIT ──────────────────────────────▶ │
│◀─ COMMIT ────────│ │
│◀─ COMMIT ────────────────────────────── │
AddBlock()
Свойства:
- Safety при ≤ f Byzantine нодах где N ≥ 3f+1
- Текущий testnet: N=2 валидатора, f=0 (узел node3 — relay-only observer)
- View-change при недоступном лидере: таймаут 10 секунд
Блок-производство:
- Fast ticker (500 ms) — при наличии транзакций в mempool
- Idle ticker (5 s) — пустые блоки для heartbeat и синхронизации
Ключевые файлы:
consensus/engine.go — PBFT engine
consensus/msg.go — ConsensusMsg типы (PRE-PREPARE, PREPARE, COMMIT, VIEW-CHANGE)
Хранилище (BadgerDB)
Весь state хранится в BadgerDB (LSM-дерево, pure Go).
Пространства ключей:
| Префикс | Тип | Описание |
|---|---|---|
block:<index_20d> |
JSON | Блоки по индексу |
height |
uint64 JSON | Текущая высота |
balance:<pubkey> |
uint64 JSON | Балансы токенов |
id:<pubkey> |
JSON | Identity (RegisterKey payload) |
validator:<pubkey> |
presence | Активный сет валидаторов |
relay:<pubkey> |
JSON | Зарегистрированные relay-ноды |
contract:<id> |
JSON | ContractRecord (метаданные + WASM) |
cstate:<id>:<key> |
raw bytes | Состояние контракта |
clog:<id>:<height_20d>:<seq_05d> |
JSON | Логи контракта |
rep:<pubkey> |
JSON | Репутация (blocks, relays, slashes) |
contact_in:<to>:<from> |
JSON | Входящие contact requests |
mail:<x25519>:<ts>:<id> |
JSON | Relay mailbox (TTL 7 дней) |
Две отдельные БД:
--db ./chaindata— chain state (consensus state machine)--mailbox-db ./mailboxdata— relay mailbox (отдельная изоляция)
P2P сеть (libp2p)
Компоненты:
- Transport: TCP
/ip4/0.0.0.0/tcp/4001 - Identity: Ed25519 peer key (вывод из node identity)
- Discovery: mDNS (локалка) + Kademlia DHT (WAN)
- Pub/Sub: GossipSub с тремя топиками:
| Топик | Содержимое |
|---|---|
dchain/tx/v1 |
Транзакции (gossip) |
dchain/blocks/v1 |
Готовые блоки (gossip от лидера) |
dchain/relay/v1 |
Relay envelopes (зашифрованные сообщения) |
- Direct streams: PBFT consensus messages (pre-prepare/prepare/commit/view-change) идут напрямую между валидаторами через
/dchain/consensus/1.0.0протокол - Sync: block range sync по
/dchain/sync/1.0.0при подключении нового пира
WASM Virtual Machine
Runtime: wazero v1.7.3, interpreter mode (детерминированный на всех платформах).
Жизненный цикл контракта:
DEPLOY_CONTRACT tx
├── validate: wazero.CompileModule() — если ошибка, tx отклоняется
├── contractID = hex(sha256(deployerPubKey || wasmBytes))[:16]
├── BadgerDB: contract:<id> → ContractRecord{WASMBytes, ABI, ...}
└── state: cstate:<id>:* — изначально пусто
CALL_CONTRACT tx
├── ABI validation: метод существует, число аргументов совпадает
├── pre-charge: tx.Fee + gasLimit × gasPrice
├── vm.Call(contractID, wasmBytes, method, argsJSON, gasLimit, hostEnv)
│ ├── compile (cached) + instrument (gas_tick в loop headers)
│ ├── register "env" host module (14 функций)
│ ├── [optional] wasi_snapshot_preview1 (для TinyGo контрактов)
│ └── fn.Call(ctx) → gasUsed, error
├── refund: (gasLimit - gasUsed) × gasPrice → обратно sender
└── logs: clog:<id>:<height>:<seq> → BadgerDB
Gas модель: каждый вызов функции (WASM или host) = 100 gas units.
gasCost (µT) = gasUsed × gasPrice (gasPrice управляется governance или константа 1 µT).
Типы контрактов:
- Binary WASM — написаны на Go через кодогенераторы (
contracts/*/gen/main.go) - TinyGo WASM — написаны на Go, компилируются с
tinygo -target wasip1
Экономика
Supply: Фиксированный. Genesis-блок минтит 21 000 000 T на ключ node1. Последующей эмиссии нет.
Unit: µT (микро-токен). 1 T = 1 000 000 µT.
Доходы валидаторов: только комиссии из транзакций блока (TotalFees). Без блок-реварда.
Комиссии:
| Операция | Минимальная fee |
|---|---|
| Все транзакции | 1 000 µT (MinFee) |
| CONTACT_REQUEST | tx.Amount → recipient (anti-spam) |
| DEPLOY_CONTRACT | 10 000 µT |
| CALL_CONTRACT | MinFee + gasUsed × gasPrice |
| RELAY_PROOF | sender → relay node (произвольно) |
Governance: gas_price, relay_fee и другие параметры можно менять on-chain через governance-контракт без хардфорка.
Relay (E2E мессенджер)
Шифрование: NaCl box (X25519 Diffie-Hellman + XSalsa20 + Poly1305).
Ключи: каждый Identity имеет два ключа:
- Ed25519 (подпись транзакций, chain identity)
- X25519 (вывод из Ed25519 seed, шифрование сообщений)
Поток сообщения:
Alice node1 (relay) Bob
│ │ │
│── seal(msg, bob.X25519) ──▶ │ │
│ POST /relay/broadcast │ │
│ │── gossip envelope ──▶ │
│ │ store mailbox │
│ │ (TTL 7 days) │
│ │ │
│ │◀── GET /relay/inbox ──│
│ │ │── open(envelope)
│ │ │ → plaintext
Anti-spam:
- Первый контакт — платный (CONTACT_REQUEST tx, fee идёт получателю)
- Envelope: max 64 KB, max 500 envelopes на получателя (FIFO)
- RELAY_PROOF: подписанное доказательство доставки, fee снимается со sender и кредитуется relay
Динамические валидаторы
Сет валидаторов хранится on-chain в validator:<pubkey>. Любой текущий валидатор может добавить или убрать другого (ADD_VALIDATOR / REMOVE_VALIDATOR tx). После commit такого блока PBFT-движок перезагружает сет без рестарта ноды.
Ключевые файлы:
blockchain/chain.go — state machine, applyTx, VMHostEnv
consensus/engine.go — PBFT, UpdateValidators()
vm/vm.go — wazero runtime, NewVM()
vm/host.go — host module "env" (14 функций)
vm/gas.go — gas counter, Remaining()
relay/mailbox.go — BadgerDB TTL mailbox
relay/crypto.go — NaCl seal/open
p2p/host.go — libp2p host, GossipSub
node/api_routes.go — HTTP API routing