// gen generates contracts/name_registry/name_registry.wasm // Run from the repo root: go run ./contracts/name_registry/gen/ // // Contract methods: register, resolve, transfer, release // Host imports from "env": get_arg_str, get_caller, get_state, set_state, log // // Every contract action emits a human-readable log entry visible in the // block explorer, e.g.: // "registered: alice" // "name taken: alice" // "not found: alice" // "owner: " // "transferred: alice" // "unauthorized: alice" // "released: alice" 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 _, sl := range slices { out = append(out, sl...) } 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) } func vec(items ...[]byte) []byte { out := u(uint64(len(items))) for _, it := range items { out = append(out, it...) } return out } func functype(params, results []byte) []byte { return cat([]byte{0x60}, u(uint64(len(params))), params, u(uint64(len(results))), results) } func importFunc(mod, name string, typeIdx uint32) []byte { return cat(wstr(mod), wstr(name), []byte{0x00}, u(uint64(typeIdx))) } func exportEntry(name string, kind byte, idx uint32) []byte { return cat(wstr(name), []byte{kind}, u(uint64(idx))) } func dataSegment(offset int32, data []byte) []byte { return cat( []byte{0x00}, []byte{0x41}, s(int64(offset)), []byte{0x0B}, u(uint64(len(data))), data, ) } 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) } var noLocals = u(0) 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 block_() []byte { return []byte{0x02, 0x40} } func loop_() []byte { return []byte{0x03, 0x40} } func if_() []byte { return []byte{0x04, 0x40} } func ifI32() []byte { return []byte{0x04, tI32} } // if that returns i32 func else_() []byte { return []byte{0x05} } 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 i32GtU() []byte { return []byte{0x4B} } func i32GeU() []byte { return []byte{0x4F} } func i32Add() []byte { return []byte{0x6A} } func i32Load8U() []byte { return []byte{0x2D, 0x00, 0x00} } func i32Store8() []byte { return []byte{0x3A, 0x00, 0x00} } // ── Memory layout ───────────────────────────────────────────────────────────── const ( offArg0 = 0x000 // arg[0] name buffer (64 bytes) offArg1 = 0x040 // arg[1] new_owner buffer (128 bytes) offCaller = 0x0C0 // caller pubkey buffer (128 bytes) offStateRead = 0x140 // existing owner buffer (128 bytes) // Verbose prefix strings — each ends with ": " for readable log messages. offRegisteredPfx = 0x200 // "registered: " 12 bytes offNameTakenPfx = 0x20C // "name taken: " 12 bytes offNotFoundPfx = 0x218 // "not found: " 11 bytes offOwnerPfx = 0x224 // "owner: " 7 bytes offTransferredPfx = 0x22C // "transferred: " 13 bytes offUnauthPfx = 0x23A // "unauthorized: " 14 bytes offReleasedPfx = 0x249 // "released: " 10 bytes // Scratch buffer for building concatenated log messages. offScratch = 0x300 // 256 bytes ) // Import function indices (order must match importSection below) const ( fnGetArgStr = 0 // get_arg_str(idx, dstPtr, dstLen i32) → i32 fnGetCaller = 1 // get_caller(bufPtr, bufLen i32) → i32 fnGetState = 2 // get_state(kP,kL,dP,dL i32) → i32 fnSetState = 3 // set_state(kP,kL,vP,vL i32) fnLog = 4 // log(msgPtr, msgLen i32) ) // Local function indices (imports first, then locals in declaration order) const ( fnBytesEqual = 5 // $bytes_equal(aPtr,bPtr,len i32) → i32 fnMemcpy = 6 // $memcpy(dst,src,len i32) fnLogPrefixName = 7 // $log_prefix_name(pfxPtr,pfxLen,sfxPtr,sfxLen i32) fnRegister = 8 fnResolve = 9 fnTransfer = 10 fnRelease = 11 ) // ── $bytes_equal helper ─────────────────────────────────────────────────────── // (aPtr i32, bPtr i32, len i32) → i32 // locals: i(3), same(4) func bytesEqualBody() []byte { return funcBody( withLocals(localDecl(2, tI32)), // locals 3=i, 4=same // same = 1; i = 0 ic32(1), lset(4), ic32(0), lset(3), block_(), loop_(), lget(3), lget(2), i32GeU(), brIf_(1), // i >= len → exit block // load mem[aPtr+i] lget(0), lget(3), i32Add(), i32Load8U(), // load mem[bPtr+i] lget(1), lget(3), i32Add(), i32Load8U(), i32Ne(), if_(), ic32(0), lset(4), br_(2), // exit block end_(), lget(3), ic32(1), i32Add(), lset(3), br_(0), // next iteration end_(), end_(), lget(4), // return same ) } // ── $memcpy helper ──────────────────────────────────────────────────────────── // (dst i32, src i32, len i32) — copies len bytes from src to dst. // locals: i(3) func memcpyBody() []byte { return funcBody( withLocals(localDecl(1, tI32)), // local 3 = i ic32(0), lset(3), // i = 0 block_(), loop_(), lget(3), lget(2), i32GeU(), brIf_(1), // if i >= len: exit // mem[dst+i] = mem[src+i] lget(0), lget(3), i32Add(), // dst+i (store address) lget(1), lget(3), i32Add(), i32Load8U(), // load mem[src+i] i32Store8(), lget(3), ic32(1), i32Add(), lset(3), // i++ br_(0), end_(), end_(), ) } // ── $log_prefix_name helper ─────────────────────────────────────────────────── // (prefixPtr i32, prefixLen i32, suffixPtr i32, suffixLen i32) // Builds "prefix" in scratch buffer and logs it. func logPrefixNameBody() []byte { return funcBody( noLocals, // memcpy(offScratch, prefixPtr, prefixLen) ic32(offScratch), lget(0), lget(1), call(fnMemcpy), // memcpy(offScratch + prefixLen, suffixPtr, suffixLen) ic32(offScratch), lget(1), i32Add(), lget(2), lget(3), call(fnMemcpy), // log(offScratch, prefixLen + suffixLen) ic32(offScratch), lget(1), lget(3), i32Add(), call(fnLog), ) } // ── isOwner: shared caller-vs-existing check ────────────────────────────────── // Assumes: caller is at offCaller with len callerLenLocal, // existing is at offStateRead with len existingLenLocal. // Returns instructions that leave i32 (1=same, 0=not) on stack. func isOwnerCheck(callerLenLocal, existingLenLocal uint32) []byte { return cat( // if callerLen != existingLen → push 0 ifI32(), ic32(0), else_(), // else call bytes_equal(offCaller, offStateRead, callerLen) ic32(offCaller), ic32(offStateRead), lget(callerLenLocal), call(fnBytesEqual), end_(), ) } func main() { // ── Type section ────────────────────────────────────────────────────────── // Type 0: (i32,i32,i32)→(i32) get_arg_str, bytes_equal // Type 1: (i32,i32)→(i32) get_caller // Type 2: (i32,i32,i32,i32)→(i32) get_state // Type 3: (i32,i32,i32,i32)→() set_state, log_prefix_name // Type 4: (i32,i32)→() log // Type 5: ()→() register, resolve, transfer, release // Type 6: (i32,i32,i32)→() memcpy typeSection := section(0x01, vec( functype([]byte{tI32, tI32, tI32}, []byte{tI32}), // 0 functype([]byte{tI32, tI32}, []byte{tI32}), // 1 functype([]byte{tI32, tI32, tI32, tI32}, []byte{tI32}), // 2 functype([]byte{tI32, tI32, tI32, tI32}, []byte{}), // 3 functype([]byte{tI32, tI32}, []byte{}), // 4 functype([]byte{}, []byte{}), // 5 functype([]byte{tI32, tI32, tI32}, []byte{}), // 6 )) // ── Import section ──────────────────────────────────────────────────────── importSection := section(0x02, vec( importFunc("env", "get_arg_str", 0), // fnGetArgStr=0 type 0 importFunc("env", "get_caller", 1), // fnGetCaller=1 type 1 importFunc("env", "get_state", 2), // fnGetState=2 type 2 importFunc("env", "set_state", 3), // fnSetState=3 type 3 importFunc("env", "log", 4), // fnLog=4 type 4 )) // ── Function section: 7 local functions ────────────────────────────────── functionSection := section(0x03, vec( u(0), // bytes_equal → type 0 u(6), // memcpy → type 6 u(3), // log_prefix_name → type 3 u(5), // register → type 5 u(5), // resolve → type 5 u(5), // transfer → type 5 u(5), // release → type 5 )) // ── Memory section ──────────────────────────────────────────────────────── memorySection := section(0x05, vec(cat([]byte{0x00}, u(1)))) // ── Export section ──────────────────────────────────────────────────────── exportSection := section(0x07, vec( exportEntry("memory", 0x02, 0), exportEntry("register", 0x00, fnRegister), exportEntry("resolve", 0x00, fnResolve), exportEntry("transfer", 0x00, fnTransfer), exportEntry("release", 0x00, fnRelease), )) // ── Data section ────────────────────────────────────────────────────────── dataSection := section(0x0B, cat( u(7), dataSegment(offRegisteredPfx, []byte("registered: ")), dataSegment(offNameTakenPfx, []byte("name taken: ")), dataSegment(offNotFoundPfx, []byte("not found: ")), dataSegment(offOwnerPfx, []byte("owner: ")), dataSegment(offTransferredPfx, []byte("transferred: ")), dataSegment(offUnauthPfx, []byte("unauthorized: ")), dataSegment(offReleasedPfx, []byte("released: ")), )) // ── Code section ───────────────────────────────────────────────────────── // register(): locals nameLen(0), callerLen(1), existingLen(2) registerBody := funcBody( withLocals(localDecl(3, tI32)), // nameLen = get_arg_str(0, offArg0, 64) ic32(0), ic32(offArg0), ic32(64), call(fnGetArgStr), lset(0), // if nameLen == 0: return lget(0), i32Eqz(), if_(), return_(), end_(), // existingLen = get_state(offArg0, nameLen, offStateRead, 128) ic32(offArg0), lget(0), ic32(offStateRead), ic32(128), call(fnGetState), lset(2), // if existingLen > 0: log("name taken: "); return lget(2), ic32(0), i32GtU(), if_(), ic32(offNameTakenPfx), ic32(12), ic32(offArg0), lget(0), call(fnLogPrefixName), return_(), end_(), // callerLen = get_caller(offCaller, 128) ic32(offCaller), ic32(128), call(fnGetCaller), lset(1), // set_state(offArg0, nameLen, offCaller, callerLen) ic32(offArg0), lget(0), ic32(offCaller), lget(1), call(fnSetState), // log("registered: ") ic32(offRegisteredPfx), ic32(12), ic32(offArg0), lget(0), call(fnLogPrefixName), ) // resolve(): locals nameLen(0), ownerLen(1) resolveBody := funcBody( withLocals(localDecl(2, tI32)), // nameLen = get_arg_str(0, offArg0, 64) ic32(0), ic32(offArg0), ic32(64), call(fnGetArgStr), lset(0), lget(0), i32Eqz(), if_(), return_(), end_(), // ownerLen = get_state(offArg0, nameLen, offStateRead, 128) ic32(offArg0), lget(0), ic32(offStateRead), ic32(128), call(fnGetState), lset(1), // if ownerLen == 0: log("not found: "); return lget(1), i32Eqz(), if_(), ic32(offNotFoundPfx), ic32(11), ic32(offArg0), lget(0), call(fnLogPrefixName), return_(), end_(), // log("owner: ") // The pubkey stored in state is the raw caller string (hex-encoded), // so the log will display the human-readable address. ic32(offOwnerPfx), ic32(7), ic32(offStateRead), lget(1), call(fnLogPrefixName), ) // transfer(): locals nameLen(0), newOwnerLen(1), callerLen(2), existingLen(3) transferBody := funcBody( withLocals(localDecl(4, tI32)), // nameLen = get_arg_str(0, offArg0, 64) ic32(0), ic32(offArg0), ic32(64), call(fnGetArgStr), lset(0), lget(0), i32Eqz(), if_(), return_(), end_(), // newOwnerLen = get_arg_str(1, offArg1, 128) ic32(1), ic32(offArg1), ic32(128), call(fnGetArgStr), lset(1), lget(1), i32Eqz(), if_(), return_(), end_(), // existingLen = get_state(offArg0, nameLen, offStateRead, 128) ic32(offArg0), lget(0), ic32(offStateRead), ic32(128), call(fnGetState), lset(3), // if existingLen == 0: not registered → anyone can claim lget(3), i32Eqz(), if_(), ic32(offArg0), lget(0), ic32(offArg1), lget(1), call(fnSetState), ic32(offTransferredPfx), ic32(13), ic32(offArg0), lget(0), call(fnLogPrefixName), return_(), end_(), // callerLen = get_caller(offCaller, 128) ic32(offCaller), ic32(128), call(fnGetCaller), lset(2), // isOwner = (callerLen == existingLen) ? bytes_equal(...) : 0 lget(2), lget(3), i32Ne(), isOwnerCheck(2, 3), // if !isOwner: log("unauthorized: "); return i32Eqz(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefixName), return_(), end_(), // Authorized ic32(offArg0), lget(0), ic32(offArg1), lget(1), call(fnSetState), ic32(offTransferredPfx), ic32(13), ic32(offArg0), lget(0), call(fnLogPrefixName), ) // release(): locals nameLen(0), callerLen(1), existingLen(2) releaseBody := funcBody( withLocals(localDecl(3, tI32)), // nameLen = get_arg_str(0, offArg0, 64) ic32(0), ic32(offArg0), ic32(64), call(fnGetArgStr), lset(0), lget(0), i32Eqz(), if_(), return_(), end_(), // existingLen = get_state(offArg0, nameLen, offStateRead, 128) ic32(offArg0), lget(0), ic32(offStateRead), ic32(128), call(fnGetState), lset(2), // if existingLen == 0: log("not found: "); return lget(2), i32Eqz(), if_(), ic32(offNotFoundPfx), ic32(11), ic32(offArg0), lget(0), call(fnLogPrefixName), return_(), end_(), // callerLen = get_caller(offCaller, 128) ic32(offCaller), ic32(128), call(fnGetCaller), lset(1), // isOwner check lget(1), lget(2), i32Ne(), isOwnerCheck(1, 2), i32Eqz(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefixName), return_(), end_(), // Store empty value → effectively releases the name ic32(offArg0), lget(0), ic32(offArg0), ic32(0), call(fnSetState), ic32(offReleasedPfx), ic32(10), ic32(offArg0), lget(0), call(fnLogPrefixName), ) codeSection := section(0x0A, cat( u(7), bytesEqualBody(), memcpyBody(), logPrefixNameBody(), registerBody, resolveBody, transferBody, releaseBody, )) // ── Assemble module ─────────────────────────────────────────────────────── module := cat( []byte{0x00, 0x61, 0x73, 0x6d}, // \0asm []byte{0x01, 0x00, 0x00, 0x00}, // version 1 typeSection, importSection, functionSection, memorySection, exportSection, dataSection, codeSection, ) out := "contracts/name_registry/name_registry.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)) }