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