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

137
contracts/counter/main.go Normal file
View File

@@ -0,0 +1,137 @@
// Counter smart contract — compiles to WASM with GOOS=wasip1 GOARCH=wasm.
//
// Methods (exported via //go:export):
// - increment — adds 1 to the stored counter
// - get — logs the current value (readable via /api/contracts/{id}/state/counter)
// - reset — resets counter to 0; only the first caller (owner) is allowed
//
// Host imports from the "env" module (see vm/host.go):
// - put_u64(keyPtr, keyLen, val) — stores uint64 as 8-byte big-endian
// - get_u64(keyPtr, keyLen) uint64 — reads 8-byte big-endian uint64
// - get_caller(buf, bufLen) int32 — writes caller pub key hex into buf
// - get_state(kPtr,kLen,dPtr,dLen) int32 — reads raw state bytes
// - set_state(kPtr,kLen,vPtr,vLen) — writes raw state bytes
// - log(msgPtr, msgLen) — emits message to node log
//
//go:build wasip1
package main
import (
"unsafe"
)
// ── host function imports ─────────────────────────────────────────────────────
//go:wasmimport env put_u64
func hostPutU64(keyPtr unsafe.Pointer, keyLen int32, val uint64)
//go:wasmimport env get_u64
func hostGetU64(keyPtr unsafe.Pointer, keyLen int32) uint64
//go:wasmimport env get_caller
func hostGetCaller(buf unsafe.Pointer, bufLen int32) int32
//go:wasmimport env get_state
func hostGetState(keyPtr unsafe.Pointer, keyLen int32, dstPtr unsafe.Pointer, dstLen int32) int32
//go:wasmimport env set_state
func hostSetState(keyPtr unsafe.Pointer, keyLen int32, valPtr unsafe.Pointer, valLen int32)
//go:wasmimport env log
func hostLog(msgPtr unsafe.Pointer, msgLen int32)
// ── helpers ───────────────────────────────────────────────────────────────────
func logMsg(s string) {
if len(s) == 0 {
return
}
b := []byte(s)
hostLog(unsafe.Pointer(&b[0]), int32(len(b)))
}
func putU64(key string, val uint64) {
b := []byte(key)
hostPutU64(unsafe.Pointer(&b[0]), int32(len(b)), val)
}
func getU64(key string) uint64 {
b := []byte(key)
return hostGetU64(unsafe.Pointer(&b[0]), int32(len(b)))
}
func getState(key string, dst []byte) int32 {
kb := []byte(key)
return hostGetState(unsafe.Pointer(&kb[0]), int32(len(kb)),
unsafe.Pointer(&dst[0]), int32(len(dst)))
}
func setState(key string, val []byte) {
kb := []byte(key)
hostSetState(unsafe.Pointer(&kb[0]), int32(len(kb)),
unsafe.Pointer(&val[0]), int32(len(val)))
}
func getCaller() string {
buf := make([]byte, 128)
n := hostGetCaller(unsafe.Pointer(&buf[0]), int32(len(buf)))
if n <= 0 {
return ""
}
return string(buf[:n])
}
// ── contract state keys ───────────────────────────────────────────────────────
const (
keyCounter = "counter"
keyOwner = "owner"
)
// ── exported contract methods ─────────────────────────────────────────────────
//go:export increment
func increment() {
val := getU64(keyCounter)
val++
putU64(keyCounter, val)
logMsg("incremented")
}
//go:export get
func get() {
logMsg("get called")
}
//go:export reset
func reset() {
caller := getCaller()
if caller == "" {
logMsg("reset: no caller")
return
}
ownerBuf := make([]byte, 128)
ownerLen := getState(keyOwner, ownerBuf)
if ownerLen == 0 {
// No owner set yet — first caller becomes the owner.
setState(keyOwner, []byte(caller))
putU64(keyCounter, 0)
logMsg("reset ok (owner set)")
return
}
owner := string(ownerBuf[:ownerLen])
if caller != owner {
logMsg("reset: unauthorized")
return
}
putU64(keyCounter, 0)
logMsg("reset ok")
}
// main is required by the Go runtime for wasip1 programs.
func main() {}