Search Apps Documentation Source Content File Folder Download Copy Actions Download

merkle.gno

1.66 Kb · 72 lines
 1package memba_collections
 2
 3import (
 4	"crypto/sha256"
 5	"encoding/hex"
 6)
 7
 8// Merkle allowlist verifier (frozen, §5).
 9//   leaf = sha256(0x00 ‖ addr.String() ‖ ":" ‖ itoa(maxQty))   (bech32 string form, S-5)
10//   node = sha256(0x01 ‖ sortedConcat(l, r))                    (domain-separated, sorted-pair)
11// The sorted pair makes the proof order-independent and second-preimage safe.
12
13func leafHash(addr address, maxQty int64) []byte {
14	data := append([]byte{0x00}, []byte(addr.String()+":"+itoa(maxQty))...)
15	h := sha256.Sum256(data)
16	return h[:]
17}
18
19func parentHash(a, b []byte) []byte {
20	var lo, hi []byte
21	if compareBytes(a, b) <= 0 {
22		lo, hi = a, b
23	} else {
24		lo, hi = b, a
25	}
26	data := make([]byte, 0, 1+len(lo)+len(hi))
27	data = append(data, 0x01)
28	data = append(data, lo...)
29	data = append(data, hi...)
30	h := sha256.Sum256(data)
31	return h[:]
32}
33
34func compareBytes(a, b []byte) int {
35	n := len(a)
36	if len(b) < n {
37		n = len(b)
38	}
39	for i := 0; i < n; i++ {
40		if a[i] != b[i] {
41			if a[i] < b[i] {
42				return -1
43			}
44			return 1
45		}
46	}
47	if len(a) < len(b) {
48		return -1
49	}
50	if len(a) > len(b) {
51		return 1
52	}
53	return 0
54}
55
56// verifyAllowlist reconstructs the leaf from (addr, maxQty), folds the proof,
57// and checks the computed root against the hex-encoded `root`. A single-leaf
58// tree (empty proof) is valid when leaf == root. An empty root rejects.
59func verifyAllowlist(root string, addr address, maxQty int64, proof []string) bool {
60	if root == "" {
61		return false
62	}
63	cur := leafHash(addr, maxQty)
64	for _, sibHex := range proof {
65		sib, err := hex.DecodeString(sibHex)
66		if err != nil {
67			return false
68		}
69		cur = parentHash(cur, sib)
70	}
71	return hex.EncodeToString(cur) == root
72}