code.vegaprotocol.io/vega@v0.79.0/core/banking/snapshot_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 banking_test 17 18 import ( 19 "bytes" 20 "context" 21 "encoding/hex" 22 "strconv" 23 "testing" 24 "time" 25 26 "code.vegaprotocol.io/vega/core/assets" 27 "code.vegaprotocol.io/vega/core/assets/builtin" 28 "code.vegaprotocol.io/vega/core/integration/stubs" 29 "code.vegaprotocol.io/vega/core/snapshot" 30 snp "code.vegaprotocol.io/vega/core/snapshot" 31 "code.vegaprotocol.io/vega/core/stats" 32 "code.vegaprotocol.io/vega/core/types" 33 "code.vegaprotocol.io/vega/libs/crypto" 34 "code.vegaprotocol.io/vega/libs/num" 35 "code.vegaprotocol.io/vega/libs/proto" 36 vgtest "code.vegaprotocol.io/vega/libs/test" 37 "code.vegaprotocol.io/vega/logging" 38 "code.vegaprotocol.io/vega/paths" 39 "code.vegaprotocol.io/vega/protos/vega" 40 snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1" 41 42 "github.com/golang/mock/gomock" 43 "github.com/stretchr/testify/assert" 44 "github.com/stretchr/testify/require" 45 ) 46 47 func deposit(eng *testEngine, asset, party string, amount *num.Uint) *types.BuiltinAssetDeposit { 48 eng.OnTick(context.Background(), time.Now()) 49 return depositAt(eng, asset, party, amount) 50 } 51 52 func depositAt(eng *testEngine, asset, party string, amount *num.Uint) *types.BuiltinAssetDeposit { 53 eng.tsvc.EXPECT().GetTimeNow().AnyTimes() 54 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 55 eng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil) 56 return &types.BuiltinAssetDeposit{ 57 VegaAssetID: asset, 58 PartyID: party, 59 Amount: amount, 60 } 61 } 62 63 func testEngineAndSnapshot(t *testing.T, vegaPath paths.Paths, now time.Time) (*testEngine, *snp.Engine) { 64 t.Helper() 65 eng := getTestEngine(t) 66 log := logging.NewTestLogger() 67 timeService := stubs.NewTimeStub() 68 timeService.SetTime(now) 69 statsData := stats.New(log, stats.NewDefaultConfig()) 70 config := snp.DefaultConfig() 71 snapshotEngine, err := snp.NewEngine(vegaPath, config, log, timeService, statsData.Blockchain) 72 require.NoError(t, err) 73 snapshotEngine.AddProviders(eng.Engine) 74 return eng, snapshotEngine 75 } 76 77 func TestSnapshotRoundTripViaEngine(t *testing.T) { 78 ctx := vgtest.VegaContext("chainid", 100) 79 80 now := time.Now() 81 deliver := now.Add(time.Hour) 82 83 testAsset := assets.NewAsset(builtin.New("VGT", &types.AssetDetails{ 84 Name: "VEGA TOKEN", 85 Symbol: "VGT", 86 })) 87 88 fromAcc := types.Account{ 89 Balance: num.NewUint(1000), 90 } 91 92 oneoff := &types.TransferFunds{ 93 Kind: types.TransferCommandKindOneOff, 94 OneOff: &types.OneOffTransfer{ 95 TransferBase: &types.TransferBase{ 96 From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", 97 FromAccountType: types.AccountTypeGeneral, 98 To: "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0", 99 ToAccountType: types.AccountTypeGeneral, 100 Asset: "eth", 101 Amount: num.NewUint(10), 102 Reference: "someref", 103 }, 104 DeliverOn: &deliver, 105 }, 106 } 107 108 oneoff2 := &types.TransferFunds{ 109 Kind: types.TransferCommandKindOneOff, 110 OneOff: &types.OneOffTransfer{ 111 TransferBase: &types.TransferBase{ 112 From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", 113 FromAccountType: types.AccountTypeGeneral, 114 To: "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0", 115 ToAccountType: types.AccountTypeGeneral, 116 Asset: "eth", 117 Amount: num.NewUint(10), 118 Reference: "someref", 119 }, 120 DeliverOn: &deliver, 121 }, 122 } 123 124 recurring := &types.TransferFunds{ 125 Kind: types.TransferCommandKindRecurring, 126 Recurring: &types.RecurringTransfer{ 127 TransferBase: &types.TransferBase{ 128 From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", 129 FromAccountType: types.AccountTypeGeneral, 130 To: "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0", 131 ToAccountType: types.AccountTypeGeneral, 132 Asset: "eth", 133 Amount: num.NewUint(10), 134 Reference: "someref", 135 }, 136 StartEpoch: 10, 137 EndEpoch: nil, // forever 138 Factor: num.MustDecimalFromString("0.9"), 139 }, 140 } 141 142 recurring2 := &types.TransferFunds{ 143 Kind: types.TransferCommandKindRecurring, 144 Recurring: &types.RecurringTransfer{ 145 TransferBase: &types.TransferBase{ 146 From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", 147 FromAccountType: types.AccountTypeGeneral, 148 To: "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccee", 149 ToAccountType: types.AccountTypeGeneral, 150 Asset: "vega", 151 Amount: num.NewUint(10), 152 Reference: "someref", 153 }, 154 StartEpoch: 10, 155 EndEpoch: nil, // forever 156 Factor: num.MustDecimalFromString("0.9"), 157 }, 158 } 159 160 d1 := &types.BuiltinAssetDeposit{ 161 VegaAssetID: "VGT1", 162 PartyID: "someparty1", 163 Amount: num.NewUint(42), 164 } 165 d2 := &types.BuiltinAssetDeposit{ 166 VegaAssetID: "VGT1", 167 PartyID: "someparty2", 168 Amount: num.NewUint(24), 169 } 170 d3 := &types.BuiltinAssetDeposit{ 171 VegaAssetID: "VGT1", 172 PartyID: "someparty3", 173 Amount: num.NewUint(29), 174 } 175 176 vegaPath := paths.New(t.TempDir()) 177 178 bankingEngine1, snapshotEngine1 := testEngineAndSnapshot(t, vegaPath, now) 179 closeSnapshotEngine1 := vgtest.OnlyOnce(snapshotEngine1.Close) 180 defer closeSnapshotEngine1() 181 182 bankingEngine1.tsvc.EXPECT().GetTimeNow().Return(now).AnyTimes() 183 bankingEngine1.broker.EXPECT().Send(gomock.Any()).AnyTimes() 184 bankingEngine1.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil) 185 bankingEngine1.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() 186 bankingEngine1.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).AnyTimes().Return(&fromAcc, nil) 187 bankingEngine1.col.EXPECT().Withdraw(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&types.LedgerMovement{}, nil) 188 189 require.NoError(t, snapshotEngine1.Start(context.Background())) 190 191 require.NoError(t, bankingEngine1.DepositBuiltinAsset(context.Background(), d1, "depositid1", 42)) 192 require.NoError(t, bankingEngine1.DepositBuiltinAsset(context.Background(), d2, "depositid2", 24)) 193 require.NoError(t, bankingEngine1.WithdrawBuiltinAsset(context.Background(), "VGT1", "someparty1", "VGT1", num.NewUint(2))) 194 require.NoError(t, bankingEngine1.WithdrawBuiltinAsset(context.Background(), "VGT1", "someparty2", "VGT1", num.NewUint(4))) 195 196 bankingEngine1.OnTick(ctx, now) 197 198 require.NoError(t, bankingEngine1.TransferFunds(ctx, oneoff)) 199 require.NoError(t, bankingEngine1.TransferFunds(ctx, recurring)) 200 201 // Take a snapshot. 202 hash1, err := snapshotEngine1.SnapshotNow(ctx) 203 require.NoError(t, err) 204 205 // Additional steps to execute the same way on the next engine to verify it yield the same result. 206 additionalSteps := func(bankingEngine *testEngine) { 207 bankingEngine.OnTick(ctx, now) 208 209 require.NoError(t, bankingEngine.DepositBuiltinAsset(context.Background(), d3, "depositid3", 29)) 210 require.NoError(t, bankingEngine.WithdrawBuiltinAsset(context.Background(), "VGT1", "someparty1", "VGT1", num.NewUint(10))) 211 require.NoError(t, bankingEngine.WithdrawBuiltinAsset(context.Background(), "VGT1", "someparty2", "VGT1", num.NewUint(5))) 212 213 bankingEngine.OnTick(ctx, now) 214 215 require.NoError(t, bankingEngine.TransferFunds(ctx, oneoff2)) 216 require.NoError(t, bankingEngine.TransferFunds(ctx, recurring2)) 217 } 218 219 additionalSteps(bankingEngine1) 220 221 state1 := map[string][]byte{} 222 for _, key := range bankingEngine1.Keys() { 223 state, additionalProvider, err := bankingEngine1.GetState(key) 224 require.NoError(t, err) 225 assert.Empty(t, additionalProvider) 226 state1[key] = state 227 } 228 229 closeSnapshotEngine1() 230 231 // Reload the engine using the previous snapshot. 232 233 bankingEngine2, snapshotEngine2 := testEngineAndSnapshot(t, vegaPath, now) 234 defer snapshotEngine2.Close() 235 236 bankingEngine2.tsvc.EXPECT().GetTimeNow().Return(now).AnyTimes() 237 bankingEngine2.broker.EXPECT().Send(gomock.Any()).AnyTimes() 238 bankingEngine2.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil) 239 bankingEngine2.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() 240 bankingEngine2.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).AnyTimes().Return(&fromAcc, nil) 241 bankingEngine2.col.EXPECT().Withdraw(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&types.LedgerMovement{}, nil) 242 243 // This triggers the state restoration from the local snapshot. 244 require.NoError(t, snapshotEngine2.Start(ctx)) 245 246 // Comparing the hash after restoration, to ensure it produces the same result. 247 hash2, _, _ := snapshotEngine2.Info() 248 require.Equal(t, hash1, hash2) 249 250 // Executing the same steps post-snapshot as the first engine 251 additionalSteps(bankingEngine2) 252 253 state2 := map[string][]byte{} 254 for _, key := range bankingEngine2.Keys() { 255 state, additionalProvider, err := bankingEngine2.GetState(key) 256 require.NoError(t, err) 257 assert.Empty(t, additionalProvider) 258 state2[key] = state 259 } 260 261 for key := range state1 { 262 if key == "withdrawals" { 263 // FIXME The withdrawal count inside the engine is not restored by 264 // the snapshot, which leads to a non-deterministic generation 265 // of the withdrawal `Ref` after restoring the state from a snapshot. 266 // Instead of starting at the count from the previous engine, it 267 // restarts at 0. 268 // It doesn't seem to be a big issue. As a result, we skip the 269 // verification for the moment. 270 continue 271 } 272 assert.Equalf(t, state1[key], state2[key], "Key %q does not have the same data", key) 273 } 274 } 275 276 func TestAssetActionsSnapshotRoundTrip(t *testing.T) { 277 aaKey := (&types.PayloadBankingAssetActions{}).Key() 278 eng := getTestEngine(t) 279 280 eng.tsvc.EXPECT().GetTimeNow().AnyTimes() 281 d1 := deposit(eng, "VGT1", "someparty1", num.NewUint(42)) 282 err := eng.DepositBuiltinAsset(context.Background(), d1, "depositid1", 42) 283 assert.NoError(t, err) 284 285 d2 := deposit(eng, "VGT1", "someparty2", num.NewUint(24)) 286 err = eng.DepositBuiltinAsset(context.Background(), d2, "depositid2", 24) 287 assert.NoError(t, err) 288 289 state, _, err := eng.GetState(aaKey) 290 require.Nil(t, err) 291 292 // verify state is consistent in the absence of change 293 stateNoChange, _, err := eng.GetState(aaKey) 294 require.Nil(t, err) 295 require.True(t, bytes.Equal(state, stateNoChange)) 296 297 // reload the state 298 var assetActions snapshotpb.Payload 299 snap := getTestEngine(t) 300 snap.tsvc.EXPECT().GetTimeNow().AnyTimes() 301 snap.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil) 302 303 proto.Unmarshal(state, &assetActions) 304 payload := types.PayloadFromProto(&assetActions) 305 _, err = snap.LoadState(context.Background(), payload) 306 require.Nil(t, err) 307 statePostReload, _, _ := snap.GetState(aaKey) 308 require.True(t, bytes.Equal(state, statePostReload)) 309 } 310 311 func TestSeenSnapshotRoundTrip(t *testing.T) { 312 seenKey := (&types.PayloadBankingSeen{}).Key() 313 eng := getTestEngine(t) 314 315 state1, _, err := eng.GetState(seenKey) 316 require.Nil(t, err) 317 eng.col.EXPECT().Deposit(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&types.LedgerMovement{}, nil) 318 319 d1 := deposit(eng, "VGT1", "someparty1", num.NewUint(42)) 320 err = eng.DepositBuiltinAsset(context.Background(), d1, "depositid1", 42) 321 assert.NoError(t, err) 322 eng.witness.f(eng.witness.r, true) 323 324 d2 := deposit(eng, "VGT2", "someparty2", num.NewUint(24)) 325 err = eng.DepositBuiltinAsset(context.Background(), d2, "depositid2", 24) 326 assert.NoError(t, err) 327 eng.witness.f(eng.witness.r, true) 328 329 eng.OnTick(context.Background(), time.Now()) 330 state2, _, err := eng.GetState(seenKey) 331 require.Nil(t, err) 332 333 require.False(t, bytes.Equal(state1, state2)) 334 335 // verify state is consistent in the absence of change 336 stateNoChange, _, err := eng.GetState(seenKey) 337 require.Nil(t, err) 338 require.True(t, bytes.Equal(state2, stateNoChange)) 339 340 // reload the state 341 var seen snapshotpb.Payload 342 snap := getTestEngine(t) 343 proto.Unmarshal(state2, &seen) 344 345 payload := types.PayloadFromProto(&seen) 346 347 _, err = snap.LoadState(context.Background(), payload) 348 require.Nil(t, err) 349 statePostReload, _, _ := snap.GetState(seenKey) 350 require.True(t, bytes.Equal(state2, statePostReload)) 351 } 352 353 func TestWithdrawalsSnapshotRoundTrip(t *testing.T) { 354 testAsset := assets.NewAsset(builtin.New("VGT", &types.AssetDetails{ 355 Name: "VEGA TOKEN", 356 Symbol: "VGT", 357 })) 358 359 withdrawalsKey := (&types.PayloadBankingWithdrawals{}).Key() 360 eng := getTestEngine(t) 361 eng.tsvc.EXPECT().GetTimeNow().AnyTimes() 362 363 for i := 0; i < 10; i++ { 364 d1 := deposit(eng, "VGT"+strconv.Itoa(i*2), "someparty"+strconv.Itoa(i*2), num.NewUint(42)) 365 err := eng.DepositBuiltinAsset(context.Background(), d1, "depositid"+strconv.Itoa(i*2), 42) 366 assert.NoError(t, err) 367 368 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 369 eng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil) 370 eng.col.EXPECT().Withdraw(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&types.LedgerMovement{}, nil) 371 err = eng.WithdrawBuiltinAsset(context.Background(), "VGT"+strconv.Itoa(i*2), "someparty"+strconv.Itoa(i*2), "VGT"+strconv.Itoa(i*2), num.NewUint(2)) 372 require.Nil(t, err) 373 err = eng.WithdrawBuiltinAsset(context.Background(), "VGT"+strconv.Itoa(i*2+1), "someparty"+strconv.Itoa(i*2), "VGT"+strconv.Itoa(i*2), num.NewUint(10)) 374 require.Nil(t, err) 375 state, _, err := eng.GetState(withdrawalsKey) 376 require.Nil(t, err) 377 378 // verify state is consistent in the absence of change 379 stateNoChange, _, err := eng.GetState(withdrawalsKey) 380 require.Nil(t, err) 381 require.True(t, bytes.Equal(state, stateNoChange)) 382 383 // reload the state 384 var withdrawals snapshotpb.Payload 385 snap := getTestEngine(t) 386 proto.Unmarshal(state, &withdrawals) 387 388 payload := types.PayloadFromProto(&withdrawals) 389 390 _, err = snap.LoadState(context.Background(), payload) 391 require.Nil(t, err) 392 statePostReload, _, _ := snap.GetState(withdrawalsKey) 393 require.True(t, bytes.Equal(state, statePostReload)) 394 } 395 } 396 397 func TestDepositSnapshotRoundTrip(t *testing.T) { 398 depositsKey := (&types.PayloadBankingDeposits{}).Key() 399 eng := getTestEngine(t) 400 eng.tsvc.EXPECT().GetTimeNow().AnyTimes() 401 402 for i := 0; i < 10; i++ { 403 d1 := deposit(eng, "VGT"+strconv.Itoa(i*2), "someparty"+strconv.Itoa(i*2), num.NewUint(42)) 404 err := eng.DepositBuiltinAsset(context.Background(), d1, "depositid"+strconv.Itoa(i*2), 42) 405 assert.NoError(t, err) 406 407 d2 := deposit(eng, "VGT"+strconv.Itoa(i*2+1), "someparty"+strconv.Itoa(i*2+1), num.NewUint(24)) 408 err = eng.DepositBuiltinAsset(context.Background(), d2, "depositid"+strconv.Itoa(i*2+1), 24) 409 assert.NoError(t, err) 410 411 state, _, err := eng.GetState(depositsKey) 412 require.Nil(t, err) 413 414 // verify state is consistent in the absence of change 415 stateNoChange, _, err := eng.GetState(depositsKey) 416 require.Nil(t, err) 417 require.True(t, bytes.Equal(state, stateNoChange)) 418 419 // reload the state 420 var deposits snapshotpb.Payload 421 snap := getTestEngine(t) 422 proto.Unmarshal(state, &deposits) 423 payload := types.PayloadFromProto(&deposits) 424 _, err = snap.LoadState(context.Background(), payload) 425 require.Nil(t, err) 426 statePostReload, _, _ := snap.GetState(depositsKey) 427 require.True(t, bytes.Equal(state, statePostReload)) 428 } 429 } 430 431 func TestOneOffTransfersSnapshotRoundTrip(t *testing.T) { 432 ctx := context.Background() 433 key := (&types.PayloadBankingScheduledTransfers{}).Key() 434 eng := getTestEngine(t) 435 436 state, _, err := eng.GetState(key) 437 require.Nil(t, err) 438 439 fromAcc := types.Account{ 440 Balance: num.NewUint(1000), 441 } 442 443 now := time.Unix(1111, 0) 444 deliver := now.Add(time.Hour) 445 eng.OnTick(ctx, now) 446 447 oneoff := &types.TransferFunds{ 448 Kind: types.TransferCommandKindOneOff, 449 OneOff: &types.OneOffTransfer{ 450 TransferBase: &types.TransferBase{ 451 From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", 452 FromAccountType: types.AccountTypeGeneral, 453 To: "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0", 454 ToAccountType: types.AccountTypeGeneral, 455 Asset: assetNameETH, 456 Amount: num.NewUint(10), 457 Reference: "someref", 458 }, 459 DeliverOn: &deliver, 460 }, 461 } 462 463 eng.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() 464 eng.assets.EXPECT().Get(gomock.Any()).Times(1).Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil) 465 eng.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).AnyTimes().Times(1).Return(&fromAcc, nil) 466 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 467 require.NoError(t, eng.TransferFunds(ctx, oneoff)) 468 469 // test the new transfer prompts a change 470 state2, _, err := eng.GetState(key) 471 require.Nil(t, err) 472 require.False(t, bytes.Equal(state, state2)) 473 474 // reload the state 475 var transfers snapshotpb.Payload 476 snap := getTestEngine(t) 477 proto.Unmarshal(state2, &transfers) 478 payload := types.PayloadFromProto(&transfers) 479 _, err = snap.LoadState(context.Background(), payload) 480 require.Nil(t, err) 481 statePostReload, _, _ := snap.GetState(key) 482 require.True(t, bytes.Equal(state2, statePostReload)) 483 } 484 485 func TestRecurringTransfersSnapshotRoundTrip(t *testing.T) { 486 ctx := context.Background() 487 key := (&types.PayloadBankingRecurringTransfers{}).Key() 488 eng := getTestEngine(t) 489 490 fromAcc := types.Account{ 491 Balance: num.NewUint(1000), 492 } 493 494 eng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil) 495 eng.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).AnyTimes().Return(&fromAcc, nil) 496 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 497 eng.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() 498 499 state, _, err := eng.GetState(key) 500 require.Nil(t, err) 501 502 recurring := &types.TransferFunds{ 503 Kind: types.TransferCommandKindRecurring, 504 Recurring: &types.RecurringTransfer{ 505 TransferBase: &types.TransferBase{ 506 From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", 507 FromAccountType: types.AccountTypeGeneral, 508 To: "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0", 509 ToAccountType: types.AccountTypeGeneral, 510 Asset: "eth", 511 Amount: num.NewUint(10), 512 Reference: "someref", 513 }, 514 StartEpoch: 10, 515 EndEpoch: nil, // forever 516 Factor: num.MustDecimalFromString("0.9"), 517 DispatchStrategy: &vega.DispatchStrategy{ 518 AssetForMetric: "zohar", 519 Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 520 Markets: []string{"mmm"}, 521 EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 522 IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, 523 WindowLength: 1, 524 LockPeriod: 1, 525 DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK, 526 }, 527 }, 528 } 529 530 p, _ := proto.Marshal(recurring.Recurring.DispatchStrategy) 531 dsHash := hex.EncodeToString(crypto.Hash(p)) 532 533 eng.marketActivityTracker.EXPECT().MarketTrackedForAsset("mmm", "zohar").Times(1).Return(true) 534 require.NoError(t, eng.TransferFunds(ctx, recurring)) 535 536 // test the new transfer prompts a change 537 state2, _, err := eng.GetState(key) 538 require.Nil(t, err) 539 require.False(t, bytes.Equal(state, state2)) 540 541 // reload the state 542 var transfers snapshotpb.Payload 543 snap := getTestEngine(t) 544 proto.Unmarshal(state2, &transfers) 545 payload := types.PayloadFromProto(&transfers) 546 _, err = snap.LoadState(context.Background(), payload) 547 require.Nil(t, err) 548 statePostReload, _, _ := snap.GetState(key) 549 require.True(t, bytes.Equal(state2, statePostReload)) 550 551 require.NotNil(t, eng.GetDispatchStrategy(dsHash)) 552 require.NotNil(t, snap.GetDispatchStrategy(dsHash)) 553 require.Equal(t, eng.GetDispatchStrategy(dsHash), snap.GetDispatchStrategy(dsHash)) 554 } 555 556 func TestRecurringGovTransfersSnapshotRoundTrip(t *testing.T) { 557 ctx := context.Background() 558 key := (&types.PayloadBankingRecurringGovernanceTransfers{}).Key() 559 e := getTestEngine(t) 560 require.NoError(t, e.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(1))) 561 e.OnTick(ctx, time.Unix(10, 0)) 562 e.OnEpoch(ctx, types.Epoch{Seq: 1, StartTime: time.Unix(10, 0), Action: vega.EpochAction_EPOCH_ACTION_START}) 563 564 endEpoch := uint64(2) 565 transfer := &types.NewTransferConfiguration{ 566 SourceType: types.AccountTypeGlobalReward, 567 DestinationType: types.AccountTypeGeneral, 568 Asset: "eth", 569 Source: "", 570 Destination: "zohar", 571 TransferType: vega.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING, 572 MaxAmount: num.NewUint(10), 573 FractionOfBalance: num.DecimalFromFloat(0.1), 574 Kind: types.TransferKindRecurring, 575 OneOffTransferConfig: nil, 576 RecurringTransferConfig: &vega.RecurringTransfer{ 577 StartEpoch: 1, 578 EndEpoch: &endEpoch, 579 DispatchStrategy: &vega.DispatchStrategy{ 580 AssetForMetric: "zohar", 581 Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 582 Markets: []string{"mmm"}, 583 EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 584 IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, 585 WindowLength: 1, 586 LockPeriod: 1, 587 DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK, 588 }, 589 }, 590 } 591 592 p, _ := proto.Marshal(transfer.RecurringTransferConfig.DispatchStrategy) 593 dsHash := hex.EncodeToString(crypto.Hash(p)) 594 595 e.broker.EXPECT().Send(gomock.Any()).Times(1) 596 require.NoError(t, e.NewGovernanceTransfer(ctx, "1", "some reference", transfer)) 597 598 // test the new transfer prompts a change 599 state, _, err := e.GetState(key) 600 require.NoError(t, err) 601 602 // reload the state 603 var transfers snapshotpb.Payload 604 snap := getTestEngine(t) 605 proto.Unmarshal(state, &transfers) 606 payload := types.PayloadFromProto(&transfers) 607 _, err = snap.LoadState(context.Background(), payload) 608 require.Nil(t, err) 609 statePostReload, _, _ := snap.GetState(key) 610 require.True(t, bytes.Equal(state, statePostReload)) 611 612 require.NotNil(t, e.GetDispatchStrategy(dsHash)) 613 require.NotNil(t, snap.GetDispatchStrategy(dsHash)) 614 require.Equal(t, e.GetDispatchStrategy(dsHash), snap.GetDispatchStrategy(dsHash)) 615 } 616 617 func TestScheduledgGovTransfersSnapshotRoundTrip(t *testing.T) { 618 ctx := context.Background() 619 key := (&types.PayloadBankingScheduledGovernanceTransfers{}).Key() 620 e := getTestEngine(t) 621 622 // let's do a massive fee, easy to test. 623 e.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(1)) 624 e.OnTick(context.Background(), time.Unix(10, 0)) 625 626 deliverOn := time.Unix(12, 0).UnixNano() 627 transfer := &types.NewTransferConfiguration{ 628 SourceType: types.AccountTypeGlobalReward, 629 DestinationType: types.AccountTypeGeneral, 630 Asset: "eth", 631 Source: "", 632 Destination: "zohar", 633 TransferType: vega.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING, 634 MaxAmount: num.NewUint(10), 635 FractionOfBalance: num.DecimalFromFloat(0.1), 636 Kind: types.TransferKindOneOff, 637 OneOffTransferConfig: &vega.OneOffTransfer{DeliverOn: deliverOn}, 638 RecurringTransferConfig: nil, 639 } 640 641 e.broker.EXPECT().Send(gomock.Any()).Times(1) 642 require.NoError(t, e.NewGovernanceTransfer(ctx, "1", "some reference", transfer)) 643 644 // test the new transfer prompts a change 645 state, _, err := e.GetState(key) 646 require.NoError(t, err) 647 648 // reload the state 649 var transfers snapshotpb.Payload 650 snap := getTestEngine(t) 651 proto.Unmarshal(state, &transfers) 652 payload := types.PayloadFromProto(&transfers) 653 _, err = snap.LoadState(context.Background(), payload) 654 require.Nil(t, err) 655 statePostReload, _, _ := snap.GetState(key) 656 require.True(t, bytes.Equal(state, statePostReload)) 657 } 658 659 func TestAssetListRoundTrip(t *testing.T) { 660 ctx := context.Background() 661 key := (&types.PayloadBankingAssetActions{}).Key() 662 eng := getTestEngine(t) 663 eng.tsvc.EXPECT().GetTimeNow().AnyTimes() 664 eng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil) 665 require.NoError(t, eng.EnableERC20(ctx, &types.ERC20AssetList{}, "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", 1000, 1000, "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", "1")) 666 667 state, _, err := eng.GetState(key) 668 require.Nil(t, err) 669 670 var pp snapshotpb.Payload 671 proto.Unmarshal(state, &pp) 672 payload := types.PayloadFromProto(&pp) 673 674 snap := getTestEngine(t) 675 snap.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil) 676 _, err = snap.LoadState(ctx, payload) 677 require.Nil(t, err) 678 679 state2, _, err := snap.GetState(key) 680 require.NoError(t, err) 681 require.True(t, bytes.Equal(state, state2)) 682 } 683 684 func getTestEngineForSnapshot(t *testing.T) *testEngine { 685 t.Helper() 686 e := getTestEngine(t) 687 688 e.tsvc.EXPECT().GetTimeNow().DoAndReturn( 689 func() time.Time { 690 return time.Unix(10, 0) 691 }).AnyTimes() 692 e.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil) 693 e.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 694 695 return e 696 } 697 698 func TestTransferFeeDiscountsSnapshotRoundTrip(t *testing.T) { 699 ctx := vgtest.VegaContext("chainid", 100) 700 701 log := logging.NewTestLogger() 702 703 vegaPath := paths.New(t.TempDir()) 704 705 now := time.Now() 706 timeService := stubs.NewTimeStub() 707 timeService.SetTime(now) 708 709 statsData := stats.New(log, stats.NewDefaultConfig()) 710 bankingEngine1 := getTestEngineForSnapshot(t) 711 712 snapshotEngine1, err := snapshot.NewEngine(vegaPath, snapshot.DefaultConfig(), log, timeService, statsData.Blockchain) 713 require.NoError(t, err) 714 715 closeSnapshotEngine1 := vgtest.OnlyOnce(snapshotEngine1.Close) 716 defer closeSnapshotEngine1() 717 718 snapshotEngine1.AddProviders(bankingEngine1) 719 720 // No snapshot yet, does nothing. 721 require.NoError(t, snapshotEngine1.Start(ctx)) 722 723 // let's do a massive fee, easy to test. 724 bankingEngine1.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(1)) 725 bankingEngine1.OnTick(context.Background(), time.Unix(10, 0)) 726 727 assetName := "asset-1" 728 assetName2 := "asset-2" 729 feeDiscounts := map[string]*num.Uint{"party-1": num.NewUint(5), "party-2": num.NewUint(10), "party-6": num.NewUint(33)} 730 feeDiscounts2 := map[string]*num.Uint{"party-1": num.NewUint(7), "party-2": num.NewUint(16), "party-4": num.NewUint(16)} 731 feeDiscounts3 := map[string]*num.Uint{"party-6": num.NewUint(2), "party-9": num.NewUint(5), "party-2": num.NewUint(33)} 732 feeDiscounts4 := map[string]*num.Uint{"party-1": num.NewUint(4), "party-2": num.NewUint(5), "party-6": num.NewUint(6)} 733 bankingEngine1.RegisterTradingFees(ctx, assetName, feeDiscounts) 734 bankingEngine1.RegisterTradingFees(ctx, assetName2, feeDiscounts2) 735 bankingEngine1.RegisterTradingFees(ctx, assetName2, feeDiscounts3) 736 737 hash1, err := snapshotEngine1.SnapshotNow(ctx) 738 require.NoError(t, err) 739 740 assetName3 := "asset-3" 741 bankingEngine1.RegisterTradingFees(ctx, assetName3, feeDiscounts4) 742 743 state1 := map[string][]byte{} 744 for _, key := range bankingEngine1.Keys() { 745 state, additionalProvider, err := bankingEngine1.GetState(key) 746 require.NoError(t, err) 747 assert.Empty(t, additionalProvider) 748 state1[key] = state 749 } 750 751 closeSnapshotEngine1() 752 753 snapshotEngine2, err := snapshot.NewEngine(vegaPath, snapshot.DefaultConfig(), log, timeService, statsData.Blockchain) 754 require.NoError(t, err) 755 defer snapshotEngine2.Close() 756 757 bankingEngine2 := getTestEngineForSnapshot(t) 758 759 snapshotEngine2.AddProviders(bankingEngine2.Engine) 760 761 require.NoError(t, snapshotEngine2.Start(ctx)) 762 763 hash2, _, _ := snapshotEngine2.Info() 764 require.Equal(t, hash1, hash2) 765 766 bankingEngine2.RegisterTradingFees(ctx, assetName3, feeDiscounts4) 767 768 state2 := map[string][]byte{} 769 for _, key := range bankingEngine2.Keys() { 770 state, additionalProvider, err := bankingEngine2.GetState(key) 771 require.NoError(t, err) 772 assert.Empty(t, additionalProvider) 773 state2[key] = state 774 } 775 776 for key := range state1 { 777 assert.Equalf(t, state1[key], state2[key], "Key %q does not have the same data", key) 778 } 779 }