Search Apps Documentation Source Content File Folder Download Copy Actions Download

rotate_test.gno

5.67 Kb · 160 lines
  1package valopers
  2
  3import (
  4	"chain"
  5	"testing"
  6
  7	"gno.land/p/nt/testutils/v0"
  8	"gno.land/p/nt/uassert/v0"
  9	"gno.land/p/nt/urequire/v0"
 10)
 11
 12const (
 13	// Second valid pubkey used as the rotation target. Distinct from
 14	// validValidatorInfo's pubkey so the signingRegistry uniqueness
 15	// check is exercised.
 16	rotateTargetPubKey = "gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zq3ds6sdvc0shfkq02h6xx5g0jp04aadexfnpsmgjxu72xz9y30aqfrlpny"
 17
 18	// Test-local mirror of the rotation_period_blocks sys-param's
 19	// default. resetState() seeds the same value into sysparams.
 20	testRotationPeriodBlocks = int64(600)
 21)
 22
 23// registerForRotation does the boilerplate setup that every rotation
 24// test starts from: clear state, register the valoper as info.Address,
 25// and return the info struct.
 26func registerForRotation(t *testing.T) struct {
 27	Moniker     string
 28	Description string
 29	ServerType  string
 30	Address     address
 31	PubKey      string
 32} {
 33	t.Helper()
 34	resetState()
 35	info := validValidatorInfo(t)
 36	testing.SetRealm(testing.NewUserRealm(info.Address))
 37	testing.SetOriginSend(chain.Coins{minFee})
 38	Register(cross, info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
 39	return info
 40}
 41
 42func TestUpdateSigningKey_HappyPath(t *testing.T) {
 43	info := registerForRotation(t)
 44
 45	// Advance past the throttle window. SkipHeights doesn't preserve
 46	// the realm context across the boundary, so re-set it before the
 47	// next cross-call.
 48	testing.SkipHeights(testRotationPeriodBlocks + 1)
 49	testing.SetRealm(testing.NewUserRealm(info.Address))
 50
 51	uassert.NotAborts(t, func() {
 52		UpdateSigningKey(cross, info.Address, rotateTargetPubKey)
 53	})
 54
 55	v := GetByAddr(info.Address)
 56	uassert.Equal(t, rotateTargetPubKey, v.SigningPubKey)
 57
 58	newSigningAddr, err := chain.PubKeyAddress(rotateTargetPubKey)
 59	urequire.NoError(t, err)
 60	uassert.Equal(t, newSigningAddr, v.SigningAddress)
 61
 62	// New entry active in registry.
 63	rawNew, ok := signingRegistry.Get(newSigningAddr.String())
 64	urequire.True(t, ok, "new signing addr in registry")
 65	newEntry := rawNew.(regEntry)
 66	uassert.Equal(t, info.Address, newEntry.OperatorAddress)
 67	uassert.Equal(t, int64(0), newEntry.RetiredAtHeight)
 68
 69	// Old entry retired (still present, RetiredAtHeight > 0).
 70	oldSigningAddr, err := chain.PubKeyAddress(info.PubKey)
 71	urequire.NoError(t, err)
 72	rawOld, ok := signingRegistry.Get(oldSigningAddr.String())
 73	urequire.True(t, ok, "old signing addr retained as retired")
 74	oldEntry := rawOld.(regEntry)
 75	uassert.Equal(t, info.Address, oldEntry.OperatorAddress)
 76	uassert.True(t, oldEntry.RetiredAtHeight > 0, "old entry must be retired (RetiredAtHeight > 0)")
 77}
 78
 79func TestUpdateSigningKey_ThrottleRejection(t *testing.T) {
 80	info := registerForRotation(t)
 81	testing.SetRealm(testing.NewUserRealm(info.Address))
 82
 83	// Try to rotate immediately, before throttle elapses.
 84	uassert.AbortsWithMessage(t, ErrRotationThrottled.Error(), func() {
 85		UpdateSigningKey(cross, info.Address, rotateTargetPubKey)
 86	})
 87
 88	// Advance just shy of the threshold; still rejected.
 89	testing.SkipHeights(testRotationPeriodBlocks - 1)
 90	testing.SetRealm(testing.NewUserRealm(info.Address))
 91	uassert.AbortsWithMessage(t, ErrRotationThrottled.Error(), func() {
 92		UpdateSigningKey(cross, info.Address, rotateTargetPubKey)
 93	})
 94
 95	// One more block — now exactly at the threshold; allowed.
 96	testing.SkipHeights(1)
 97	testing.SetRealm(testing.NewUserRealm(info.Address))
 98	uassert.NotAborts(t, func() {
 99		UpdateSigningKey(cross, info.Address, rotateTargetPubKey)
100	})
101}
102
103func TestUpdateSigningKey_RejectsNonAuthListCaller(t *testing.T) {
104	info := registerForRotation(t)
105	testing.SkipHeights(testRotationPeriodBlocks + 1)
106
107	// Switch to a caller not on the auth list.
108	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("attacker")))
109
110	// Pin the exact authorizable error so this catches an auth
111	// regression rather than any abort. Empty-substring match (the
112	// previous form) accepted any panic, including unrelated ones.
113	uassert.AbortsContains(t, "not in authorized list", func() {
114		UpdateSigningKey(cross, info.Address, rotateTargetPubKey)
115	})
116}
117
118func TestUpdateSigningKey_RejectsReuseOfActiveKey(t *testing.T) {
119	info := registerForRotation(t)
120	testing.SkipHeights(testRotationPeriodBlocks + 1)
121	testing.SetRealm(testing.NewUserRealm(info.Address))
122
123	// Try to "rotate" to the same key — already in registry.
124	uassert.AbortsWithMessage(t, ErrSigningKeyTaken.Error(), func() {
125		UpdateSigningKey(cross, info.Address, info.PubKey)
126	})
127}
128
129func TestUpdateSigningKey_RejectsReuseOfRetiredKey(t *testing.T) {
130	info := registerForRotation(t)
131
132	// First rotation: info.PubKey -> rotateTargetPubKey. info.PubKey
133	// becomes retired.
134	testing.SkipHeights(testRotationPeriodBlocks + 1)
135	testing.SetRealm(testing.NewUserRealm(info.Address))
136	UpdateSigningKey(cross, info.Address, rotateTargetPubKey)
137
138	// Second rotation back to info.PubKey must fail — it's retired
139	// but signingRegistry retains it forever.
140	testing.SkipHeights(testRotationPeriodBlocks + 1)
141	testing.SetRealm(testing.NewUserRealm(info.Address))
142	uassert.AbortsWithMessage(t, ErrSigningKeyTaken.Error(), func() {
143		UpdateSigningKey(cross, info.Address, info.PubKey)
144	})
145}
146
147func TestUpdateSigningKey_LastRotationHeightAdvances(t *testing.T) {
148	info := registerForRotation(t)
149	testing.SkipHeights(testRotationPeriodBlocks + 1)
150	testing.SetRealm(testing.NewUserRealm(info.Address))
151	UpdateSigningKey(cross, info.Address, rotateTargetPubKey)
152
153	v := GetByAddr(info.Address)
154	uassert.True(t, v.LastRotationHeight > 0, "LastRotationHeight set on rotation")
155
156	// Immediately after, throttle rejects again.
157	uassert.AbortsWithMessage(t, ErrRotationThrottled.Error(), func() {
158		UpdateSigningKey(cross, info.Address, "gpub1pgfj7ard9eg82cjtv4u4xetrwqer2dntxyfzxz3pqddddqg2glc8x4fl7vxjlnr7p5a3czm5kcdp4239sg6yqdc4rc2r5cjrffs")
159	})
160}