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

merkle.gno

2.44 Kb · 124 lines
  1// Package merkle verifies sorted-pair SHA-256 Merkle proofs with domain-separated leaves.
  2package merkle
  3
  4import (
  5	"bytes"
  6	"crypto/sha256"
  7	"encoding/hex"
  8	"errors"
  9	"strings"
 10)
 11
 12const MaxDepth = 32
 13
 14type Hash = [32]byte
 15
 16var ErrHash = errors.New("launchpad/merkle: hash must be 64 hex characters")
 17
 18func Leaf(data []byte) Hash {
 19	buf := make([]byte, 0, len(data)+1)
 20	buf = append(buf, 0x00)
 21	buf = append(buf, data...)
 22	return sha256.Sum256(buf)
 23}
 24
 25func node(a, b Hash) Hash {
 26	if bytes.Compare(a[:], b[:]) > 0 {
 27		a, b = b, a
 28	}
 29	buf := make([]byte, 0, 65)
 30	buf = append(buf, 0x01)
 31	buf = append(buf, a[:]...)
 32	buf = append(buf, b[:]...)
 33	return sha256.Sum256(buf)
 34}
 35
 36func Root(leaves []Hash) Hash {
 37	if len(leaves) == 0 {
 38		panic("launchpad/merkle: no leaves")
 39	}
 40	level := append([]Hash{}, leaves...)
 41	for len(level) > 1 {
 42		next := make([]Hash, 0, (len(level)+1)/2)
 43		for i := 0; i < len(level); i += 2 {
 44			if i+1 == len(level) {
 45				next = append(next, level[i])
 46			} else {
 47				next = append(next, node(level[i], level[i+1]))
 48			}
 49		}
 50		level = next
 51	}
 52	return level[0]
 53}
 54
 55func Proof(leaves []Hash, index int) []Hash {
 56	if index < 0 || index >= len(leaves) {
 57		panic("launchpad/merkle: index out of range")
 58	}
 59	level := append([]Hash{}, leaves...)
 60	proof := []Hash{}
 61	for len(level) > 1 {
 62		next := make([]Hash, 0, (len(level)+1)/2)
 63		for i := 0; i < len(level); i += 2 {
 64			if i+1 == len(level) {
 65				next = append(next, level[i])
 66				continue
 67			}
 68			if i == index {
 69				proof = append(proof, level[i+1])
 70			} else if i+1 == index {
 71				proof = append(proof, level[i])
 72			}
 73			next = append(next, node(level[i], level[i+1]))
 74		}
 75		index /= 2
 76		level = next
 77	}
 78	return proof
 79}
 80
 81func Verify(root, leaf Hash, proof []Hash) bool {
 82	if len(proof) > MaxDepth {
 83		return false
 84	}
 85	h := leaf
 86	for _, p := range proof {
 87		h = node(h, p)
 88	}
 89	return h == root
 90}
 91
 92func ParseHash(s string) (Hash, error) {
 93	var h Hash
 94	if len(s) != 64 {
 95		return h, ErrHash
 96	}
 97	b, err := hex.DecodeString(s)
 98	if err != nil {
 99		return h, ErrHash
100	}
101	copy(h[:], b)
102	return h, nil
103}
104
105func ParseProof(s string) ([]Hash, error) {
106	if s == "" {
107		return []Hash{}, nil
108	}
109	parts := strings.Split(s, ",")
110	if len(parts) > MaxDepth {
111		return nil, errors.New("launchpad/merkle: proof too long")
112	}
113	out := make([]Hash, len(parts))
114	for i, p := range parts {
115		h, err := ParseHash(p)
116		if err != nil {
117			return nil, err
118		}
119		out[i] = h
120	}
121	return out, nil
122}
123
124func FormatHash(h Hash) string { return hex.EncodeToString(h[:]) }