github.com/koko1123/flow-go-1@v0.29.6/fvm/derived/table_test.go (about) 1 package derived 2 3 import ( 4 "testing" 5 6 "github.com/stretchr/testify/assert" 7 "github.com/stretchr/testify/require" 8 9 "github.com/koko1123/flow-go-1/fvm/state" 10 "github.com/koko1123/flow-go-1/fvm/utils" 11 "github.com/koko1123/flow-go-1/model/flow" 12 ) 13 14 func newEmptyTestBlock() *DerivedDataTable[string, *string] { 15 return NewEmptyTable[string, *string]() 16 } 17 18 func TestDerivedDataTableWithTransactionOffset(t *testing.T) { 19 block := NewEmptyTableWithOffset[string, *string](18) 20 21 require.Equal( 22 t, 23 LogicalTime(17), 24 block.LatestCommitExecutionTimeForTestingOnly()) 25 } 26 27 func TestTxnDerivedDataNormalTransactionInvalidExecutionTimeBound(t *testing.T) { 28 block := newEmptyTestBlock() 29 30 _, err := block.NewTableTransaction(-1, -1) 31 require.ErrorContains(t, err, "execution time out of bound") 32 33 _, err = block.NewTableTransaction(0, 0) 34 require.NoError(t, err) 35 36 _, err = block.NewTableTransaction(0, EndOfBlockExecutionTime) 37 require.ErrorContains(t, err, "execution time out of bound") 38 39 _, err = block.NewTableTransaction(0, EndOfBlockExecutionTime-1) 40 require.NoError(t, err) 41 } 42 43 func TestTxnDerivedDataNormalTransactionInvalidSnapshotTime(t *testing.T) { 44 block := newEmptyTestBlock() 45 46 _, err := block.NewTableTransaction(10, 0) 47 require.ErrorContains(t, err, "snapshot > execution") 48 49 _, err = block.NewTableTransaction(10, 10) 50 require.NoError(t, err) 51 52 _, err = block.NewTableTransaction(999, 998) 53 require.ErrorContains(t, err, "snapshot > execution") 54 55 _, err = block.NewTableTransaction(999, 999) 56 require.NoError(t, err) 57 } 58 59 func TestTxnDerivedDataSnapshotReadTransactionInvalidExecutionTimeBound(t *testing.T) { 60 block := newEmptyTestBlock() 61 62 _, err := block.NewSnapshotReadTableTransaction( 63 ParentBlockTime, 64 ParentBlockTime) 65 require.ErrorContains(t, err, "execution time out of bound") 66 67 _, err = block.NewSnapshotReadTableTransaction(ParentBlockTime, 0) 68 require.NoError(t, err) 69 70 _, err = block.NewSnapshotReadTableTransaction(0, ChildBlockTime) 71 require.ErrorContains(t, err, "execution time out of bound") 72 73 _, err = block.NewSnapshotReadTableTransaction( 74 0, 75 EndOfBlockExecutionTime) 76 require.NoError(t, err) 77 } 78 79 func TestTxnDerivedDataValidateRejectOutOfOrderCommit(t *testing.T) { 80 block := newEmptyTestBlock() 81 82 testTxn, err := block.NewTableTransaction(0, 0) 83 require.NoError(t, err) 84 85 testSetupTxn, err := block.NewTableTransaction(0, 1) 86 require.NoError(t, err) 87 88 validateErr := testTxn.Validate() 89 require.NoError(t, validateErr) 90 91 err = testSetupTxn.Commit() 92 require.NoError(t, err) 93 94 validateErr = testTxn.Validate() 95 require.ErrorContains(t, validateErr, "non-increasing time") 96 require.False(t, validateErr.IsRetryable()) 97 } 98 99 func TestTxnDerivedDataValidateRejectNonIncreasingExecutionTime(t *testing.T) { 100 block := newEmptyTestBlock() 101 102 testSetupTxn, err := block.NewTableTransaction(0, 0) 103 require.NoError(t, err) 104 105 err = testSetupTxn.Commit() 106 require.NoError(t, err) 107 108 testTxn, err := block.NewTableTransaction(0, 0) 109 require.NoError(t, err) 110 111 validateErr := testTxn.Validate() 112 require.ErrorContains(t, validateErr, "non-increasing time") 113 require.False(t, validateErr.IsRetryable()) 114 } 115 116 func TestTxnDerivedDataValidateRejectCommitGapForNormalTxn(t *testing.T) { 117 block := newEmptyTestBlock() 118 119 commitTime := LogicalTime(5) 120 testSetupTxn, err := block.NewTableTransaction(0, commitTime) 121 require.NoError(t, err) 122 123 err = testSetupTxn.Commit() 124 require.NoError(t, err) 125 126 require.Equal(t, commitTime, block.LatestCommitExecutionTimeForTestingOnly()) 127 128 testTxn, err := block.NewTableTransaction(10, 10) 129 require.NoError(t, err) 130 131 validateErr := testTxn.Validate() 132 require.ErrorContains(t, validateErr, "missing commit range [6, 10)") 133 require.False(t, validateErr.IsRetryable()) 134 } 135 136 func TestTxnDerivedDataValidateRejectCommitGapForSnapshotRead(t *testing.T) { 137 block := newEmptyTestBlock() 138 139 commitTime := LogicalTime(5) 140 testSetupTxn, err := block.NewTableTransaction(0, commitTime) 141 require.NoError(t, err) 142 143 err = testSetupTxn.Commit() 144 require.NoError(t, err) 145 146 require.Equal(t, commitTime, block.LatestCommitExecutionTimeForTestingOnly()) 147 148 testTxn, err := block.NewSnapshotReadTableTransaction(10, 10) 149 require.NoError(t, err) 150 151 validateErr := testTxn.Validate() 152 require.ErrorContains(t, validateErr, "missing commit range [6, 10)") 153 require.False(t, validateErr.IsRetryable()) 154 } 155 156 func TestTxnDerivedDataValidateRejectOutdatedReadSet(t *testing.T) { 157 block := newEmptyTestBlock() 158 159 testSetupTxn1, err := block.NewTableTransaction(0, 0) 160 require.NoError(t, err) 161 162 testSetupTxn2, err := block.NewTableTransaction(0, 1) 163 require.NoError(t, err) 164 165 testTxn, err := block.NewTableTransaction(0, 2) 166 require.NoError(t, err) 167 168 key := "abc" 169 valueString := "value" 170 expectedValue := &valueString 171 expectedState := &state.State{} 172 173 testSetupTxn1.Set(key, expectedValue, expectedState) 174 175 testSetupTxn1.AddInvalidator(testInvalidator{}) 176 177 err = testSetupTxn1.Commit() 178 require.NoError(t, err) 179 180 validateErr := testTxn.Validate() 181 require.NoError(t, validateErr) 182 183 actualProg, actualState, ok := testTxn.Get(key) 184 require.True(t, ok) 185 require.Same(t, expectedValue, actualProg) 186 require.Same(t, expectedState, actualState) 187 188 validateErr = testTxn.Validate() 189 require.NoError(t, validateErr) 190 191 testSetupTxn2.AddInvalidator(testInvalidator{invalidateAll: true}) 192 193 err = testSetupTxn2.Commit() 194 require.NoError(t, err) 195 196 validateErr = testTxn.Validate() 197 require.ErrorContains(t, validateErr, "outdated read set") 198 require.True(t, validateErr.IsRetryable()) 199 } 200 201 func TestTxnDerivedDataValidateRejectOutdatedWriteSet(t *testing.T) { 202 block := newEmptyTestBlock() 203 204 testSetupTxn, err := block.NewTableTransaction(0, 0) 205 require.NoError(t, err) 206 207 testSetupTxn.AddInvalidator(testInvalidator{invalidateAll: true}) 208 209 err = testSetupTxn.Commit() 210 require.NoError(t, err) 211 212 require.Equal(t, 1, len(block.InvalidatorsForTestingOnly())) 213 214 testTxn, err := block.NewTableTransaction(0, 1) 215 require.NoError(t, err) 216 217 value := "value" 218 testTxn.Set("key", &value, &state.State{}) 219 220 validateErr := testTxn.Validate() 221 require.ErrorContains(t, validateErr, "outdated write set") 222 require.True(t, validateErr.IsRetryable()) 223 } 224 225 func TestTxnDerivedDataValidateIgnoreInvalidatorsOlderThanSnapshot(t *testing.T) { 226 block := newEmptyTestBlock() 227 228 testSetupTxn, err := block.NewTableTransaction(0, 0) 229 require.NoError(t, err) 230 231 testSetupTxn.AddInvalidator(testInvalidator{invalidateAll: true}) 232 err = testSetupTxn.Commit() 233 require.NoError(t, err) 234 235 require.Equal(t, 1, len(block.InvalidatorsForTestingOnly())) 236 237 testTxn, err := block.NewTableTransaction(1, 1) 238 require.NoError(t, err) 239 240 value := "value" 241 testTxn.Set("key", &value, &state.State{}) 242 243 err = testTxn.Validate() 244 require.NoError(t, err) 245 } 246 247 func TestTxnDerivedDataCommitEndOfBlockSnapshotRead(t *testing.T) { 248 block := newEmptyTestBlock() 249 250 commitTime := LogicalTime(5) 251 testSetupTxn, err := block.NewTableTransaction(0, commitTime) 252 require.NoError(t, err) 253 254 err = testSetupTxn.Commit() 255 require.NoError(t, err) 256 257 require.Equal(t, commitTime, block.LatestCommitExecutionTimeForTestingOnly()) 258 259 testTxn, err := block.NewSnapshotReadTableTransaction( 260 EndOfBlockExecutionTime, 261 EndOfBlockExecutionTime) 262 require.NoError(t, err) 263 264 err = testTxn.Commit() 265 require.NoError(t, err) 266 267 require.Equal(t, commitTime, block.LatestCommitExecutionTimeForTestingOnly()) 268 } 269 270 func TestTxnDerivedDataCommitSnapshotReadDontAdvanceTime(t *testing.T) { 271 block := newEmptyTestBlock() 272 273 commitTime := LogicalTime(71) 274 testSetupTxn, err := block.NewTableTransaction(0, commitTime) 275 require.NoError(t, err) 276 277 err = testSetupTxn.Commit() 278 require.NoError(t, err) 279 280 repeatedTime := commitTime + 1 281 for i := 0; i < 10; i++ { 282 txn, err := block.NewSnapshotReadTableTransaction(0, repeatedTime) 283 require.NoError(t, err) 284 285 err = txn.Commit() 286 require.NoError(t, err) 287 } 288 289 require.Equal( 290 t, 291 commitTime, 292 block.LatestCommitExecutionTimeForTestingOnly()) 293 } 294 295 func TestTxnDerivedDataCommitWriteOnlyTransactionNoInvalidation(t *testing.T) { 296 block := newEmptyTestBlock() 297 298 testTxn, err := block.NewTableTransaction(0, 0) 299 require.NoError(t, err) 300 301 key := "234" 302 303 actualValue, actualState, ok := testTxn.Get(key) 304 require.False(t, ok) 305 require.Nil(t, actualValue) 306 require.Nil(t, actualState) 307 308 valueString := "stuff" 309 expectedValue := &valueString 310 expectedState := &state.State{} 311 312 testTxn.Set(key, expectedValue, expectedState) 313 314 actualValue, actualState, ok = testTxn.Get(key) 315 require.True(t, ok) 316 require.Same(t, expectedValue, actualValue) 317 require.Same(t, expectedState, actualState) 318 319 testTxn.AddInvalidator(testInvalidator{}) 320 321 err = testTxn.Commit() 322 require.NoError(t, err) 323 324 // Sanity check 325 326 require.Equal( 327 t, 328 LogicalTime(0), 329 block.LatestCommitExecutionTimeForTestingOnly()) 330 331 require.Equal(t, 0, len(block.InvalidatorsForTestingOnly())) 332 333 entries := block.EntriesForTestingOnly() 334 require.Equal(t, 1, len(entries)) 335 336 entry, ok := entries[key] 337 require.True(t, ok) 338 require.False(t, entry.isInvalid) 339 require.Same(t, expectedValue, entry.Value) 340 require.Same(t, expectedState, entry.State) 341 } 342 343 func TestTxnDerivedDataCommitWriteOnlyTransactionWithInvalidation(t *testing.T) { 344 block := newEmptyTestBlock() 345 346 testTxnTime := LogicalTime(47) 347 testTxn, err := block.NewTableTransaction(0, testTxnTime) 348 require.NoError(t, err) 349 350 key := "999" 351 352 actualValue, actualState, ok := testTxn.Get(key) 353 require.False(t, ok) 354 require.Nil(t, actualValue) 355 require.Nil(t, actualState) 356 357 valueString := "blah" 358 expectedValue := &valueString 359 expectedState := &state.State{} 360 361 testTxn.Set(key, expectedValue, expectedState) 362 363 actualValue, actualState, ok = testTxn.Get(key) 364 require.True(t, ok) 365 require.Same(t, expectedValue, actualValue) 366 require.Same(t, expectedState, actualState) 367 368 invalidator := testInvalidator{invalidateAll: true} 369 370 testTxn.AddInvalidator(invalidator) 371 372 err = testTxn.Commit() 373 require.NoError(t, err) 374 375 // Sanity check 376 377 require.Equal( 378 t, 379 testTxnTime, 380 block.LatestCommitExecutionTimeForTestingOnly()) 381 382 require.Equal( 383 t, 384 chainedTableInvalidators[string, *string]{ 385 { 386 TableInvalidator: invalidator, 387 executionTime: testTxnTime, 388 }, 389 }, 390 block.InvalidatorsForTestingOnly()) 391 392 require.Equal(t, 0, len(block.EntriesForTestingOnly())) 393 } 394 395 func TestTxnDerivedDataCommitUseOriginalEntryOnDuplicateWriteEntries(t *testing.T) { 396 block := newEmptyTestBlock() 397 398 testSetupTxn, err := block.NewTableTransaction(0, 11) 399 require.NoError(t, err) 400 401 testTxn, err := block.NewTableTransaction(10, 12) 402 require.NoError(t, err) 403 404 key := "17" 405 valueString := "foo" 406 expectedValue := &valueString 407 expectedState := &state.State{} 408 409 testSetupTxn.Set(key, expectedValue, expectedState) 410 411 err = testSetupTxn.Commit() 412 require.NoError(t, err) 413 414 entries := block.EntriesForTestingOnly() 415 require.Equal(t, 1, len(entries)) 416 417 expectedEntry, ok := entries[key] 418 require.True(t, ok) 419 420 otherString := "other" 421 otherValue := &otherString 422 otherState := &state.State{} 423 424 testTxn.Set(key, otherValue, otherState) 425 426 err = testTxn.Commit() 427 require.NoError(t, err) 428 429 entries = block.EntriesForTestingOnly() 430 require.Equal(t, 1, len(entries)) 431 432 actualEntry, ok := entries[key] 433 require.True(t, ok) 434 435 require.Same(t, expectedEntry, actualEntry) 436 require.False(t, actualEntry.isInvalid) 437 require.Same(t, expectedValue, actualEntry.Value) 438 require.Same(t, expectedState, actualEntry.State) 439 require.NotSame(t, otherValue, actualEntry.Value) 440 require.NotSame(t, otherState, actualEntry.State) 441 } 442 443 func TestTxnDerivedDataCommitReadOnlyTransactionNoInvalidation(t *testing.T) { 444 block := newEmptyTestBlock() 445 446 testSetupTxn, err := block.NewTableTransaction(0, 0) 447 require.NoError(t, err) 448 449 testTxn, err := block.NewTableTransaction(0, 1) 450 require.NoError(t, err) 451 452 key1 := "key1" 453 valStr1 := "value1" 454 expectedValue1 := &valStr1 455 expectedState1 := &state.State{} 456 457 testSetupTxn.Set(key1, expectedValue1, expectedState1) 458 459 key2 := "key2" 460 valStr2 := "value2" 461 expectedValue2 := &valStr2 462 expectedState2 := &state.State{} 463 464 testSetupTxn.Set(key2, expectedValue2, expectedState2) 465 466 err = testSetupTxn.Commit() 467 require.NoError(t, err) 468 469 actualValue, actualState, ok := testTxn.Get(key1) 470 require.True(t, ok) 471 require.Same(t, expectedValue1, actualValue) 472 require.Same(t, expectedState1, actualState) 473 474 actualValue, actualState, ok = testTxn.Get(key2) 475 require.True(t, ok) 476 require.Same(t, expectedValue2, actualValue) 477 require.Same(t, expectedState2, actualState) 478 479 actualValue, actualState, ok = testTxn.Get("key3") 480 require.False(t, ok) 481 require.Nil(t, actualValue) 482 require.Nil(t, actualState) 483 484 testTxn.AddInvalidator(testInvalidator{}) 485 486 err = testTxn.Commit() 487 require.NoError(t, err) 488 489 // Sanity check 490 491 require.Equal( 492 t, 493 LogicalTime(1), 494 block.LatestCommitExecutionTimeForTestingOnly()) 495 496 require.Equal(t, 0, len(block.InvalidatorsForTestingOnly())) 497 498 entries := block.EntriesForTestingOnly() 499 require.Equal(t, 2, len(entries)) 500 501 entry, ok := entries[key1] 502 require.True(t, ok) 503 require.False(t, entry.isInvalid) 504 require.Same(t, expectedValue1, entry.Value) 505 require.Same(t, expectedState1, entry.State) 506 507 entry, ok = entries[key2] 508 require.True(t, ok) 509 require.False(t, entry.isInvalid) 510 require.Same(t, expectedValue2, entry.Value) 511 require.Same(t, expectedState2, entry.State) 512 } 513 514 func TestTxnDerivedDataCommitReadOnlyTransactionWithInvalidation(t *testing.T) { 515 block := newEmptyTestBlock() 516 517 testSetupTxn1Time := LogicalTime(2) 518 testSetupTxn1, err := block.NewTableTransaction(0, testSetupTxn1Time) 519 require.NoError(t, err) 520 521 testSetupTxn2, err := block.NewTableTransaction(0, 4) 522 require.NoError(t, err) 523 524 testTxnTime := LogicalTime(6) 525 testTxn, err := block.NewTableTransaction(0, testTxnTime) 526 require.NoError(t, err) 527 528 testSetupTxn1Invalidator := testInvalidator{ 529 invalidateName: "blah", 530 } 531 testSetupTxn1.AddInvalidator(testSetupTxn1Invalidator) 532 533 err = testSetupTxn1.Commit() 534 require.NoError(t, err) 535 536 key1 := "key1" 537 valStr1 := "v1" 538 expectedValue1 := &valStr1 539 expectedState1 := &state.State{} 540 541 testSetupTxn2.Set(key1, expectedValue1, expectedState1) 542 543 key2 := "key2" 544 valStr2 := "v2" 545 expectedValue2 := &valStr2 546 expectedState2 := &state.State{} 547 548 testSetupTxn2.Set(key2, expectedValue2, expectedState2) 549 550 err = testSetupTxn2.Commit() 551 require.NoError(t, err) 552 553 actualValue, actualState, ok := testTxn.Get(key1) 554 require.True(t, ok) 555 require.Same(t, expectedValue1, actualValue) 556 require.Same(t, expectedState1, actualState) 557 558 actualValue, actualState, ok = testTxn.Get(key2) 559 require.True(t, ok) 560 require.Same(t, expectedValue2, actualValue) 561 require.Same(t, expectedState2, actualState) 562 563 actualValue, actualState, ok = testTxn.Get("key3") 564 require.False(t, ok) 565 require.Nil(t, actualValue) 566 require.Nil(t, actualState) 567 568 testTxnInvalidator := testInvalidator{invalidateAll: true} 569 testTxn.AddInvalidator(testTxnInvalidator) 570 571 err = testTxn.Commit() 572 require.NoError(t, err) 573 574 // Sanity check 575 576 require.Equal( 577 t, 578 testTxnTime, 579 block.LatestCommitExecutionTimeForTestingOnly()) 580 581 require.Equal( 582 t, 583 chainedTableInvalidators[string, *string]{ 584 { 585 TableInvalidator: testSetupTxn1Invalidator, 586 executionTime: testSetupTxn1Time, 587 }, 588 { 589 TableInvalidator: testTxnInvalidator, 590 executionTime: testTxnTime, 591 }, 592 }, 593 block.InvalidatorsForTestingOnly()) 594 595 require.Equal(t, 0, len(block.EntriesForTestingOnly())) 596 } 597 598 func TestTxnDerivedDataCommitValidateError(t *testing.T) { 599 block := newEmptyTestBlock() 600 601 testSetupTxn, err := block.NewTableTransaction(0, 10) 602 require.NoError(t, err) 603 604 err = testSetupTxn.Commit() 605 require.NoError(t, err) 606 607 testTxn, err := block.NewTableTransaction(10, 10) 608 require.NoError(t, err) 609 610 commitErr := testTxn.Commit() 611 require.ErrorContains(t, commitErr, "non-increasing time") 612 require.False(t, commitErr.IsRetryable()) 613 } 614 615 func TestTxnDerivedDataCommitSnapshotReadDoesNotAdvanceCommitTime(t *testing.T) { 616 block := newEmptyTestBlock() 617 618 expectedTime := LogicalTime(10) 619 testSetupTxn, err := block.NewTableTransaction(0, expectedTime) 620 require.NoError(t, err) 621 622 err = testSetupTxn.Commit() 623 require.NoError(t, err) 624 625 testTxn, err := block.NewSnapshotReadTableTransaction(0, 11) 626 require.NoError(t, err) 627 628 err = testTxn.Commit() 629 require.NoError(t, err) 630 631 require.Equal( 632 t, 633 expectedTime, 634 block.LatestCommitExecutionTimeForTestingOnly()) 635 } 636 637 func TestTxnDerivedDataCommitBadSnapshotReadInvalidator(t *testing.T) { 638 block := newEmptyTestBlock() 639 640 testTxn, err := block.NewSnapshotReadTableTransaction(0, 42) 641 require.NoError(t, err) 642 643 testTxn.AddInvalidator(testInvalidator{invalidateAll: true}) 644 645 commitErr := testTxn.Commit() 646 require.ErrorContains(t, commitErr, "snapshot read can't invalidate") 647 require.False(t, commitErr.IsRetryable()) 648 } 649 650 func TestTxnDerivedDataCommitFineGrainInvalidation(t *testing.T) { 651 block := newEmptyTestBlock() 652 653 // Setup the database with two read entries 654 655 testSetupTxn, err := block.NewTableTransaction(0, 0) 656 require.NoError(t, err) 657 658 readKey1 := "read-key-1" 659 readValStr1 := "read-value-1" 660 readValue1 := &readValStr1 661 readState1 := &state.State{} 662 663 readKey2 := "read-key-2" 664 readValStr2 := "read-value-2" 665 readValue2 := &readValStr2 666 readState2 := &state.State{} 667 668 testSetupTxn.Set(readKey1, readValue1, readState1) 669 testSetupTxn.Set(readKey2, readValue2, readState2) 670 671 err = testSetupTxn.Commit() 672 require.NoError(t, err) 673 674 // Setup the test transaction by read both existing entries and writing 675 // two new ones, 676 677 testTxnTime := LogicalTime(15) 678 testTxn, err := block.NewTableTransaction(1, testTxnTime) 679 require.NoError(t, err) 680 681 actualValue, actualState, ok := testTxn.Get(readKey1) 682 require.True(t, ok) 683 require.Same(t, readValue1, actualValue) 684 require.Same(t, readState1, actualState) 685 686 actualValue, actualState, ok = testTxn.Get(readKey2) 687 require.True(t, ok) 688 require.Same(t, readValue2, actualValue) 689 require.Same(t, readState2, actualState) 690 691 writeKey1 := "write key 1" 692 writeValStr1 := "write value 1" 693 writeValue1 := &writeValStr1 694 writeState1 := &state.State{} 695 696 writeKey2 := "write key 2" 697 writeValStr2 := "write value 2" 698 writeValue2 := &writeValStr2 699 writeState2 := &state.State{} 700 701 testTxn.Set(writeKey1, writeValue1, writeState1) 702 testTxn.Set(writeKey2, writeValue2, writeState2) 703 704 // Actual test. Invalidate one pre-existing entry and one new entry. 705 706 invalidator1 := testInvalidator{ 707 invalidateName: readKey1, 708 } 709 invalidator2 := testInvalidator{ 710 invalidateName: writeKey1, 711 } 712 testTxn.AddInvalidator(nil) 713 testTxn.AddInvalidator(invalidator1) 714 testTxn.AddInvalidator(testInvalidator{}) 715 testTxn.AddInvalidator(invalidator2) 716 testTxn.AddInvalidator(testInvalidator{}) 717 718 err = testTxn.Commit() 719 require.NoError(t, err) 720 721 require.Equal( 722 t, 723 testTxnTime, 724 block.LatestCommitExecutionTimeForTestingOnly()) 725 726 require.Equal( 727 t, 728 chainedTableInvalidators[string, *string]{ 729 { 730 TableInvalidator: invalidator1, 731 executionTime: testTxnTime, 732 }, 733 { 734 TableInvalidator: invalidator2, 735 executionTime: testTxnTime, 736 }, 737 }, 738 block.InvalidatorsForTestingOnly()) 739 740 entries := block.EntriesForTestingOnly() 741 require.Equal(t, 2, len(entries)) 742 743 entry, ok := entries[readKey2] 744 require.True(t, ok) 745 require.False(t, entry.isInvalid) 746 require.Same(t, readValue2, entry.Value) 747 require.Same(t, readState2, entry.State) 748 749 entry, ok = entries[writeKey2] 750 require.True(t, ok) 751 require.False(t, entry.isInvalid) 752 require.Same(t, writeValue2, entry.Value) 753 require.Same(t, writeState2, entry.State) 754 } 755 756 func TestDerivedDataTableNewChildDerivedBlockData(t *testing.T) { 757 parentBlock := newEmptyTestBlock() 758 759 require.Equal( 760 t, 761 ParentBlockTime, 762 parentBlock.LatestCommitExecutionTimeForTestingOnly()) 763 require.Equal(t, 0, len(parentBlock.InvalidatorsForTestingOnly())) 764 require.Equal(t, 0, len(parentBlock.EntriesForTestingOnly())) 765 766 txn, err := parentBlock.NewTableTransaction(0, 0) 767 require.NoError(t, err) 768 769 txn.AddInvalidator(testInvalidator{invalidateAll: true}) 770 771 err = txn.Commit() 772 require.NoError(t, err) 773 774 txn, err = parentBlock.NewTableTransaction(1, 1) 775 require.NoError(t, err) 776 777 key := "foo bar" 778 valStr := "zzz" 779 value := &valStr 780 state := &state.State{} 781 782 txn.Set(key, value, state) 783 784 err = txn.Commit() 785 require.NoError(t, err) 786 787 // Sanity check parent block 788 789 require.Equal( 790 t, 791 LogicalTime(1), 792 parentBlock.LatestCommitExecutionTimeForTestingOnly()) 793 794 require.Equal(t, 1, len(parentBlock.InvalidatorsForTestingOnly())) 795 796 parentEntries := parentBlock.EntriesForTestingOnly() 797 require.Equal(t, 1, len(parentEntries)) 798 799 parentEntry, ok := parentEntries[key] 800 require.True(t, ok) 801 require.False(t, parentEntry.isInvalid) 802 require.Same(t, value, parentEntry.Value) 803 require.Same(t, state, parentEntry.State) 804 805 // Verify child is correctly initialized 806 807 childBlock := parentBlock.NewChildTable() 808 809 require.Equal( 810 t, 811 ParentBlockTime, 812 childBlock.LatestCommitExecutionTimeForTestingOnly()) 813 814 require.Equal(t, 0, len(childBlock.InvalidatorsForTestingOnly())) 815 816 childEntries := childBlock.EntriesForTestingOnly() 817 require.Equal(t, 1, len(childEntries)) 818 819 childEntry, ok := childEntries[key] 820 require.True(t, ok) 821 require.False(t, childEntry.isInvalid) 822 require.Same(t, value, childEntry.Value) 823 require.Same(t, state, childEntry.State) 824 825 require.NotSame(t, parentEntry, childEntry) 826 } 827 828 type testValueComputer struct { 829 value int 830 called bool 831 } 832 833 func (computer *testValueComputer) Compute( 834 txnState *state.TransactionState, 835 key string, 836 ) ( 837 int, 838 error, 839 ) { 840 computer.called = true 841 _, err := txnState.Get("addr", key, true) 842 if err != nil { 843 return 0, err 844 } 845 846 return computer.value, nil 847 } 848 849 func TestTxnDerivedDataGetOrCompute(t *testing.T) { 850 blockDerivedData := NewEmptyTable[string, int]() 851 852 key := "key" 853 value := 12345 854 855 t.Run("compute value", func(t *testing.T) { 856 view := utils.NewSimpleView() 857 txnState := state.NewTransactionState(view, state.DefaultParameters()) 858 859 txnDerivedData, err := blockDerivedData.NewTableTransaction(0, 0) 860 assert.Nil(t, err) 861 862 computer := &testValueComputer{value: value} 863 val, err := txnDerivedData.GetOrCompute(txnState, key, computer) 864 assert.Nil(t, err) 865 assert.Equal(t, value, val) 866 assert.True(t, computer.called) 867 868 _, ok := view.Ledger.RegisterTouches[flow.RegisterID{Owner: "addr", Key: key}] 869 assert.True(t, ok) 870 871 // Commit to setup the next test. 872 err = txnDerivedData.Commit() 873 assert.Nil(t, err) 874 }) 875 876 t.Run("get value", func(t *testing.T) { 877 view := utils.NewSimpleView() 878 txnState := state.NewTransactionState(view, state.DefaultParameters()) 879 880 txnDerivedData, err := blockDerivedData.NewTableTransaction(1, 1) 881 assert.Nil(t, err) 882 883 computer := &testValueComputer{value: value} 884 val, err := txnDerivedData.GetOrCompute(txnState, key, computer) 885 assert.Nil(t, err) 886 assert.Equal(t, value, val) 887 assert.False(t, computer.called) 888 889 _, ok := view.Ledger.RegisterTouches[flow.RegisterID{Owner: "addr", Key: key}] 890 assert.True(t, ok) 891 }) 892 }