Search Apps Documentation Source Content File Folder Download Copy Actions Download

validators_test.gno

4.76 Kb · 178 lines
  1package validators
  2
  3import (
  4	"chain/runtime"
  5	"math"
  6	"testing"
  7
  8	"gno.land/p/nt/bptree/v0"
  9	"gno.land/p/nt/poa/v0"
 10	"gno.land/p/nt/testutils/v0"
 11	"gno.land/p/nt/uassert/v0"
 12	"gno.land/p/nt/ufmt/v0"
 13	"gno.land/p/sys/validators"
 14)
 15
 16// cur is a zero-value realm used as a placeholder when forwarding to
 17// uassert/urequire dispatch helpers that gained an `rlm realm` param.
 18// These tests pass `func()` callbacks (no crossing inside the callback),
 19// so rlm is ignored — a nil realm here is safe.
 20var cur realm
 21
 22// generateTestValidators generates a dummy validator set
 23func generateTestValidators(count int) []validators.Validator {
 24	vals := make([]validators.Validator, 0, count)
 25
 26	for i := 0; i < count; i++ {
 27		val := validators.Validator{
 28			Address:     testutils.TestAddress(ufmt.Sprintf("%d", i)),
 29			PubKey:      "public-key",
 30			VotingPower: 10,
 31		}
 32
 33		vals = append(vals, val)
 34	}
 35
 36	return vals
 37}
 38
 39func TestValidators_AddRemove(t *testing.T) {
 40	// Clear any changes
 41	changes = bptree.NewBPTree32()
 42
 43	var (
 44		vals          = generateTestValidators(100)
 45		initialHeight = int64(123)
 46	)
 47
 48	// Add in the validators
 49	for _, val := range vals {
 50		addValidator(val)
 51
 52		// Make sure the validator is added
 53		uassert.True(t, vp.IsValidator(val.Address))
 54
 55		testing.SkipHeights(1)
 56	}
 57
 58	for i := initialHeight; i < initialHeight+int64(len(vals)); i++ {
 59		// Make sure the changes are saved
 60		chs := GetChanges(i, initialHeight+int64(len(vals)))
 61
 62		// We use the funky index calculation to make sure
 63		// changes are properly handled for each block span
 64		uassert.Equal(t, initialHeight+int64(len(vals))-i, int64(len(chs)))
 65
 66		for index, val := range vals[i-initialHeight:] {
 67			// Make sure the changes are equal to the additions
 68			ch := chs[index]
 69
 70			uassert.Equal(t, val.Address, ch.Address)
 71			uassert.Equal(t, val.PubKey, ch.PubKey)
 72			uassert.Equal(t, val.VotingPower, ch.VotingPower)
 73		}
 74	}
 75
 76	// Save the beginning height for the removal
 77	initialRemoveHeight := runtime.ChainHeight()
 78
 79	// Clear any changes
 80	changes = bptree.NewBPTree32()
 81
 82	// Remove the validators
 83	for _, val := range vals {
 84		removeValidator(val.Address)
 85
 86		// Make sure the validator is removed
 87		uassert.False(t, vp.IsValidator(val.Address))
 88
 89		testing.SkipHeights(1)
 90	}
 91
 92	for i := initialRemoveHeight; i < initialRemoveHeight+int64(len(vals)); i++ {
 93		// Make sure the changes are saved
 94		chs := GetChanges(i, initialRemoveHeight+int64(len(vals)))
 95
 96		// We use the funky index calculation to make sure
 97		// changes are properly handled for each block span
 98		uassert.Equal(t, initialRemoveHeight+int64(len(vals))-i, int64(len(chs)))
 99
100		for index, val := range vals[i-initialRemoveHeight:] {
101			// Make sure the changes are equal to the additions
102			ch := chs[index]
103
104			uassert.Equal(t, val.Address, ch.Address)
105			uassert.Equal(t, val.PubKey, ch.PubKey)
106			uassert.Equal(t, uint64(0), ch.VotingPower)
107		}
108	}
109}
110
111// TestGetChanges_BoundedRange verifies that GetChanges(from, to) correctly
112// returns only changes within the [from, to] block range.
113func TestGetChanges_BoundedRange(t *testing.T) {
114	changes = bptree.NewBPTree32()
115	vp = poa.NewPoA()
116
117	vals := generateTestValidators(3)
118
119	// Store additions at block h1
120	h1 := runtime.ChainHeight()
121	for _, val := range vals {
122		addValidator(val)
123	}
124	testing.SkipHeights(1)
125
126	// Store removals at block h2
127	h2 := runtime.ChainHeight()
128	for _, val := range vals {
129		removeValidator(val.Address)
130	}
131	testing.SkipHeights(1)
132
133	// Query spanning both blocks returns all changes
134	all := GetChanges(h1, h2)
135	uassert.Equal(t, 6, len(all))
136
137	// Query for h1 only returns additions
138	atH1 := GetChanges(h1, h1)
139	uassert.Equal(t, 3, len(atH1))
140	for i, ch := range atH1 {
141		uassert.Equal(t, vals[i].Address, ch.Address)
142		uassert.True(t, ch.VotingPower > 0)
143	}
144
145	// Query for h2 only returns removals
146	atH2 := GetChanges(h2, h2)
147	uassert.Equal(t, 3, len(atH2))
148	for i, ch := range atH2 {
149		uassert.Equal(t, vals[i].Address, ch.Address)
150		uassert.Equal(t, uint64(0), ch.VotingPower)
151	}
152
153	// Query beyond stored range returns empty
154	uassert.Equal(t, 0, len(GetChanges(h2+1, h2+1)))
155}
156
157func TestGetChanges_PanicsOnInvalidRange(cur realm, t *testing.T) {
158	uassert.PanicsWithMessage(t, cur, "invalid range: from must be <= to", func() {
159		GetChanges(10, 5)
160	})
161}
162
163func TestGetChanges_ClampsMaxInt64(t *testing.T) {
164	changes = bptree.NewBPTree32()
165
166	vals := generateTestValidators(1)
167
168	// Simulate a validator change at block math.MaxInt64-1 (the boundary value).
169	changes.Set(getBlockID(math.MaxInt64-1), []change{
170		{blockNum: math.MaxInt64 - 1, validator: vals[0]},
171	})
172
173	// Passing math.MaxInt64 as "to" means "get all updates from here onwards".
174	// The clamp (to = MaxInt64-1) must still include the boundary block.
175	result := GetChanges(math.MaxInt64-1, math.MaxInt64)
176	uassert.Equal(t, 1, len(result))
177	uassert.Equal(t, vals[0].Address, result[0].Address)
178}