@ -43,10 +43,11 @@ import (
// (v) Geth restarts normally, but it's requested to be rewound to a lower point via SetHead
// (vi) Geth restarts normally with a stale snapshot
type snapshotTest struct {
legacy bool // Flag whether the loaded snapshot is in legacy format
crash bool // Flag whether the Geth restarts from the previous crash
gapped int // Number of blocks to insert without enabling snapshot
setHead uint64 // Block number to set head back to
legacy bool // Flag whether the loaded snapshot is in legacy format
crash bool // Flag whether the Geth restarts from the previous crash
restartCrash int // Number of blocks to insert after the normal stop, then the crash happens
gapped int // Number of blocks to insert without enabling snapshot
setHead uint64 // Block number to set head back to
chainBlocks int // Number of blocks to generate for the canonical chain
snapshotBlock uint64 // Block number of the relevant snapshot disk layer
@ -565,10 +566,50 @@ func TestSetHeadWithLegacySnapshot(t *testing.T) {
} )
}
// Tests the Geth was running with snapshot(legacy-format) enabled and upgrades
// the disk layer journal(journal generator) to latest format. After that the Geth
// is restarted from a crash. In this case Geth will find the new-format disk layer
// journal but with legacy-format diff journal(the new-format is never committed),
// and the invalid diff journal is expected to be dropped.
func TestRecoverSnapshotFromCrashWithLegacyDiffJournal ( t * testing . T ) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
//
// Commit: G
// Snapshot: G
//
// SetHead(0)
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10
//
// Expected head header : C10
// Expected head fast block: C10
// Expected head block : C8
// Expected snapshot disk : C10
testSnapshot ( t , & snapshotTest {
legacy : true ,
crash : false ,
restartCrash : 2 ,
gapped : 0 ,
setHead : 0 ,
chainBlocks : 8 ,
snapshotBlock : 0 ,
commitBlock : 0 ,
expCanonicalBlocks : 10 ,
expHeadHeader : 10 ,
expHeadFastBlock : 10 ,
expHeadBlock : 8 , // The persisted state in the first running
expSnapshotBottom : 10 , // The persisted disk layer in the second running
} )
}
func testSnapshot ( t * testing . T , tt * snapshotTest ) {
// It's hard to follow the test case, visualize the input
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
//fmt.Println(tt.dump())
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
// fmt.Println(tt.dump())
// Create a temporary persistent database
datadir , err := ioutil . TempDir ( "" , "" )
@ -694,6 +735,30 @@ func testSnapshot(t *testing.T, tt *snapshotTest) {
chain . SetHead ( tt . setHead )
chain . Stop ( )
chain , err = NewBlockChain ( db , nil , params . AllEthashProtocolChanges , engine , vm . Config { } , nil , nil )
if err != nil {
t . Fatalf ( "Failed to recreate chain: %v" , err )
}
defer chain . Stop ( )
} else if tt . restartCrash != 0 {
// Firstly, stop the chain properly, with all snapshot journal
// and state committed.
chain . Stop ( )
// Restart chain, forcibly flush the disk layer journal with new format
newBlocks , _ := GenerateChain ( params . TestChainConfig , blocks [ len ( blocks ) - 1 ] , engine , gendb , tt . restartCrash , func ( i int , b * BlockGen ) { } )
chain , err = NewBlockChain ( db , cacheConfig , params . AllEthashProtocolChanges , engine , vm . Config { } , nil , nil )
if err != nil {
t . Fatalf ( "Failed to recreate chain: %v" , err )
}
chain . InsertChain ( newBlocks )
chain . Snapshot ( ) . Cap ( newBlocks [ len ( newBlocks ) - 1 ] . Root ( ) , 0 )
// Simulate the blockchain crash
// Don't call chain.Stop here, so that no snapshot
// journal and latest state will be committed
// Restart the chain after the crash
chain , err = NewBlockChain ( db , nil , params . AllEthashProtocolChanges , engine , vm . Config { } , nil , nil )
if err != nil {
t . Fatalf ( "Failed to recreate chain: %v" , err )