github.com/onflow/flow-go@v0.33.17/ledger/complete/ledger_test.go (about) 1 package complete_test 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "math" 8 "math/rand" 9 "testing" 10 11 "github.com/rs/zerolog" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 "go.uber.org/atomic" 15 16 "github.com/onflow/flow-go/ledger" 17 "github.com/onflow/flow-go/ledger/common/pathfinder" 18 "github.com/onflow/flow-go/ledger/common/proof" 19 "github.com/onflow/flow-go/ledger/common/testutils" 20 "github.com/onflow/flow-go/ledger/complete" 21 "github.com/onflow/flow-go/ledger/complete/wal" 22 "github.com/onflow/flow-go/ledger/complete/wal/fixtures" 23 "github.com/onflow/flow-go/ledger/partial/ptrie" 24 "github.com/onflow/flow-go/module/metrics" 25 "github.com/onflow/flow-go/utils/unittest" 26 ) 27 28 func TestNewLedger(t *testing.T) { 29 metricsCollector := &metrics.NoopCollector{} 30 wal := &fixtures.NoopWAL{} 31 _, err := complete.NewLedger(wal, 100, metricsCollector, zerolog.Logger{}, complete.DefaultPathFinderVersion) 32 assert.NoError(t, err) 33 34 } 35 36 func TestLedger_Update(t *testing.T) { 37 t.Run("empty update", func(t *testing.T) { 38 39 wal := &fixtures.NoopWAL{} 40 41 l, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion) 42 require.NoError(t, err) 43 44 compactor := fixtures.NewNoopCompactor(l) 45 <-compactor.Ready() 46 defer func() { 47 <-l.Done() 48 <-compactor.Done() 49 }() 50 51 // create empty update 52 currentState := l.InitialState() 53 up, err := ledger.NewEmptyUpdate(currentState) 54 require.NoError(t, err) 55 56 newState, trieUpdate, err := l.Set(up) 57 require.NoError(t, err) 58 require.True(t, trieUpdate.IsEmpty()) 59 60 // state shouldn't change 61 assert.Equal(t, currentState, newState) 62 }) 63 64 t.Run("non-empty update and query", func(t *testing.T) { 65 66 // UpdateFixture 67 wal := &fixtures.NoopWAL{} 68 led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion) 69 require.NoError(t, err) 70 71 compactor := fixtures.NewNoopCompactor(led) 72 <-compactor.Ready() 73 defer func() { 74 <-led.Done() 75 <-compactor.Done() 76 }() 77 78 curSC := led.InitialState() 79 80 u := testutils.UpdateFixture() 81 u.SetState(curSC) 82 83 newSc, _, err := led.Set(u) 84 require.NoError(t, err) 85 assert.NotEqual(t, curSC, newSc) 86 87 q, err := ledger.NewQuery(newSc, u.Keys()) 88 require.NoError(t, err) 89 90 retValues, err := led.Get(q) 91 require.NoError(t, err) 92 93 for i, v := range u.Values() { 94 assert.Equal(t, v, retValues[i]) 95 } 96 }) 97 } 98 99 func TestLedger_Get(t *testing.T) { 100 t.Run("empty query", func(t *testing.T) { 101 102 wal := &fixtures.NoopWAL{} 103 104 led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion) 105 require.NoError(t, err) 106 107 compactor := fixtures.NewNoopCompactor(led) 108 <-compactor.Ready() 109 defer func() { 110 <-led.Done() 111 <-compactor.Done() 112 }() 113 114 curSC := led.InitialState() 115 q, err := ledger.NewEmptyQuery(curSC) 116 require.NoError(t, err) 117 118 retValues, err := led.Get(q) 119 require.NoError(t, err) 120 assert.Equal(t, len(retValues), 0) 121 }) 122 123 t.Run("empty keys", func(t *testing.T) { 124 125 wal := &fixtures.NoopWAL{} 126 127 led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion) 128 require.NoError(t, err) 129 130 compactor := fixtures.NewNoopCompactor(led) 131 <-compactor.Ready() 132 defer func() { 133 <-led.Done() 134 <-compactor.Done() 135 }() 136 137 curS := led.InitialState() 138 139 q := testutils.QueryFixture() 140 q.SetState(curS) 141 142 retValues, err := led.Get(q) 143 require.NoError(t, err) 144 145 assert.Equal(t, 2, len(retValues)) 146 assert.Equal(t, 0, len(retValues[0])) 147 assert.Equal(t, 0, len(retValues[1])) 148 149 }) 150 } 151 152 // TestLedger_GetSingleValue tests reading value from a single path. 153 func TestLedger_GetSingleValue(t *testing.T) { 154 155 wal := &fixtures.NoopWAL{} 156 led, err := complete.NewLedger( 157 wal, 158 100, 159 &metrics.NoopCollector{}, 160 zerolog.Logger{}, 161 complete.DefaultPathFinderVersion, 162 ) 163 require.NoError(t, err) 164 165 compactor := fixtures.NewNoopCompactor(led) 166 <-compactor.Ready() 167 defer func() { 168 <-led.Done() 169 <-compactor.Done() 170 }() 171 172 state := led.InitialState() 173 174 t.Run("non-existent key", func(t *testing.T) { 175 176 keys := testutils.RandomUniqueKeys(10, 2, 1, 10) 177 178 for _, k := range keys { 179 qs, err := ledger.NewQuerySingleValue(state, k) 180 require.NoError(t, err) 181 182 retValue, err := led.GetSingleValue(qs) 183 require.NoError(t, err) 184 assert.Equal(t, 0, len(retValue)) 185 } 186 }) 187 188 t.Run("existent key", func(t *testing.T) { 189 190 u := testutils.UpdateFixture() 191 u.SetState(state) 192 193 newState, _, err := led.Set(u) 194 require.NoError(t, err) 195 assert.NotEqual(t, state, newState) 196 197 for i, k := range u.Keys() { 198 q, err := ledger.NewQuerySingleValue(newState, k) 199 require.NoError(t, err) 200 201 retValue, err := led.GetSingleValue(q) 202 require.NoError(t, err) 203 assert.Equal(t, u.Values()[i], retValue) 204 } 205 }) 206 207 t.Run("mix of existent and non-existent keys", func(t *testing.T) { 208 209 u := testutils.UpdateFixture() 210 u.SetState(state) 211 212 newState, _, err := led.Set(u) 213 require.NoError(t, err) 214 assert.NotEqual(t, state, newState) 215 216 // Save expected values for existent keys 217 expectedValues := make(map[string]ledger.Value) 218 for i, key := range u.Keys() { 219 encKey := ledger.EncodeKey(&key) 220 expectedValues[string(encKey)] = u.Values()[i] 221 } 222 223 // Create a randomly ordered mix of existent and non-existent keys 224 var queryKeys []ledger.Key 225 queryKeys = append(queryKeys, u.Keys()...) 226 queryKeys = append(queryKeys, testutils.RandomUniqueKeys(10, 2, 1, 10)...) 227 228 rand.Shuffle(len(queryKeys), func(i, j int) { 229 queryKeys[i], queryKeys[j] = queryKeys[j], queryKeys[i] 230 }) 231 232 for _, k := range queryKeys { 233 qs, err := ledger.NewQuerySingleValue(newState, k) 234 require.NoError(t, err) 235 236 retValue, err := led.GetSingleValue(qs) 237 require.NoError(t, err) 238 239 encKey := ledger.EncodeKey(&k) 240 if value, ok := expectedValues[string(encKey)]; ok { 241 require.Equal(t, value, retValue) 242 } else { 243 require.Equal(t, 0, len(retValue)) 244 } 245 } 246 }) 247 } 248 249 func TestLedgerValueSizes(t *testing.T) { 250 t.Run("empty query", func(t *testing.T) { 251 252 wal := &fixtures.NoopWAL{} 253 led, err := complete.NewLedger( 254 wal, 255 100, 256 &metrics.NoopCollector{}, 257 zerolog.Logger{}, 258 complete.DefaultPathFinderVersion, 259 ) 260 require.NoError(t, err) 261 262 compactor := fixtures.NewNoopCompactor(led) 263 <-compactor.Ready() 264 defer func() { 265 <-led.Done() 266 <-compactor.Done() 267 }() 268 269 curState := led.InitialState() 270 q, err := ledger.NewEmptyQuery(curState) 271 require.NoError(t, err) 272 273 retSizes, err := led.ValueSizes(q) 274 require.NoError(t, err) 275 require.Equal(t, 0, len(retSizes)) 276 }) 277 278 t.Run("non-existent keys", func(t *testing.T) { 279 280 wal := &fixtures.NoopWAL{} 281 led, err := complete.NewLedger( 282 wal, 283 100, 284 &metrics.NoopCollector{}, 285 zerolog.Logger{}, 286 complete.DefaultPathFinderVersion, 287 ) 288 require.NoError(t, err) 289 290 compactor := fixtures.NewNoopCompactor(led) 291 <-compactor.Ready() 292 defer func() { 293 <-led.Done() 294 <-compactor.Done() 295 }() 296 297 curState := led.InitialState() 298 q := testutils.QueryFixture() 299 q.SetState(curState) 300 301 retSizes, err := led.ValueSizes(q) 302 require.NoError(t, err) 303 require.Equal(t, len(q.Keys()), len(retSizes)) 304 for _, size := range retSizes { 305 assert.Equal(t, 0, size) 306 } 307 }) 308 309 t.Run("existent keys", func(t *testing.T) { 310 311 wal := &fixtures.NoopWAL{} 312 led, err := complete.NewLedger( 313 wal, 314 100, 315 &metrics.NoopCollector{}, 316 zerolog.Logger{}, 317 complete.DefaultPathFinderVersion, 318 ) 319 require.NoError(t, err) 320 321 compactor := fixtures.NewNoopCompactor(led) 322 <-compactor.Ready() 323 defer func() { 324 <-led.Done() 325 <-compactor.Done() 326 }() 327 328 curState := led.InitialState() 329 u := testutils.UpdateFixture() 330 u.SetState(curState) 331 332 newState, _, err := led.Set(u) 333 require.NoError(t, err) 334 assert.NotEqual(t, curState, newState) 335 336 q, err := ledger.NewQuery(newState, u.Keys()) 337 require.NoError(t, err) 338 339 retSizes, err := led.ValueSizes(q) 340 require.NoError(t, err) 341 require.Equal(t, len(q.Keys()), len(retSizes)) 342 for i, size := range retSizes { 343 assert.Equal(t, u.Values()[i].Size(), size) 344 } 345 }) 346 347 t.Run("mix of existent and non-existent keys", func(t *testing.T) { 348 349 wal := &fixtures.NoopWAL{} 350 led, err := complete.NewLedger( 351 wal, 352 100, 353 &metrics.NoopCollector{}, 354 zerolog.Logger{}, 355 complete.DefaultPathFinderVersion, 356 ) 357 require.NoError(t, err) 358 359 compactor := fixtures.NewNoopCompactor(led) 360 <-compactor.Ready() 361 defer func() { 362 <-led.Done() 363 <-compactor.Done() 364 }() 365 366 curState := led.InitialState() 367 u := testutils.UpdateFixture() 368 u.SetState(curState) 369 370 newState, _, err := led.Set(u) 371 require.NoError(t, err) 372 assert.NotEqual(t, curState, newState) 373 374 // Save expected value sizes for existent keys 375 expectedValueSizes := make(map[string]int) 376 for i, key := range u.Keys() { 377 encKey := ledger.EncodeKey(&key) 378 expectedValueSizes[string(encKey)] = len(u.Values()[i]) 379 } 380 381 // Create a randomly ordered mix of existent and non-existent keys 382 var queryKeys []ledger.Key 383 queryKeys = append(queryKeys, u.Keys()...) 384 queryKeys = append(queryKeys, testutils.RandomUniqueKeys(10, 2, 1, 10)...) 385 386 rand.Shuffle(len(queryKeys), func(i, j int) { 387 queryKeys[i], queryKeys[j] = queryKeys[j], queryKeys[i] 388 }) 389 390 q, err := ledger.NewQuery(newState, queryKeys) 391 require.NoError(t, err) 392 393 retSizes, err := led.ValueSizes(q) 394 require.NoError(t, err) 395 require.Equal(t, len(q.Keys()), len(retSizes)) 396 for i, key := range q.Keys() { 397 encKey := ledger.EncodeKey(&key) 398 assert.Equal(t, expectedValueSizes[string(encKey)], retSizes[i]) 399 } 400 }) 401 } 402 403 func TestLedger_Proof(t *testing.T) { 404 t.Run("empty query", func(t *testing.T) { 405 wal := &fixtures.NoopWAL{} 406 407 led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion) 408 require.NoError(t, err) 409 410 compactor := fixtures.NewNoopCompactor(led) 411 <-compactor.Ready() 412 defer func() { 413 <-led.Done() 414 <-compactor.Done() 415 }() 416 417 curSC := led.InitialState() 418 q, err := ledger.NewEmptyQuery(curSC) 419 require.NoError(t, err) 420 421 retProof, err := led.Prove(q) 422 require.NoError(t, err) 423 424 proof, err := ledger.DecodeTrieBatchProof(retProof) 425 require.NoError(t, err) 426 assert.Equal(t, 0, len(proof.Proofs)) 427 }) 428 429 t.Run("non-existing keys", func(t *testing.T) { 430 431 wal := &fixtures.NoopWAL{} 432 433 led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion) 434 require.NoError(t, err) 435 436 compactor := fixtures.NewNoopCompactor(led) 437 <-compactor.Ready() 438 defer func() { 439 <-led.Done() 440 <-compactor.Done() 441 }() 442 443 curS := led.InitialState() 444 q := testutils.QueryFixture() 445 q.SetState(curS) 446 require.NoError(t, err) 447 448 retProof, err := led.Prove(q) 449 require.NoError(t, err) 450 451 trieProof, err := ledger.DecodeTrieBatchProof(retProof) 452 require.NoError(t, err) 453 assert.Equal(t, 2, len(trieProof.Proofs)) 454 assert.True(t, proof.VerifyTrieBatchProof(trieProof, curS)) 455 456 }) 457 458 t.Run("existing keys", func(t *testing.T) { 459 460 wal := &fixtures.NoopWAL{} 461 led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion) 462 require.NoError(t, err) 463 464 compactor := fixtures.NewNoopCompactor(led) 465 <-compactor.Ready() 466 defer func() { 467 <-led.Done() 468 <-compactor.Done() 469 }() 470 471 curS := led.InitialState() 472 473 u := testutils.UpdateFixture() 474 u.SetState(curS) 475 476 newSc, _, err := led.Set(u) 477 require.NoError(t, err) 478 assert.NotEqual(t, curS, newSc) 479 480 q, err := ledger.NewQuery(newSc, u.Keys()) 481 require.NoError(t, err) 482 483 retProof, err := led.Prove(q) 484 require.NoError(t, err) 485 486 trieProof, err := ledger.DecodeTrieBatchProof(retProof) 487 require.NoError(t, err) 488 assert.Equal(t, 2, len(trieProof.Proofs)) 489 assert.True(t, proof.VerifyTrieBatchProof(trieProof, newSc)) 490 }) 491 } 492 493 func Test_WAL(t *testing.T) { 494 const ( 495 numInsPerStep = 2 496 keyNumberOfParts = 10 497 keyPartMinByteSize = 1 498 keyPartMaxByteSize = 100 499 valueMaxByteSize = 2 << 16 //16kB 500 size = 10 501 checkpointDistance = math.MaxInt // A large number to prevent checkpoint creation. 502 checkpointsToKeep = 1 503 ) 504 505 metricsCollector := &metrics.NoopCollector{} 506 logger := zerolog.Logger{} 507 508 unittest.RunWithTempDir(t, func(dir string) { 509 510 diskWal, err := wal.NewDiskWAL(zerolog.Nop(), nil, metricsCollector, dir, size, pathfinder.PathByteSize, wal.SegmentSize) 511 require.NoError(t, err) 512 513 // cache size intentionally is set to size to test deletion 514 led, err := complete.NewLedger(diskWal, size, metricsCollector, logger, complete.DefaultPathFinderVersion) 515 require.NoError(t, err) 516 517 compactor, err := complete.NewCompactor(led, diskWal, zerolog.Nop(), size, checkpointDistance, checkpointsToKeep, atomic.NewBool(false), metrics.NewNoopCollector()) 518 require.NoError(t, err) 519 520 <-compactor.Ready() 521 522 var state = led.InitialState() 523 524 //saved data after updates 525 savedData := make(map[string]map[string]ledger.Value) 526 527 for i := 0; i < size; i++ { 528 529 keys := testutils.RandomUniqueKeys(numInsPerStep, keyNumberOfParts, keyPartMinByteSize, keyPartMaxByteSize) 530 values := testutils.RandomValues(numInsPerStep, 1, valueMaxByteSize) 531 update, err := ledger.NewUpdate(state, keys, values) 532 assert.NoError(t, err) 533 state, _, err = led.Set(update) 534 require.NoError(t, err) 535 536 data := make(map[string]ledger.Value, len(keys)) 537 for j, key := range keys { 538 encKey := ledger.EncodeKey(&key) 539 data[string(encKey)] = values[j] 540 } 541 542 savedData[string(state[:])] = data 543 } 544 545 <-led.Done() 546 <-compactor.Done() 547 548 diskWal2, err := wal.NewDiskWAL(zerolog.Nop(), nil, metricsCollector, dir, size, pathfinder.PathByteSize, wal.SegmentSize) 549 require.NoError(t, err) 550 551 led2, err := complete.NewLedger(diskWal2, size+10, metricsCollector, logger, complete.DefaultPathFinderVersion) 552 require.NoError(t, err) 553 554 compactor2, err := complete.NewCompactor(led2, diskWal2, zerolog.Nop(), uint(size), checkpointDistance, checkpointsToKeep, atomic.NewBool(false), metrics.NewNoopCollector()) 555 require.NoError(t, err) 556 557 <-compactor2.Ready() 558 559 // random map iteration order is a benefit here 560 for state, data := range savedData { 561 562 keys := make([]ledger.Key, 0, len(data)) 563 for encKey := range data { 564 key, err := ledger.DecodeKey([]byte(encKey)) 565 assert.NoError(t, err) 566 keys = append(keys, *key) 567 } 568 569 var ledgerState ledger.State 570 copy(ledgerState[:], state) 571 query, err := ledger.NewQuery(ledgerState, keys) 572 assert.NoError(t, err) 573 registerValues, err := led2.Get(query) 574 require.NoError(t, err) 575 576 for i, key := range keys { 577 registerValue := registerValues[i] 578 encKey := ledger.EncodeKey(&key) 579 assert.True(t, data[string(encKey)].Equals(registerValue)) 580 } 581 } 582 583 <-led2.Done() 584 <-compactor2.Done() 585 }) 586 } 587 588 func TestLedgerFunctionality(t *testing.T) { 589 const ( 590 checkpointDistance = math.MaxInt // A large number to prevent checkpoint creation. 591 checkpointsToKeep = 1 592 ) 593 594 // You can manually increase this for more coverage 595 experimentRep := 2 596 metricsCollector := &metrics.NoopCollector{} 597 logger := zerolog.Logger{} 598 599 for e := 0; e < experimentRep; e++ { 600 numInsPerStep := 100 601 numHistLookupPerStep := 10 602 keyNumberOfParts := 10 603 keyPartMinByteSize := 1 604 keyPartMaxByteSize := 100 605 stateComSize := 32 606 valueMaxByteSize := 2 << 16 //16kB 607 activeTries := 1000 608 steps := 40 // number of steps 609 histStorage := make(map[string]ledger.Value) // historic storage string(key, state) -> value 610 latestValue := make(map[string]ledger.Value) // key to value 611 unittest.RunWithTempDir(t, func(dbDir string) { 612 diskWal, err := wal.NewDiskWAL(zerolog.Nop(), nil, metricsCollector, dbDir, activeTries, pathfinder.PathByteSize, wal.SegmentSize) 613 require.NoError(t, err) 614 led, err := complete.NewLedger(diskWal, activeTries, metricsCollector, logger, complete.DefaultPathFinderVersion) 615 assert.NoError(t, err) 616 compactor, err := complete.NewCompactor(led, diskWal, zerolog.Nop(), uint(activeTries), checkpointDistance, checkpointsToKeep, atomic.NewBool(false), metrics.NewNoopCollector()) 617 require.NoError(t, err) 618 <-compactor.Ready() 619 620 state := led.InitialState() 621 for i := 0; i < steps; i++ { 622 // add new keys 623 // TODO update some of the existing keys and shuffle them 624 keys := testutils.RandomUniqueKeys(numInsPerStep, keyNumberOfParts, keyPartMinByteSize, keyPartMaxByteSize) 625 values := testutils.RandomValues(numInsPerStep, 1, valueMaxByteSize) 626 update, err := ledger.NewUpdate(state, keys, values) 627 assert.NoError(t, err) 628 newState, _, err := led.Set(update) 629 assert.NoError(t, err) 630 631 // capture new values for future query 632 for j, k := range keys { 633 encKey := ledger.EncodeKey(&k) 634 histStorage[string(newState[:])+string(encKey)] = values[j] 635 latestValue[string(encKey)] = values[j] 636 } 637 638 // read values and compare values 639 query, err := ledger.NewQuery(newState, keys) 640 assert.NoError(t, err) 641 retValues, err := led.Get(query) 642 assert.NoError(t, err) 643 // byte{} is returned as nil 644 assert.True(t, valuesMatches(values, retValues)) 645 646 // get value sizes and compare them 647 retSizes, err := led.ValueSizes(query) 648 assert.NoError(t, err) 649 assert.Equal(t, len(query.Keys()), len(retSizes)) 650 for i, size := range retSizes { 651 assert.Equal(t, values[i].Size(), size) 652 } 653 654 // validate proofs (check individual proof and batch proof) 655 proofs, err := led.Prove(query) 656 assert.NoError(t, err) 657 658 bProof, err := ledger.DecodeTrieBatchProof(proofs) 659 assert.NoError(t, err) 660 661 // validate batch proofs 662 isValid := proof.VerifyTrieBatchProof(bProof, newState) 663 assert.True(t, isValid) 664 665 // validate proofs as a batch 666 _, err = ptrie.NewPSMT(ledger.RootHash(newState), bProof) 667 assert.NoError(t, err) 668 669 // query all exising keys (check no drop) 670 for ek, v := range latestValue { 671 k, err := ledger.DecodeKey([]byte(ek)) 672 assert.NoError(t, err) 673 query, err := ledger.NewQuery(newState, []ledger.Key{*k}) 674 assert.NoError(t, err) 675 rv, err := led.Get(query) 676 assert.NoError(t, err) 677 assert.True(t, v.Equals(rv[0])) 678 } 679 680 // query some of historic values (map return is random) 681 j := 0 682 for s := range histStorage { 683 value := histStorage[s] 684 var state ledger.State 685 copy(state[:], s[:stateComSize]) 686 enk := []byte(s[stateComSize:]) 687 key, err := ledger.DecodeKey(enk) 688 assert.NoError(t, err) 689 query, err := ledger.NewQuery(state, []ledger.Key{*key}) 690 assert.NoError(t, err) 691 rv, err := led.Get(query) 692 assert.NoError(t, err) 693 assert.True(t, value.Equals(rv[0])) 694 j++ 695 if j >= numHistLookupPerStep { 696 break 697 } 698 } 699 state = newState 700 } 701 <-led.Done() 702 <-compactor.Done() 703 }) 704 } 705 } 706 707 func TestWALUpdateFailuresBubbleUp(t *testing.T) { 708 unittest.RunWithTempDir(t, func(dir string) { 709 710 const ( 711 capacity = 100 712 checkpointDistance = math.MaxInt // A large number to prevent checkpoint creation. 713 checkpointsToKeep = 1 714 ) 715 716 theError := fmt.Errorf("error error") 717 718 metricsCollector := &metrics.NoopCollector{} 719 720 diskWAL, err := wal.NewDiskWAL(zerolog.Nop(), nil, metricsCollector, dir, capacity, pathfinder.PathByteSize, wal.SegmentSize) 721 require.NoError(t, err) 722 723 w := &CustomUpdateWAL{ 724 DiskWAL: diskWAL, 725 updateFn: func(update *ledger.TrieUpdate) (int, bool, error) { 726 return 0, false, theError 727 }, 728 } 729 730 led, err := complete.NewLedger(w, capacity, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion) 731 require.NoError(t, err) 732 733 compactor, err := complete.NewCompactor(led, w, zerolog.Nop(), capacity, checkpointDistance, checkpointsToKeep, atomic.NewBool(false), metrics.NewNoopCollector()) 734 require.NoError(t, err) 735 736 <-compactor.Ready() 737 738 defer func() { 739 <-led.Done() 740 <-compactor.Done() 741 }() 742 743 key := ledger.NewKey([]ledger.KeyPart{ledger.NewKeyPart(0, []byte{1, 2, 3})}) 744 745 values := []ledger.Value{[]byte{1, 2, 3}} 746 update, err := ledger.NewUpdate(led.InitialState(), []ledger.Key{key}, values) 747 require.NoError(t, err) 748 749 _, _, err = led.Set(update) 750 require.Error(t, err) 751 require.True(t, errors.Is(err, theError)) 752 }) 753 } 754 755 func valuesMatches(expected []ledger.Value, got []ledger.Value) bool { 756 if len(expected) != len(got) { 757 return false 758 } 759 // replace nils 760 for i, v := range got { 761 if v == nil { 762 got[i] = []byte{} 763 } 764 if !bytes.Equal(expected[i], got[i]) { 765 return false 766 } 767 } 768 return true 769 } 770 771 func noOpMigration(p []ledger.Payload) ([]ledger.Payload, error) { 772 return p, nil 773 } 774 775 func migrationByValue(p []ledger.Payload) ([]ledger.Payload, error) { 776 ret := make([]ledger.Payload, 0, len(p)) 777 for _, p := range p { 778 if p.Value().Equals([]byte{'A'}) { 779 k, err := p.Key() 780 if err != nil { 781 return nil, err 782 } 783 pp := *ledger.NewPayload(k, ledger.Value([]byte{'C'})) 784 ret = append(ret, pp) 785 } else { 786 ret = append(ret, p) 787 } 788 } 789 return ret, nil 790 } 791 792 type CustomUpdateWAL struct { 793 *wal.DiskWAL 794 updateFn func(update *ledger.TrieUpdate) (int, bool, error) 795 } 796 797 func (w *CustomUpdateWAL) RecordUpdate(update *ledger.TrieUpdate) (int, bool, error) { 798 return w.updateFn(update) 799 } 800 801 func migrationByKey(p []ledger.Payload) ([]ledger.Payload, error) { 802 ret := make([]ledger.Payload, 0, len(p)) 803 for _, p := range p { 804 k, err := p.Key() 805 if err != nil { 806 return nil, err 807 } 808 if k.String() == "/1/1/22/2" { 809 pp := *ledger.NewPayload(k, ledger.Value([]byte{'D'})) 810 ret = append(ret, pp) 811 } else { 812 ret = append(ret, p) 813 } 814 } 815 816 return ret, nil 817 }