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:
331
contracts/counter/gen/main.go
Normal file
331
contracts/counter/gen/main.go
Normal file
@@ -0,0 +1,331 @@
|
||||
// gen generates contracts/counter/counter.wasm — binary WASM for the counter contract.
|
||||
// Run from the repo root: go run ./contracts/counter/gen/
|
||||
//
|
||||
// Contract methods exported: increment, get, reset
|
||||
// Host imports from "env": put_u64, get_u64, log, get_caller, get_state, set_state
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ── LEB128 ───────────────────────────────────────────────────────────────────
|
||||
|
||||
func u(v uint64) []byte {
|
||||
var b []byte
|
||||
for {
|
||||
bt := byte(v & 0x7f)
|
||||
v >>= 7
|
||||
if v != 0 {
|
||||
bt |= 0x80
|
||||
}
|
||||
b = append(b, bt)
|
||||
if v == 0 {
|
||||
return b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func s(v int64) []byte {
|
||||
var b []byte
|
||||
for {
|
||||
bt := byte(v & 0x7f)
|
||||
v >>= 7
|
||||
sign := (bt & 0x40) != 0
|
||||
if (v == 0 && !sign) || (v == -1 && sign) {
|
||||
return append(b, bt)
|
||||
}
|
||||
b = append(b, bt|0x80)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Builders ──────────────────────────────────────────────────────────────────
|
||||
|
||||
func cat(slices ...[]byte) []byte {
|
||||
var out []byte
|
||||
for _, s := range slices {
|
||||
out = append(out, s...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func wstr(str string) []byte { return cat(u(uint64(len(str))), []byte(str)) }
|
||||
|
||||
func section(id byte, content []byte) []byte {
|
||||
return cat([]byte{id}, u(uint64(len(content))), content)
|
||||
}
|
||||
|
||||
// vec encodes a vector: count followed by concatenated items.
|
||||
func vec(items ...[]byte) []byte {
|
||||
out := u(uint64(len(items)))
|
||||
for _, it := range items {
|
||||
out = append(out, it...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// functype encodes a WASM function type (0x60 prefix).
|
||||
func functype(params, results []byte) []byte {
|
||||
return cat([]byte{0x60}, u(uint64(len(params))), params, u(uint64(len(results))), results)
|
||||
}
|
||||
|
||||
// importFunc encodes a function import entry.
|
||||
func importFunc(mod, name string, typeIdx uint32) []byte {
|
||||
return cat(wstr(mod), wstr(name), []byte{0x00}, u(uint64(typeIdx)))
|
||||
}
|
||||
|
||||
// exportEntry encodes an export entry.
|
||||
func exportEntry(name string, kind byte, idx uint32) []byte {
|
||||
return cat(wstr(name), []byte{kind}, u(uint64(idx)))
|
||||
}
|
||||
|
||||
// dataSegment encodes an active data segment for memory 0.
|
||||
func dataSegment(offset int32, data []byte) []byte {
|
||||
return cat(
|
||||
[]byte{0x00}, // active segment, implicit mem 0
|
||||
[]byte{0x41}, s(int64(offset)), []byte{0x0B}, // i32.const offset; end
|
||||
u(uint64(len(data))), data,
|
||||
)
|
||||
}
|
||||
|
||||
// funcBody encodes one function body: localDecls + instructions + end.
|
||||
func funcBody(localDecls []byte, instrs ...[]byte) []byte {
|
||||
inner := cat(localDecls)
|
||||
for _, ins := range instrs {
|
||||
inner = append(inner, ins...)
|
||||
}
|
||||
inner = append(inner, 0x0B) // end
|
||||
return cat(u(uint64(len(inner))), inner)
|
||||
}
|
||||
|
||||
// noLocals is an empty local decl list.
|
||||
var noLocals = u(0)
|
||||
|
||||
// localDecl encodes n locals of a given type.
|
||||
func localDecl(n uint32, typ byte) []byte { return cat(u(uint64(n)), []byte{typ}) }
|
||||
func withLocals(decls ...[]byte) []byte {
|
||||
return cat(u(uint64(len(decls))), cat(decls...))
|
||||
}
|
||||
|
||||
// ── Instructions ──────────────────────────────────────────────────────────────
|
||||
|
||||
const (
|
||||
tI32 byte = 0x7F
|
||||
tI64 byte = 0x7E
|
||||
)
|
||||
|
||||
func call(fn uint32) []byte { return cat([]byte{0x10}, u(uint64(fn))) }
|
||||
func lget(i uint32) []byte { return cat([]byte{0x20}, u(uint64(i))) }
|
||||
func lset(i uint32) []byte { return cat([]byte{0x21}, u(uint64(i))) }
|
||||
func ic32(v int32) []byte { return cat([]byte{0x41}, s(int64(v))) }
|
||||
func ic64(v int64) []byte { return cat([]byte{0x42}, s(v)) }
|
||||
func block_() []byte { return []byte{0x02, 0x40} }
|
||||
func loop_() []byte { return []byte{0x03, 0x40} }
|
||||
func if_() []byte { return []byte{0x04, 0x40} }
|
||||
func end_() []byte { return []byte{0x0B} }
|
||||
func br_(lbl uint32) []byte { return cat([]byte{0x0C}, u(uint64(lbl))) }
|
||||
func brIf_(lbl uint32) []byte { return cat([]byte{0x0D}, u(uint64(lbl))) }
|
||||
func return_() []byte { return []byte{0x0F} }
|
||||
func i32Eqz() []byte { return []byte{0x45} }
|
||||
func i32Ne() []byte { return []byte{0x47} }
|
||||
func i32GeU() []byte { return []byte{0x4F} }
|
||||
func i32Add() []byte { return []byte{0x6A} }
|
||||
func i64Add() []byte { return []byte{0x7C} }
|
||||
func i32Load8U() []byte { return []byte{0x2D, 0x00, 0x00} } // align=0, offset=0
|
||||
|
||||
// ── Memory layout constants ───────────────────────────────────────────────────
|
||||
|
||||
const (
|
||||
offCounter = 0x00 // "counter" (7 bytes)
|
||||
offOwner = 0x10 // "owner" (5 bytes)
|
||||
offIncMsg = 0x20 // "incremented" (11 bytes)
|
||||
offGetMsg = 0x30 // "get called" (10 bytes)
|
||||
offResetOk = 0x40 // "reset ok" (8 bytes)
|
||||
offUnauth = 0x50 // "unauthorized" (12 bytes)
|
||||
offCallerBuf = 0x60 // caller buf (128 bytes)
|
||||
offOwnerBuf = 0xE0 // owner buf (128 bytes)
|
||||
)
|
||||
|
||||
// Import function indices
|
||||
const (
|
||||
fnPutU64 = 0 // put_u64(keyPtr, keyLen i32, val i64)
|
||||
fnGetU64 = 1 // get_u64(keyPtr, keyLen i32) → i64
|
||||
fnLog = 2 // log(msgPtr, msgLen i32)
|
||||
fnGetCaller = 3 // get_caller(bufPtr, bufLen i32) → i32
|
||||
fnGetState = 4 // get_state(kPtr,kLen,dPtr,dLen i32) → i32
|
||||
fnSetState = 5 // set_state(kPtr,kLen,vPtr,vLen i32)
|
||||
)
|
||||
|
||||
// Local function indices (imports are 0-5, locals start at 6)
|
||||
const (
|
||||
fnIncrement = 6
|
||||
fnGet = 7
|
||||
fnReset = 8
|
||||
)
|
||||
|
||||
func main() {
|
||||
// ── Type section ─────────────────────────────────────────────────────────
|
||||
// Type 0: (i32,i32,i64)→() put_u64
|
||||
// Type 1: (i32,i32)→(i64) get_u64
|
||||
// Type 2: (i32,i32)→() log
|
||||
// Type 3: (i32,i32)→(i32) get_caller
|
||||
// Type 4: (i32,i32,i32,i32)→(i32) get_state
|
||||
// Type 5: (i32,i32,i32,i32)→() set_state
|
||||
// Type 6: ()→() increment, get, reset
|
||||
typeSection := section(0x01, vec(
|
||||
functype([]byte{tI32, tI32, tI64}, []byte{}), // 0
|
||||
functype([]byte{tI32, tI32}, []byte{tI64}), // 1
|
||||
functype([]byte{tI32, tI32}, []byte{}), // 2
|
||||
functype([]byte{tI32, tI32}, []byte{tI32}), // 3
|
||||
functype([]byte{tI32, tI32, tI32, tI32}, []byte{tI32}), // 4
|
||||
functype([]byte{tI32, tI32, tI32, tI32}, []byte{}), // 5
|
||||
functype([]byte{}, []byte{}), // 6
|
||||
))
|
||||
|
||||
// ── Import section ────────────────────────────────────────────────────────
|
||||
importSection := section(0x02, vec(
|
||||
importFunc("env", "put_u64", fnPutU64),
|
||||
importFunc("env", "get_u64", fnGetU64),
|
||||
importFunc("env", "log", fnLog),
|
||||
importFunc("env", "get_caller", fnGetCaller),
|
||||
importFunc("env", "get_state", fnGetState),
|
||||
importFunc("env", "set_state", fnSetState),
|
||||
))
|
||||
|
||||
// ── Function section: 3 local functions, all type 6 ──────────────────────
|
||||
functionSection := section(0x03, vec(u(6), u(6), u(6)))
|
||||
|
||||
// ── Memory section: 1 page (64 KiB) ──────────────────────────────────────
|
||||
// limits type 0x00 = min only; type 0x01 = min+max
|
||||
memorySection := section(0x05, vec(cat([]byte{0x00}, u(1)))) // min=1, no max
|
||||
|
||||
// ── Export section ────────────────────────────────────────────────────────
|
||||
exportSection := section(0x07, vec(
|
||||
exportEntry("memory", 0x02, 0),
|
||||
exportEntry("increment", 0x00, fnIncrement),
|
||||
exportEntry("get", 0x00, fnGet),
|
||||
exportEntry("reset", 0x00, fnReset),
|
||||
))
|
||||
|
||||
// ── Data section ──────────────────────────────────────────────────────────
|
||||
dataSection := section(0x0B, cat(
|
||||
u(6), // 6 segments
|
||||
dataSegment(offCounter, []byte("counter")),
|
||||
dataSegment(offOwner, []byte("owner")),
|
||||
dataSegment(offIncMsg, []byte("incremented")),
|
||||
dataSegment(offGetMsg, []byte("get called")),
|
||||
dataSegment(offResetOk, []byte("reset ok")),
|
||||
dataSegment(offUnauth, []byte("unauthorized")),
|
||||
))
|
||||
|
||||
// ── Code section ─────────────────────────────────────────────────────────
|
||||
|
||||
// increment():
|
||||
// local $val i64
|
||||
// $val = get_u64("counter")
|
||||
// $val++
|
||||
// put_u64("counter", $val)
|
||||
// log("incremented")
|
||||
incrementBody := funcBody(
|
||||
withLocals(localDecl(1, tI64)),
|
||||
ic32(offCounter), ic32(7), call(fnGetU64), lset(0),
|
||||
lget(0), ic64(1), i64Add(), lset(0),
|
||||
ic32(offCounter), ic32(7), lget(0), call(fnPutU64),
|
||||
ic32(offIncMsg), ic32(11), call(fnLog),
|
||||
)
|
||||
|
||||
// get():
|
||||
// log("get called")
|
||||
getBody := funcBody(
|
||||
noLocals,
|
||||
ic32(offGetMsg), ic32(10), call(fnLog),
|
||||
)
|
||||
|
||||
// reset():
|
||||
// locals: callerLen(0), ownerLen(1), i(2), same(3) — all i32
|
||||
// callerLen = get_caller(callerBuf, 128)
|
||||
// ownerLen = get_state("owner", ownerBuf, 128)
|
||||
// if ownerLen == 0:
|
||||
// set_state("owner", callerBuf[:callerLen])
|
||||
// put_u64("counter", 0)
|
||||
// log("reset ok")
|
||||
// return
|
||||
// if callerLen != ownerLen: log unauthorized; return
|
||||
// same = 1; i = 0
|
||||
// block:
|
||||
// loop:
|
||||
// if i >= callerLen: br 1 (exit block)
|
||||
// if callerBuf[i] != ownerBuf[i]: same=0; br 1
|
||||
// i++; continue loop
|
||||
// if !same: log unauthorized; return
|
||||
// put_u64("counter", 0); log("reset ok")
|
||||
resetBody := funcBody(
|
||||
withLocals(localDecl(4, tI32)),
|
||||
// callerLen = get_caller(callerBuf, 128)
|
||||
ic32(offCallerBuf), ic32(128), call(fnGetCaller), lset(0),
|
||||
// ownerLen = get_state("owner", 5, ownerBuf, 128)
|
||||
ic32(offOwner), ic32(5), ic32(offOwnerBuf), ic32(128), call(fnGetState), lset(1),
|
||||
// if ownerLen == 0:
|
||||
lget(1), i32Eqz(), if_(),
|
||||
ic32(offOwner), ic32(5), ic32(offCallerBuf), lget(0), call(fnSetState),
|
||||
ic32(offCounter), ic32(7), ic64(0), call(fnPutU64),
|
||||
ic32(offResetOk), ic32(8), call(fnLog),
|
||||
return_(),
|
||||
end_(),
|
||||
// if callerLen != ownerLen: unauthorized
|
||||
lget(0), lget(1), i32Ne(), if_(),
|
||||
ic32(offUnauth), ic32(12), call(fnLog),
|
||||
return_(),
|
||||
end_(),
|
||||
// same = 1; i = 0
|
||||
ic32(1), lset(3),
|
||||
ic32(0), lset(2),
|
||||
// block $break
|
||||
block_(),
|
||||
loop_(),
|
||||
lget(2), lget(0), i32GeU(), brIf_(1), // i >= callerLen → break
|
||||
// load callerBuf[i]
|
||||
ic32(offCallerBuf), lget(2), i32Add(), i32Load8U(),
|
||||
// load ownerBuf[i]
|
||||
ic32(offOwnerBuf), lget(2), i32Add(), i32Load8U(),
|
||||
i32Ne(), if_(),
|
||||
ic32(0), lset(3),
|
||||
br_(2), // break out of block
|
||||
end_(),
|
||||
lget(2), ic32(1), i32Add(), lset(2),
|
||||
br_(0), // continue loop
|
||||
end_(),
|
||||
end_(),
|
||||
// if !same: unauthorized
|
||||
lget(3), i32Eqz(), if_(),
|
||||
ic32(offUnauth), ic32(12), call(fnLog),
|
||||
return_(),
|
||||
end_(),
|
||||
// authorized
|
||||
ic32(offCounter), ic32(7), ic64(0), call(fnPutU64),
|
||||
ic32(offResetOk), ic32(8), call(fnLog),
|
||||
)
|
||||
|
||||
codeSection := section(0x0A, cat(u(3), incrementBody, getBody, resetBody))
|
||||
|
||||
// ── Assemble module ───────────────────────────────────────────────────────
|
||||
module := cat(
|
||||
[]byte{0x00, 0x61, 0x73, 0x6d}, // magic \0asm
|
||||
[]byte{0x01, 0x00, 0x00, 0x00}, // version 1
|
||||
typeSection,
|
||||
importSection,
|
||||
functionSection,
|
||||
memorySection,
|
||||
exportSection,
|
||||
dataSection,
|
||||
codeSection,
|
||||
)
|
||||
|
||||
out := "contracts/counter/counter.wasm"
|
||||
if err := os.WriteFile(out, module, 0644); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "write:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Written %s (%d bytes)\n", out, len(module))
|
||||
}
|
||||
Reference in New Issue
Block a user