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:
93
node/api_well_known_version.go
Normal file
93
node/api_well_known_version.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user