Files
dchain/node/api_well_known_version.go
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

94 lines
3.6 KiB
Go

// Package node — /api/well-known-version endpoint.
//
// Clients hit this to feature-detect the node they're talking to, without
// hardcoding "node >= 0.5 supports channels" into every screen. The response
// lists three coordinates a client cares about:
//
// - node_version — human-readable build tag (ldflags-injectable)
// - protocol_version — integer bumped only on wire-protocol breaking changes
// - features — stable string tags for "this binary implements X"
//
// Feature tags are ADDITIVE — once a tag ships in a release, it keeps being
// returned forever (even if the implementation moves around internally). The
// client uses them as "is this feature here or not?", not "what version is
// this feature at?". Versioning a feature is done by shipping a new tag
// (e.g. "channels_v2" alongside "channels_v1" for a deprecation window).
//
// Response shape:
//
// {
// "node_version": "0.5.0-dev",
// "protocol_version": 1,
// "features": [
// "channels_v1",
// "fan_out",
// "native_username_registry",
// "ws_submit_tx",
// "access_token"
// ],
// "chain_id": "dchain-ddb9a7e37fc8"
// }
package node
import (
"fmt"
"net/http"
"sort"
"go-blockchain/node/version"
)
// ProtocolVersion is bumped only when the wire-protocol changes in a way
// that a client compiled against version N cannot talk to a node at
// version N+1 (or vice versa) without updating. Adding new optional fields,
// new EventTypes, new WS ops, new HTTP endpoints — none of those bump this.
//
// Bumping this means a coordinated release: every client and every node
// operator must update before the old version stops working.
const ProtocolVersion = 1
// nodeFeatures is the baked-in list of feature tags this binary implements.
// Append-only. When you add a new tag, add it here AND document what it means
// so clients can feature-detect reliably.
//
// Naming convention: snake_case, versioned suffix for anything that might get
// a breaking successor (e.g. `channels_v1`, not `channels`).
var nodeFeatures = []string{
"access_token", // DCHAIN_API_TOKEN gating on writes (+ optional reads)
"channels_v1", // /api/channels/:id + /members with X25519 enrichment
"chain_id", // /api/network-info returns chain_id
"contract_logs", // /api/contract/:id/logs endpoint
"fan_out", // client-side per-recipient envelope sealing
"identity_registry", // /api/identity/:pub returns X25519 pub + relay hints
"native_username_registry", // native:username_registry contract
"onboarding_api", // /api/network-info for joiner bootstrap
"payment_channels", // off-chain payment channel open/close
"relay_mailbox", // /relay/send + /relay/inbox
"ws_submit_tx", // WebSocket submit_tx op
}
func registerWellKnownVersionAPI(mux *http.ServeMux, q ExplorerQuery) {
mux.HandleFunc("/api/well-known-version", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
jsonErr(w, fmt.Errorf("method not allowed"), 405)
return
}
// Return a copy so callers can't mutate the shared slice.
feats := make([]string, len(nodeFeatures))
copy(feats, nodeFeatures)
sort.Strings(feats)
resp := map[string]any{
"node_version": version.Tag,
"build": version.Info(),
"protocol_version": ProtocolVersion,
"features": feats,
}
// Include chain_id if the node exposes it (same helper as network-info).
if q.ChainID != nil {
resp["chain_id"] = q.ChainID()
}
jsonOK(w, resp)
})
}