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.

hash.gno

3.57 Kb · 111 lines
  1package safe
  2
  3import (
  4	"bytes"
  5	"crypto/sha256"
  6	"encoding/hex"
  7	"strconv"
  8	"strings"
  9)
 10
 11// MaxMerkleDepth bounds a proof: 2^32 leaves is far beyond any allowlist.
 12const MaxMerkleDepth = 32
 13
 14func Sha256Hex(s string) string {
 15	sum := sha256.Sum256([]byte(s))
 16	return hex.EncodeToString(sum[:])
 17}
 18
 19// --- commit–reveal v2 (design §4.2) --------------------------------------
 20//
 21// Every formula is domain-separated by platform, purpose, version, chain and
 22// collection, so a secret or commit is useless anywhere else (RA M-5). They
 23// live here, not in core, so the frontend's verifier, the beacon service and
 24// the tests all port one definition.
 25
 26// Commit is the value a creator ("creator") or the beacon ("beacon") commits
 27// to for collection cid on chain chainID.
 28func Commit(chainID, cid, role, secret string) string {
 29	return Sha256Hex(Domain + "|commit|v2|" + chainID + "|" + cid + "|" + role + "|" + secret)
 30}
 31
 32// InitialEntropy seeds a collection's mint-entropy accumulator.
 33func InitialEntropy(chainID, cid string) string {
 34	return Sha256Hex(Domain + "|acc|v2|" + chainID + "|" + cid)
 35}
 36
 37// MixEntropy folds one mint call into the accumulator. The inputs are
 38// public, but none existed when the secrets were committed, which is what
 39// stops a committer from choosing the map their own mints receive.
 40func MixEntropy(acc string, to address, height, first, last int64) string {
 41	return Sha256Hex(acc + "|" + to.String() + "|" + strconv.FormatInt(height, 10) + "|" +
 42		strconv.FormatInt(first, 10) + "-" + strconv.FormatInt(last, 10))
 43}
 44
 45// Salt binds both secrets ("" when not revealed) to the final accumulator.
 46func Salt(cid, creatorSecret, beaconSecret, acc string) string {
 47	return Sha256Hex(Domain + "|salt|v2|" + cid + "|" + creatorSecret + "|" + beaconSecret + "|" + acc)
 48}
 49
 50// Seed is the per-token engine seed once the salt is final.
 51func Seed(cid, tid, salt string) string {
 52	return Sha256Hex(Domain + "|seed|v1|" + cid + "|" + tid + "|" + salt)
 53}
 54
 55// --- allowlist merkle proofs (design §4.6) -------------------------------
 56
 57// AllowlistLeaf is the leaf for addr in collection cid's allowlist tree.
 58func AllowlistLeaf(cid string, addr address) string {
 59	return Sha256Hex(Domain + "|al|v1|" + cid + "|" + addr.String())
 60}
 61
 62// MerkleVerify checks a sorted-pair sha256 proof ("hex,hex,…", leaf to
 63// root). An empty proof means the tree has a single leaf equal to the root.
 64// Malformed input is simply false: proofs come from untrusted callers.
 65func MerkleVerify(rootHex, leafHex, proof string) bool {
 66	root, err := hex.DecodeString(rootHex)
 67	if err != nil || len(root) != 32 {
 68		return false
 69	}
 70	node, err := hex.DecodeString(leafHex)
 71	if err != nil || len(node) != 32 {
 72		return false
 73	}
 74	if proof != "" {
 75		steps := strings.Split(proof, ",")
 76		if len(steps) > MaxMerkleDepth {
 77			return false
 78		}
 79		for _, step := range steps {
 80			sibling, err := hex.DecodeString(step)
 81			if err != nil || len(sibling) != 32 {
 82				return false
 83			}
 84			node = hashPair(node, sibling)
 85		}
 86	}
 87	return bytes.Equal(node, root)
 88}
 89
 90// hashPair hashes the two children in byte order, so a proof needs no
 91// left/right flags.
 92func hashPair(a, b []byte) []byte {
 93	if bytes.Compare(b, a) < 0 {
 94		a, b = b, a
 95	}
 96	buf := make([]byte, 0, 64)
 97	buf = append(buf, a...)
 98	buf = append(buf, b...)
 99	sum := sha256.Sum256(buf)
100	return sum[:]
101}
102
103// MerkleParent is hashPair on hex nodes, for tests and the off-chain builder.
104func MerkleParent(aHex, bHex string) string {
105	a, errA := hex.DecodeString(aHex)
106	b, errB := hex.DecodeString(bHex)
107	if errA != nil || errB != nil {
108		panic("MerkleParent: invalid hex")
109	}
110	return hex.EncodeToString(hashPair(a, b))
111}