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

181
CHANGELOG.md Normal file
View File

@@ -0,0 +1,181 @@
# DChain CHANGELOG
Consolidated record of what landed. Replaces the now-deleted
`REFACTOR_PLAN.md`, `NODE_ONBOARDING.md`, and `ROADMAP.md` — every numbered
item there is either shipped (listed below) or explicitly deferred.
---
## Production-ready stack (shipped)
### Consensus & chain
- **PBFT multi-sig validator admission**. `ADD_VALIDATOR` requires
⌈2/3⌉ cosigs from the current set + candidate must have ≥ `MinValidatorStake`
(1 T) locked via STAKE. Same gate on forced `REMOVE_VALIDATOR`; self-
removal stays unilateral.
- **Equivocation slashing**. `SLASH` tx with `reason=equivocation` carries
both conflicting PREPARE/COMMIT messages as evidence; `ValidateEquivocation`
verifies on-chain — any node can report, no trust. Offender's stake is
burned and they're evicted from the set.
- **Liveness tracking**. PBFT records per-validator last-seen seqNum;
`LivenessReport()` + `MissedBlocks()` surface stalemates. Exposed via
`dchain_max_missed_blocks` Prometheus gauge.
- **Fair mempool**. Per-sender FIFO queues drained round-robin into
proposals; one spammer can't starve others.
- **Block-reward fix**. Synthetic BLOCK_REWARD transactions use `From=""`
so self-validators don't appear to pay themselves in history.
### Storage & stability
- **Re-entrant-deadlock fix**. Dedicated `configMu` and `nativeMu` separate
from `c.mu` — applyTx can safely read config/native registry while
`AddBlock` holds the write lock.
- **BadgerDB tuning**. `WithValueLogFileSize(64 MiB)` + `WithNumVersionsToKeep(1)`
+ background `StartValueLogGC` every 5 minutes + one-shot `CompactNow()`
at startup. Reclaims gigabytes from upgraded nodes automatically.
- **`TipIndex()`** — lock-free reads so `/api/blocks` and `/api/txs/recent`
never hang even when `AddBlock` is stuck.
- **Chronological tx index** (`txchron:<block20d>:<seq04d>`). `RecentTxs`
runs in O(limit) instead of O(empty blocks) — important when tps is low.
- **WASM VM timeout + `WithCloseOnContextDone`**. Any contract call aborts
at 30 s hard cap; gas metering evasion can no longer freeze the chain.
### Native contracts
- **`native:username_registry`** (v2.1.0). Replaces WASM registry — 100×
faster, no VM failure surface. `register(name)` requires exact
`tx.Amount = 10 000 µT` (burned, visible in history). Min length 4,
lowercase `a-z 0-9 _ -`, first char letter, reserved words blacklist.
- **Native dispatcher** in `applyTx` → checks `native:*` IDs first, falls
through to WASM VM otherwise. ABI JSON + contract metadata surfaced via
`/api/contracts/:id` with `"native": true` flag.
- **Well-known auto-discovery**. `/api/well-known-contracts` returns
canonical contract IDs indexed by ABI name; native always wins. Client
auto-syncs on connect.
### WebSocket gateway (push-based UX)
- **`GET /api/ws`** — persistent bidirectional JSON-framed connection.
- **Topics**: `blocks`, `tx`, `addr:<pub>`, `inbox:<x25519>`,
`contract_log`, `contract:<id>`, `typing:<x25519>`.
- **`auth` op**. Client signs server-issued nonce with Ed25519; hub binds
connection to pubkey so scoped subscriptions (`addr:*`, `inbox:*`,
`typing:*`) are accepted only for owned identities.
- **`submit_tx` op**. Low-latency tx submission with correlated
`submit_ack` frame; removes the HTTP round-trip. Client falls back to
POST `/api/tx` automatically if WS is down.
- **Typing indicators**. Ephemeral `typing` op, authenticated, scoped to
recipient. Mobile client shows "печатает…" in chat header.
- **Per-connection quotas**. Max 10 connections / IP, 32 subs / connection.
Bounded outbox drops oldest on overflow with `{event:"lag"}` notice.
- **Fanout mirrors SSE**. `eventBus` dispatches to SSE + WS + future
consumers from one emit site.
### Relay mailbox
- **Push notifications**. `Mailbox.SetOnStore` hook → `wsHub.EmitInbox(...)`
on every fresh envelope. Client's `useMessages` subscribes instead of
polling every 3 s.
- **Relay TTL**. `REGISTER_RELAY` and HEARTBEAT (from registered relays)
refresh a `relayhb:<pub>` timestamp; `/api/relays` filters anything
older than 2 hours. Stale relays are delisted automatically.
### Node onboarding
- **`--join <url1,url2,…>`** — multi-seed bootstrap. Tries each URL in
order, persists the live list to `<db>/seeds.json` on first success so
subsequent restarts don't need the CLI flag.
- **`/api/network-info`** — one-shot payload (chain_id, genesis_hash,
validators, peers, contracts, stats) for joiners.
- **`/api/peers`** — live libp2p peer list with multiaddrs.
- **Genesis-hash verification**. A node with expected hash aborts if its
local block 0 doesn't match (protection against forged seeds). Override
with `--allow-genesis-mismatch` for migrations.
- **Gap-fill on gossip**. Blocks with `b.Index > tip+1` trigger
`SyncFromPeerFull` to the gossiping peer (rate-limited 1 per peer per
minute). Nodes recover from brief outages without restart.
### API surface & security
- **Rate limiter** (`node/api_guards.go`). Per-IP token bucket on
`/api/tx` and `/v2/chain/transactions`: 10 tx/s, burst 20.
- **Request-size cap**. `/api/tx` body ≤ 64 KiB.
- **Timestamp validation**. ±1 h window on submit, refuses clock-skewed
or replayed txs.
- **Humanised errors in client**. `humanizeTxError` translates 429 /
400+timestamp / 400+signature / network-failure into Russian user-
facing text.
### Observability & ops
- **Prometheus `/metrics`**. Zero-dep in-tree implementation (`node/metrics.go`)
with counters (blocks, txs, submit accepted/rejected), gauges
(ws connections, peer count, max missed blocks), histogram (block
commit seconds).
- **Load test**. `cmd/loadtest` — N concurrent WS clients with auth +
scoped subs + TRANSFER at rate. Validates chain advances, reject rate,
ws-drop count. Smoke at 20 clients × 15 s → 136 accepted / 0 rejected.
- **Structured logging**. `--log-format=text|json` flag. JSON mode routes
both `slog.*` and legacy `log.Printf` through one JSON handler for
Loki/ELK ingestion.
- **Observer mode**. `--observer` (env `DCHAIN_OBSERVER`) disables PBFT
producer + heartbeat + auto-relay-register; node still gossips and
serves HTTP/WS. For horizontally-scaling read-only API frontends.
### Deployment
- **`deploy/single/`** — one-node production bundle:
- Same `Dockerfile.slim` as the cluster variant.
- Compose stack: 1 node + Caddy + optional Prometheus/Grafana.
- Supports three operator-chosen access modes:
- Public (no token) — anyone can read + submit.
- Public reads, token-gated writes (`DCHAIN_API_TOKEN` set) —
reads stay open, submit tx requires `Authorization: Bearer`.
- Fully private (`DCHAIN_API_TOKEN` + `DCHAIN_API_PRIVATE`) —
every endpoint requires the token.
- Runbook covers three scenarios: genesis node, joiner, private.
- **`deploy/prod/`** — 3-validator cluster for federations/consortiums.
- **Access-token middleware** in `node/api_guards.go`:
- `withWriteTokenGuard` gates POST /api/tx and WS submit_tx.
- `withReadTokenGuard` gates reads when `--api-private` is set.
- WS upgrade applies the same check; `submit_tx` ops on a
non-authenticated connection are rejected with `submit_ack`
rejected.
- **All CLI flags accept `DCHAIN_*` env fallbacks** for Docker-driven
configuration, including the new `DCHAIN_API_TOKEN` /
`DCHAIN_API_PRIVATE`.
### Client (React Native / Expo)
- WebSocket module `lib/ws.ts` with reconnect, auto-resubscribe,
auto-auth on reconnect.
- `useBalance`, `useContacts`, `useMessages` — all push-based with HTTP
polling fallback after 15 s disconnect.
- `useWellKnownContracts` — auto-syncs `settings.contractId` with node's
canonical registry.
- Safe-area-aware layout throughout. Tab bar no longer hides under home
indicator on iPhone.
- Username purchase UI with live validation (min 4, first letter, charset).
- Transaction detail sheet with system-tx handling (BLOCK_REWARD shows
"Сеть" as counterpart, not validator's self-pay).
---
## Deliberately deferred
- **Split `blockchain/chain.go`** into `state/`, `applytx/`, `mempool/`,
`index/`, `events/` subpackages. A ~2.5k-line single-file refactor is
high risk; to be attempted after the chain has been running in prod
long enough that regressions there would be caught fast.
- **Full `p2p/` rewrite with typed event channel.** The libp2p integration
works; event-bus was added at the node layer instead (see `node/events.go`).
- **Full mempool admission pricing** (gas-priced priority queues).
Current fair round-robin works within spam-proofing needs.
---
## Compatibility notes
- BadgerDB tuning is compatible with databases created by previous
versions; the first run reclaims old value-log space via `CompactNow()`.
- `AddValidatorPayload` / `RemoveValidatorPayload` gained a `cosigs`
field; older payloads without it still parse (default empty), but will
fail the ⌈2/3⌉ threshold on chains with >1 validator.
- `BLOCK_REWARD` transactions changed from `From=validator` to `From=""`.
Old indexed records keep their previous `From`; new ones use the new
shape. Explorer/client handle both.
- Registration fee for usernames moved from internal `ctx.Debit` to
`tx.Amount`. The WASM username_registry is superseded by
`native:username_registry`; well-known endpoint returns the native
version as canonical.