Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

valopers_test.gno

22.04 Kb · 698 lines
  1package valopers
  2
  3import (
  4	"chain"
  5	"strings"
  6	"testing"
  7
  8	"gno.land/p/nt/avl/v0"
  9	"gno.land/p/nt/bptree/v0"
 10	"gno.land/p/nt/ownable/v0/exts/authorizable"
 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.
 19var 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.
 25var 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.
 30func 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.
 41func enableRegisterFee() {
 42	testing.SetSysParamUint64("node", "valoper", "register_fee", uint64(minFee.Amount))
 43}
 44
 45func 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
 69func 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	t.Run("squat guard rejects mismatched OriginCaller", func(cur realm, t *testing.T) {
142		resetState()
143
144		info := validValidatorInfo(t)
145		// Caller is NOT info.Address: post-genesis squat guard fires.
146		testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("attacker")))
147		testing.SetOriginSend(chain.Coins{minFee})
148
149		uassert.AbortsWithMessage(t, cur, ErrOperatorSquatGuard.Error(), func() {
150			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
151		})
152	})
153
154	t.Run("successful registration", func(cur realm, t *testing.T) {
155		resetState()
156
157		info := validValidatorInfo(t)
158		testing.SetRealm(testing.NewUserRealm(info.Address))
159
160		// Send coins.
161		testing.SetOriginSend(chain.Coins{minFee})
162
163		uassert.NotAborts(t, cur, func() {
164			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
165		})
166
167		uassert.NotPanics(t, cur, func() {
168			valoper := GetByAddr(info.Address)
169
170			uassert.Equal(t, info.Moniker, valoper.Moniker)
171			uassert.Equal(t, info.Description, valoper.Description)
172			uassert.Equal(t, info.ServerType, valoper.ServerType)
173			uassert.Equal(t, info.Address, valoper.OperatorAddress)
174			uassert.Equal(t, info.PubKey, valoper.SigningPubKey)
175			uassert.Equal(t, true, valoper.KeepRunning)
176
177			// SigningAddress is derived from the pubkey and present.
178			derived, err := chain.PubKeyAddress(info.PubKey)
179			uassert.NoError(t, err)
180			uassert.Equal(t, derived, valoper.SigningAddress)
181
182			// signingRegistry tracks the active entry.
183			uassert.True(t, signingRegistry.Has(derived.String()), "signingRegistry must contain the active entry")
184		})
185	})
186
187	t.Run("signing-key reuse rejected", func(cur realm, t *testing.T) {
188		resetState()
189
190		info := validValidatorInfo(t)
191		testing.SetRealm(testing.NewUserRealm(info.Address))
192		testing.SetOriginSend(chain.Coins{minFee})
193
194		// First registration succeeds.
195		uassert.NotAborts(t, cur, func() {
196			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
197		})
198
199		// Second attempt with a different operator addr but the same
200		// pubkey must fail signingRegistry uniqueness.
201		other := testutils.TestAddress("other-op")
202		testing.SetRealm(testing.NewUserRealm(other))
203		testing.SetOriginSend(chain.Coins{minFee})
204
205		uassert.AbortsWithMessage(t, cur, ErrSigningKeyTaken.Error(), func() {
206			Register(cross(cur), info.Moniker, info.Description, info.ServerType, other, info.PubKey)
207		})
208	})
209
210	t.Run("front-running guard rejects post-genesis if signing addr already validates", func(cur realm, t *testing.T) {
211		resetState()
212
213		info := validValidatorInfo(t)
214		// Seed v3's valset:current with the very signing address that
215		// `info.PubKey` derives to (g1sp8v98...). Any post-genesis
216		// Register attempting the same pubkey now trips
217		// `ChainHeight()>0 && validators.IsValidator(signingAddr)`.
218		testing.SetSysParamStrings("node", "valset", "current", []string{info.PubKey + ":1"})
219
220		testing.SetRealm(testing.NewUserRealm(info.Address))
221		testing.SetOriginSend(chain.Coins{minFee})
222
223		uassert.AbortsWithMessage(t, cur, ErrFrontrunValidator.Error(), func() {
224			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
225		})
226
227		// Cleanup: clear the seeded valset to avoid leaking into
228		// later subtests if the package state isn't reset between
229		// them in this test runner mode.
230		testing.SetSysParamStrings("node", "valset", "current", []string{})
231	})
232}
233
234func TestValopers_Register_AuthOwnerIsOperatorAddress(cur realm, t *testing.T) {
235	// Pin: the Authorizable owner is bound to the OperatorAddress
236	// (the addr arg), NOT to OriginCaller. This matters in the
237	// genesis-mode deployer pattern: one signer (e.g., the hardfork
238	// ceremony deployer) registers profiles for many operators.
239	// Each operator must end up on their own profile's auth list
240	// so they can manage it post-genesis without depending on the
241	// deployer.
242	t.Run("genesis deployer pattern: operator (not deployer) is owner", func(cur realm, t *testing.T) {
243		resetState()
244
245		info := validValidatorInfo(t)
246		deployer := testutils.TestAddress("deployer")
247
248		// Genesis mode: ChainHeight()==0 bypasses the squat guard so
249		// deployer (OriginCaller) can register a profile for a
250		// different operator addr.
251		testing.SetHeight(0)
252		testing.SetRealm(testing.NewUserRealm(deployer))
253		testing.SetOriginCaller(deployer)
254		testing.SetOriginSend(chain.Coins{minFee})
255
256		uassert.NotPanics(t, cur, func() {
257			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
258		})
259
260		// Auth owner must be the operator addr (info.Address), NOT
261		// the deployer.
262		v := GetByAddr(info.Address)
263		uassert.Equal(t, info.Address.String(), v.Auth().Owner().String(),
264			"auth owner must equal OperatorAddress, not OriginCaller")
265
266		// Operator can manage their own profile post-genesis.
267		testing.SetHeight(100)
268		testing.SetRealm(testing.NewUserRealm(info.Address))
269		testing.SetOriginCaller(info.Address)
270		uassert.NotPanics(t, cur, func() {
271			UpdateMoniker(cross(cur), info.Address, "operator-renamed")
272		})
273		uassert.Equal(t, "operator-renamed", GetByAddr(info.Address).Moniker)
274
275		// Deployer cannot manage the operator's profile (not on the
276		// auth list).
277		testing.SetRealm(testing.NewUserRealm(deployer))
278		testing.SetOriginCaller(deployer)
279		uassert.AbortsContains(t, cur, "caller is not in authorized list", func() {
280			UpdateMoniker(cross(cur), info.Address, "deployer-attempt")
281		})
282	})
283
284	t.Run("post-genesis self-Register: operator is owner", func(cur realm, t *testing.T) {
285		// At H>0 the squat guard forces OriginCaller==addr, so owner
286		// would be the same regardless. This subtest pins that the
287		// post-genesis behavior is unchanged.
288		resetState()
289
290		info := validValidatorInfo(t)
291		testing.SetHeight(100)
292		testing.SetRealm(testing.NewUserRealm(info.Address))
293		testing.SetOriginCaller(info.Address)
294		testing.SetOriginSend(chain.Coins{minFee})
295
296		uassert.NotPanics(t, cur, func() {
297			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
298		})
299
300		v := GetByAddr(info.Address)
301		uassert.Equal(t, info.Address.String(), v.Auth().Owner().String())
302	})
303}
304
305func TestValopers_UpdateAuthMembers(cur realm, t *testing.T) {
306	test2Address := testutils.TestAddress("test2")
307
308	t.Run("unauthorized member adds member", func(cur realm, t *testing.T) {
309		resetState()
310
311		info := validValidatorInfo(t)
312		testing.SetRealm(testing.NewUserRealm(info.Address))
313		testing.SetOriginSend(chain.Coins{minFee})
314
315		// Add the valoper.
316		uassert.NotPanics(t, cur, func() {
317			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
318		})
319
320		// A different caller (not on the auth list) tries to add a member.
321		testing.SetRealm(testing.NewUserRealm(test2Address))
322
323		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotSuperuser.Error(), func() {
324			AddToAuthList(cross(cur), info.Address, test2Address)
325		})
326	})
327
328	t.Run("unauthorized member deletes member", func(cur realm, t *testing.T) {
329		resetState()
330
331		info := validValidatorInfo(t)
332		testing.SetRealm(testing.NewUserRealm(info.Address))
333		testing.SetOriginSend(chain.Coins{minFee})
334
335		uassert.NotPanics(t, cur, func() {
336			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
337		})
338
339		uassert.NotPanics(t, cur, func() {
340			AddToAuthList(cross(cur), info.Address, test2Address)
341		})
342
343		// A different caller tries to delete a member.
344		testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("attacker")))
345
346		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotSuperuser.Error(), func() {
347			DeleteFromAuthList(cross(cur), info.Address, test2Address)
348		})
349	})
350
351	t.Run("authorized member adds member", func(cur realm, t *testing.T) {
352		resetState()
353
354		info := validValidatorInfo(t)
355		testing.SetRealm(testing.NewUserRealm(info.Address))
356		testing.SetOriginSend(chain.Coins{minFee})
357
358		uassert.NotPanics(t, cur, func() {
359			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
360		})
361
362		uassert.NotPanics(t, cur, func() {
363			AddToAuthList(cross(cur), info.Address, test2Address)
364		})
365
366		testing.SetRealm(testing.NewUserRealm(test2Address))
367
368		newMoniker := "new moniker"
369		uassert.NotPanics(t, cur, func() {
370			UpdateMoniker(cross(cur), info.Address, newMoniker)
371		})
372
373		uassert.NotPanics(t, cur, func() {
374			valoper := GetByAddr(info.Address)
375			uassert.Equal(t, newMoniker, valoper.Moniker)
376		})
377	})
378}
379
380func TestValopers_UpdateMoniker(cur realm, t *testing.T) {
381	test2Address := testutils.TestAddress("test2")
382
383	t.Run("non-existing valoper", func(cur realm, t *testing.T) {
384		resetState()
385
386		info := validValidatorInfo(t)
387
388		uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func() {
389			UpdateMoniker(cross(cur), info.Address, "new moniker")
390		})
391	})
392
393	t.Run("invalid caller", func(cur realm, t *testing.T) {
394		resetState()
395
396		info := validValidatorInfo(t)
397		testing.SetRealm(testing.NewUserRealm(info.Address))
398		testing.SetOriginSend(chain.Coins{minFee})
399
400		uassert.NotPanics(t, cur, func() {
401			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
402		})
403
404		// Change the caller to someone not on the auth list.
405		testing.SetRealm(testing.NewUserRealm(test2Address))
406
407		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func() {
408			UpdateMoniker(cross(cur), info.Address, "new moniker")
409		})
410	})
411
412	t.Run("invalid moniker", func(cur realm, t *testing.T) {
413		resetState()
414
415		info := validValidatorInfo(t)
416		testing.SetRealm(testing.NewUserRealm(info.Address))
417		testing.SetOriginSend(chain.Coins{minFee})
418
419		uassert.NotPanics(t, cur, func() {
420			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
421		})
422
423		invalidMonikers := []string{
424			"",     // Empty
425			"    ", // Whitespace
426			"a",    // Too short
427			"a very long moniker that is longer than 32 characters", // Too long
428			"!@#$%^&*()+{}|:<>?/.,;'",                               // Invalid characters
429			" space in front",
430			"space in back ",
431		}
432
433		for _, invalidMoniker := range invalidMonikers {
434			uassert.AbortsWithMessage(t, cur, ErrInvalidMoniker.Error(), func() {
435				UpdateMoniker(cross(cur), info.Address, invalidMoniker)
436			})
437		}
438	})
439
440	t.Run("too long moniker", func(cur realm, t *testing.T) {
441		resetState()
442
443		info := validValidatorInfo(t)
444		testing.SetRealm(testing.NewUserRealm(info.Address))
445		testing.SetOriginSend(chain.Coins{minFee})
446
447		uassert.NotPanics(t, cur, func() {
448			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
449		})
450
451		uassert.AbortsWithMessage(t, cur, ErrInvalidMoniker.Error(), func() {
452			UpdateMoniker(cross(cur), info.Address, strings.Repeat("a", MonikerMaxLength+1))
453		})
454	})
455
456	t.Run("successful update", func(cur realm, t *testing.T) {
457		resetState()
458
459		info := validValidatorInfo(t)
460		testing.SetRealm(testing.NewUserRealm(info.Address))
461		testing.SetOriginSend(chain.Coins{minFee})
462
463		uassert.NotPanics(t, cur, func() {
464			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
465		})
466
467		newMoniker := "new moniker"
468		uassert.NotPanics(t, cur, func() {
469			UpdateMoniker(cross(cur), info.Address, newMoniker)
470		})
471
472		uassert.NotPanics(t, cur, func() {
473			valoper := GetByAddr(info.Address)
474			uassert.Equal(t, newMoniker, valoper.Moniker)
475		})
476	})
477}
478
479func TestValopers_UpdateDescription(cur realm, t *testing.T) {
480	test2Address := testutils.TestAddress("test2")
481
482	t.Run("non-existing valoper", func(cur realm, t *testing.T) {
483		resetState()
484
485		uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func() {
486			UpdateDescription(cross(cur), validValidatorInfo(t).Address, "new description")
487		})
488	})
489
490	t.Run("invalid caller", func(cur realm, t *testing.T) {
491		resetState()
492
493		info := validValidatorInfo(t)
494		testing.SetRealm(testing.NewUserRealm(info.Address))
495		testing.SetOriginSend(chain.Coins{minFee})
496
497		uassert.NotPanics(t, cur, func() {
498			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
499		})
500
501		testing.SetRealm(testing.NewUserRealm(test2Address))
502
503		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func() {
504			UpdateDescription(cross(cur), info.Address, "new description")
505		})
506	})
507
508	t.Run("empty description", func(cur realm, t *testing.T) {
509		resetState()
510
511		info := validValidatorInfo(t)
512		testing.SetRealm(testing.NewUserRealm(info.Address))
513		testing.SetOriginSend(chain.Coins{minFee})
514
515		uassert.NotPanics(t, cur, func() {
516			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
517		})
518
519		uassert.AbortsWithMessage(t, cur, ErrInvalidDescription.Error(), func() {
520			UpdateDescription(cross(cur), info.Address, "")
521		})
522	})
523
524	t.Run("too long description", func(cur realm, t *testing.T) {
525		resetState()
526
527		info := validValidatorInfo(t)
528		testing.SetRealm(testing.NewUserRealm(info.Address))
529		testing.SetOriginSend(chain.Coins{minFee})
530
531		uassert.NotPanics(t, cur, func() {
532			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
533		})
534
535		uassert.AbortsWithMessage(t, cur, ErrInvalidDescription.Error(), func() {
536			UpdateDescription(cross(cur), info.Address, strings.Repeat("a", DescriptionMaxLength+1))
537		})
538	})
539
540	t.Run("successful update", func(cur realm, t *testing.T) {
541		resetState()
542
543		info := validValidatorInfo(t)
544		testing.SetRealm(testing.NewUserRealm(info.Address))
545		testing.SetOriginSend(chain.Coins{minFee})
546
547		uassert.NotPanics(t, cur, func() {
548			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
549		})
550
551		newDescription := "new description"
552		uassert.NotPanics(t, cur, func() {
553			UpdateDescription(cross(cur), info.Address, newDescription)
554		})
555
556		uassert.NotPanics(t, cur, func() {
557			valoper := GetByAddr(info.Address)
558			uassert.Equal(t, newDescription, valoper.Description)
559		})
560	})
561}
562
563func TestValopers_UpdateKeepRunning(cur realm, t *testing.T) {
564	test2Address := testutils.TestAddress("test2")
565
566	t.Run("non-existing valoper", func(cur realm, t *testing.T) {
567		resetState()
568
569		uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func() {
570			UpdateKeepRunning(cross(cur), validValidatorInfo(t).Address, false)
571		})
572	})
573
574	t.Run("invalid caller", func(cur realm, t *testing.T) {
575		resetState()
576
577		info := validValidatorInfo(t)
578		testing.SetRealm(testing.NewUserRealm(info.Address))
579		testing.SetOriginSend(chain.Coins{minFee})
580
581		uassert.NotPanics(t, cur, func() {
582			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
583		})
584
585		testing.SetRealm(testing.NewUserRealm(test2Address))
586
587		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func() {
588			UpdateKeepRunning(cross(cur), info.Address, false)
589		})
590	})
591
592	t.Run("successful update", func(cur realm, t *testing.T) {
593		resetState()
594
595		info := validValidatorInfo(t)
596		testing.SetRealm(testing.NewUserRealm(info.Address))
597		testing.SetOriginSend(chain.Coins{minFee})
598
599		uassert.NotPanics(t, cur, func() {
600			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
601		})
602
603		uassert.NotPanics(t, cur, func() {
604			UpdateKeepRunning(cross(cur), info.Address, false)
605		})
606
607		uassert.NotPanics(t, cur, func() {
608			valoper := GetByAddr(info.Address)
609			uassert.Equal(t, false, valoper.KeepRunning)
610		})
611	})
612}
613
614func TestValopers_UpdateServerType(cur realm, t *testing.T) {
615	test2Address := testutils.TestAddress("test2")
616
617	t.Run("non-existing valoper", func(cur realm, t *testing.T) {
618		resetState()
619
620		uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func() {
621			UpdateServerType(cross(cur), validValidatorInfo(t).Address, ServerTypeCloud)
622		})
623	})
624
625	t.Run("invalid caller", func(cur realm, t *testing.T) {
626		resetState()
627
628		info := validValidatorInfo(t)
629		testing.SetRealm(testing.NewUserRealm(info.Address))
630		testing.SetOriginSend(chain.Coins{minFee})
631
632		uassert.NotPanics(t, cur, func() {
633			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
634		})
635
636		testing.SetRealm(testing.NewUserRealm(test2Address))
637
638		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func() {
639			UpdateServerType(cross(cur), info.Address, ServerTypeCloud)
640		})
641	})
642
643	t.Run("invalid server type", func(cur realm, t *testing.T) {
644		resetState()
645
646		info := validValidatorInfo(t)
647		testing.SetRealm(testing.NewUserRealm(info.Address))
648		testing.SetOriginSend(chain.Coins{minFee})
649
650		uassert.NotPanics(t, cur, func() {
651			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
652		})
653
654		invalidServerTypes := []string{
655			"",
656			"invalid",
657			"Cloud",      // case sensitive
658			"ON-PREM",    // case sensitive
659			"datacenter", // wrong format
660		}
661
662		for _, invalidType := range invalidServerTypes {
663			uassert.AbortsWithMessage(t, cur, ErrInvalidServerType.Error(), func() {
664				UpdateServerType(cross(cur), info.Address, invalidType)
665			})
666		}
667	})
668
669	t.Run("successful update", func(cur realm, t *testing.T) {
670		resetState()
671
672		info := validValidatorInfo(t)
673		testing.SetRealm(testing.NewUserRealm(info.Address))
674		testing.SetOriginSend(chain.Coins{minFee})
675
676		uassert.NotPanics(t, cur, func() {
677			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
678		})
679
680		uassert.NotPanics(t, cur, func() {
681			UpdateServerType(cross(cur), info.Address, ServerTypeCloud)
682		})
683
684		uassert.NotPanics(t, cur, func() {
685			valoper := GetByAddr(info.Address)
686			uassert.Equal(t, ServerTypeCloud, valoper.ServerType)
687		})
688
689		uassert.NotPanics(t, cur, func() {
690			UpdateServerType(cross(cur), info.Address, ServerTypeDataCenter)
691		})
692
693		uassert.NotPanics(t, cur, func() {
694			valoper := GetByAddr(info.Address)
695			uassert.Equal(t, ServerTypeDataCenter, valoper.ServerType)
696		})
697	})
698}