Kind Realm (renderable)
Name valopers
Namespace gnops
Exported functions 13
Module gno.land/r/gnops/valopers
gno 0.9 valopers_test.gno gno
⧉
1 package valopers 2 3 import ( 4 "chain" 5 "strings" 6 "testing" 7 8 AddToAuthList (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}, addr string, member string)
Auth () string
DeleteFromAuthList (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}, addr string, member string)
GetByAddr (addr string) struct{Moniker string; Description string; ServerType string; OperatorAddress .uverse.address; SigningPubKey string; SigningAddress .uverse.address; LastRotationHeight int64; KeepRunning bool; auth *gno.land/p/nt/ownable/exts/authorizable/v0.Authorizable}
NewAuthorityRotationProposalRequest (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}, newContractPath string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
NewInstructionsProposalRequest (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}, newInstructions string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
Register (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}, moniker string, description string, serverType string, addr string, pubKey string)
Render (fullPath string) string
UpdateDescription (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}, addr string, description string)
UpdateKeepRunning (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}, addr string, keepRunning bool)
UpdateMoniker (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}, addr string, moniker string)
UpdateServerType (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}, addr string, serverType string)
UpdateSigningKey (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}, addr string, newPubKey string)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
"gno.land/p/nt/avl/v0"
9 "gno.land/p/nt/bptree/v0"
10 "gno.land/p/nt/ownable/exts/authorizable/v0"
11 "gno.land/p/nt/testutils/v0"
12 "gno.land/p/nt/uassert/v0"
13 "gno.land/p/nt/ufmt/v0"
14 )
15
16 // Test-local fee constant. Production reads register_fee from sysparams;
17 // these tests use this value as a reference Coin for OriginSend setup
18 // and (when needed) seed it via testing.SetSysParamUint64.
19 var minFee = chain.NewCoin( "ugnot" , 20 * 1_000_000 )
20
21 // cur is a zero-value realm used as a placeholder when forwarding to
22 // uassert dispatch helpers that gained an `rlm realm` param. These tests
23 // pass `func()` callbacks (no crossing inside the callback), so rlm is
24 // ignored — a nil realm here is safe.
25 var cur realm
26
27 // resetState clears realm-level state and zeroes the valoper sys-params
28 // so subtests don't leak through valopers (operator slots),
29 // signingRegistry (signing-address uniqueness), or sys-param values.
30 func resetState() {
31 valopers = avl.NewTree()
32 signingRegistry = bptree.NewBPTree32()
33 testing.SetSysParamUint64( "node" , "valoper" , "register_fee" , 0 )
34 testing.SetSysParamUint64( "node" , "valoper" , "rotation_fee" , 0 )
35 testing.SetSysParamInt64( "node" , "valoper" , "rotation_period_blocks" , 600 )
36 }
37
38 // enableRegisterFee seeds register_fee = minFee.Amount in sysparams
39 // so the Register fee path fires. Subtests that exercise fee
40 // rejection or sufficient-fee acceptance call this after resetState.
41 func enableRegisterFee() {
42 testing.SetSysParamUint64( "node" , "valoper" , "register_fee" , uint64 (minFee.Amount))
43 }
44
45 func validValidatorInfo(t *testing.T) struct {
46 Moniker string
47 Description string
48 ServerType string
49 Address address
50 PubKey string
51 } {
52 t.Helper()
53
54 return struct {
55 Moniker string
56 Description string
57 ServerType string
58 Address address
59 PubKey string
60 }{
61 Moniker: "test-1" ,
62 Description: "test-1's description" ,
63 ServerType: ServerTypeOnPrem,
64 Address: address( "g1sp8v98h2gadm5jggtzz9w5ksexqn68ympsd68h" ),
65 PubKey: "gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zqwpdwpd0f9fvqla089ndw5g9hcsufad77fml2vlu73fk8q8sh8v72cza5p" ,
66 }
67 }
68
69 func TestValopers_Register(cur realm, t *testing.T) {
70 t.Run( "already a valoper" , func (cur realm, t *testing.T) {
71 resetState()
72
73 info := validValidatorInfo(t)
74 testing.SetRealm(testing.NewUserRealm(info.Address))
75
76 v := Valoper{
77 Moniker: info.Moniker,
78 Description: info.Description,
79 ServerType: info.ServerType,
80 OperatorAddress: info.Address,
81 SigningPubKey: info.PubKey,
82 KeepRunning: true ,
83 }
84
85 // Add the valoper directly to the slot.
86 valopers.Set(v.OperatorAddress.String(), v)
87
88 // Send coins.
89 testing.SetOriginSend(chain.Coins{minFee})
90
91 uassert.AbortsWithMessage(t, cur, ErrValoperExists.Error(), func () {
92 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
93 })
94 })
95
96 t.Run( "no coins deposited" , func (cur realm, t *testing.T) {
97 resetState()
98 enableRegisterFee()
99
100 info := validValidatorInfo(t)
101 testing.SetRealm(testing.NewUserRealm(info.Address))
102
103 // Send no coins.
104 testing.SetOriginSend(chain.Coins{chain.NewCoin( "ugnot" , 0 )})
105
106 uassert.AbortsWithMessage(t, cur, ufmt.Sprintf( "payment must not be less than %d%s" , minFee.Amount, minFee.Denom), func () {
107 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
108 })
109 })
110
111 t.Run( "insufficient coins amount deposited" , func (cur realm, t *testing.T) {
112 resetState()
113 enableRegisterFee()
114
115 info := validValidatorInfo(t)
116 testing.SetRealm(testing.NewUserRealm(info.Address))
117
118 // Send invalid coins.
119 testing.SetOriginSend(chain.Coins{chain.NewCoin( "ugnot" , minFee.Amount- 1 )})
120
121 uassert.AbortsWithMessage(t, cur, ufmt.Sprintf( "payment must not be less than %d%s" , minFee.Amount, minFee.Denom), func () {
122 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
123 })
124 })
125
126 t.Run( "coin amount deposited is not ugnot" , func (cur realm, t *testing.T) {
127 resetState()
128 enableRegisterFee()
129
130 info := validValidatorInfo(t)
131 testing.SetRealm(testing.NewUserRealm(info.Address))
132
133 // Send invalid coins.
134 testing.SetOriginSend(chain.Coins{chain.NewCoin( "gnogno" , minFee.Amount)})
135
136 uassert.AbortsWithMessage(t, cur, "incompatible coin denominations: gnogno, ugnot" , func () {
137 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
138 })
139 })
140
141 // A fee above MaxInt64 wraps negative in Coin.Amount, which would make
142 // the IsLT amount check false for every payment: the realm would read
143 // the fee as configured and collect 1ugnot. uint64(int64(-1)) is what
144 // valopers/proposal.ProposeNewMinFeeProposalRequest stores for a
145 // negative newMinFee, so this is reachable from one governance typo.
146 t.Run( "unrepresentable fee fails closed" , func (cur realm, t *testing.T) {
147 resetState()
148 testing.SetSysParamUint64( "node" , "valoper" , "register_fee" , ^ uint64 ( 0 ))
149
150 info := validValidatorInfo(t)
151 testing.SetRealm(testing.NewUserRealm(info.Address))
152
153 // Nowhere near the configured fee, and previously accepted.
154 testing.SetOriginSend(chain.Coins{chain.NewCoin( "ugnot" , 1 )})
155
156 uassert.AbortsWithMessage(t, cur, ErrFeeParamOutOfRange.Error(), func () {
157 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
158 })
159 uassert.False(t, isValoper(info.Address), "must not register" )
160 })
161
162 t.Run( "squat guard rejects mismatched OriginCaller" , func (cur realm, t *testing.T) {
163 resetState()
164
165 info := validValidatorInfo(t)
166 // Caller is NOT info.Address: post-genesis squat guard fires.
167 testing.SetRealm(testing.NewUserRealm(testutils.TestAddress( "attacker" )))
168 testing.SetOriginSend(chain.Coins{minFee})
169
170 uassert.AbortsWithMessage(t, cur, ErrOperatorSquatGuard.Error(), func () {
171 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
172 })
173 })
174
175 t.Run( "successful registration" , func (cur realm, t *testing.T) {
176 resetState()
177
178 info := validValidatorInfo(t)
179 testing.SetRealm(testing.NewUserRealm(info.Address))
180
181 // Send coins.
182 testing.SetOriginSend(chain.Coins{minFee})
183
184 uassert.NotAborts(t, cur, func () {
185 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
186 })
187
188 uassert.NotPanics(t, cur, func () {
189 valoper := GetByAddr(info.Address)
190
191 uassert.Equal(t, info.Moniker, valoper.Moniker)
192 uassert.Equal(t, info.Description, valoper.Description)
193 uassert.Equal(t, info.ServerType, valoper.ServerType)
194 uassert.Equal(t, info.Address, valoper.OperatorAddress)
195 uassert.Equal(t, info.PubKey, valoper.SigningPubKey)
196 uassert.Equal(t, true , valoper.KeepRunning)
197
198 // SigningAddress is derived from the pubkey and present.
199 derived, err := chain.PubKeyAddress(info.PubKey)
200 uassert.NoError(t, err)
201 uassert.Equal(t, derived, valoper.SigningAddress)
202
203 // signingRegistry tracks the active entry.
204 uassert.True(t, signingRegistry.Has(derived.String()), "signingRegistry must contain the active entry" )
205 })
206 })
207
208 t.Run( "signing-key reuse rejected" , func (cur realm, t *testing.T) {
209 resetState()
210
211 info := validValidatorInfo(t)
212 testing.SetRealm(testing.NewUserRealm(info.Address))
213 testing.SetOriginSend(chain.Coins{minFee})
214
215 // First registration succeeds.
216 uassert.NotAborts(t, cur, func () {
217 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
218 })
219
220 // Second attempt with a different operator addr but the same
221 // pubkey must fail signingRegistry uniqueness.
222 other := testutils.TestAddress( "other-op" )
223 testing.SetRealm(testing.NewUserRealm(other))
224 testing.SetOriginSend(chain.Coins{minFee})
225
226 uassert.AbortsWithMessage(t, cur, ErrSigningKeyTaken.Error(), func () {
227 Register(cross(cur), info.Moniker, info.Description, info.ServerType, other, info.PubKey)
228 })
229 })
230
231 t.Run( "front-running guard rejects post-genesis if signing addr already validates" , func (cur realm, t *testing.T) {
232 resetState()
233
234 info := validValidatorInfo(t)
235 // Seed v0's valset:current with the very signing address that
236 // `info.PubKey` derives to (g1sp8v98...). Any post-genesis
237 // Register attempting the same pubkey now trips
238 // `ChainHeight()>0 && validators.IsValidator(signingAddr)`.
239 testing.SetSysParamStrings( "node" , "valset" , "current" , [] string {info.PubKey + ":1" })
240
241 testing.SetRealm(testing.NewUserRealm(info.Address))
242 testing.SetOriginSend(chain.Coins{minFee})
243
244 uassert.AbortsWithMessage(t, cur, ErrFrontrunValidator.Error(), func () {
245 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
246 })
247
248 // Cleanup: clear the seeded valset to avoid leaking into
249 // later subtests if the package state isn't reset between
250 // them in this test runner mode.
251 testing.SetSysParamStrings( "node" , "valset" , "current" , [] string {})
252 })
253 }
254
255 func TestValopers_Register_AuthOwnerIsOperatorAddress(cur realm, t *testing.T) {
256 // Pin: the Authorizable owner is bound to the OperatorAddress
257 // (the addr arg), NOT to OriginCaller. This matters in the
258 // genesis-mode deployer pattern: one signer (e.g., the hardfork
259 // ceremony deployer) registers profiles for many operators.
260 // Each operator must end up on their own profile's auth list
261 // so they can manage it post-genesis without depending on the
262 // deployer.
263 t.Run( "genesis deployer pattern: operator (not deployer) is owner" , func (cur realm, t *testing.T) {
264 resetState()
265
266 info := validValidatorInfo(t)
267 deployer := testutils.TestAddress( "deployer" )
268
269 // Genesis mode: ChainHeight()==0 bypasses the squat guard so
270 // deployer (OriginCaller) can register a profile for a
271 // different operator addr.
272 testing.SetHeight( 0 )
273 testing.SetRealm(testing.NewUserRealm(deployer))
274 testing.SetOriginCaller(deployer)
275 testing.SetOriginSend(chain.Coins{minFee})
276
277 uassert.NotPanics(t, cur, func () {
278 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
279 })
280
281 // Auth owner must be the operator addr (info.Address), NOT
282 // the deployer.
283 //
284 // Asserted through the exported AuthOwner() accessor, not the
285 // private v.auth: AuthOwner is the API that replaced the removed
286 // Auth() capability leak, so it is what external readers see. A
287 // second assertion against the underlying Authorizable pins that
288 // the accessor actually reports the auth list's owner rather
289 // than echoing OperatorAddress, which is what a plausible
290 // "simplification" of AuthOwner would do.
291 v := GetByAddr(info.Address)
292 uassert.Equal(t, info.Address.String(), v.AuthOwner().String(),
293 "auth owner must equal OperatorAddress, not OriginCaller" )
294 uassert.Equal(t, v.auth.Owner().String(), v.AuthOwner().String(),
295 "AuthOwner must report the Authorizable's owner" )
296
297 // Operator can manage their own profile post-genesis.
298 testing.SetHeight( 100 )
299 testing.SetRealm(testing.NewUserRealm(info.Address))
300 testing.SetOriginCaller(info.Address)
301 uassert.NotPanics(t, cur, func () {
302 UpdateMoniker(cross(cur), info.Address, "operator-renamed" )
303 })
304 uassert.Equal(t, "operator-renamed" , GetByAddr(info.Address).Moniker)
305
306 // Deployer cannot manage the operator's profile (not on the
307 // auth list).
308 testing.SetRealm(testing.NewUserRealm(deployer))
309 testing.SetOriginCaller(deployer)
310 uassert.AbortsContains(t, cur, "caller is not in authorized list" , func () {
311 UpdateMoniker(cross(cur), info.Address, "deployer-attempt" )
312 })
313 })
314
315 t.Run( "post-genesis self-Register: operator is owner" , func (cur realm, t *testing.T) {
316 // At H>0 the squat guard forces OriginCaller==addr, so owner
317 // would be the same regardless. This subtest pins that the
318 // post-genesis behavior is unchanged.
319 resetState()
320
321 info := validValidatorInfo(t)
322 testing.SetHeight( 100 )
323 testing.SetRealm(testing.NewUserRealm(info.Address))
324 testing.SetOriginCaller(info.Address)
325 testing.SetOriginSend(chain.Coins{minFee})
326
327 uassert.NotPanics(t, cur, func () {
328 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
329 })
330
331 v := GetByAddr(info.Address)
332 uassert.Equal(t, info.Address.String(), v.AuthOwner().String())
333 })
334 }
335
336 func TestValopers_UpdateAuthMembers(cur realm, t *testing.T) {
337 test2Address := testutils.TestAddress( "test2" )
338
339 t.Run( "unauthorized member adds member" , func (cur realm, t *testing.T) {
340 resetState()
341
342 info := validValidatorInfo(t)
343 testing.SetRealm(testing.NewUserRealm(info.Address))
344 testing.SetOriginSend(chain.Coins{minFee})
345
346 // Add the valoper.
347 uassert.NotPanics(t, cur, func () {
348 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
349 })
350
351 // A different caller (not on the auth list) tries to add a member.
352 testing.SetRealm(testing.NewUserRealm(test2Address))
353
354 uassert.AbortsWithMessage(t, cur, authorizable.ErrNotSuperuser.Error(), func () {
355 AddToAuthList(cross(cur), info.Address, test2Address)
356 })
357 })
358
359 t.Run( "unauthorized member deletes member" , func (cur realm, t *testing.T) {
360 resetState()
361
362 info := validValidatorInfo(t)
363 testing.SetRealm(testing.NewUserRealm(info.Address))
364 testing.SetOriginSend(chain.Coins{minFee})
365
366 uassert.NotPanics(t, cur, func () {
367 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
368 })
369
370 uassert.NotPanics(t, cur, func () {
371 AddToAuthList(cross(cur), info.Address, test2Address)
372 })
373
374 // A different caller tries to delete a member.
375 testing.SetRealm(testing.NewUserRealm(testutils.TestAddress( "attacker" )))
376
377 uassert.AbortsWithMessage(t, cur, authorizable.ErrNotSuperuser.Error(), func () {
378 DeleteFromAuthList(cross(cur), info.Address, test2Address)
379 })
380 })
381
382 t.Run( "authorized member adds member" , func (cur realm, t *testing.T) {
383 resetState()
384
385 info := validValidatorInfo(t)
386 testing.SetRealm(testing.NewUserRealm(info.Address))
387 testing.SetOriginSend(chain.Coins{minFee})
388
389 uassert.NotPanics(t, cur, func () {
390 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
391 })
392
393 uassert.NotPanics(t, cur, func () {
394 AddToAuthList(cross(cur), info.Address, test2Address)
395 })
396
397 testing.SetRealm(testing.NewUserRealm(test2Address))
398
399 newMoniker := "new moniker"
400 uassert.NotPanics(t, cur, func () {
401 UpdateMoniker(cross(cur), info.Address, newMoniker)
402 })
403
404 uassert.NotPanics(t, cur, func () {
405 valoper := GetByAddr(info.Address)
406 uassert.Equal(t, newMoniker, valoper.Moniker)
407 })
408 })
409 }
410
411 func TestValopers_UpdateMoniker(cur realm, t *testing.T) {
412 test2Address := testutils.TestAddress( "test2" )
413
414 t.Run( "non-existing valoper" , func (cur realm, t *testing.T) {
415 resetState()
416
417 info := validValidatorInfo(t)
418
419 uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func () {
420 UpdateMoniker(cross(cur), info.Address, "new moniker" )
421 })
422 })
423
424 t.Run( "invalid caller" , func (cur realm, t *testing.T) {
425 resetState()
426
427 info := validValidatorInfo(t)
428 testing.SetRealm(testing.NewUserRealm(info.Address))
429 testing.SetOriginSend(chain.Coins{minFee})
430
431 uassert.NotPanics(t, cur, func () {
432 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
433 })
434
435 // Change the caller to someone not on the auth list.
436 testing.SetRealm(testing.NewUserRealm(test2Address))
437
438 uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func () {
439 UpdateMoniker(cross(cur), info.Address, "new moniker" )
440 })
441 })
442
443 t.Run( "invalid moniker" , func (cur realm, t *testing.T) {
444 resetState()
445
446 info := validValidatorInfo(t)
447 testing.SetRealm(testing.NewUserRealm(info.Address))
448 testing.SetOriginSend(chain.Coins{minFee})
449
450 uassert.NotPanics(t, cur, func () {
451 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
452 })
453
454 invalidMonikers := [] string {
455 "" , // Empty
456 " " , // Whitespace
457 "a" , // Too short
458 "a very long moniker that is longer than 32 characters" , // Too long
459 "!@#$%^&*()+{}|:<>?/.,;'" , // Invalid characters
460 " space in front" ,
461 "space in back " ,
462 }
463
464 for _, invalidMoniker := range invalidMonikers {
465 uassert.AbortsWithMessage(t, cur, ErrInvalidMoniker.Error(), func () {
466 UpdateMoniker(cross(cur), info.Address, invalidMoniker)
467 })
468 }
469 })
470
471 t.Run( "too long moniker" , func (cur realm, t *testing.T) {
472 resetState()
473
474 info := validValidatorInfo(t)
475 testing.SetRealm(testing.NewUserRealm(info.Address))
476 testing.SetOriginSend(chain.Coins{minFee})
477
478 uassert.NotPanics(t, cur, func () {
479 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
480 })
481
482 uassert.AbortsWithMessage(t, cur, ErrInvalidMoniker.Error(), func () {
483 UpdateMoniker(cross(cur), info.Address, strings.Repeat( "a" , MonikerMaxLength+ 1 ))
484 })
485 })
486
487 t.Run( "successful update" , func (cur realm, t *testing.T) {
488 resetState()
489
490 info := validValidatorInfo(t)
491 testing.SetRealm(testing.NewUserRealm(info.Address))
492 testing.SetOriginSend(chain.Coins{minFee})
493
494 uassert.NotPanics(t, cur, func () {
495 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
496 })
497
498 newMoniker := "new moniker"
499 uassert.NotPanics(t, cur, func () {
500 UpdateMoniker(cross(cur), info.Address, newMoniker)
501 })
502
503 uassert.NotPanics(t, cur, func () {
504 valoper := GetByAddr(info.Address)
505 uassert.Equal(t, newMoniker, valoper.Moniker)
506 })
507 })
508 }
509
510 func TestValopers_UpdateDescription(cur realm, t *testing.T) {
511 test2Address := testutils.TestAddress( "test2" )
512
513 t.Run( "non-existing valoper" , func (cur realm, t *testing.T) {
514 resetState()
515
516 uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func () {
517 UpdateDescription(cross(cur), validValidatorInfo(t).Address, "new description" )
518 })
519 })
520
521 t.Run( "invalid caller" , func (cur realm, t *testing.T) {
522 resetState()
523
524 info := validValidatorInfo(t)
525 testing.SetRealm(testing.NewUserRealm(info.Address))
526 testing.SetOriginSend(chain.Coins{minFee})
527
528 uassert.NotPanics(t, cur, func () {
529 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
530 })
531
532 testing.SetRealm(testing.NewUserRealm(test2Address))
533
534 uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func () {
535 UpdateDescription(cross(cur), info.Address, "new description" )
536 })
537 })
538
539 t.Run( "empty description" , func (cur realm, t *testing.T) {
540 resetState()
541
542 info := validValidatorInfo(t)
543 testing.SetRealm(testing.NewUserRealm(info.Address))
544 testing.SetOriginSend(chain.Coins{minFee})
545
546 uassert.NotPanics(t, cur, func () {
547 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
548 })
549
550 uassert.AbortsWithMessage(t, cur, ErrInvalidDescription.Error(), func () {
551 UpdateDescription(cross(cur), info.Address, "" )
552 })
553 })
554
555 t.Run( "too long description" , func (cur realm, t *testing.T) {
556 resetState()
557
558 info := validValidatorInfo(t)
559 testing.SetRealm(testing.NewUserRealm(info.Address))
560 testing.SetOriginSend(chain.Coins{minFee})
561
562 uassert.NotPanics(t, cur, func () {
563 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
564 })
565
566 uassert.AbortsWithMessage(t, cur, ErrInvalidDescription.Error(), func () {
567 UpdateDescription(cross(cur), info.Address, strings.Repeat( "a" , DescriptionMaxLength+ 1 ))
568 })
569 })
570
571 t.Run( "successful update" , func (cur realm, t *testing.T) {
572 resetState()
573
574 info := validValidatorInfo(t)
575 testing.SetRealm(testing.NewUserRealm(info.Address))
576 testing.SetOriginSend(chain.Coins{minFee})
577
578 uassert.NotPanics(t, cur, func () {
579 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
580 })
581
582 newDescription := "new description"
583 uassert.NotPanics(t, cur, func () {
584 UpdateDescription(cross(cur), info.Address, newDescription)
585 })
586
587 uassert.NotPanics(t, cur, func () {
588 valoper := GetByAddr(info.Address)
589 uassert.Equal(t, newDescription, valoper.Description)
590 })
591 })
592 }
593
594 func TestValopers_UpdateKeepRunning(cur realm, t *testing.T) {
595 test2Address := testutils.TestAddress( "test2" )
596
597 t.Run( "non-existing valoper" , func (cur realm, t *testing.T) {
598 resetState()
599
600 uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func () {
601 UpdateKeepRunning(cross(cur), validValidatorInfo(t).Address, false )
602 })
603 })
604
605 t.Run( "invalid caller" , func (cur realm, t *testing.T) {
606 resetState()
607
608 info := validValidatorInfo(t)
609 testing.SetRealm(testing.NewUserRealm(info.Address))
610 testing.SetOriginSend(chain.Coins{minFee})
611
612 uassert.NotPanics(t, cur, func () {
613 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
614 })
615
616 testing.SetRealm(testing.NewUserRealm(test2Address))
617
618 uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func () {
619 UpdateKeepRunning(cross(cur), info.Address, false )
620 })
621 })
622
623 t.Run( "successful update" , func (cur realm, t *testing.T) {
624 resetState()
625
626 info := validValidatorInfo(t)
627 testing.SetRealm(testing.NewUserRealm(info.Address))
628 testing.SetOriginSend(chain.Coins{minFee})
629
630 uassert.NotPanics(t, cur, func () {
631 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
632 })
633
634 uassert.NotPanics(t, cur, func () {
635 UpdateKeepRunning(cross(cur), info.Address, false )
636 })
637
638 uassert.NotPanics(t, cur, func () {
639 valoper := GetByAddr(info.Address)
640 uassert.Equal(t, false , valoper.KeepRunning)
641 })
642 })
643 }
644
645 func TestValopers_UpdateServerType(cur realm, t *testing.T) {
646 test2Address := testutils.TestAddress( "test2" )
647
648 t.Run( "non-existing valoper" , func (cur realm, t *testing.T) {
649 resetState()
650
651 uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func () {
652 UpdateServerType(cross(cur), validValidatorInfo(t).Address, ServerTypeCloud)
653 })
654 })
655
656 t.Run( "invalid caller" , func (cur realm, t *testing.T) {
657 resetState()
658
659 info := validValidatorInfo(t)
660 testing.SetRealm(testing.NewUserRealm(info.Address))
661 testing.SetOriginSend(chain.Coins{minFee})
662
663 uassert.NotPanics(t, cur, func () {
664 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
665 })
666
667 testing.SetRealm(testing.NewUserRealm(test2Address))
668
669 uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func () {
670 UpdateServerType(cross(cur), info.Address, ServerTypeCloud)
671 })
672 })
673
674 t.Run( "invalid server type" , func (cur realm, t *testing.T) {
675 resetState()
676
677 info := validValidatorInfo(t)
678 testing.SetRealm(testing.NewUserRealm(info.Address))
679 testing.SetOriginSend(chain.Coins{minFee})
680
681 uassert.NotPanics(t, cur, func () {
682 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
683 })
684
685 invalidServerTypes := [] string {
686 "" ,
687 "invalid" ,
688 "Cloud" , // case sensitive
689 "ON-PREM" , // case sensitive
690 "datacenter" , // wrong format
691 }
692
693 for _, invalidType := range invalidServerTypes {
694 uassert.AbortsWithMessage(t, cur, ErrInvalidServerType.Error(), func () {
695 UpdateServerType(cross(cur), info.Address, invalidType)
696 })
697 }
698 })
699
700 t.Run( "successful update" , func (cur realm, t *testing.T) {
701 resetState()
702
703 info := validValidatorInfo(t)
704 testing.SetRealm(testing.NewUserRealm(info.Address))
705 testing.SetOriginSend(chain.Coins{minFee})
706
707 uassert.NotPanics(t, cur, func () {
708 Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
709 })
710
711 uassert.NotPanics(t, cur, func () {
712 UpdateServerType(cross(cur), info.Address, ServerTypeCloud)
713 })
714
715 uassert.NotPanics(t, cur, func () {
716 valoper := GetByAddr(info.Address)
717 uassert.Equal(t, ServerTypeCloud, valoper.ServerType)
718 })
719
720 uassert.NotPanics(t, cur, func () {
721 UpdateServerType(cross(cur), info.Address, ServerTypeDataCenter)
722 })
723
724 uassert.NotPanics(t, cur, func () {
725 valoper := GetByAddr(info.Address)
726 uassert.Equal(t, ServerTypeDataCenter, valoper.ServerType)
727 })
728 })
729 }
730
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.