PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/moul/x/daily/guestbook/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / x / daily / guestbook
Files
3 (README)(gnomod.toml)
Exported functions
3
Module
gno.land/r/moul/x/daily/guestbook/v0
gno
0.9

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • guestbook.gnogno
guestbook.gnogno
1// Package guestbook is a public, on-chain guestbook realm for gno.land.2//3// Anyone can sign the guestbook with a short message via the Sign crossing4// function. Every entry records the caller's address, the message, and the5// block height at which it was signed. Render lists all entries newest-first6// as a Markdown table.7package guestbook89import (10	"strconv"11	"strings"1213	"chain"14	"chain/runtime"1516	"gno.land/p/nt/avl/v0"17)1819// Entry is a single signed guestbook line.20type Entry struct {21	ID      int    // 1-based sequence number22	Author  string // bech32 address of the signer23	Message string // the message left by the signer24	Height  int64  // block height at which the entry was signed25}2627const maxMessageLen = 2802829var (30	// entries is keyed by a zero-padded sequence so avl iteration is ordered.31	entries = avl.NewTree()32	count   int33)3435// Sign appends a new entry to the guestbook attributed to the immediate caller.36//37// It is a crossing function (gno 0.9 interrealm convention): callers invoke it38// as Sign(cross(cur), "hello"). The cur.IsCurrent() guard is the authentication39// primitive — without it a stale/forged realm value could spoof the author.40func Sign(cur realm, message string) {41	if !cur.IsCurrent() {42		panic("guestbook: spoofed realm; Sign must be called via cross(cur)")43	}4445	author := cur.Previous().Address().String()46	e := addEntry(author, message, runtime.ChainHeight())4748	chain.Emit("Signed",49		"id", strconv.Itoa(e.ID),50		"author", e.Author,51		"height", strconv.FormatInt(e.Height, 10),52	)53}5455// addEntry validates the message and appends an entry. Non-crossing internal56// helper so the append/validation logic is unit-testable without a realm frame.57// Panics (aborting the tx, reverting state) on an invalid message.58func addEntry(author, message string, height int64) *Entry {59	msg := strings.TrimSpace(message)60	if msg == "" {61		panic("guestbook: message must not be empty")62	}63	if len(msg) > maxMessageLen {64		panic("guestbook: message too long (max " + strconv.Itoa(maxMessageLen) + " bytes)")65	}6667	count++68	e := &Entry{69		ID:      count,70		Author:  author,71		Message: msg,72		Height:  height,73	}74	entries.Set(seqKey(count), e)75	return e76}7778// Count returns the total number of signatures. Read-only, non-crossing.79func Count() int {80	return count81}8283// Render lists every entry newest-first as a Markdown table plus a total count.84func Render(path string) string {85	var b strings.Builder8687	b.WriteString("# Guestbook\n\n")8889	if count == 0 {90		b.WriteString("_No one has signed yet. Be the first!_\n")91		return b.String()92	}9394	b.WriteString("**Total signatures:** ")95	b.WriteString(strconv.Itoa(count))96	b.WriteString("\n\n")9798	b.WriteString("| # | Who | Message | Height |\n")99	b.WriteString("|---|-----|---------|--------|\n")100101	// avl iterates ascending by key; ReverseIterate gives newest-first.102	entries.ReverseIterate("", "", func(_ string, v any) bool {103		e := v.(*Entry)104		b.WriteString("| ")105		b.WriteString(strconv.Itoa(e.ID))106		b.WriteString(" | ")107		b.WriteString(shortAddr(e.Author))108		b.WriteString(" | ")109		b.WriteString(escapeCell(e.Message))110		b.WriteString(" | ")111		b.WriteString(strconv.FormatInt(e.Height, 10))112		b.WriteString(" |\n")113		return false114	})115116	return b.String()117}118119// seqKey renders a sequence number as a fixed-width, zero-padded string so120// avl's lexicographic key order matches numeric order.121func seqKey(n int) string {122	s := strconv.Itoa(n)123	const width = 16124	if len(s) >= width {125		return s126	}127	return strings.Repeat("0", width-len(s)) + s128}129130// shortAddr abbreviates a bech32 address for compact display.131func shortAddr(addr string) string {132	if len(addr) <= 12 {133		return addr134	}135	return addr[:8] + "…" + addr[len(addr)-4:]136}137138// escapeCell neutralizes characters that would break a Markdown table cell.139func escapeCell(s string) string {140	s = strings.ReplaceAll(s, "\\", "\\\\")141	s = strings.ReplaceAll(s, "|", "\\|")142	s = strings.ReplaceAll(s, "\r\n", " ")143	s = strings.ReplaceAll(s, "\n", " ")144	s = strings.ReplaceAll(s, "\r", " ")145	return s146}147

Functions

  • Count() int

  • Render(path string) string

  • Sign(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, message string)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.