Files
dchain/CHANGELOG.md
vsecoder 7e7393e4f8 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
2026-04-17 14:16:44 +03:00

9.2 KiB
Raw Blame History

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.