Search Apps Documentation Source Content File Folder Download Copy Actions Download

validators_test.gno

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