github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/storehouse/register_store_test.go (about) 1 package storehouse_test 2 3 import ( 4 "fmt" 5 "sync" 6 "testing" 7 8 "github.com/stretchr/testify/require" 9 10 "github.com/onflow/flow-go/engine/execution" 11 "github.com/onflow/flow-go/engine/execution/storehouse" 12 "github.com/onflow/flow-go/engine/execution/testutil" 13 "github.com/onflow/flow-go/model/flow" 14 "github.com/onflow/flow-go/storage/pebble" 15 "github.com/onflow/flow-go/utils/unittest" 16 ) 17 18 type notifier struct { 19 height uint64 20 } 21 22 func (n *notifier) OnFinalizedAndExecutedHeightUpdated(height uint64) { 23 n.height = height 24 } 25 26 func withRegisterStore(t *testing.T, fn func( 27 t *testing.T, 28 rs *storehouse.RegisterStore, 29 diskStore execution.OnDiskRegisterStore, 30 finalized *testutil.MockFinalizedReader, 31 rootHeight uint64, 32 endHeight uint64, 33 headers map[uint64]*flow.Header, 34 n *notifier, 35 )) { 36 pebble.RunWithRegistersStorageAtInitialHeights(t, 10, 10, func(diskStore *pebble.Registers) { 37 log := unittest.Logger() 38 var wal execution.ExecutedFinalizedWAL 39 finalized, headerByHeight, highest := testutil.NewMockFinalizedReader(10, 100) 40 n := ¬ifier{height: 10} 41 rs, err := storehouse.NewRegisterStore(diskStore, wal, finalized, log, n) 42 require.NoError(t, err) 43 fn(t, rs, diskStore, finalized, 10, highest, headerByHeight, n) 44 }) 45 } 46 47 // GetRegister should fail for 48 // 1. unknown blockID 49 // 2. height lower than OnDiskRegisterStore's root height 50 // 3. height too high 51 // 4. known block, but unknown register 52 func TestRegisterStoreGetRegisterFail(t *testing.T) { 53 t.Parallel() 54 withRegisterStore(t, func( 55 t *testing.T, 56 rs *storehouse.RegisterStore, 57 diskStore execution.OnDiskRegisterStore, 58 finalized *testutil.MockFinalizedReader, 59 rootHeight uint64, 60 endHeight uint64, 61 headerByHeight map[uint64]*flow.Header, 62 n *notifier, 63 ) { 64 // unknown block 65 _, err := rs.GetRegister(rootHeight+1, unknownBlock, unknownReg.Key) 66 require.Error(t, err) 67 require.ErrorIs(t, err, storehouse.ErrNotExecuted) 68 69 // too high 70 block11 := headerByHeight[rootHeight+1] 71 _, err = rs.GetRegister(rootHeight+1, block11.ID(), unknownReg.Key) 72 require.Error(t, err) 73 require.ErrorIs(t, err, storehouse.ErrNotExecuted) 74 75 // lower than root height 76 _, err = rs.GetRegister(rootHeight-1, unknownBlock, unknownReg.Key) 77 require.Error(t, err) 78 // TODO: enable it once implemented 79 // require.ErrorIs(t, err, storehouse.ErrPruned) 80 81 // known block, unknown register 82 rootBlock := headerByHeight[rootHeight] 83 val, err := rs.GetRegister(rootHeight, rootBlock.ID(), unknownReg.Key) 84 require.NoError(t, err) 85 require.Nil(t, val) 86 }) 87 } 88 89 // SaveRegisters should fail for 90 // 1. mismatching parent 91 // 2. saved block 92 func TestRegisterStoreSaveRegistersShouldFail(t *testing.T) { 93 t.Parallel() 94 withRegisterStore(t, func( 95 t *testing.T, 96 rs *storehouse.RegisterStore, 97 diskStore execution.OnDiskRegisterStore, 98 finalized *testutil.MockFinalizedReader, 99 rootHeight uint64, 100 endHeight uint64, 101 headerByHeight map[uint64]*flow.Header, 102 n *notifier, 103 ) { 104 wrongParent := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(rootHeight + 1)) 105 err := rs.SaveRegisters(wrongParent, flow.RegisterEntries{}) 106 require.Error(t, err) 107 require.Contains(t, err.Error(), "parent") 108 109 err = rs.SaveRegisters(headerByHeight[rootHeight], flow.RegisterEntries{}) 110 require.Error(t, err) 111 require.Contains(t, err.Error(), "pruned") 112 }) 113 } 114 115 // SaveRegisters should ok, and 116 // 1. GetRegister can get saved registers, 117 // 2. IsBlockExecuted should return true 118 // 119 // if SaveRegisters with empty register, then 120 // 1. LastFinalizedAndExecutedHeight should be updated 121 // 2. IsBlockExecuted should return true 122 func TestRegisterStoreSaveRegistersShouldOK(t *testing.T) { 123 t.Parallel() 124 withRegisterStore(t, func( 125 t *testing.T, 126 rs *storehouse.RegisterStore, 127 diskStore execution.OnDiskRegisterStore, 128 finalized *testutil.MockFinalizedReader, 129 rootHeight uint64, 130 endHeight uint64, 131 headerByHeight map[uint64]*flow.Header, 132 n *notifier, 133 ) { 134 // not executed 135 executed, err := rs.IsBlockExecuted(rootHeight+1, headerByHeight[rootHeight+1].ID()) 136 require.NoError(t, err) 137 require.False(t, executed) 138 139 // save block 11 140 reg := makeReg("X", "1") 141 err = rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{reg}) 142 require.NoError(t, err) 143 144 // should get value 145 val, err := rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), reg.Key) 146 require.NoError(t, err) 147 require.Equal(t, reg.Value, val) 148 149 // should become executed 150 executed, err = rs.IsBlockExecuted(rootHeight+1, headerByHeight[rootHeight+1].ID()) 151 require.NoError(t, err) 152 require.True(t, executed) 153 154 // block 12 is empty 155 err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{}) 156 require.NoError(t, err) 157 158 // should get same value 159 val, err = rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+2].ID(), reg.Key) 160 require.NoError(t, err) 161 require.Equal(t, reg.Value, val) 162 163 // should become executed 164 executed, err = rs.IsBlockExecuted(rootHeight+1, headerByHeight[rootHeight+2].ID()) 165 require.NoError(t, err) 166 require.True(t, executed) 167 }) 168 } 169 170 // if 11 is latest finalized, then 171 // 1. IsBlockExecuted should return true for finalized block 10 172 // 2. IsBlockExecuted should return false for conflicting block 10 173 // 4. IsBlockExecuted should return true for executed and unfinalized block 12 174 // 3. IsBlockExecuted should return false for unexecuted block 13 175 func TestRegisterStoreIsBlockExecuted(t *testing.T) { 176 t.Parallel() 177 withRegisterStore(t, func( 178 t *testing.T, 179 rs *storehouse.RegisterStore, 180 diskStore execution.OnDiskRegisterStore, 181 finalized *testutil.MockFinalizedReader, 182 rootHeight uint64, 183 endHeight uint64, 184 headerByHeight map[uint64]*flow.Header, 185 n *notifier, 186 ) { 187 // save block 11 188 reg := makeReg("X", "1") 189 err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{reg}) 190 require.NoError(t, err) 191 192 // save block 12 193 err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("X", "2")}) 194 require.NoError(t, err) 195 196 require.NoError(t, finalized.MockFinal(rootHeight+1)) 197 198 require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized 199 200 require.Equal(t, rootHeight+1, rs.LastFinalizedAndExecutedHeight()) 201 202 executed, err := rs.IsBlockExecuted(rootHeight, headerByHeight[rootHeight].ID()) 203 require.NoError(t, err) 204 require.True(t, executed) 205 206 executed, err = rs.IsBlockExecuted(rootHeight+1, headerByHeight[rootHeight+1].ID()) 207 require.NoError(t, err) 208 require.True(t, executed) 209 210 executed, err = rs.IsBlockExecuted(rootHeight+2, headerByHeight[rootHeight+2].ID()) 211 require.NoError(t, err) 212 require.True(t, executed) 213 214 executed, err = rs.IsBlockExecuted(rootHeight+3, headerByHeight[rootHeight+3].ID()) 215 require.NoError(t, err) 216 require.False(t, executed) 217 }) 218 } 219 220 // Test reading registers from finalized block 221 func TestRegisterStoreReadingFromDisk(t *testing.T) { 222 t.Parallel() 223 withRegisterStore(t, func( 224 t *testing.T, 225 rs *storehouse.RegisterStore, 226 diskStore execution.OnDiskRegisterStore, 227 finalized *testutil.MockFinalizedReader, 228 rootHeight uint64, 229 endHeight uint64, 230 headerByHeight map[uint64]*flow.Header, 231 n *notifier, 232 ) { 233 234 // R <- 11 (X: 1, Y: 2) <- 12 (Y: 3) <- 13 (X: 4) 235 // save block 11 236 err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1"), makeReg("Y", "2")}) 237 require.NoError(t, err) 238 239 // save block 12 240 err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("Y", "3")}) 241 require.NoError(t, err) 242 243 // save block 13 244 err = rs.SaveRegisters(headerByHeight[rootHeight+3], flow.RegisterEntries{makeReg("X", "4")}) 245 require.NoError(t, err) 246 247 require.Equal(t, rootHeight, n.height) 248 249 require.NoError(t, finalized.MockFinal(rootHeight+2)) 250 require.NoError(t, rs.OnBlockFinalized()) // notify 12 is finalized 251 252 require.Equal(t, rootHeight+2, n.height) 253 254 val, err := rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("Y", "2").Key) 255 require.NoError(t, err) 256 // value at block 11 is now stored in OnDiskRegisterStore, which is 2 257 require.Equal(t, makeReg("Y", "2").Value, val) 258 259 val, err = rs.GetRegister(rootHeight+2, headerByHeight[rootHeight+2].ID(), makeReg("X", "1").Key) 260 require.NoError(t, err) 261 // value at block 12 is now stored in OnDiskRegisterStore, which is 1 262 require.Equal(t, makeReg("X", "1").Value, val) 263 264 val, err = rs.GetRegister(rootHeight+3, headerByHeight[rootHeight+3].ID(), makeReg("Y", "3").Key) 265 require.NoError(t, err) 266 // value at block 13 was stored in OnDiskRegisterStore at block 12, which is 3 267 require.Equal(t, makeReg("Y", "3").Value, val) 268 269 _, err = rs.GetRegister(rootHeight+4, headerByHeight[rootHeight+4].ID(), makeReg("Y", "3").Key) 270 require.Error(t, err) 271 }) 272 } 273 274 func TestRegisterStoreReadingFromInMemStore(t *testing.T) { 275 t.Parallel() 276 withRegisterStore(t, func( 277 t *testing.T, 278 rs *storehouse.RegisterStore, 279 diskStore execution.OnDiskRegisterStore, 280 finalized *testutil.MockFinalizedReader, 281 rootHeight uint64, 282 endHeight uint64, 283 headerByHeight map[uint64]*flow.Header, 284 n *notifier, 285 ) { 286 287 // R <- 11 (X: 1, Y: 2) <- 12 (Y: 3) 288 // ^- 11 (X: 4) 289 290 // save block 11 291 err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1"), makeReg("Y", "2")}) 292 require.NoError(t, err) 293 294 // save block 12 295 err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("Y", "3")}) 296 require.NoError(t, err) 297 298 // save block 11 fork 299 block11Fork := unittest.BlockWithParentFixture(headerByHeight[rootHeight]).Header 300 err = rs.SaveRegisters(block11Fork, flow.RegisterEntries{makeReg("X", "4")}) 301 require.NoError(t, err) 302 303 val, err := rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("X", "1").Key) 304 require.NoError(t, err) 305 require.Equal(t, makeReg("X", "1").Value, val) 306 307 val, err = rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("Y", "2").Key) 308 require.NoError(t, err) 309 require.Equal(t, makeReg("Y", "2").Value, val) 310 311 val, err = rs.GetRegister(rootHeight+2, headerByHeight[rootHeight+2].ID(), makeReg("X", "1").Key) 312 require.NoError(t, err) 313 require.Equal(t, makeReg("X", "1").Value, val) 314 315 val, err = rs.GetRegister(rootHeight+2, headerByHeight[rootHeight+2].ID(), makeReg("Y", "3").Key) 316 require.NoError(t, err) 317 require.Equal(t, makeReg("Y", "3").Value, val) 318 319 val, err = rs.GetRegister(rootHeight+1, block11Fork.ID(), makeReg("X", "4").Key) 320 require.NoError(t, err) 321 require.Equal(t, makeReg("X", "4").Value, val) 322 323 // finalizing 11 should prune block 11 fork, and won't be able to read register from block 11 fork 324 require.NoError(t, finalized.MockFinal(rootHeight+1)) 325 require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized 326 327 val, err = rs.GetRegister(rootHeight+1, block11Fork.ID(), makeReg("X", "4").Key) 328 require.Error(t, err, fmt.Sprintf("%v", val)) 329 // pruned conflicting forks are considered not executed 330 require.ErrorIs(t, err, storehouse.ErrNotExecuted) 331 }) 332 } 333 334 func TestRegisterStoreReadRegisterAtPrunedHeight(t *testing.T) { 335 t.Parallel() 336 withRegisterStore(t, func( 337 t *testing.T, 338 rs *storehouse.RegisterStore, 339 diskStore execution.OnDiskRegisterStore, 340 finalized *testutil.MockFinalizedReader, 341 rootHeight uint64, 342 endHeight uint64, 343 headerByHeight map[uint64]*flow.Header, 344 n *notifier, 345 ) { 346 347 // R <- 11 (X: 1) 348 349 // if execute first then finalize later, should be able to read register at pruned height 350 // save block 11 351 err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1")}) 352 require.NoError(t, err) 353 require.Equal(t, 2, finalized.FinalizedCalled()) // called by SaveRegisters with height 11 354 355 // finalize block 11 356 require.NoError(t, finalized.MockFinal(rootHeight+1)) 357 require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized 358 359 val, err := rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("X", "").Key) 360 require.NoError(t, err) 361 require.Equal(t, makeReg("X", "1").Value, val) 362 363 // R <- 11 (X: 1) <- 12 (X: 2) 364 // if finalize first then execute later, should not be able to read register at pruned height 365 // finalize block 12 366 require.NoError(t, finalized.MockFinal(rootHeight+2)) 367 require.NoError(t, rs.OnBlockFinalized()) // notify 12 is finalized 368 369 // save block 12 370 err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("X", "2")}) 371 require.NoError(t, err) 372 373 val, err = rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("X", "").Key) 374 require.NoError(t, err) 375 require.Equal(t, makeReg("X", "1").Value, val) 376 377 val, err = rs.GetRegister(rootHeight+2, headerByHeight[rootHeight+2].ID(), makeReg("X", "").Key) 378 require.NoError(t, err) 379 require.Equal(t, makeReg("X", "2").Value, val) 380 }) 381 } 382 383 // Test that when getting register during executing a finalized block or finalize an executed block, 384 // FinalizedBlockIDAtHeight should not be called 385 func TestRegisterStoreExecuteFinalizedBlockOrFinalizeExecutedBlockShouldNotCallFinalizedHeight(t *testing.T) { 386 t.Parallel() 387 withRegisterStore(t, func( 388 t *testing.T, 389 rs *storehouse.RegisterStore, 390 diskStore execution.OnDiskRegisterStore, 391 finalized *testutil.MockFinalizedReader, 392 rootHeight uint64, 393 endHeight uint64, 394 headerByHeight map[uint64]*flow.Header, 395 n *notifier, 396 ) { 397 398 require.Equal(t, 1, finalized.FinalizedCalled()) // called by NewRegisterStore 399 // R <- 11 (X: 1) 400 401 val, err := rs.GetRegister(rootHeight, headerByHeight[rootHeight].ID(), makeReg("X", "").Key) 402 require.NoError(t, err) 403 require.Nil(t, val) 404 require.Equal(t, 1, finalized.FinalizedCalled()) // no FinalizedBlockIDAtHeight called 405 406 // if execute first then finalize later, should be able to read register at pruned height 407 // save block 11 408 err = rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1")}) 409 require.NoError(t, err) 410 require.Equal(t, 2, finalized.FinalizedCalled()) // called by SaveRegisters with height 11 411 412 // finalize block 11 413 require.NoError(t, finalized.MockFinal(rootHeight+1)) 414 require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized 415 require.Equal(t, 4, finalized.FinalizedCalled()) // called by Checking whether height 11 and 12 are finalized 416 417 // R <- 11 (X: 1) <- 12 (X: 2) 418 // if finalize first then execute later, should not be able to read register at pruned height 419 // finalize block 12 420 require.NoError(t, finalized.MockFinal(rootHeight+2)) 421 require.NoError(t, rs.OnBlockFinalized()) // notify 12 is finalized 422 require.Equal(t, 5, finalized.FinalizedCalled()) // called by Checking whether height 12 and 13 are finalized 423 424 val, err = rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("X", "").Key) 425 require.NoError(t, err) 426 require.Equal(t, makeReg("X", "1").Value, val) 427 require.Equal(t, 5, finalized.FinalizedCalled()) // no FinalizedBlockIDAtHeight call 428 429 // save block 12 430 err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("X", "2")}) 431 require.NoError(t, err) 432 require.Equal(t, 7, finalized.FinalizedCalled()) // called by SaveRegisters with height 12 and 13 433 434 }) 435 } 436 437 // Execute first then finalize later 438 // SaveRegisters(1), SaveRegisters(2), SaveRegisters(3), then 439 // OnBlockFinalized(1), OnBlockFinalized(2), OnBlockFinalized(3) should 440 // 1. update LastFinalizedAndExecutedHeight 441 // 2. InMemoryRegisterStore should have correct pruned height 442 // 3. NewRegisterStore with the same OnDiskRegisterStore again should return correct LastFinalizedAndExecutedHeight 443 func TestRegisterStoreExecuteFirstFinalizeLater(t *testing.T) { 444 t.Parallel() 445 withRegisterStore(t, func( 446 t *testing.T, 447 rs *storehouse.RegisterStore, 448 diskStore execution.OnDiskRegisterStore, 449 finalized *testutil.MockFinalizedReader, 450 rootHeight uint64, 451 endHeight uint64, 452 headerByHeight map[uint64]*flow.Header, 453 n *notifier, 454 ) { 455 // save block 11 456 err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1")}) 457 require.NoError(t, err) 458 require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight()) 459 460 // save block 12 461 err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("X", "2")}) 462 require.NoError(t, err) 463 require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight()) 464 465 // save block 13 466 err = rs.SaveRegisters(headerByHeight[rootHeight+3], flow.RegisterEntries{makeReg("X", "3")}) 467 require.NoError(t, err) 468 require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight()) 469 470 require.Equal(t, rootHeight, n.height) 471 472 require.NoError(t, finalized.MockFinal(rootHeight+1)) 473 require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized 474 require.Equal(t, rootHeight+1, rs.LastFinalizedAndExecutedHeight()) 475 require.Equal(t, rootHeight+1, n.height) 476 477 require.NoError(t, finalized.MockFinal(rootHeight+2)) 478 require.NoError(t, rs.OnBlockFinalized()) // notify 12 is finalized 479 require.Equal(t, rootHeight+2, rs.LastFinalizedAndExecutedHeight()) 480 require.Equal(t, rootHeight+2, n.height) 481 482 require.NoError(t, finalized.MockFinal(rootHeight+3)) 483 require.NoError(t, rs.OnBlockFinalized()) // notify 13 is finalized 484 require.Equal(t, rootHeight+3, rs.LastFinalizedAndExecutedHeight()) 485 require.Equal(t, rootHeight+3, n.height) 486 }) 487 } 488 489 // Finalize first then execute later 490 // OnBlockFinalized(1), OnBlockFinalized(2), OnBlockFinalized(3), then 491 // SaveRegisters(1), SaveRegisters(2), SaveRegisters(3) should 492 // 1. update LastFinalizedAndExecutedHeight 493 // 2. InMemoryRegisterStore should have correct pruned height 494 // 3. NewRegisterStore with the same OnDiskRegisterStore again should return correct LastFinalizedAndExecutedHeight 495 func TestRegisterStoreFinalizeFirstExecuteLater(t *testing.T) { 496 t.Parallel() 497 withRegisterStore(t, func( 498 t *testing.T, 499 rs *storehouse.RegisterStore, 500 diskStore execution.OnDiskRegisterStore, 501 finalized *testutil.MockFinalizedReader, 502 rootHeight uint64, 503 endHeight uint64, 504 headerByHeight map[uint64]*flow.Header, 505 n *notifier, 506 ) { 507 require.NoError(t, finalized.MockFinal(rootHeight+1)) 508 require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized 509 require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight(), fmt.Sprintf("LastFinalizedAndExecutedHeight: %d", rs.LastFinalizedAndExecutedHeight())) 510 511 require.NoError(t, finalized.MockFinal(rootHeight+2)) 512 require.NoError(t, rs.OnBlockFinalized()) // notify 12 is finalized 513 require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight(), fmt.Sprintf("LastFinalizedAndExecutedHeight: %d", rs.LastFinalizedAndExecutedHeight())) 514 515 require.NoError(t, finalized.MockFinal(rootHeight+3)) 516 require.NoError(t, rs.OnBlockFinalized()) // notify 13 is finalized 517 require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight()) 518 519 require.Equal(t, rootHeight, n.height) 520 521 // save block 11 522 err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1")}) 523 require.NoError(t, err) 524 require.Equal(t, rootHeight+1, rs.LastFinalizedAndExecutedHeight()) 525 require.Equal(t, rootHeight+1, n.height) 526 527 // save block 12 528 err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("X", "2")}) 529 require.NoError(t, err) 530 require.Equal(t, rootHeight+2, rs.LastFinalizedAndExecutedHeight()) 531 require.Equal(t, rootHeight+2, n.height) 532 533 // save block 13 534 err = rs.SaveRegisters(headerByHeight[rootHeight+3], flow.RegisterEntries{makeReg("X", "3")}) 535 require.NoError(t, err) 536 require.Equal(t, rootHeight+3, rs.LastFinalizedAndExecutedHeight()) 537 require.Equal(t, rootHeight+3, n.height) 538 }) 539 } 540 541 // Finalize and Execute concurrently 542 // SaveRegisters(1), SaveRegisters(2), ... SaveRegisters(100), happen concurrently with 543 // OnBlockFinalized(1), OnBlockFinalized(2), ... OnBlockFinalized(100), should update LastFinalizedAndExecutedHeight 544 func TestRegisterStoreConcurrentFinalizeAndExecute(t *testing.T) { 545 t.Parallel() 546 withRegisterStore(t, func( 547 t *testing.T, 548 rs *storehouse.RegisterStore, 549 diskStore execution.OnDiskRegisterStore, 550 finalized *testutil.MockFinalizedReader, 551 rootHeight uint64, 552 endHeight uint64, 553 headerByHeight map[uint64]*flow.Header, 554 n *notifier, 555 ) { 556 557 var wg sync.WaitGroup 558 savedHeights := make(chan uint64, len(headerByHeight)) // enough buffer so that producer won't be blocked 559 560 wg.Add(1) 561 go func() { 562 defer wg.Done() 563 564 for savedHeight := range savedHeights { 565 err := finalized.MockFinal(savedHeight) 566 require.NoError(t, err) 567 require.NoError(t, rs.OnBlockFinalized(), fmt.Sprintf("saved height %v", savedHeight)) 568 } 569 }() 570 571 for height := rootHeight + 1; height <= endHeight; height++ { 572 if height >= 50 { 573 savedHeights <- height 574 } 575 576 err := rs.SaveRegisters(headerByHeight[height], flow.RegisterEntries{makeReg("X", fmt.Sprintf("%d", height))}) 577 require.NoError(t, err) 578 } 579 close(savedHeights) 580 581 wg.Wait() // wait until all heights are finalized 582 583 // after all heights are executed and finalized, the LastFinalizedAndExecutedHeight should be the last height 584 require.Equal(t, endHeight, rs.LastFinalizedAndExecutedHeight()) 585 }) 586 }