Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

b58demo.gno

4.42 Kb · 139 lines
  1// Package b58demo is a small gnoweb demo of the Base58 codec provided by the
  2// [p/moul/x/daily/b58](/p/moul/x/daily/b58/v0) library: it renders the alphabet,
  3// a gallery of worked examples, and an interactive hex → Base58 round-tripper.
  4//
  5// It contains no codec logic of its own — everything comes from `b58.Encode`,
  6// `b58.Decode` and `b58.Alphabet`.
  7package b58demo
  8
  9import (
 10	"encoding/hex"
 11	"strings"
 12
 13	"gno.land/p/g1magld5ptgzaktqcexxq0pgq5lmdf0kz6re8hlv/md/v0"
 14	"gno.land/p/g1magld5ptgzaktqcexxq0pgq5lmdf0kz6re8hlv/x/daily/b58/v0"
 15)
 16
 17// Render produces the gnoweb Markdown view.
 18//
 19//   - "/"        → the alphabet + a small gallery of worked examples.
 20//   - "/<hex>"   → decode the hex input to bytes, show its Base58, and the
 21//     hex-encoded decoded round-trip to prove Encode/Decode are inverse.
 22func Render(path string) string {
 23	arg := strings.TrimPrefix(path, "/")
 24	arg = strings.TrimSpace(arg)
 25
 26	if arg == "" {
 27		return renderHome()
 28	}
 29	return renderHex(arg)
 30}
 31
 32func renderHome() string {
 33	var sb strings.Builder
 34	sb.WriteString("# Base58 (Bitcoin alphabet)\n\n")
 35	sb.WriteString("Demo of the [`p/moul/x/daily/b58`](/p/moul/x/daily/b58/v0) library — ")
 36	sb.WriteString("base conversion from raw bytes (base-256) to base-58, using the ")
 37	sb.WriteString("Bitcoin alphabet (no `0`, `O`, `I`, `l`), pure byte-slice math, no `math/big`.\n\n")
 38
 39	sb.WriteString("## Alphabet\n\n")
 40	sb.WriteString("```\n")
 41	sb.WriteString(b58.Alphabet)
 42	sb.WriteString("\n```\n\n")
 43	sb.WriteString("58 characters, index 0…57.\n\n")
 44
 45	sb.WriteString("## Examples\n\n")
 46	sb.WriteString("| input (utf-8) | hex | Base58 |\n")
 47	sb.WriteString("|---|---|---|\n")
 48	examples := []string{"", "hello world", "gno.land", "Bitcoin"}
 49	for _, e := range examples {
 50		h := hex.EncodeToString([]byte(e))
 51		if h == "" {
 52			h = "(empty)"
 53		}
 54		label := e
 55		if label == "" {
 56			label = "(empty)"
 57		}
 58		sb.WriteString("| `" + label + "` | `" + h + "` | `" + b58.Encode([]byte(e)) + "` |\n")
 59	}
 60	sb.WriteString("\n")
 61
 62	sb.WriteString("Leading zero bytes → leading `1`s:\n\n")
 63	sb.WriteString("| bytes | Base58 |\n|---|---|\n")
 64	sb.WriteString("| `[0x00]` | `" + b58.Encode([]byte{0}) + "` |\n")
 65	sb.WriteString("| `[0x00 0x00 0x00]` | `" + b58.Encode([]byte{0, 0, 0}) + "` |\n\n")
 66
 67	sb.WriteString("## Try it\n\n")
 68	sb.WriteString("Append a hex string to the path to encode/round-trip it:\n\n")
 69	sb.WriteString("- `/68656c6c6f` — the bytes for \"hello\"\n")
 70	sb.WriteString("- `/00000000` — four zero bytes\n")
 71	sb.WriteString("- `/deadbeef`\n")
 72	return sb.String()
 73}
 74
 75func renderHex(arg string) string {
 76	var sb strings.Builder
 77	sb.WriteString("# Base58 — round-trip\n\n")
 78	sb.WriteString("[← alphabet & examples](/r/moul/x/daily/b58demo/v0)\n\n")
 79
 80	raw, err := hex.DecodeString(arg)
 81	if err != nil {
 82		sb.WriteString("**Invalid hex input.**\n\n")
 83		// arg is the visitor's URL path and this branch is the one ANY
 84		// non-hex path reaches, so the span has to be built by something that
 85		// sizes its fence past a backtick in the content. Hand-rolled
 86		// backticks here let a crafted path close the span and render its own
 87		// markdown on a gno.land page.
 88		sb.WriteString(md.InlineCode(arg) + " is not a valid hex string. ")
 89		sb.WriteString("Provide an even number of hex digits, e.g. `/deadbeef`.\n")
 90		return sb.String()
 91	}
 92
 93	encoded := b58.Encode(raw)
 94	decoded := b58.Decode(encoded)
 95	roundTrip := hex.EncodeToString(decoded)
 96
 97	inHex := arg
 98	if inHex == "" {
 99		inHex = "(empty)"
100	}
101	outHex := roundTrip
102	if outHex == "" {
103		outHex = "(empty)"
104	}
105	enc := encoded
106	if enc == "" {
107		enc = "(empty)"
108	}
109
110	sb.WriteString("| step | value |\n|---|---|\n")
111	//gnovet:ignore render-path-in-code-span inHex is the output of a successful hex.DecodeString, so [0-9a-fA-F] only
112	sb.WriteString("| hex input | `" + inHex + "` |\n")
113	sb.WriteString("| bytes | `" + byteList(raw) + "` |\n")
114	sb.WriteString("| **Base58** | `" + enc + "` |\n")
115	sb.WriteString("| decoded (hex) | `" + outHex + "` |\n\n")
116
117	if roundTrip == arg {
118		sb.WriteString("✓ Round-trip matches: `Decode(Encode(x)) == x`.\n")
119	} else {
120		sb.WriteString("✗ Round-trip mismatch.\n")
121	}
122	return sb.String()
123}
124
125// byteList renders a byte slice as a compact space-separated hex list.
126func byteList(b []byte) string {
127	if len(b) == 0 {
128		return "(none)"
129	}
130	const digits = "0123456789abcdef"
131	out := make([]byte, 0, len(b)*3)
132	for i, c := range b {
133		if i > 0 {
134			out = append(out, ' ')
135		}
136		out = append(out, digits[c>>4], digits[c&0x0f])
137	}
138	return string(out)
139}