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:
137
contracts/counter/main.go
Normal file
137
contracts/counter/main.go
Normal 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() {}
|
||||
Reference in New Issue
Block a user