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

commitrevealdemo.gno

4.18 Kb · 102 lines
  1// Package commitrevealdemo is a small gnoweb demo of the commit-reveal scheme
  2// 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 commitrevealdemo
  8
  9import (
 10	"strings"
 11
 12	"gno.land/p/moul/x/daily/commitreveal/v0"
 13)
 14
 15// Render renders the demo for gnoweb.
 16func Render(path string) string {
 17	var b strings.Builder
 18	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")
 21
 22	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")
 26
 27	aliceSalt := "alice-secret-salt-1"
 28	bobSalt := "bob-secret-salt-0001"
 29	aliceCommit := commitreveal.MustCommit("rock", aliceSalt)
 30	bobCommit := commitreveal.MustCommit("paper", bobSalt)
 31
 32	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")
 38
 39	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))
 47
 48	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")
 50
 51	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")
 69
 70	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}
 79
 80func 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}
 89
 90func short(c string) string { return c[:16] + "…" }
 91
 92func 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)) + s
 99		i /= 10
100	}
101	return s
102}