@@ -22,6 +22,7 @@ import (
22
22
"math/big"
23
23
"math/rand"
24
24
"testing"
25
+ "time"
25
26
26
27
"github.com/VictoriaMetrics/fastcache"
27
28
"github.com/ethereum/go-ethereum/common"
@@ -324,7 +325,7 @@ func TestPostCapBasicDataAccess(t *testing.T) {
324
325
}
325
326
}
326
327
327
- // TestSnaphots tests the functionality for retrieveing the snapshot
328
+ // TestSnaphots tests the functionality for retrieving the snapshot
328
329
// with given head root and the desired depth.
329
330
func TestSnaphots (t * testing.T ) {
330
331
// setAccount is a helper to construct a random account entry and assign it to
@@ -423,3 +424,63 @@ func TestSnaphots(t *testing.T) {
423
424
}
424
425
}
425
426
}
427
+
428
+ // TestReadStateDuringFlattening tests the scenario that, during the
429
+ // bottom diff layers are merging which tags these as stale, the read
430
+ // happens via a pre-created top snapshot layer which tries to access
431
+ // the state in these stale layers. Ensure this read can retrieve the
432
+ // right state back(block until the flattening is finished) instead of
433
+ // an unexpected error(snapshot layer is stale).
434
+ func TestReadStateDuringFlattening (t * testing.T ) {
435
+ // setAccount is a helper to construct a random account entry and assign it to
436
+ // an account slot in a snapshot
437
+ setAccount := func (accKey string ) map [common.Hash ][]byte {
438
+ return map [common.Hash ][]byte {
439
+ common .HexToHash (accKey ): randomAccount (),
440
+ }
441
+ }
442
+ // Create a starting base layer and a snapshot tree out of it
443
+ base := & diskLayer {
444
+ diskdb : rawdb .NewMemoryDatabase (),
445
+ root : common .HexToHash ("0x01" ),
446
+ cache : fastcache .New (1024 * 500 ),
447
+ }
448
+ snaps := & Tree {
449
+ layers : map [common.Hash ]snapshot {
450
+ base .root : base ,
451
+ },
452
+ }
453
+ // 4 layers in total, 3 diff layers and 1 disk layers
454
+ snaps .Update (common .HexToHash ("0xa1" ), common .HexToHash ("0x01" ), nil , setAccount ("0xa1" ), nil )
455
+ snaps .Update (common .HexToHash ("0xa2" ), common .HexToHash ("0xa1" ), nil , setAccount ("0xa2" ), nil )
456
+ snaps .Update (common .HexToHash ("0xa3" ), common .HexToHash ("0xa2" ), nil , setAccount ("0xa3" ), nil )
457
+
458
+ // Obtain the topmost snapshot handler for state accessing
459
+ snap := snaps .Snapshot (common .HexToHash ("0xa3" ))
460
+
461
+ // Register the testing hook to access the state after flattening
462
+ var result = make (chan * Account )
463
+ snaps .onFlatten = func () {
464
+ // Spin up a thread to read the account from the pre-created
465
+ // snapshot handler. It's expected to be blocked.
466
+ go func () {
467
+ account , _ := snap .Account (common .HexToHash ("0xa1" ))
468
+ result <- account
469
+ }()
470
+ select {
471
+ case res := <- result :
472
+ t .Fatalf ("Unexpected return %v" , res )
473
+ case <- time .NewTimer (time .Millisecond * 300 ).C :
474
+ }
475
+ }
476
+ // Cap the snap tree, which will mark the bottom-most layer as stale.
477
+ snaps .Cap (common .HexToHash ("0xa3" ), 1 )
478
+ select {
479
+ case account := <- result :
480
+ if account == nil {
481
+ t .Fatal ("Failed to retrieve account" )
482
+ }
483
+ case <- time .NewTimer (time .Millisecond * 300 ).C :
484
+ t .Fatal ("Unexpected blocker" )
485
+ }
486
+ }
0 commit comments