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}