code.vegaprotocol.io/vega@v0.79.0/core/staking/stake_verifier_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package staking_test 17 18 import ( 19 "context" 20 "testing" 21 "time" 22 23 bmocks "code.vegaprotocol.io/vega/core/broker/mocks" 24 "code.vegaprotocol.io/vega/core/staking" 25 "code.vegaprotocol.io/vega/core/staking/mocks" 26 "code.vegaprotocol.io/vega/core/types" 27 "code.vegaprotocol.io/vega/core/validators" 28 "code.vegaprotocol.io/vega/libs/num" 29 "code.vegaprotocol.io/vega/logging" 30 31 "github.com/golang/mock/gomock" 32 "github.com/stretchr/testify/assert" 33 ) 34 35 type stakeVerifierTest struct { 36 *staking.StakeVerifier 37 38 ctrl *gomock.Controller 39 tsvc *mocks.MockTimeService 40 broker *bmocks.MockBroker 41 accs *staking.Accounting 42 ocv *mocks.MockEthOnChainVerifier 43 witness *mocks.MockWitness 44 evtfwd *mocks.MockEvtForwarder 45 evtSrc *mocks.MockEthereumEventSource 46 47 onTick func(context.Context, time.Time) 48 } 49 50 func getStakeVerifierTest(t *testing.T) *stakeVerifierTest { 51 t.Helper() 52 ctrl := gomock.NewController(t) 53 broker := bmocks.NewMockBroker(ctrl) 54 log := logging.NewTestLogger() 55 cfg := staking.NewDefaultConfig() 56 ocv := mocks.NewMockEthOnChainVerifier(ctrl) 57 ts := mocks.NewMockTimeService(ctrl) 58 witness := mocks.NewMockWitness(ctrl) 59 evtfwd := mocks.NewMockEvtForwarder(ctrl) 60 evtSrc := mocks.NewMockEthereumEventSource(ctrl) 61 62 accs := staking.NewAccounting(log, cfg, ts, broker, nil, evtfwd, witness, true, evtSrc) 63 64 svt := &stakeVerifierTest{ 65 StakeVerifier: staking.NewStakeVerifier(log, cfg, accs, witness, ts, broker, ocv, evtSrc), 66 ctrl: ctrl, 67 broker: broker, 68 accs: accs, 69 ocv: ocv, 70 tsvc: ts, 71 witness: witness, 72 evtfwd: evtfwd, 73 evtSrc: evtSrc, 74 } 75 svt.onTick = svt.StakeVerifier.OnTick 76 77 return svt 78 } 79 80 func TestStakeVerifier(t *testing.T) { 81 t.Run("can process stake event deposited OK", testProcessStakeEventDepositedOK) 82 t.Run("can process stake event deposited KO", testProcessStakeEventDepositedKO) 83 t.Run("can process stake event removed OK", testProcessStakeEventRemovedOK) 84 t.Run("can process stake event removed KO", testProcessStakeEventRemovedKO) 85 t.Run("can process multiple events OK", testProcessStakeEventMultiOK) 86 t.Run("duplicates", testDuplicates) 87 } 88 89 func testProcessStakeEventDepositedOK(t *testing.T) { 90 stakev := getStakeVerifierTest(t) 91 defer stakev.ctrl.Finish() 92 assert.NotNil(t, stakev) 93 94 stakev.tsvc.EXPECT().GetTimeNow().Times(2) 95 stakev.broker.EXPECT().Send(gomock.Any()).Times(2) 96 97 var f func(interface{}, bool) 98 var evt interface{} 99 stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()). 100 Times(1). 101 DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error { 102 f = fn 103 evt = evtR 104 return nil 105 }) 106 107 event := &types.StakeDeposited{ 108 BlockNumber: 42, 109 LogIndex: 1789, 110 TxID: "somehash", 111 ID: "someid", 112 VegaPubKey: "somepubkey", 113 EthereumAddress: "0xnothex", 114 Amount: num.NewUint(1000), 115 BlockTime: 100000, 116 } 117 118 err := stakev.ProcessStakeDeposited(context.Background(), event) 119 120 assert.NoError(t, err) 121 assert.NotNil(t, f) 122 123 // now we'll use the callback to set the event OK 124 // no expectation there. 125 f(evt, true) 126 127 stakev.broker.EXPECT().Send(gomock.Any()).Times(1) 128 stakev.ocv.EXPECT().GetStakingBridgeAddresses().Times(1) 129 stakev.onTick(context.Background(), time.Unix(10, 0)) 130 131 balance, err := stakev.accs.GetAvailableBalance("somepubkey") 132 assert.NoError(t, err) 133 assert.Equal(t, 1000, int(balance.Uint64())) 134 } 135 136 func testProcessStakeEventDepositedKO(t *testing.T) { 137 stakev := getStakeVerifierTest(t) 138 defer stakev.ctrl.Finish() 139 assert.NotNil(t, stakev) 140 141 stakev.tsvc.EXPECT().GetTimeNow().Times(2) 142 stakev.broker.EXPECT().Send(gomock.Any()).Times(1) 143 144 var f func(interface{}, bool) 145 var evt interface{} 146 stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()). 147 Times(1). 148 DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error { 149 f = fn 150 evt = evtR 151 return nil 152 }) 153 154 event := &types.StakeDeposited{ 155 BlockNumber: 42, 156 LogIndex: 1789, 157 TxID: "somehash", 158 ID: "someid", 159 VegaPubKey: "somepubkey", 160 EthereumAddress: "0xnothex", 161 Amount: num.NewUint(1000), 162 BlockTime: 100000, 163 } 164 165 err := stakev.ProcessStakeDeposited(context.Background(), event) 166 167 assert.NoError(t, err) 168 assert.NotNil(t, f) 169 170 // now we'll use the callback to set the event OK 171 // no expectation there. 172 f(evt, false) 173 174 stakev.broker.EXPECT().Send(gomock.Any()).Times(1) 175 stakev.onTick(context.Background(), time.Unix(10, 0)) 176 177 balance, err := stakev.accs.GetAvailableBalance("somepubkey") 178 assert.EqualError(t, err, staking.ErrNoBalanceForParty.Error()) 179 assert.Equal(t, 0, int(balance.Uint64())) 180 } 181 182 func testProcessStakeEventRemovedOK(t *testing.T) { 183 stakev := getStakeVerifierTest(t) 184 defer stakev.ctrl.Finish() 185 assert.NotNil(t, stakev) 186 187 stakev.tsvc.EXPECT().GetTimeNow().Times(2) 188 stakev.broker.EXPECT().Send(gomock.Any()).Times(2) 189 190 var f func(interface{}, bool) 191 var evt interface{} 192 stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()). 193 Times(1). 194 DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error { 195 f = fn 196 evt = evtR 197 return nil 198 }) 199 200 event := &types.StakeRemoved{ 201 BlockNumber: 42, 202 LogIndex: 1789, 203 TxID: "somehash", 204 ID: "someid", 205 VegaPubKey: "somepubkey", 206 EthereumAddress: "0xnothex", 207 Amount: num.NewUint(1000), 208 BlockTime: 100000, 209 } 210 211 err := stakev.ProcessStakeRemoved(context.Background(), event) 212 213 assert.NoError(t, err) 214 assert.NotNil(t, f) 215 216 // now we'll use the callback to set the event OK 217 // no expectation there. 218 f(evt, true) 219 220 stakev.ocv.EXPECT().GetStakingBridgeAddresses().Times(1) 221 stakev.broker.EXPECT().Send(gomock.Any()).Times(1) 222 stakev.onTick(context.Background(), time.Unix(10, 0)) 223 224 // we get a 0 balance, as the only event is a removed. 225 balance, err := stakev.accs.GetAvailableBalance("somepubkey") 226 assert.NoError(t, err) 227 assert.Equal(t, 0, int(balance.Uint64())) 228 } 229 230 func testProcessStakeEventRemovedKO(t *testing.T) { 231 stakev := getStakeVerifierTest(t) 232 defer stakev.ctrl.Finish() 233 assert.NotNil(t, stakev) 234 235 stakev.tsvc.EXPECT().GetTimeNow().Times(2) 236 stakev.broker.EXPECT().Send(gomock.Any()).Times(1) 237 238 var f func(interface{}, bool) 239 var evt interface{} 240 stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()). 241 Times(1). 242 DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error { 243 f = fn 244 evt = evtR 245 return nil 246 }) 247 248 event := &types.StakeRemoved{ 249 BlockNumber: 42, 250 LogIndex: 1789, 251 TxID: "somehash", 252 ID: "someid", 253 VegaPubKey: "somepubkey", 254 EthereumAddress: "0xnothex", 255 Amount: num.NewUint(1000), 256 BlockTime: 100000, 257 } 258 259 err := stakev.ProcessStakeRemoved(context.Background(), event) 260 261 assert.NoError(t, err) 262 assert.NotNil(t, f) 263 264 // now we'll use the callback to set the event OK 265 // no expectation there. 266 f(evt, false) 267 268 stakev.broker.EXPECT().Send(gomock.Any()).Times(1) 269 stakev.onTick(context.Background(), time.Unix(10, 0)) 270 271 balance, err := stakev.accs.GetAvailableBalance("somepubkey") 272 assert.EqualError(t, err, staking.ErrNoBalanceForParty.Error()) 273 assert.Equal(t, 0, int(balance.Uint64())) 274 } 275 276 func testProcessStakeEventMultiOK(t *testing.T) { 277 stakev := getStakeVerifierTest(t) 278 defer stakev.ctrl.Finish() 279 assert.NotNil(t, stakev) 280 281 stakev.tsvc.EXPECT().GetTimeNow().Times(2) 282 stakev.broker.EXPECT().Send(gomock.Any()).Times(2) 283 stakev.ocv.EXPECT().GetStakingBridgeAddresses().AnyTimes() 284 285 var f func(interface{}, bool) 286 var evt interface{} 287 stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()). 288 Times(1). 289 DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error { 290 f = fn 291 evt = evtR 292 return nil 293 }) 294 295 event := &types.StakeDeposited{ 296 BlockNumber: 42, 297 LogIndex: 1789, 298 TxID: "somehash", 299 ID: "someid", 300 VegaPubKey: "somepubkey", 301 EthereumAddress: "0xnothex", 302 Amount: num.NewUint(1000), 303 BlockTime: 100000, 304 } 305 306 err := stakev.ProcessStakeDeposited(context.Background(), event) 307 308 assert.NoError(t, err) 309 assert.NotNil(t, f) 310 311 // now we'll use the callback to set the event OK 312 // no expectation there. 313 f(evt, true) 314 315 stakev.broker.EXPECT().Send(gomock.Any()).Times(1) 316 stakev.onTick(context.Background(), time.Unix(10, 0)) 317 318 balance, err := stakev.accs.GetAvailableBalance("somepubkey") 319 assert.NoError(t, err) 320 assert.Equal(t, 1000, int(balance.Uint64())) 321 322 // no we remove some stake 323 324 stakev.tsvc.EXPECT().GetTimeNow().Times(2) 325 stakev.broker.EXPECT().Send(gomock.Any()).Times(1) 326 f = nil 327 evt = nil 328 stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()). 329 Times(1). 330 DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error { 331 f = fn 332 evt = evtR 333 return nil 334 }) 335 336 eventR := &types.StakeRemoved{ 337 BlockNumber: 42, 338 LogIndex: 1789, 339 TxID: "somehash", 340 ID: "someid2", 341 VegaPubKey: "somepubkey", 342 EthereumAddress: "0xnothex", 343 Amount: num.NewUint(500), 344 BlockTime: 200000, 345 } 346 347 err = stakev.ProcessStakeRemoved(context.Background(), eventR) 348 349 assert.NoError(t, err) 350 assert.NotNil(t, f) 351 352 // now we'll use the callback to set the event OK 353 // no expectation there. 354 f(evt, true) 355 356 stakev.broker.EXPECT().Send(gomock.Any()).Times(1) 357 stakev.onTick(context.Background(), time.Unix(10, 0)) 358 359 balance, err = stakev.accs.GetAvailableBalance("somepubkey") 360 assert.NoError(t, err) 361 assert.Equal(t, 500, int(balance.Uint64())) 362 } 363 364 func testDuplicates(t *testing.T) { 365 stakev := getStakeVerifierTest(t) 366 defer stakev.ctrl.Finish() 367 assert.NotNil(t, stakev) 368 369 stakev.tsvc.EXPECT().GetTimeNow().Times(1) 370 stakev.broker.EXPECT().Send(gomock.Any()).Times(1) 371 372 stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()). 373 AnyTimes() 374 event := &types.StakeDeposited{ 375 BlockNumber: 42, 376 LogIndex: 1789, 377 TxID: "somehash", 378 ID: "someid", 379 VegaPubKey: "somepubkey", 380 EthereumAddress: "0xnothex", 381 Amount: num.NewUint(1000), 382 BlockTime: 100000, 383 } 384 385 // no error at first 386 err := stakev.ProcessStakeDeposited(context.Background(), event) 387 assert.NoError(t, err) 388 // same event 389 err = stakev.ProcessStakeDeposited(context.Background(), event) 390 assert.EqualError(t, err, staking.ErrDuplicatedStakeDepositedEvent.Error()) 391 392 event2 := &types.StakeRemoved{ 393 BlockNumber: 42, 394 LogIndex: 1789, 395 TxID: "somehash", 396 ID: "someid", 397 VegaPubKey: "somepubkey", 398 EthereumAddress: "0xnothex", 399 Amount: num.NewUint(1000), 400 BlockTime: 100000, 401 } 402 // stake removed now 403 err = stakev.ProcessStakeRemoved(context.Background(), event2) 404 assert.EqualError(t, err, staking.ErrDuplicatedStakeRemovedEvent.Error()) 405 }