1// Package commitrevealdemo is a small gnoweb demo of the commit-reveal scheme2// provided by the [p/moul/x/daily/commitreveal](/p/moul/x/daily/commitreveal/v0)3// library: the two phases, and what the salt is for.4//5// It contains no crypto of its own. Stateless, so Render is deterministic —6// which is precisely what the library is for.7package commitrevealdemo89import (10 "strings"1112 "gno.land/p/moul/x/daily/commitreveal/v0"13)1415// Render renders the demo for gnoweb.16func Render(path string) string {17 var b strings.Builder18 b.WriteString("# Commit–Reveal\n\n")19 b.WriteString("Bind to a choice without disclosing it, demoing the ")20 b.WriteString("[`p/moul/x/daily/commitreveal`](/p/moul/x/daily/commitreveal/v0) library.\n\n")2122 b.WriteString("## The problem\n\n")23 b.WriteString("A transaction is public before it executes. In a sealed-bid auction or ")24 b.WriteString("a simultaneous-move game, whoever moves last reads everyone else's ")25 b.WriteString("move and wins for free.\n\n")2627 aliceSalt := "alice-secret-salt-1"28 bobSalt := "bob-secret-salt-0001"29 aliceCommit := commitreveal.MustCommit("rock", aliceSalt)30 bobCommit := commitreveal.MustCommit("paper", bobSalt)3132 b.WriteString("## Phase 1 — commit\n\n")33 b.WriteString("Each player publishes only `H(value ‖ salt)`. Nothing about the move ")34 b.WriteString("leaks, but neither can change it later.\n\n")35 b.WriteString("| player | commitment |\n|---|---|\n")36 b.WriteString("| alice | `" + short(aliceCommit) + "` |\n")37 b.WriteString("| bob | `" + short(bobCommit) + "` |\n")3839 b.WriteString("\n## Phase 2 — reveal\n\n")40 b.WriteString("Now the values and salts are published and checked against the ")41 b.WriteString("commitments recorded earlier:\n\n")42 b.WriteString("| check | result |\n|---|---|\n")43 row(&b, "alice opens with `rock`", commitreveal.Open(aliceCommit, "rock", aliceSalt))44 row(&b, "bob opens with `paper`", commitreveal.Open(bobCommit, "paper", bobSalt))45 row(&b, "bob tries `scissors` instead", commitreveal.Open(bobCommit, "scissors", bobSalt))46 row(&b, "bob claims alice's commitment", commitreveal.Open(aliceCommit, "rock", bobSalt))4748 b.WriteString("\nBob cannot switch his move after seeing Alice's, and cannot pass off ")49 b.WriteString("her commitment as his own.\n\n")5051 b.WriteString("## Why the salt is mandatory\n\n")52 b.WriteString("Rock-paper-scissors has three possible moves. Without a salt there are ")53 b.WriteString("exactly three possible hashes, and hashing all three breaks the scheme ")54 b.WriteString("outright. The library refuses a salt shorter than `")55 b.WriteString(itoa(commitreveal.MinSaltLen) + "` bytes rather than leaving that as advice:\n\n")56 _, err := commitreveal.Commit("rock", "tooshort")57 b.WriteString("- `Commit(\"rock\", \"tooshort\")` → `")58 if err != nil {59 b.WriteString(err.Error())60 }61 b.WriteString("`\n\n")62 b.WriteString("The salt also keeps two players who pick the *same* move from ")63 b.WriteString("publishing the same commitment:\n\n")64 one := commitreveal.MustCommit("rock", "salt-one-0123456789")65 two := commitreveal.MustCommit("rock", "salt-two-0123456789")66 b.WriteString("| same move, different salt | commitment |\n|---|---|\n")67 b.WriteString("| player 1 | `" + short(one) + "` |\n")68 b.WriteString("| player 2 | `" + short(two) + "` |\n")6970 b.WriteString("\n## Two details that are easy to get wrong\n\n")71 b.WriteString("- **Length-prefixed hashing.** With plain concatenation `(\"ab\",\"cd…\")` ")72 b.WriteString("and `(\"abc\",\"d…\")` hash identically, so one commitment could be opened ")73 b.WriteString("two different ways.\n")74 b.WriteString("- **Constant-time comparison.** A short-circuiting check leaks, through ")75 b.WriteString("timing, how many leading bytes of a guess were right — enough to rebuild ")76 b.WriteString("a commitment byte by byte.\n")77 return b.String()78}7980func row(b *strings.Builder, label string, err error) {81 b.WriteString("| " + label + " | ")82 if err == nil {83 b.WriteString("✅ accepted")84 } else {85 b.WriteString("❌ `" + err.Error() + "`")86 }87 b.WriteString(" |\n")88}8990func short(c string) string { return c[:16] + "…" }9192func itoa(i int) string {93 if i == 0 {94 return "0"95 }96 s := ""97 for i > 0 {98 s = string(rune('0'+i%10)) + s99 i /= 10100 }101 return s102}103Render(path string) string
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
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.