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.

render.gno

5.65 Kb · 152 lines
  1package merkledrop
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"gno.land/p/nym-moultest123/x/merkle/v0"
  8	"gno.land/p/nt/avl/v0"
  9)
 10
 11// demoAllocations is the example drop seeded at deploy, so the realm renders
 12// working proofs instead of an empty page. A real drop calls SetDrop with a
 13// root computed off chain and stores no allocations at all.
 14var demoAllocations = []Allocation{
 15	{Address: "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", Amount: 100},
 16	{Address: "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj", Amount: 250},
 17	{Address: "g1manfred47kzduec920z88wfr64ylksmdcedlf5", Amount: 500},
 18	{Address: "g1sss9uxef4l6lwc0mxq8n5v0e4vqpml7c39cxq2", Amount: 750},
 19}
 20
 21// seed installs the example drop. Also called by the example tests: realm
 22// globals persist for a whole test binary and examples run after every Test,
 23// so a pinned Render must start from a known state.
 24func seed() {
 25	demo = demoAllocations
 26	tree := demoTree()
 27	root = tree.Root()
 28	total = len(demo)
 29	closesAt = 0
 30	claimed, paid = avl.Tree{}, 0
 31}
 32
 33func demoTree() *merkle.Tree {
 34	leaves := make([][]byte, len(demo))
 35	for i, a := range demo {
 36		leaves[i] = []byte(Leaf(i, a.Address, a.Amount))
 37	}
 38	return merkle.New(leaves)
 39}
 40
 41// DemoProof returns the proof for one seeded allocation, so the drop can be
 42// exercised end to end without an off-chain generator. It is empty once a real
 43// drop replaces the seeded one.
 44func DemoProof(index int) string {
 45	if index < 0 || index >= len(demo) {
 46		return ""
 47	}
 48	p, err := demoTree().Proof(index)
 49	if err != nil {
 50		return ""
 51	}
 52	return p.Hex()
 53}
 54
 55// Render serves the drop overview at "" and one allocation at "proof/<index>".
 56func Render(path string) string {
 57	path = strings.TrimSpace(path)
 58	if strings.HasPrefix(path, "proof/") {
 59		return renderProof(strings.TrimPrefix(path, "proof/"))
 60	}
 61	return renderIndex()
 62}
 63
 64func renderIndex() string {
 65	var b strings.Builder
 66	b.WriteString("# MerkleDrop v1\n\n")
 67	b.WriteString("A Merkle root gates an airdrop that pays real ugnot. Successor to\n")
 68	b.WriteString("[v0](/r/nym-moultest123/x/daily/merkledrop/v0), which moved no coins and whose proof scheme was\n")
 69	b.WriteString("safe only by accident of encoding lengths.\n\n")
 70
 71	b.WriteString("## Drop\n\n")
 72	b.WriteString("| Field | Value |\n|---|---|\n")
 73	if len(root) == 0 {
 74		b.WriteString("| Root | _none configured_ |\n")
 75	} else {
 76		b.WriteString("| Root | `" + Root() + "` |\n")
 77	}
 78	b.WriteString("| Allocations | " + strconv.Itoa(total) + " |\n")
 79	b.WriteString("| Closes at height | " + closesLabel() + " |\n")
 80	b.WriteString("| Claimed | " + strconv.Itoa(Claims()) + " |\n")
 81	b.WriteString("| Paid out | " + strconv.FormatInt(Paid(), 10) + " ugnot |\n")
 82	b.WriteString("| Funding address | `" + Address().String() + "` |\n\n")
 83
 84	b.WriteString("## How it differs from v0\n\n")
 85	b.WriteString("1. **Domain separated leaves.** v0 hashed leaves bare and combined nodes\n")
 86	b.WriteString("   commutatively, so an inner-node hash was also a valid leaf hash. It was not\n")
 87	b.WriteString("   exploitable, but only because a v0 leaf preimage tops out at 61 bytes while an\n")
 88	b.WriteString("   inner preimage is always 64. v1 tags leaves `0x00` and inner nodes `0x01`.\n")
 89	b.WriteString("2. **Proofs bound to a position.** v0 folded a sibling list of any length. v1\n")
 90	b.WriteString("   carries index and total, rebuilds the tree shape, and caps the depth.\n")
 91	b.WriteString("3. **A settable root and a closing height.** v0's root was a `const`.\n")
 92	b.WriteString("4. **Real coins.** v0 kept a `uint64` ledger and moved nothing.\n\n")
 93
 94	b.WriteString("## Leaf encoding\n\n")
 95	b.WriteString("Reproduce this off chain to rebuild the tree:\n\n")
 96	b.WriteString("```\n")
 97	b.WriteString("leaf = \"" + leafPrefix + "|<index>|<address>|<amount>\"\n")
 98	b.WriteString("tree = Tendermint simple tree, leaf 0x00, inner 0x01\n")
 99	b.WriteString("```\n\n")
100
101	if len(demo) == 0 {
102		b.WriteString("## Allocations\n\n")
103		b.WriteString("A live drop commits only its root, so there is nothing to list here.\n")
104		return b.String()
105	}
106	b.WriteString("## Example allocations\n\n")
107	b.WriteString("Seeded at deploy so the drop can be exercised without a generator. A live drop\n")
108	b.WriteString("commits only a root and stores none of this.\n\n")
109	b.WriteString("| # | Address | Amount | Claimed |\n|---|---|---|---|\n")
110	for i, a := range demo {
111		b.WriteString("| [" + strconv.Itoa(i) + "](/r/nym-moultest123/x/daily/merkledrop/v1:proof/" + strconv.Itoa(i) + ") | `" +
112			a.Address.String() + "` | " + strconv.FormatInt(a.Amount, 10) + " | " + yesNo(HasClaimed(i)) + " |\n")
113	}
114	return b.String()
115}
116
117func renderProof(raw string) string {
118	i, err := strconv.Atoi(raw)
119	if err != nil || i < 0 || i >= len(demo) {
120		return "# MerkleDrop v1\n\nNo allocation `" + raw + "`.\n"
121	}
122	a := demo[i]
123	var b strings.Builder
124	b.WriteString("# MerkleDrop v1: allocation " + strconv.Itoa(i) + "\n\n")
125	b.WriteString("| Field | Value |\n|---|---|\n")
126	b.WriteString("| Index | " + strconv.Itoa(i) + " |\n")
127	b.WriteString("| Total | " + strconv.Itoa(total) + " |\n")
128	b.WriteString("| Address | `" + a.Address.String() + "` |\n")
129	b.WriteString("| Amount | " + strconv.FormatInt(a.Amount, 10) + " ugnot |\n")
130	b.WriteString("| Claimed | " + yesNo(HasClaimed(i)) + " |\n\n")
131	b.WriteString("## Leaf\n\n")
132	b.WriteString("```\n" + Leaf(i, a.Address, a.Amount) + "\n```\n\n")
133	b.WriteString("## Proof\n\n")
134	b.WriteString("```\n" + DemoProof(i) + "\n```\n\n")
135	b.WriteString("Only `" + a.Address.String() + "` can spend it: the address is inside the leaf, so\n")
136	b.WriteString("the proof verifies for no one else.\n")
137	return b.String()
138}
139
140func closesLabel() string {
141	if closesAt == 0 {
142		return "never"
143	}
144	return strconv.FormatInt(closesAt, 10)
145}
146
147func yesNo(b bool) string {
148	if b {
149		return "yes"
150	}
151	return "no"
152}