PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

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/p/moul/x/daily/commitreveal/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / daily / commitreveal
Files
3 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/daily/commitreveal/v0
gno
0.9

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • commitreveal.gnogno
commitreveal.gnogno
1// Package commitreveal implements the commit-then-reveal scheme as a pure,2// reusable package.3//4// The problem it solves: anything submitted to a chain is public before it is5// executed, so a naive sealed-bid auction or simultaneous-move game lets the6// last player read everyone else's move and win for free. Commit-reveal splits7// the action in two — first publish H(value || salt), later publish the value8// and salt. The commitment binds you to a choice without disclosing it.9//10// The SALT is not optional and this package refuses to let a caller skip it.11// Without one, a commitment over a small domain is trivially brute-forced: a12// rock-paper-scissors move has three possible hashes, so hashing all three13// breaks the scheme entirely. MinSaltLen is enforced at commit time rather than14// left as advice in a comment.15//16// Verification is CONSTANT-TIME over the digest. A short-circuiting comparison17// leaks, through timing, how many leading bytes of a guess were right, which is18// enough to reconstruct a commitment byte by byte.19//20// This package computes and checks commitments; it stores nothing and knows21// nothing about phases or deadlines. The realm owns that.22//23// A live demo of this package is at24// [r/moul/x/daily/commitrevealdemo](/r/moul/x/daily/commitrevealdemo/v0).25package commitreveal2627import (28	"crypto/sha256"29	"encoding/hex"30	"errors"31)3233// MinSaltLen is the shortest salt accepted. Short salts make a small-domain34// commitment brute-forceable, which defeats the whole scheme.35const MinSaltLen = 163637// MaxValueLen bounds the committed value so gas stays predictable.38const MaxValueLen = 40963940var (41	ErrShortSalt   = errors.New("commitreveal: salt is shorter than MinSaltLen")42	ErrLongValue   = errors.New("commitreveal: value exceeds MaxValueLen")43	ErrMismatch    = errors.New("commitreveal: reveal does not match the commitment")44	ErrBadHexDigest = errors.New("commitreveal: commitment is not a valid hex digest")45)4647// DigestLen is the length in bytes of a commitment digest (SHA-256).48const DigestLen = 324950// Commit returns the hex-encoded commitment for value and salt.51//52// The salt is length-prefixed rather than simply concatenated: with plain53// concatenation, ("ab","cd") and ("a","bcd") hash identically, so one54// commitment could be opened two different ways.55func Commit(value, salt string) (string, error) {56	if len(salt) < MinSaltLen {57		return "", ErrShortSalt58	}59	if len(value) > MaxValueLen {60		return "", ErrLongValue61	}62	return hex.EncodeToString(digest(value, salt)), nil63}6465// MustCommit is Commit, panicking on invalid input. For tests and for callers66// that have already validated.67func MustCommit(value, salt string) string {68	c, err := Commit(value, salt)69	if err != nil {70		panic(err.Error())71	}72	return c73}7475// Verify reports whether value and salt open the given commitment. The digest76// comparison is constant-time.77func Verify(commitment, value, salt string) bool {78	if len(salt) < MinSaltLen || len(value) > MaxValueLen {79		return false80	}81	want, err := hex.DecodeString(commitment)82	if err != nil || len(want) != DigestLen {83		return false84	}85	return equalConstantTime(want, digest(value, salt))86}8788// Open verifies and reports why it failed, for callers wanting a reason rather89// than a bool.90func Open(commitment, value, salt string) error {91	if len(salt) < MinSaltLen {92		return ErrShortSalt93	}94	if len(value) > MaxValueLen {95		return ErrLongValue96	}97	want, err := hex.DecodeString(commitment)98	if err != nil || len(want) != DigestLen {99		return ErrBadHexDigest100	}101	if !equalConstantTime(want, digest(value, salt)) {102		return ErrMismatch103	}104	return nil105}106107// ValidCommitment reports whether s is well-formed as a commitment: hex, and108// exactly DigestLen bytes. It says nothing about what it commits to.109func ValidCommitment(s string) bool {110	b, err := hex.DecodeString(s)111	return err == nil && len(b) == DigestLen112}113114// digest computes SHA-256 over a length-prefixed encoding of value and salt.115// gno's crypto/sha256 exposes only Sum256, so the message is assembled first116// rather than streamed through a hash.Hash.117func digest(value, salt string) []byte {118	buf := make([]byte, 0, 8+len(value)+len(salt))119	buf = append(buf, lengthPrefix(len(value))...)120	buf = append(buf, value...)121	buf = append(buf, lengthPrefix(len(salt))...)122	buf = append(buf, salt...)123	sum := sha256.Sum256(buf)124	return sum[:]125}126127// lengthPrefix encodes n as 4 big-endian bytes.128func lengthPrefix(n int) []byte {129	return []byte{130		byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n),131	}132}133134// equalConstantTime compares two byte slices without short-circuiting, so the135// time taken does not reveal how many leading bytes matched.136func equalConstantTime(a, b []byte) bool {137	if len(a) != len(b) {138		return false139	}140	var diff byte141	for i := range a {142		diff |= a[i] ^ b[i]143	}144	return diff == 0145}146

Functions

not supported for pure packages by the node (vm/qfuncs)

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