Kind Realm (renderable)
Name v0
Namespace moul / vesting
Exported functions 7
Module gno.land/r/moul/vesting/v0
gno 0.9 ⧉
1 package vesting 2 3 import ( 4 "strings" 5 "testing" 6 7 "chain/runtime" 8 Count () int
Declare (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, original int64, start int64, end int64, delayed bool)
Forget (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
LockedOf (addr string) (locked int64, ok bool)
Render (path string) string
ScheduleOf (addr string) (original int64, start int64, end int64, delayed bool, ok bool)
SpendableOf (addr string) (spendable int64, ok bool)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
9 "gno.land/p/nt/uassert/v0"
10 "gno.land/p/nt/testutils/v0"
11 )
12
13 const (
14 mainnetStart int64 = 1789225200
15 mainnetEnd int64 = 1852383600
16 moul = address( "g1manfred47kzduec920z88wfr64ylksmdcedlf5" )
17 )
18
19 // TestSeededSurvivesInit is the property the seed exists for: this realm is
20 // private = true, so a redeploy re-runs init() and wipes everything else. The
21 // one schedule the page is about has to come back without a transaction.
22 func TestSeededSurvivesInit(t *testing.T) {
23 o, s, e, delayed, ok := ScheduleOf(moul)
24 uassert.True(t, ok, "moul's schedule is missing after init" )
25 uassert.Equal(t, int64 ( 106560000000 ), o)
26 uassert.Equal(t, mainnetStart, s)
27 uassert.Equal(t, mainnetEnd, e)
28 uassert.False(t, delayed)
29 }
30
31 // TestDeclareWritesOnlyYourOwnRow is the entire trust model. A caller cannot
32 // name an address, so it cannot describe one but its own.
33 func TestDeclareWritesOnlyYourOwnRow(cur realm, t *testing.T) {
34 alice := testutils.TestAddress( "alice" )
35 bob := testutils.TestAddress( "bob" )
36
37 testing.SetRealm(testing.NewUserRealm(alice))
38 Declare(cross(cur), 500 , 100 , 200 , false )
39
40 o, s, e, delayed, ok := ScheduleOf(alice)
41 uassert.True(t, ok)
42 uassert.Equal(t, int64 ( 500 ), o)
43 uassert.Equal(t, int64 ( 100 ), s)
44 uassert.Equal(t, int64 ( 200 ), e)
45 uassert.False(t, delayed)
46
47 // Nothing alice did touched bob.
48 _, _, _, _, ok = ScheduleOf(bob)
49 uassert.False(t, ok, "declaring for alice wrote a row for bob" )
50 }
51
52 func TestDeclareReplacesAndForgetRemoves(cur realm, t *testing.T) {
53 carol := testutils.TestAddress( "carol" )
54
55 testing.SetRealm(testing.NewUserRealm(carol))
56 Declare(cross(cur), 500 , 100 , 200 , false )
57 testing.SetRealm(testing.NewUserRealm(carol))
58 Declare(cross(cur), 900 , 10 , 20 , true )
59
60 o, s, e, delayed, ok := ScheduleOf(carol)
61 uassert.True(t, ok)
62 uassert.Equal(t, int64 ( 900 ), o)
63 uassert.Equal(t, int64 ( 10 ), s)
64 uassert.Equal(t, int64 ( 20 ), e)
65 uassert.True(t, delayed, "the delayed flag did not survive" )
66
67 testing.SetRealm(testing.NewUserRealm(carol))
68 Forget(cross(cur))
69 _, _, _, _, ok = ScheduleOf(carol)
70 uassert.False(t, ok, "Forget left the row behind" )
71 }
72
73 func TestForgetWithoutADeclarationAborts(cur realm, t *testing.T) {
74 dave := testutils.TestAddress( "dave" )
75 testing.SetRealm(testing.NewUserRealm(dave))
76 uassert.AbortsContains(t, cur, "nothing declared" , func () { Forget(cross(cur)) })
77 }
78
79 // TestSeededRowIsNotWritable: the seeds are source, so a signer who happens to
80 // hold a seeded address still cannot rewrite it from a transaction. Otherwise
81 // a redeploy would silently revert whatever they set.
82 func TestSeededRowIsNotWritable(cur realm, t *testing.T) {
83 testing.SetRealm(testing.NewUserRealm(moul))
84 uassert.AbortsContains(t, cur, "seeded in the source" , func () {
85 Declare(cross(cur), 1 , 2 , 3 , false )
86 })
87 testing.SetRealm(testing.NewUserRealm(moul))
88 uassert.AbortsContains(t, cur, "seeded in the source" , func () { Forget(cross(cur)) })
89 }
90
91 func TestDeclareRejectsAnImpossibleSchedule(cur realm, t *testing.T) {
92 erin := testutils.TestAddress( "erin" )
93 testing.SetRealm(testing.NewUserRealm(erin))
94 uassert.AbortsContains(t, cur, "start time must be before end time" , func () {
95 Declare(cross(cur), 100 , 200 , 100 , false )
96 })
97 testing.SetRealm(testing.NewUserRealm(erin))
98 uassert.AbortsContains(t, cur, "cannot be negative" , func () {
99 Declare(cross(cur), - 1 , 0 , 100 , false )
100 })
101 }
102
103 // TestDeclareZeroMeansNoSchedule lets an address say "nothing of mine is
104 // locked", which is a real and useful claim.
105 func TestDeclareZeroMeansNoSchedule(cur realm, t *testing.T) {
106 frank := testutils.TestAddress( "frank" )
107 testing.SetRealm(testing.NewUserRealm(frank))
108 Declare(cross(cur), 0 , 0 , 0 , false )
109
110 o, _, _, _, ok := ScheduleOf(frank)
111 uassert.True(t, ok)
112 uassert.Equal(t, int64 ( 0 ), o)
113
114 spend, known := SpendableOf(frank)
115 uassert.True(t, known)
116 uassert.Equal(t, balanceOf(frank), spend)
117 }
118
119 // TestUnknownAddressReportsItsBalanceAndSaysSo: the balance is real, the
120 // spendable figure is not, and the caller is told which is which.
121 func TestUnknownAddressReportsItsBalanceAndSaysSo(t *testing.T) {
122 stranger := testutils.TestAddress( "stranger" )
123
124 spend, ok := SpendableOf(stranger)
125 uassert.False(t, ok, "an unknown address must not claim a known schedule" )
126 uassert.Equal(t, balanceOf(stranger), spend)
127
128 locked, ok := LockedOf(stranger)
129 uassert.False(t, ok)
130 uassert.Equal(t, int64 ( 0 ), locked)
131 }
132
133 func TestCountTracksTheRegistry(cur realm, t *testing.T) {
134 before := Count()
135 grace := testutils.TestAddress( "grace" )
136 testing.SetRealm(testing.NewUserRealm(grace))
137 Declare(cross(cur), 10 , 1 , 2 , false )
138 uassert.Equal(t, before+ 1 , Count())
139 testing.SetRealm(testing.NewUserRealm(grace))
140 Forget(cross(cur))
141 uassert.Equal(t, before, Count())
142 }
143
144 // TestLockedIsCappedByTheBalance: an account that spent while its coins were
145 // free can owe the schedule more than it now holds. The honest answer is that
146 // nothing moves, never a negative spendable.
147 func TestLockedIsCappedByTheBalance(cur realm, t *testing.T) {
148 poor := testutils.TestAddress( "poor" )
149 testing.SetRealm(testing.NewUserRealm(poor))
150 Declare(cross(cur), 1000000000000 , mainnetStart, mainnetEnd, false )
151
152 locked, ok := LockedOf(poor)
153 uassert.True(t, ok)
154 uassert.Equal(t, balanceOf(poor), locked, "locked must not exceed the balance" )
155
156 spend, _ := SpendableOf(poor)
157 uassert.Equal(t, int64 ( 0 ), spend)
158 uassert.True(t, spend >= 0 , "spendable went negative" )
159 }
160
161 func TestIsAddr(t *testing.T) {
162 uassert.True(t, isAddr( "g1manfred47kzduec920z88wfr64ylksmdcedlf5" ))
163 uassert.False(t, isAddr( "" ))
164 uassert.False(t, isAddr( "g1manfred" ))
165 uassert.False(t, isAddr( "x1manfred47kzduec920z88wfr64ylksmdcedlf5" ))
166 uassert.False(t, isAddr( "g1MANFRED47kzduec920z88wfr64ylksmdcedlf5" ))
167 uassert.False(t, isAddr( "g1manfred47kzduec920z88wfr64ylksmdcedlf5x" ))
168 }
169
170 func TestGnotFormatting(t *testing.T) {
171 uassert.Equal(t, "0.000000 GNOT" , gnot( 0 ))
172 uassert.Equal(t, "0.000001 GNOT" , gnot( 1 ))
173 uassert.Equal(t, "1.000000 GNOT" , gnot( 1000000 ))
174 uassert.Equal(t, "106560.000000 GNOT" , gnot( 106560000000 ))
175 uassert.Equal(t, "1.234567 GNOT" , gnot( 1234567 ))
176 uassert.Equal(t, "-1.500000 GNOT" , gnot(- 1500000 ))
177 }
178
179 func TestPermilleFormatting(t *testing.T) {
180 uassert.Equal(t, "0.0%" , permille( 0 ))
181 uassert.Equal(t, "5.0%" , permille( 50 ))
182 uassert.Equal(t, "49.9%" , permille( 499 ))
183 uassert.Equal(t, "100.0%" , permille( 1000 ))
184 }
185
186 func TestDurationFormatting(t *testing.T) {
187 uassert.Equal(t, "complete" , duration( 0 ))
188 uassert.Equal(t, "complete" , duration(- 1 ))
189 uassert.Equal(t, "0h" , duration( 60 ))
190 uassert.Equal(t, "1h" , duration( 3600 ))
191 uassert.Equal(t, "1d 0h" , duration( 86400 ))
192 uassert.Equal(t, "2d 3h" , duration( 2 * 86400 + 3 * 3600 ))
193 }
194
195 // TestCivilDates pins the date arithmetic against dates that are known exactly,
196 // including the two ends of the real mainnet term and a leap day.
197 func TestCivilDates(t *testing.T) {
198 uassert.Equal(t, "1970-01-01" , tstamp( 0 ))
199 uassert.Equal(t, "2000-02-29" , tstamp( 951782400 ))
200 uassert.Equal(t, "2026-09-12" , tstamp(mainnetStart))
201 uassert.Equal(t, "2028-09-12" , tstamp(mainnetEnd))
202 }
203
204 // TestRenderShapes walks the three routes the page serves and checks each one
205 // names its source, which is the promise the whole realm makes.
206 func TestRenderShapes(t *testing.T) {
207 index := Render( "" )
208 uassert.True(t, strings.Contains(index, "# Vesting" ), "index has no title" )
209 uassert.True(t, strings.Contains(index, "no realm can read one" ),
210 "index does not say the schedule is supplied" )
211
212 seeded := Render(moul.String())
213 uassert.True(t, strings.Contains(seeded, "seeded in this realm's source" ),
214 "a seeded row does not say where it came from" )
215 uassert.True(t, strings.Contains(seeded, "spendable now" ))
216
217 query := Render(moul.String() + "?o=1000000&s=100&e=200" )
218 uassert.True(t, strings.Contains(query, "supplied in the URL by you" ),
219 "a query schedule does not say it was supplied" )
220 uassert.True(t, strings.Contains(query, "Nothing was stored." ))
221
222 unknown := Render(testutils.TestAddress( "nobody" ).String())
223 uassert.True(t, strings.Contains(unknown, "No schedule on file" ))
224 uassert.True(t, strings.Contains(unknown, "gnokey query auth/accounts/" ),
225 "the unknown page does not say how to find the schedule" )
226
227 bad := Render( "not-an-address" )
228 uassert.True(t, strings.Contains(bad, "is not a gno.land address" ))
229 }
230
231 // TestRenderEscapesWhatTheCallerTyped: the path is attacker-controlled and a
232 // live realm can never be patched, so the escaping is checked before deploy.
233 func TestRenderEscapesWhatTheCallerTyped(t *testing.T) {
234 out := Render( "[x](/r/evil)" )
235 uassert.False(t, strings.Contains(out, "[x](/r/evil)" ),
236 "a markdown link typed into the path reached the page unescaped" )
237 }
238
239 // TestRenderQueryRejectsGarbage keeps a typo readable instead of rendering a
240 // schedule of zeroes as though it were real.
241 func TestRenderQueryRejectsGarbage(t *testing.T) {
242 out := Render(moul.String() + "?o=abc" )
243 uassert.True(t, strings.Contains(out, "is not a number" ))
244
245 out = Render(moul.String() + "?o=100&s=200&e=100" )
246 uassert.True(t, strings.Contains(out, "not a valid schedule" ))
247 }
248
249 // TestChainHeightIsReadable guards the one call that would make Declare
250 // non-deterministic if it were wrong.
251 func TestChainHeightIsReadable(t *testing.T) {
252 uassert.Equal(t, runtime.ChainHeight(), chainHeight())
253 }
254
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.