@@ -43,10 +43,11 @@ import (
4343// (v) Geth restarts normally, but it's requested to be rewound to a lower point via SetHead
4444// (vi) Geth restarts normally with a stale snapshot
4545type snapshotTest struct {
46- legacy bool // Flag whether the loaded snapshot is in legacy format
47- crash bool // Flag whether the Geth restarts from the previous crash
48- gapped int // Number of blocks to insert without enabling snapshot
49- setHead uint64 // Block number to set head back to
46+ legacy bool // Flag whether the loaded snapshot is in legacy format
47+ crash bool // Flag whether the Geth restarts from the previous crash
48+ restartCrash int // Number of blocks to insert after the normal stop, then the crash happens
49+ gapped int // Number of blocks to insert without enabling snapshot
50+ setHead uint64 // Block number to set head back to
5051
5152 chainBlocks int // Number of blocks to generate for the canonical chain
5253 snapshotBlock uint64 // Block number of the relevant snapshot disk layer
@@ -565,10 +566,50 @@ func TestSetHeadWithLegacySnapshot(t *testing.T) {
565566 })
566567}
567568
569+ // Tests the Geth was running with snapshot(legacy-format) enabled and upgrades
570+ // the disk layer journal(journal generator) to latest format. After that the Geth
571+ // is restarted from a crash. In this case Geth will find the new-format disk layer
572+ // journal but with legacy-format diff journal(the new-format is never committed),
573+ // and the invalid diff journal is expected to be dropped.
574+ func TestRecoverSnapshotFromCrashWithLegacyDiffJournal (t * testing.T ) {
575+ // Chain:
576+ // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
577+ //
578+ // Commit: G
579+ // Snapshot: G
580+ //
581+ // SetHead(0)
582+ //
583+ // ------------------------------
584+ //
585+ // Expected in leveldb:
586+ // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10
587+ //
588+ // Expected head header : C10
589+ // Expected head fast block: C10
590+ // Expected head block : C8
591+ // Expected snapshot disk : C10
592+ testSnapshot (t , & snapshotTest {
593+ legacy : true ,
594+ crash : false ,
595+ restartCrash : 2 ,
596+ gapped : 0 ,
597+ setHead : 0 ,
598+ chainBlocks : 8 ,
599+ snapshotBlock : 0 ,
600+ commitBlock : 0 ,
601+ expCanonicalBlocks : 10 ,
602+ expHeadHeader : 10 ,
603+ expHeadFastBlock : 10 ,
604+ expHeadBlock : 8 , // The persisted state in the first running
605+ expSnapshotBottom : 10 , // The persisted disk layer in the second running
606+ })
607+ }
608+
568609func testSnapshot (t * testing.T , tt * snapshotTest ) {
569610 // It's hard to follow the test case, visualize the input
570- //log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
571- //fmt.Println(tt.dump())
611+ // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
612+ // fmt.Println(tt.dump())
572613
573614 // Create a temporary persistent database
574615 datadir , err := ioutil .TempDir ("" , "" )
@@ -694,6 +735,30 @@ func testSnapshot(t *testing.T, tt *snapshotTest) {
694735 chain .SetHead (tt .setHead )
695736 chain .Stop ()
696737
738+ chain , err = NewBlockChain (db , nil , params .AllEthashProtocolChanges , engine , vm.Config {}, nil , nil )
739+ if err != nil {
740+ t .Fatalf ("Failed to recreate chain: %v" , err )
741+ }
742+ defer chain .Stop ()
743+ } else if tt .restartCrash != 0 {
744+ // Firstly, stop the chain properly, with all snapshot journal
745+ // and state committed.
746+ chain .Stop ()
747+
748+ // Restart chain, forcibly flush the disk layer journal with new format
749+ newBlocks , _ := GenerateChain (params .TestChainConfig , blocks [len (blocks )- 1 ], engine , gendb , tt .restartCrash , func (i int , b * BlockGen ) {})
750+ chain , err = NewBlockChain (db , cacheConfig , params .AllEthashProtocolChanges , engine , vm.Config {}, nil , nil )
751+ if err != nil {
752+ t .Fatalf ("Failed to recreate chain: %v" , err )
753+ }
754+ chain .InsertChain (newBlocks )
755+ chain .Snapshot ().Cap (newBlocks [len (newBlocks )- 1 ].Root (), 0 )
756+
757+ // Simulate the blockchain crash
758+ // Don't call chain.Stop here, so that no snapshot
759+ // journal and latest state will be committed
760+
761+ // Restart the chain after the crash
697762 chain , err = NewBlockChain (db , nil , params .AllEthashProtocolChanges , engine , vm.Config {}, nil , nil )
698763 if err != nil {
699764 t .Fatalf ("Failed to recreate chain: %v" , err )
0 commit comments