code.vegaprotocol.io/vega@v0.79.0/core/checkpoint/engine_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 checkpoint_test 17 18 import ( 19 "context" 20 "encoding/base64" 21 "encoding/hex" 22 "encoding/json" 23 "errors" 24 "testing" 25 "time" 26 27 "code.vegaprotocol.io/vega/core/checkpoint" 28 "code.vegaprotocol.io/vega/core/checkpoint/mocks" 29 "code.vegaprotocol.io/vega/core/types" 30 "code.vegaprotocol.io/vega/libs/proto" 31 "code.vegaprotocol.io/vega/logging" 32 33 "github.com/golang/mock/gomock" 34 "github.com/stretchr/testify/require" 35 ) 36 37 type testEngine struct { 38 *checkpoint.Engine 39 ctrl *gomock.Controller 40 } 41 42 func getTestEngine(t *testing.T) *testEngine { 43 t.Helper() 44 ctrl := gomock.NewController(t) 45 log := logging.NewTestLogger() 46 eng, _ := checkpoint.New(log, checkpoint.NewDefaultConfig()) 47 return &testEngine{ 48 Engine: eng, 49 ctrl: ctrl, 50 } 51 } 52 53 func TestGetCheckpoints(t *testing.T) { 54 t.Run("test getting checkpoints loading in components via constructor - no duplicates", testGetCheckpointsConstructor) 55 t.Run("test getting checkpoints loading in components using Add method - no duplicates", testGetCheckpointsAdd) 56 t.Run("test adding duplicate components using Add methods", testAddDuplicate) 57 t.Run("test adding duplicate component via constructor", testDuplicateConstructor) 58 } 59 60 func TestCheckpointIntervals(t *testing.T) { 61 t.Run("test getting checkpoint before interval has passed", testCheckpointBeforeInterval) 62 t.Run("test updating interval creates new checkpoint sooner", testCheckpointUpdatedInterval) 63 t.Run("test getting checkpoint before interval for balance", testCheckpointBalanceInterval) 64 } 65 66 func TestLoadCheckpoints(t *testing.T) { 67 t.Run("test loading checkpoints after generating them - success", testLoadCheckpoints) 68 t.Run("load non-registered components", testLoadMissingCheckpoint) 69 t.Run("load checkpoint with invalid hash", testLoadInvalidHash) 70 t.Run("load sparse checkpoint", testLoadSparse) 71 t.Run("error loading checkpoint", testLoadError) 72 } 73 74 func TestLoadAssets(t *testing.T) { 75 t.Run("test loading assets first, enables assets in collateral", testLoadAssets) 76 } 77 78 type genesis struct { 79 CP *checkpoint.GenesisState `json:"checkpoint"` 80 } 81 82 func testGetCheckpointsConstructor(t *testing.T) { 83 t.Parallel() 84 ctrl := gomock.NewController(t) 85 ctx := context.Background() 86 defer ctrl.Finish() 87 components := map[types.CheckpointName]*mocks.MockState{ 88 types.GovernanceCheckpoint: mocks.NewMockState(ctrl), 89 types.AssetsCheckpoint: mocks.NewMockState(ctrl), 90 } 91 for k, c := range components { 92 c.EXPECT().Name().Times(1).Return(k) 93 } 94 log := logging.NewTestLogger() 95 eng, err := checkpoint.New(log, checkpoint.NewDefaultConfig(), components[types.GovernanceCheckpoint], components[types.AssetsCheckpoint]) 96 require.NoError(t, err) 97 data := map[types.CheckpointName][]byte{ 98 types.GovernanceCheckpoint: []byte("foodata"), 99 types.AssetsCheckpoint: []byte("bardata"), 100 } 101 for k, c := range components { 102 c.EXPECT().Checkpoint().Times(1).Return(data[k], nil) 103 } 104 // initialise time 105 tm := time.Now().Add(-2 * time.Hour) 106 _, _ = eng.Checkpoint(ctx, tm) 107 raw, err := eng.Checkpoint(ctx, time.Now()) 108 require.NoError(t, err) 109 // now to check if the checkpoint contains the expected data 110 for k, c := range components { 111 c.EXPECT().Load(gomock.Any(), data[k]).Times(1).Return(nil) 112 } 113 // pretend like the genesis block specified this hash to restore 114 set := genesis{ 115 CP: &checkpoint.GenesisState{ 116 CheckpointHash: hex.EncodeToString(raw.Hash), 117 CheckpointState: base64.StdEncoding.EncodeToString(raw.State), 118 }, 119 } 120 gen, err := json.Marshal(set) 121 require.NoError(t, err) 122 require.NoError(t, eng.UponGenesis(ctx, gen)) 123 } 124 125 func testGetCheckpointsAdd(t *testing.T) { 126 t.Parallel() 127 eng := getTestEngine(t) 128 ctx := context.Background() 129 130 components := map[types.CheckpointName]*mocks.MockState{ 131 types.GovernanceCheckpoint: mocks.NewMockState(eng.ctrl), 132 types.AssetsCheckpoint: mocks.NewMockState(eng.ctrl), 133 } 134 data := map[types.CheckpointName][]byte{ 135 types.GovernanceCheckpoint: []byte("foodata"), 136 types.AssetsCheckpoint: []byte("bardata"), 137 } 138 for k, c := range components { 139 c.EXPECT().Name().Times(1).Return(k) 140 } 141 require.NoError(t, eng.Add(components[types.GovernanceCheckpoint], components[types.AssetsCheckpoint])) 142 for k, c := range components { 143 c.EXPECT().Checkpoint().Times(1).Return(data[k], nil) 144 } 145 tm := time.Now().Add(-2 * time.Hour) 146 _, _ = eng.Checkpoint(ctx, tm) 147 raw, err := eng.Checkpoint(ctx, time.Now()) 148 require.NoError(t, err) 149 // now to check if the checkpoint contains the expected data 150 for k, c := range components { 151 c.EXPECT().Load(gomock.Any(), data[k]).Times(1).Return(nil) 152 } 153 // pretend like the genesis block specified this hash to restore 154 set := genesis{ 155 CP: &checkpoint.GenesisState{ 156 CheckpointHash: hex.EncodeToString(raw.Hash), 157 CheckpointState: base64.StdEncoding.EncodeToString(raw.State), 158 }, 159 } 160 gen, err := json.Marshal(set) 161 require.NoError(t, err) 162 require.NoError(t, eng.UponGenesis(ctx, gen)) 163 } 164 165 func testAddDuplicate(t *testing.T) { 166 t.Parallel() 167 eng := getTestEngine(t) 168 defer eng.ctrl.Finish() 169 comp := mocks.NewMockState(eng.ctrl) 170 comp.EXPECT().Name().Times(2).Return(types.GovernanceCheckpoint) 171 require.NoError(t, eng.Add(comp, comp)) // adding the exact same component (same ptr value) 172 comp2 := mocks.NewMockState(eng.ctrl) 173 comp2.EXPECT().Name().Times(1).Return(types.GovernanceCheckpoint) 174 require.Error(t, eng.Add(comp2)) 175 } 176 177 func testDuplicateConstructor(t *testing.T) { 178 t.Parallel() 179 ctrl := gomock.NewController(t) 180 defer ctrl.Finish() 181 comp := mocks.NewMockState(ctrl) 182 comp.EXPECT().Name().Times(3).Return(types.GovernanceCheckpoint) 183 comp2 := mocks.NewMockState(ctrl) 184 comp2.EXPECT().Name().Times(1).Return(types.GovernanceCheckpoint) 185 // this is all good 186 log := logging.NewTestLogger() 187 cfg := checkpoint.NewDefaultConfig() 188 eng, err := checkpoint.New(log, cfg, comp, comp) 189 require.NoError(t, err) 190 require.NotNil(t, eng) 191 eng, err = checkpoint.New(log, cfg, comp, comp2) 192 require.Error(t, err) 193 require.Nil(t, eng) 194 } 195 196 func testLoadCheckpoints(t *testing.T) { 197 t.Parallel() 198 eng := getTestEngine(t) 199 ctx := context.Background() 200 defer eng.ctrl.Finish() 201 components := map[types.CheckpointName]*mocks.MockState{ 202 types.GovernanceCheckpoint: mocks.NewMockState(eng.ctrl), 203 types.AssetsCheckpoint: mocks.NewMockState(eng.ctrl), 204 } 205 data := map[types.CheckpointName][]byte{ 206 types.GovernanceCheckpoint: []byte("foodata"), 207 types.AssetsCheckpoint: []byte("bardata"), 208 } 209 for k, c := range components { 210 c.EXPECT().Name().Times(1).Return(k) 211 } 212 require.NoError(t, eng.Add(components[types.GovernanceCheckpoint], components[types.AssetsCheckpoint])) 213 for k, c := range components { 214 c.EXPECT().Checkpoint().Times(1).Return(data[k], nil) 215 } 216 tm := time.Now().Add(-2 * time.Hour) 217 _, _ = eng.Checkpoint(ctx, tm) 218 snapshot, err := eng.Checkpoint(ctx, time.Now()) 219 require.NoError(t, err) 220 require.NotEmpty(t, snapshot) 221 // create new components to load data in to 222 wComps := map[types.CheckpointName]*wrappedMock{ 223 types.GovernanceCheckpoint: wrapMock(mocks.NewMockState(eng.ctrl)), 224 types.AssetsCheckpoint: wrapMock(mocks.NewMockState(eng.ctrl)), 225 } 226 for k, c := range wComps { 227 c.EXPECT().Name().Times(1).Return(k) 228 c.EXPECT().Load(gomock.Any(), data[k]).Times(1).Return(nil) 229 } 230 log := logging.NewTestLogger() 231 cfg := checkpoint.NewDefaultConfig() 232 newEng, err := checkpoint.New(log, cfg, wComps[types.GovernanceCheckpoint], wComps[types.AssetsCheckpoint]) 233 require.NoError(t, err) 234 require.NotNil(t, newEng) 235 // pretend like the genesis block specified this hash to restore 236 set := genesis{ 237 CP: &checkpoint.GenesisState{ 238 CheckpointHash: hex.EncodeToString(snapshot.Hash), 239 CheckpointState: base64.StdEncoding.EncodeToString(snapshot.State), 240 }, 241 } 242 gen, err := json.Marshal(set) 243 require.NoError(t, err) 244 require.NoError(t, newEng.UponGenesis(ctx, gen)) 245 for k, exp := range data { 246 wc := wComps[k] 247 require.EqualValues(t, exp, wc.data) 248 } 249 } 250 251 func testLoadMissingCheckpoint(t *testing.T) { 252 t.Parallel() 253 eng := getTestEngine(t) 254 ctx := context.Background() 255 defer eng.ctrl.Finish() 256 257 // create checkpoint data 258 cp := &types.Checkpoint{ 259 Assets: []byte("assets"), 260 } 261 snap := &types.CheckpointState{} 262 snap.SetCheckpoint(cp) 263 cp.Assets = []byte("foobar") 264 b, err := proto.Marshal(cp.IntoProto()) 265 require.NoError(t, err) 266 // pretend like the genesis block specified this hash to restore 267 set := genesis{ 268 CP: &checkpoint.GenesisState{ 269 CheckpointHash: hex.EncodeToString(snap.Hash), 270 CheckpointState: base64.StdEncoding.EncodeToString(b), 271 }, 272 } 273 gen, err := json.Marshal(set) 274 require.NoError(t, err) 275 require.Error(t, eng.UponGenesis(ctx, gen), "could not load checkpoint: received(09234807e4af85f17c66b48ee3bca89dffd1f1233659f9f940a2b17b0b8c6bc5), expected(e3795ed41024acefa48c9bdce4f52cf6909f4672dc3112fd0fc6cb1e18c83531): incompatible hashes") 276 } 277 278 func testLoadInvalidHash(t *testing.T) { 279 t.Parallel() 280 eng := getTestEngine(t) 281 ctx := context.Background() 282 defer eng.ctrl.Finish() 283 284 cp := &types.Checkpoint{ 285 Assets: []byte("assets"), 286 } 287 snap := &types.CheckpointState{} 288 snap.SetCheckpoint(cp) 289 // update data -> hash is invalid 290 cp.Assets = []byte("foobar") 291 b, err := proto.Marshal(cp.IntoProto()) 292 require.NoError(t, err) 293 // pretend like the genesis block specified this hash to restore 294 set := genesis{ 295 CP: &checkpoint.GenesisState{ 296 CheckpointHash: hex.EncodeToString(snap.Hash), 297 CheckpointState: base64.StdEncoding.EncodeToString(b), 298 }, 299 } 300 gen, err := json.Marshal(set) 301 require.NoError(t, err) 302 require.Error(t, eng.UponGenesis(ctx, gen), "could not load checkpoint: received(09234807e4af85f17c66b48ee3bca89dffd1f1233659f9f940a2b17b0b8c6bc5), expected(e3795ed41024acefa48c9bdce4f52cf6909f4672dc3112fd0fc6cb1e18c83531): incompatible hashes") 303 } 304 305 func testLoadSparse(t *testing.T) { 306 t.Parallel() 307 ctrl := gomock.NewController(t) 308 ctx := context.Background() 309 defer ctrl.Finish() 310 components := map[types.CheckpointName]*mocks.MockState{ 311 types.GovernanceCheckpoint: mocks.NewMockState(ctrl), 312 types.AssetsCheckpoint: mocks.NewMockState(ctrl), 313 } 314 for k, c := range components { 315 c.EXPECT().Name().Times(1).Return(k) 316 } 317 log := logging.NewTestLogger() 318 cfg := checkpoint.NewDefaultConfig() 319 eng, err := checkpoint.New(log, cfg, components[types.GovernanceCheckpoint]) 320 require.NoError(t, err) 321 data := map[types.CheckpointName][]byte{ 322 types.GovernanceCheckpoint: []byte("foodata"), 323 } 324 c := components[types.GovernanceCheckpoint] 325 c.EXPECT().Checkpoint().Times(1).Return(data[types.GovernanceCheckpoint], nil) 326 tm := time.Now().Add(-2 * time.Hour) 327 _, _ = eng.Checkpoint(ctx, tm) 328 snapshot, err := eng.Checkpoint(ctx, time.Now()) 329 require.NoError(t, err) 330 require.NoError(t, eng.Add(components[types.AssetsCheckpoint])) // load another component, not part of the checkpoints map 331 c.EXPECT().Load(gomock.Any(), data[types.GovernanceCheckpoint]).Times(1).Return(nil) 332 // pretend like the genesis block specified this hash to restore 333 set := genesis{ 334 CP: &checkpoint.GenesisState{ 335 CheckpointHash: hex.EncodeToString(snapshot.Hash), 336 CheckpointState: base64.StdEncoding.EncodeToString(snapshot.State), 337 }, 338 } 339 gen, err := json.Marshal(set) 340 require.NoError(t, err) 341 require.NoError(t, eng.UponGenesis(ctx, gen)) 342 } 343 344 func testLoadError(t *testing.T) { 345 t.Parallel() 346 ctrl := gomock.NewController(t) 347 ctx := context.Background() 348 defer ctrl.Finish() 349 components := map[types.CheckpointName]*mocks.MockState{ 350 types.GovernanceCheckpoint: mocks.NewMockState(ctrl), 351 types.AssetsCheckpoint: mocks.NewMockState(ctrl), 352 } 353 for k, c := range components { 354 c.EXPECT().Name().Times(1).Return(k) 355 } 356 log := logging.NewTestLogger() 357 cfg := checkpoint.NewDefaultConfig() 358 eng, err := checkpoint.New(log, cfg, components[types.GovernanceCheckpoint], components[types.AssetsCheckpoint]) 359 require.NoError(t, err) 360 data := map[types.CheckpointName][]byte{ 361 types.GovernanceCheckpoint: []byte("foodata"), 362 types.AssetsCheckpoint: []byte("bardata"), 363 } 364 for k, c := range components { 365 c.EXPECT().Checkpoint().Times(1).Return(data[k], nil) 366 } 367 ret := map[types.CheckpointName]error{ 368 types.GovernanceCheckpoint: errors.New("random error"), 369 types.AssetsCheckpoint: nil, // we always load checkpoints in order, so bar will go first, and should not return an error 370 } 371 tm := time.Now().Add(-2 * time.Hour) 372 _, _ = eng.Checkpoint(ctx, tm) 373 checkpoints, err := eng.Checkpoint(ctx, time.Now()) 374 require.NoError(t, err) 375 for k, r := range ret { 376 c := components[k] 377 c.EXPECT().Load(gomock.Any(), data[k]).Times(1).Return(r) 378 } 379 // pretend like the genesis block specified this hash to restore 380 set := genesis{ 381 CP: &checkpoint.GenesisState{ 382 CheckpointHash: hex.EncodeToString(checkpoints.Hash), 383 CheckpointState: base64.StdEncoding.EncodeToString(checkpoints.State), 384 }, 385 } 386 gen, err := json.Marshal(set) 387 require.NoError(t, err) 388 err = eng.UponGenesis(ctx, gen) 389 require.Error(t, err) 390 require.True(t, errors.Is(err, ret[types.GovernanceCheckpoint])) 391 } 392 393 func testCheckpointBeforeInterval(t *testing.T) { 394 t.Parallel() 395 ctrl := gomock.NewController(t) 396 defer ctrl.Finish() 397 ctx := context.Background() 398 components := map[types.CheckpointName]*mocks.MockState{ 399 types.GovernanceCheckpoint: mocks.NewMockState(ctrl), 400 types.AssetsCheckpoint: mocks.NewMockState(ctrl), 401 } 402 for k, c := range components { 403 c.EXPECT().Name().Times(1).Return(k) 404 } 405 log := logging.NewTestLogger() 406 cfg := checkpoint.NewDefaultConfig() 407 eng, err := checkpoint.New(log, cfg, components[types.GovernanceCheckpoint], components[types.AssetsCheckpoint]) 408 require.NoError(t, err) 409 // set interval of 1 hour 410 hour, _ := time.ParseDuration("1h") 411 eng.OnTimeElapsedUpdate(ctx, hour) 412 data := map[types.CheckpointName][]byte{ 413 types.GovernanceCheckpoint: []byte("foodata"), 414 types.AssetsCheckpoint: []byte("bardata"), 415 } 416 for k, c := range components { 417 c.EXPECT().Checkpoint().Times(1).Return(data[k], nil) 418 } 419 tm := time.Now().Add(-2 * time.Hour) 420 _, _ = eng.Checkpoint(ctx, tm) 421 now := time.Now() 422 raw, err := eng.Checkpoint(ctx, now) 423 require.NoError(t, err) 424 require.NotNil(t, raw) 425 426 halfHour := time.Duration(int64(hour) / 2) 427 now = now.Add(halfHour) 428 raw, err = eng.Checkpoint(ctx, now) 429 require.Nil(t, raw) 430 require.Nil(t, err) 431 } 432 433 func testCheckpointBalanceInterval(t *testing.T) { 434 t.Parallel() 435 ctrl := gomock.NewController(t) 436 defer ctrl.Finish() 437 ctx := context.Background() 438 components := map[types.CheckpointName]*mocks.MockState{ 439 types.GovernanceCheckpoint: mocks.NewMockState(ctrl), 440 types.AssetsCheckpoint: mocks.NewMockState(ctrl), 441 } 442 for k, c := range components { 443 c.EXPECT().Name().Times(1).Return(k) 444 } 445 log := logging.NewTestLogger() 446 cfg := checkpoint.NewDefaultConfig() 447 eng, err := checkpoint.New(log, cfg, components[types.GovernanceCheckpoint], components[types.AssetsCheckpoint]) 448 require.NoError(t, err) 449 // set interval of 1 hour 450 hour, _ := time.ParseDuration("1h") 451 eng.OnTimeElapsedUpdate(ctx, hour) 452 data := map[types.CheckpointName][]byte{ 453 types.GovernanceCheckpoint: []byte("foodata"), 454 types.AssetsCheckpoint: []byte("bardata"), 455 } 456 for k, c := range components { 457 c.EXPECT().Checkpoint().Times(2).Return(data[k], nil) 458 } 459 tm := time.Now().Add(-2 * time.Hour) 460 _, _ = eng.Checkpoint(ctx, tm) 461 now := time.Now() 462 raw, err := eng.Checkpoint(ctx, now) 463 require.NoError(t, err) 464 require.NotNil(t, raw) 465 466 halfHour := time.Duration(int64(hour) / 2) 467 now = now.Add(halfHour) 468 // progress time, but still not time to create a new checkpoint 469 raw, err = eng.Checkpoint(ctx, now) 470 require.Nil(t, raw) 471 require.Nil(t, err) 472 // for a withdrawal, though, we will create one regardless 473 _, err = eng.BalanceCheckpoint(ctx) 474 require.NoError(t, err) 475 } 476 477 // same test as above, but the interval is upadted to trigger a second checkpoint 478 // to be created anyway. 479 func testCheckpointUpdatedInterval(t *testing.T) { 480 t.Parallel() 481 ctrl := gomock.NewController(t) 482 defer ctrl.Finish() 483 ctx := context.Background() 484 components := map[types.CheckpointName]*mocks.MockState{ 485 types.GovernanceCheckpoint: mocks.NewMockState(ctrl), 486 types.AssetsCheckpoint: mocks.NewMockState(ctrl), 487 } 488 for k, c := range components { 489 c.EXPECT().Name().Times(1).Return(k) 490 } 491 log := logging.NewTestLogger() 492 cfg := checkpoint.NewDefaultConfig() 493 eng, err := checkpoint.New(log, cfg, components[types.GovernanceCheckpoint], components[types.AssetsCheckpoint]) 494 require.NoError(t, err) 495 // set interval of 1 hour 496 hour, _ := time.ParseDuration("1h") 497 eng.OnTimeElapsedUpdate(ctx, hour) 498 data := map[types.CheckpointName][]byte{ 499 types.GovernanceCheckpoint: []byte("foodata"), 500 types.AssetsCheckpoint: []byte("bardata"), 501 } 502 for k, c := range components { 503 // we expect 2 calls 504 c.EXPECT().Checkpoint().Times(2).Return(data[k], nil) 505 } 506 tm := time.Now().Add(-2 * time.Hour) 507 _, _ = eng.Checkpoint(ctx, tm) 508 now := time.Now() 509 raw, err := eng.Checkpoint(ctx, now) 510 require.NoError(t, err) 511 require.NotNil(t, raw) 512 513 // this is before we ought to create a checkpoint, and should return nil 514 halfHour := time.Duration(int64(hour) / 2) 515 now = now.Add(halfHour) 516 raw, err = eng.Checkpoint(ctx, now) 517 require.Nil(t, raw) 518 require.Nil(t, err) 519 520 // now the second calls to the components are made 521 now = now.Add(time.Second) // t+30m1s 522 eng.OnTimeElapsedUpdate(ctx, halfHour) // delta is 30 min 523 raw, err = eng.Checkpoint(ctx, now) 524 require.NoError(t, err) 525 require.NotNil(t, raw) 526 } 527 528 func testLoadAssets(t *testing.T) { 529 t.Parallel() 530 eng := getTestEngine(t) 531 ctx := context.Background() 532 defer eng.ctrl.Finish() 533 // set up mocks 534 data := map[types.CheckpointName][]byte{ 535 types.GovernanceCheckpoint: []byte("foodata"), 536 types.AssetsCheckpoint: []byte("bardata"), 537 types.CollateralCheckpoint: []byte("collateraldata"), 538 } 539 assets := mocks.NewMockAssetsState(eng.ctrl) 540 assets.EXPECT().Name().Times(1).Return(types.AssetsCheckpoint) 541 assets.EXPECT().Checkpoint().Times(1).Return(data[types.AssetsCheckpoint], nil) 542 collateral := mocks.NewMockCollateralState(eng.ctrl) 543 collateral.EXPECT().Name().Times(1).Return(types.CollateralCheckpoint) 544 collateral.EXPECT().Checkpoint().Times(1).Return(data[types.CollateralCheckpoint], nil) 545 governance := mocks.NewMockState(eng.ctrl) 546 governance.EXPECT().Name().Times(1).Return(types.GovernanceCheckpoint) 547 governance.EXPECT().Checkpoint().Times(1).Return(data[types.GovernanceCheckpoint], nil) 548 // add the mocks to the engine 549 require.NoError(t, eng.Add(governance, assets, collateral)) 550 // get the checkpoint data 551 tm := time.Now().Add(-2 * time.Hour) 552 _, _ = eng.Checkpoint(ctx, tm) 553 raw, err := eng.Checkpoint(ctx, time.Now()) 554 require.NoError(t, err) 555 // calling load with this checkpoint now is a noop 556 // require.Error(t, eng.Load(ctx, raw)) 557 // pretend like the genesis block specified this hash to restore 558 set := genesis{ 559 CP: &checkpoint.GenesisState{ 560 CheckpointHash: hex.EncodeToString(raw.Hash), 561 CheckpointState: base64.StdEncoding.EncodeToString(raw.State), 562 }, 563 } 564 565 // now we do expect the calls to be made, but only once 566 governance.EXPECT().Load(gomock.Any(), data[types.GovernanceCheckpoint]).Times(1).Return(nil) 567 assets.EXPECT().Load(gomock.Any(), data[types.AssetsCheckpoint]).Times(1).Return(nil) 568 collateral.EXPECT().Load(gomock.Any(), data[types.CollateralCheckpoint]).Times(1).Return(nil) 569 // but assets ought to receive an additional call 570 // return this stubbed asset, we only care about the ID anyway 571 enabled := types.Asset{ 572 ID: "asset", 573 } 574 assets.EXPECT().GetEnabledAssets().Times(1).Return([]*types.Asset{ 575 &enabled, 576 }) 577 collateral.EXPECT().EnableAsset(ctx, enabled).Times(1).Return(nil) 578 579 // now set the engine to accept the hash of the data we want to load 580 gen, err := json.Marshal(set) 581 require.NoError(t, err) 582 require.NoError(t, eng.UponGenesis(ctx, gen)) 583 } 584 585 type wrappedMock struct { 586 *mocks.MockState 587 data []byte 588 } 589 590 func wrapMock(m *mocks.MockState) *wrappedMock { 591 return &wrappedMock{ 592 MockState: m, 593 } 594 } 595 596 func (w *wrappedMock) Load(ctx context.Context, data []byte) error { 597 w.data = data 598 return w.MockState.Load(ctx, data) 599 }