Skip to content

Commit 0a3f9f2

Browse files
Vanja/chore/increase test coverage (#1250)
* Add coverage to builtin/solidity * Add licence * Add unit tests on aggregation and delegation * Add unit tests
1 parent 23106e0 commit 0a3f9f2

7 files changed

Lines changed: 1123 additions & 0 deletions

File tree

builtin/staker/delta/delta_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (c) 2025 The VeChainThor developers
2+
//
3+
// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying
4+
// file LICENSE or <https://www.gnu.org/licenses/lgpl-3.0.html>
5+
package delta
6+
7+
import (
8+
"math/big"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
func TestNewRenewal_Defaults(t *testing.T) {
15+
r := NewRenewal()
16+
assert.Equal(t, big.NewInt(0), r.NewLockedVET)
17+
assert.Equal(t, big.NewInt(0), r.NewLockedWeight)
18+
assert.Equal(t, big.NewInt(0), r.QueuedDecrease)
19+
assert.Equal(t, big.NewInt(0), r.QueuedDecreaseWeight)
20+
}
21+
22+
func TestRenewal_Add(t *testing.T) {
23+
base := &Renewal{
24+
NewLockedVET: big.NewInt(10),
25+
NewLockedWeight: big.NewInt(20),
26+
QueuedDecrease: big.NewInt(30),
27+
QueuedDecreaseWeight: big.NewInt(40),
28+
}
29+
inc := &Renewal{
30+
NewLockedVET: big.NewInt(1),
31+
NewLockedWeight: big.NewInt(2),
32+
QueuedDecrease: big.NewInt(3),
33+
QueuedDecreaseWeight: big.NewInt(4),
34+
}
35+
36+
got := base.Add(inc)
37+
assert.Same(t, base, got)
38+
assert.Equal(t, big.NewInt(11), got.NewLockedVET)
39+
assert.Equal(t, big.NewInt(22), got.NewLockedWeight)
40+
assert.Equal(t, big.NewInt(33), got.QueuedDecrease)
41+
assert.Equal(t, big.NewInt(44), got.QueuedDecreaseWeight)
42+
}
43+
44+
func TestRenewal_Add_Nil(t *testing.T) {
45+
base := &Renewal{
46+
NewLockedVET: big.NewInt(5),
47+
NewLockedWeight: big.NewInt(6),
48+
QueuedDecrease: big.NewInt(7),
49+
QueuedDecreaseWeight: big.NewInt(8),
50+
}
51+
got := base.Add(nil)
52+
assert.Same(t, base, got)
53+
assert.Equal(t, big.NewInt(5), got.NewLockedVET)
54+
assert.Equal(t, big.NewInt(6), got.NewLockedWeight)
55+
assert.Equal(t, big.NewInt(7), got.QueuedDecrease)
56+
assert.Equal(t, big.NewInt(8), got.QueuedDecreaseWeight)
57+
}
58+
59+
func TestExit_Add(t *testing.T) {
60+
base := &Exit{
61+
ExitedTVL: big.NewInt(100),
62+
ExitedTVLWeight: big.NewInt(200),
63+
QueuedDecrease: big.NewInt(300),
64+
QueuedDecreaseWeight: big.NewInt(400),
65+
}
66+
inc := &Exit{
67+
ExitedTVL: big.NewInt(1),
68+
ExitedTVLWeight: big.NewInt(2),
69+
QueuedDecrease: big.NewInt(3),
70+
QueuedDecreaseWeight: big.NewInt(4),
71+
}
72+
73+
got := base.Add(inc)
74+
assert.Same(t, base, got)
75+
assert.Equal(t, big.NewInt(101), got.ExitedTVL)
76+
assert.Equal(t, big.NewInt(202), got.ExitedTVLWeight)
77+
assert.Equal(t, big.NewInt(303), got.QueuedDecrease)
78+
assert.Equal(t, big.NewInt(404), got.QueuedDecreaseWeight)
79+
}
80+
81+
func TestExit_Add_Nil(t *testing.T) {
82+
base := &Exit{
83+
ExitedTVL: big.NewInt(10),
84+
ExitedTVLWeight: big.NewInt(20),
85+
QueuedDecrease: big.NewInt(30),
86+
QueuedDecreaseWeight: big.NewInt(40),
87+
}
88+
got := base.Add(nil)
89+
assert.Same(t, base, got)
90+
assert.Equal(t, big.NewInt(10), got.ExitedTVL)
91+
assert.Equal(t, big.NewInt(20), got.ExitedTVLWeight)
92+
assert.Equal(t, big.NewInt(30), got.QueuedDecrease)
93+
assert.Equal(t, big.NewInt(40), got.QueuedDecreaseWeight)
94+
}

builtin/staker/globalstats/service.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ func (s *Service) QueuedStake() (*big.Int, *big.Int, error) {
5454
// ApplyRenewal adjusts global totals during validator/delegation transitions.
5555
// Called when validators are activated or delegations move between states.
5656
func (s *Service) ApplyRenewal(renewal *delta.Renewal) error {
57+
if renewal.NewLockedVET == nil {
58+
renewal.NewLockedVET = new(big.Int)
59+
}
60+
if renewal.NewLockedWeight == nil {
61+
renewal.NewLockedWeight = new(big.Int)
62+
}
63+
if renewal.QueuedDecreaseWeight == nil {
64+
renewal.QueuedDecreaseWeight = new(big.Int)
65+
}
66+
if renewal.QueuedDecrease == nil {
67+
renewal.QueuedDecrease = new(big.Int)
68+
}
5769
if err := s.lockedVET.Add(renewal.NewLockedVET); err != nil {
5870
return err
5971
}
@@ -71,6 +83,19 @@ func (s *Service) ApplyRenewal(renewal *delta.Renewal) error {
7183
}
7284

7385
func (s *Service) ApplyExit(exit *delta.Exit) error {
86+
if exit.ExitedTVL == nil {
87+
exit.ExitedTVL = new(big.Int)
88+
}
89+
if exit.ExitedTVLWeight == nil {
90+
exit.ExitedTVLWeight = new(big.Int)
91+
}
92+
if exit.QueuedDecreaseWeight == nil {
93+
exit.QueuedDecreaseWeight = new(big.Int)
94+
}
95+
if exit.QueuedDecrease == nil {
96+
exit.QueuedDecrease = new(big.Int)
97+
}
98+
7499
if err := s.lockedVET.Sub(exit.ExitedTVL); err != nil {
75100
return err
76101
}
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// Copyright (c) 2025 The VeChainThor developers
2+
//
3+
// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying
4+
// file LICENSE or <https://www.gnu.org/licenses/lgpl-3.0.html>
5+
package globalstats
6+
7+
import (
8+
"math/big"
9+
"testing"
10+
11+
"github.com/ethereum/go-ethereum/rlp"
12+
"github.com/stretchr/testify/assert"
13+
14+
"github.com/vechain/thor/v2/builtin/solidity"
15+
"github.com/vechain/thor/v2/builtin/staker/delta"
16+
"github.com/vechain/thor/v2/builtin/staker/stakes"
17+
"github.com/vechain/thor/v2/muxdb"
18+
"github.com/vechain/thor/v2/state"
19+
"github.com/vechain/thor/v2/thor"
20+
"github.com/vechain/thor/v2/trie"
21+
)
22+
23+
func poisonUintSlot(st *state.State, contract thor.Address, slot thor.Bytes32) {
24+
st.SetRawStorage(contract, slot, rlp.RawValue{0xFF})
25+
}
26+
27+
func setUintSlot(st *state.State, contract thor.Address, slot thor.Bytes32, v *big.Int) {
28+
st.SetStorage(contract, slot, thor.BytesToBytes32(v.Bytes()))
29+
}
30+
31+
var maxUint256 = func() *big.Int {
32+
return new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
33+
}()
34+
35+
func newSvc() (*Service, thor.Address, *state.State) {
36+
db := muxdb.NewMem()
37+
st := state.New(db, trie.Root{})
38+
addr := thor.BytesToAddress([]byte("gs"))
39+
svc := New(solidity.NewContext(addr, st, nil))
40+
return svc, addr, st
41+
}
42+
43+
func TestService_QueuedStake_Empty(t *testing.T) {
44+
svc, _, _ := newSvc()
45+
qVET, qW, err := svc.QueuedStake()
46+
assert.NoError(t, err)
47+
assert.Equal(t, big.NewInt(0).String(), qVET.String())
48+
assert.Equal(t, big.NewInt(0).String(), qW.String())
49+
50+
qVET, qW, err = svc.GetQueuedStake()
51+
assert.NoError(t, err)
52+
assert.Equal(t, big.NewInt(0).String(), qVET.String())
53+
assert.Equal(t, big.NewInt(0).String(), qW.String())
54+
}
55+
56+
func TestService_AddRemove_Queued(t *testing.T) {
57+
svc, _, _ := newSvc()
58+
59+
st := stakes.NewWeightedStake(big.NewInt(1000), 200) // weight: 2000
60+
assert.NoError(t, svc.AddQueued(st))
61+
62+
qVET, qW, err := svc.QueuedStake()
63+
assert.NoError(t, err)
64+
assert.Equal(t, big.NewInt(1000), qVET)
65+
assert.Equal(t, big.NewInt(2000), qW)
66+
67+
assert.NoError(t, svc.RemoveQueued(st))
68+
qVET, qW, err = svc.QueuedStake()
69+
assert.NoError(t, err)
70+
assert.Equal(t, big.NewInt(0).String(), qVET.String())
71+
assert.Equal(t, big.NewInt(0).String(), qW.String())
72+
}
73+
74+
func TestService_ApplyRenewal(t *testing.T) {
75+
svc, _, _ := newSvc()
76+
77+
// seed some queued for decrease
78+
assert.NoError(t, svc.AddQueued(stakes.NewWeightedStake(big.NewInt(500), 200))) // weight 1000
79+
80+
r := &delta.Renewal{
81+
NewLockedVET: big.NewInt(300),
82+
NewLockedWeight: big.NewInt(600),
83+
QueuedDecrease: big.NewInt(500),
84+
QueuedDecreaseWeight: big.NewInt(1000),
85+
}
86+
assert.NoError(t, svc.ApplyRenewal(r))
87+
88+
lockedV, lockedW, err := svc.GetLockedVET()
89+
assert.NoError(t, err)
90+
assert.Equal(t, big.NewInt(300), lockedV)
91+
assert.Equal(t, big.NewInt(600), lockedW)
92+
93+
qVET, qW, err := svc.QueuedStake()
94+
assert.NoError(t, err)
95+
assert.Equal(t, big.NewInt(0).String(), qVET.String())
96+
assert.Equal(t, big.NewInt(0).String(), qW.String())
97+
}
98+
99+
func TestService_ApplyExit(t *testing.T) {
100+
svc, _, _ := newSvc()
101+
102+
assert.NoError(t, svc.ApplyRenewal(&delta.Renewal{
103+
NewLockedVET: big.NewInt(1000),
104+
NewLockedWeight: big.NewInt(2000),
105+
}))
106+
107+
assert.NoError(t, svc.AddQueued(stakes.NewWeightedStake(big.NewInt(300), 200))) // weight 600
108+
109+
exit := &delta.Exit{
110+
ExitedTVL: big.NewInt(400),
111+
ExitedTVLWeight: big.NewInt(800),
112+
QueuedDecrease: big.NewInt(300),
113+
QueuedDecreaseWeight: big.NewInt(600),
114+
}
115+
assert.NoError(t, svc.ApplyExit(exit))
116+
117+
lockedV, lockedW, err := svc.GetLockedVET()
118+
assert.NoError(t, err)
119+
assert.Equal(t, big.NewInt(600), lockedV) // 1000 - 400
120+
assert.Equal(t, big.NewInt(1200), lockedW) // 2000 - 800
121+
122+
qVET, qW, err := svc.QueuedStake()
123+
assert.NoError(t, err)
124+
assert.Equal(t, big.NewInt(0).String(), qVET.String())
125+
assert.Equal(t, big.NewInt(0).String(), qW.String())
126+
}
127+
128+
func TestService_QueuedStake_GetQueuedVetError(t *testing.T) {
129+
svc, addr, st := newSvc()
130+
poisonUintSlot(st, addr, slotQueuedVET)
131+
132+
_, _, err := svc.QueuedStake()
133+
assert.Error(t, err)
134+
}
135+
136+
func TestService_QueuedStake_GetQueuedWeightError(t *testing.T) {
137+
svc, addr, st := newSvc()
138+
setUintSlot(st, addr, slotQueuedVET, big.NewInt(0))
139+
poisonUintSlot(st, addr, slotQueuedWeight)
140+
141+
_, _, err := svc.QueuedStake()
142+
assert.Error(t, err)
143+
}
144+
145+
func TestService_GetLockedVET_GetLockedVetError(t *testing.T) {
146+
svc, addr, st := newSvc()
147+
poisonUintSlot(st, addr, slotLockedVET)
148+
149+
_, _, err := svc.GetLockedVET()
150+
assert.Error(t, err)
151+
}
152+
153+
func TestService_GetLockedVET_GetLockedWeightError(t *testing.T) {
154+
svc, addr, st := newSvc()
155+
setUintSlot(st, addr, slotLockedVET, big.NewInt(0))
156+
poisonUintSlot(st, addr, slotLockedWeight)
157+
158+
_, _, err := svc.GetLockedVET()
159+
assert.Error(t, err)
160+
}
161+
162+
func TestService_AddQueued_QueuedVetOverflow(t *testing.T) {
163+
svc, addr, st := newSvc()
164+
setUintSlot(st, addr, slotQueuedVET, new(big.Int).Set(maxUint256))
165+
setUintSlot(st, addr, slotQueuedWeight, new(big.Int).Set(maxUint256))
166+
167+
err := svc.AddQueued(stakes.NewWeightedStake(big.NewInt(1), 1))
168+
assert.ErrorContains(t, err, "uint256 overflow")
169+
170+
setUintSlot(st, addr, slotQueuedVET, new(big.Int).SetUint64(1))
171+
err = svc.AddQueued(stakes.NewWeightedStake(big.NewInt(1), 100))
172+
assert.ErrorContains(t, err, "uint256 overflow")
173+
}
174+
175+
func TestService_RemoveQueued_QueuedVetNegative(t *testing.T) {
176+
svc, _, _ := newSvc()
177+
err := svc.RemoveQueued(stakes.NewWeightedStake(big.NewInt(1), 1))
178+
assert.ErrorContains(t, err, "queued-stake uint256 cannot be negative")
179+
}
180+
181+
func TestService_ApplyRenewal_LockedVetOverflow(t *testing.T) {
182+
svc, addr, st := newSvc()
183+
setUintSlot(st, addr, slotLockedVET, new(big.Int).Set(maxUint256))
184+
r := &delta.Renewal{NewLockedVET: big.NewInt(1)}
185+
186+
err := svc.ApplyRenewal(r)
187+
assert.ErrorContains(t, err, "uint256 overflow")
188+
}
189+
190+
func TestService_ApplyRenewal_LockedWeightOverflow(t *testing.T) {
191+
svc, addr, st := newSvc()
192+
setUintSlot(st, addr, slotLockedWeight, new(big.Int).Set(maxUint256))
193+
r := &delta.Renewal{NewLockedWeight: big.NewInt(1)}
194+
195+
err := svc.ApplyRenewal(r)
196+
assert.ErrorContains(t, err, "uint256 overflow")
197+
}
198+
199+
func TestService_ApplyRenewal_QueuedVetNegative(t *testing.T) {
200+
svc, _, _ := newSvc()
201+
r := &delta.Renewal{QueuedDecrease: big.NewInt(1)}
202+
err := svc.ApplyRenewal(r)
203+
assert.ErrorContains(t, err, "queued-stake uint256 cannot be negative")
204+
}
205+
206+
func TestService_ApplyRenewal_QueuedWeightNegative(t *testing.T) {
207+
svc, _, _ := newSvc()
208+
r := &delta.Renewal{QueuedDecreaseWeight: big.NewInt(1)}
209+
err := svc.ApplyRenewal(r)
210+
assert.ErrorContains(t, err, "queued-weight uint256 cannot be negative")
211+
}
212+
213+
func TestService_ApplyExit_Negative(t *testing.T) {
214+
svc, _, _ := newSvc()
215+
err := svc.ApplyExit(&delta.Exit{ExitedTVL: big.NewInt(1)})
216+
assert.ErrorContains(t, err, "total-stake uint256 cannot be negative")
217+
218+
err = svc.ApplyExit(&delta.Exit{ExitedTVLWeight: big.NewInt(1)})
219+
assert.ErrorContains(t, err, "total-weight uint256 cannot be negative")
220+
221+
err = svc.ApplyExit(&delta.Exit{QueuedDecrease: big.NewInt(1)})
222+
assert.ErrorContains(t, err, "queued-stake uint256 cannot be negative")
223+
224+
err = svc.ApplyExit(&delta.Exit{QueuedDecreaseWeight: big.NewInt(1)})
225+
assert.ErrorContains(t, err, "queued-weight uint256 cannot be negative")
226+
}

0 commit comments

Comments
 (0)