PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidators

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/gnops/valopers

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
valopers
Namespace
gnops
Files
14 (gnomod.toml)
Exported functions
13
Module
gno.land/r/gnops/valopers
gno
0.9

Files (14)

  • gnomod.tomltoml
  • admin.gnogno
  • init.gnogno
  • valopers.gnogno
  • admin_test.gnogno
  • pubkey_type_test.gnogno
  • rotate_test.gnogno
  • valopers_test.gnogno
  • z_1_filetest.gnogno
  • z_2_filetest.gnogno
  • z_auth_readonly_filetest.gnogno
  • z_foreign_realm_capability_filetest.gnogno
  • z_govdao_only_principal_filetest.gnogno
  • z_operator_authlist_capability_filetest.gnogno
  • valopers_test.gnogno
    1package valopers23import (4	"chain"5	"strings"6	"testing"78

    Functions

    • 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.

    Rendered

    RenderedRawgnoweb ↗
    "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.
    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 // 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
    255func 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
    336func 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
    411func 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
    510func 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
    594func 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
    645func 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.