commitreveal.gno
2.34 Kb · 61 lines
1// Package commitreveal provides a minimal commit-reveal primitive:
2// commit to (salt, data) by publishing only their hash, then later
3// reveal the actual salt and data for anyone to verify against the
4// published commitment. This is the standard way to prevent front-
5// running on gno.land -- e.g. sealed-bid auctions, commit-based voting,
6// or any scheme where revealing intent early would let others react to
7// it before the reveal phase.
8//
9// This package holds no chain state and does not manage commit/reveal
10// deadlines itself -- the importing realm stores the returned [32]byte
11// commitment and decides its own phase timing (typically via
12// chain/runtime.ChainHeight(), the same way gno.land/p/demo/ratelimit
13// takes a caller-supplied block height instead of reading the chain
14// itself).
15package commitreveal
16
17import (
18 "crypto/sha256"
19)
20
21const domainPrefix = byte(0x01)
22
23// minSaltLen is a floor on salt entropy. A short salt lets an observer
24// brute-force it (combined with a guessed data value) before the reveal
25// phase, defeating the whole point of committing first -- so Commit
26// refuses to produce a commitment from a salt this short.
27const minSaltLen = 16
28
29func hashCommit(salt, data []byte) [32]byte {
30 buf := make([]byte, 0, 1+len(salt)+len(data))
31 buf = append(buf, domainPrefix)
32 buf = append(buf, salt...)
33 buf = append(buf, data...)
34 return sha256.Sum256(buf)
35}
36
37// Commit produces the commitment hash for (salt, data). salt must be at
38// least 16 bytes; data must be non-empty. Panics otherwise -- this is
39// the "I'm constructing a real commitment" path, so invalid input here
40// is a caller bug, not something to fail quietly on.
41func Commit(salt, data []byte) [32]byte {
42 if len(salt) < minSaltLen {
43 panic("commitreveal: salt must be at least 16 bytes for adequate entropy")
44 }
45 if len(data) == 0 {
46 panic("commitreveal: data cannot be empty")
47 }
48 return hashCommit(salt, data)
49}
50
51// Verify checks whether (salt, data) reveals commitHash. Unlike Commit,
52// this never panics -- a reveal attempt is untrusted input from
53// whoever's revealing, and a malformed one (e.g. a short salt someone
54// hand-crafted) should just fail verification, not abort the caller's
55// whole transaction.
56func Verify(commitHash [32]byte, salt, data []byte) bool {
57 if len(salt) < minSaltLen || len(data) == 0 {
58 return false
59 }
60 return hashCommit(salt, data) == commitHash
61}