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