example_test.gno
4.61 Kb · 104 lines
1package reaper
2
3import (
4 "strings"
5
6 "gno.land/p/moul/x/storagecost/v0"
7 "gno.land/p/nt/ufmt/v0"
8)
9
10// Every Example in this file is a *query*, and that is a constraint rather
11// than a choice: an example function takes no arguments, so it never receives
12// a `cur realm` and can never call Post, Reap or Compact. What is left is
13// exactly the surface a reader of the live realm has without a key, so each
14// one carries the `vm/qeval` line that produces the same answer on chain:
15//
16// gnokey query vm/qeval -remote https://rpc.gno.land \
17// -data 'gno.land/r/moul/x/reaper/v0.<expr>'
18//
19// The board is empty by the time these run, because every Test in this
20// package drains what it posted and examples run after all of them.
21
22// ExampleRender pins the page's skeleton: the title, the two sections that are
23// always present, and the footer crediting the two packages that own the parts
24// this realm does not.
25//
26// vm/qrender gno.land/r/moul/x/reaper/v0:
27//
28// It deliberately prints only the invariant lines. Everything interesting on
29// the page is priced from the current block height and the notes outstanding,
30// and an Example runs after every Test in the package and sees whatever state
31// they left. The variable half is pinned by TestRenderShowsTheBountyAndTheReapLink
32// instead, which controls the board first.
33func ExampleRender() {
34 for _, line := range strings.Split(Render(""), "\n") {
35 switch {
36 case strings.HasPrefix(line, "# "),
37 strings.HasPrefix(line, "## "),
38 strings.HasPrefix(line, "A noticeboard"),
39 strings.HasPrefix(line, "The arithmetic is"):
40 print(line + "\n")
41 }
42 }
43 // Output:
44 // # Reaper
45 // A noticeboard whose garbage is a standing bounty. Posting a note locks a storage deposit. Once the note expires, anyone can delete it and the chain refunds that deposit **to whoever signs the deleting transaction**.
46 // ## On the table right now
47 // ## Board
48 // The arithmetic is [p/moul/x/storagecost](/p/moul/x/storagecost/v0); the storage is [p/moul/ulist](/p/moul/ulist/v1), whose `Compact` reclaims dead nodes without moving a live index. Byte figures on this page are estimates from payload length: no stdlib call exposes a realm's real locked storage.
49}
50
51// ExampleReapable is the poll a reaping bot runs before it spends anything:
52// three counters, three queries, no key and no gas.
53//
54// vm/qeval gno.land/r/moul/x/reaper/v0.Live()
55// vm/qeval gno.land/r/moul/x/reaper/v0.Reapable()
56// vm/qeval gno.land/r/moul/x/reaper/v0.Compactable()
57//
58// Reapable and Compactable are separate numbers because reaping and
59// compacting are separate transactions, and compaction returns nothing at all
60// while a live note still sits below the dead ones. A bot that sees
61// `reapable 0, compactable 0` should not sign anything, which is the answer
62// here.
63func ExampleReapable() {
64 print(ufmt.Sprintf("live %d, reapable %d, compactable %d\n",
65 Live(), Reapable(), Compactable()))
66 // Output:
67 // live 0, reapable 0, compactable 0
68}
69
70// ExampleBounty is the single query that decides whether reaping is worth
71// doing, and on an empty board it has to refuse to advertise a profit.
72//
73// vm/qeval gno.land/r/moul/x/reaper/v0.Bounty().String()
74//
75// Fee is what the reaping transaction costs at the floor gas price, and the
76// chain charges it whether or not anything is freed. BreakEven turns that fee
77// back into bytes: free fewer than 50 and the reaper is paying to tidy up.
78// That threshold, not the size of the board, is what makes the mechanism a
79// market rather than a chore.
80func ExampleBounty() {
81 print(Bounty().String() + "\n")
82 // Output:
83 // 0 bytes, refunds 0 GNOT against 0.005 GNOT of gas, break-even 50 bytes: not worth it yet
84}
85
86// ExampleBounty_worth is the same query answered by a board worth draining:
87// ten expired notes of a kilobyte each.
88//
89// It recomputes the quote instead of posting, because an example has no
90// `cur realm` to post with. The arithmetic is the one Bounty applies, so the
91// numbers are the ones the live query would return with that board on it.
92//
93// This is the whole concept in two lines. The poster locked 1.8944 GNOT to
94// occupy that space; a stranger who never posted anything spends 0.005 GNOT of
95// gas to delete it and the chain hands them the deposit. Nobody minted a
96// token, and no pool was funded.
97func ExampleBounty_worth() {
98 q := storagecost.EvaluateAtFloor(storagecost.EstimateBytes(10*1024), gasWantedReap)
99 print(q.String() + "\n")
100 print(ufmt.Sprintf("the reaper is paid %dx what the transaction costs\n", q.Refund/q.Fee))
101 // Output:
102 // 18944 bytes, refunds 1.8944 GNOT against 0.005 GNOT of gas, break-even 50 bytes: worth 1.8894 GNOT
103 // the reaper is paid 378x what the transaction costs
104}