plugeth/core/blockchain_repair_test.go
Péter Szilágyi 8cbdc8638f
core: define and test chain rewind corner cases (#21409)
* core: define and test chain reparation cornercases

* core: write up a variety of set-head tests

* core, eth: unify chain rollbacks, handle all the cases

* core: make linter smile

* core: remove commented out legacy code

* core, eth/downloader: fix review comments

* core: revert a removed recovery mechanism
2020-08-20 13:01:24 +03:00

1654 lines
49 KiB
Go

// Copyright 2020 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Tests that abnormal program termination (i.e.crash) and restart doesn't leave
// the database in some strange state with gaps in the chain, nor with block data
// dangling in the future.
package core
import (
"io/ioutil"
"math/big"
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
)
// Tests a recovery for a short canonical chain where a recent block was already
// committed to disk and then the process crashed. In this case we expect the full
// chain to be rolled back to the committed block, but the chain data itself left
// in the database for replaying.
func TestShortRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
//
// Frozen: none
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 0,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 8,
expSidechainBlocks: 0,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 4,
})
}
// Tests a recovery for a short canonical chain where the fast sync pivot point was
// already committed, after which the process crashed. In this case we expect the full
// chain to be rolled back to the committed block, but the chain data itself left in
// the database for replaying.
func TestShortFastSyncedRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
//
// Frozen: none
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 0,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 8,
expSidechainBlocks: 0,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 4,
})
}
// Tests a recovery for a short canonical chain where the fast sync pivot point was
// not yet committed, but the process crashed. In this case we expect the chain to
// detect that it was fast syncing and not delete anything, since we can just pick
// up directly where we left off.
func TestShortFastSyncingRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
//
// Frozen: none
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 0,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 8,
expSidechainBlocks: 0,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 0,
})
}
// Tests a recovery for a short canonical chain and a shorter side chain, where a
// recent block was already committed to disk and then the process crashed. In this
// test scenario the side chain is below the committed block. In this case we expect
// the canonical chain to be rolled back to the committed block, but the chain data
// itself left in the database for replaying.
func TestShortOldForkedRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
// └->S1->S2->S3
//
// Frozen: none
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
// └->S1->S2->S3
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 3,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 8,
expSidechainBlocks: 3,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 4,
})
}
// Tests a recovery for a short canonical chain and a shorter side chain, where
// the fast sync pivot point was already committed to disk and then the process
// crashed. In this test scenario the side chain is below the committed block. In
// this case we expect the canonical chain to be rolled back to the committed block,
// but the chain data itself left in the database for replaying.
func TestShortOldForkedFastSyncedRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
// └->S1->S2->S3
//
// Frozen: none
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
// └->S1->S2->S3
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 3,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 8,
expSidechainBlocks: 3,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 4,
})
}
// Tests a recovery for a short canonical chain and a shorter side chain, where
// the fast sync pivot point was not yet committed, but the process crashed. In this
// test scenario the side chain is below the committed block. In this case we expect
// the chain to detect that it was fast syncing and not delete anything, since we
// can just pick up directly where we left off.
func TestShortOldForkedFastSyncingRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
// └->S1->S2->S3
//
// Frozen: none
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
// └->S1->S2->S3
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 3,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 8,
expSidechainBlocks: 3,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 0,
})
}
// Tests a recovery for a short canonical chain and a shorter side chain, where a
// recent block was already committed to disk and then the process crashed. In this
// test scenario the side chain reaches above the committed block. In this case we
// expect the canonical chain to be rolled back to the committed block, but the
// chain data itself left in the database for replaying.
func TestShortNewlyForkedRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
// └->S1->S2->S3->S4->S5->S6
//
// Frozen: none
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
// └->S1->S2->S3->S4->S5->S6
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 6,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 8,
expSidechainBlocks: 6,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 4,
})
}
// Tests a recovery for a short canonical chain and a shorter side chain, where
// the fast sync pivot point was already committed to disk and then the process
// crashed. In this test scenario the side chain reaches above the committed block.
// In this case we expect the canonical chain to be rolled back to the committed
// block, but the chain data itself left in the database for replaying.
func TestShortNewlyForkedFastSyncedRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
// └->S1->S2->S3->S4->S5->S6
//
// Frozen: none
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
// └->S1->S2->S3->S4->S5->S6
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 6,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 8,
expSidechainBlocks: 6,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 4,
})
}
// Tests a recovery for a short canonical chain and a shorter side chain, where
// the fast sync pivot point was not yet committed, but the process crashed. In
// this test scenario the side chain reaches above the committed block. In this
// case we expect the chain to detect that it was fast syncing and not delete
// anything, since we can just pick up directly where we left off.
func TestShortNewlyForkedFastSyncingRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
// └->S1->S2->S3->S4->S5->S6
//
// Frozen: none
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
// └->S1->S2->S3->S4->S5->S6
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 6,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 8,
expSidechainBlocks: 6,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 0,
})
}
// Tests a recovery for a short canonical chain and a longer side chain, where a
// recent block was already committed to disk and then the process crashed. In this
// case we expect the canonical chain to be rolled back to the committed block, but
// the chain data itself left in the database for replaying.
func TestShortReorgedRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10
//
// Frozen: none
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 10,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 8,
expSidechainBlocks: 10,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 4,
})
}
// Tests a recovery for a short canonical chain and a longer side chain, where
// the fast sync pivot point was already committed to disk and then the process
// crashed. In this case we expect the canonical chain to be rolled back to the
// committed block, but the chain data itself left in the database for replaying.
func TestShortReorgedFastSyncedRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10
//
// Frozen: none
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 10,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 8,
expSidechainBlocks: 10,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 4,
})
}
// Tests a recovery for a short canonical chain and a longer side chain, where
// the fast sync pivot point was not yet committed, but the process crashed. In
// this case we expect the chain to detect that it was fast syncing and not delete
// anything, since we can just pick up directly where we left off.
func TestShortReorgedFastSyncingRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10
//
// Frozen: none
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in leveldb:
// G->C1->C2->C3->C4->C5->C6->C7->C8
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10
//
// Expected head header : C8
// Expected head fast block: C8
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 8,
sidechainBlocks: 10,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 8,
expSidechainBlocks: 10,
expFrozen: 0,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: 0,
})
}
// Tests a recovery for a long canonical chain with frozen blocks where a recent
// block - newer than the ancient limit - was already committed to disk and then
// the process crashed. In this case we expect the chain to be rolled back to the
// committed block, with everything afterwads kept as fast sync data.
func TestLongShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
//
// Frozen:
// G->C1->C2
//
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 0,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks where a recent
// block - older than the ancient limit - was already committed to disk and then
// the process crashed. In this case we expect the chain to be rolled back to the
// committed block, with everything afterwads deleted.
func TestLongDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4
//
// Expected in leveldb: none
//
// Expected head header : C4
// Expected head fast block: C4
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 0,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 4,
expSidechainBlocks: 0,
expFrozen: 5,
expHeadHeader: 4,
expHeadFastBlock: 4,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks where the fast
// sync pivot point - newer than the ancient limit - was already committed, after
// which the process crashed. In this case we expect the chain to be rolled back
// to the committed block, with everything afterwads kept as fast sync data.
func TestLongFastSyncedShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
//
// Frozen:
// G->C1->C2
//
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 0,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks where the fast
// sync pivot point - older than the ancient limit - was already committed, after
// which the process crashed. In this case we expect the chain to be rolled back
// to the committed block, with everything afterwads deleted.
func TestLongFastSyncedDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4
//
// Expected in leveldb: none
//
// Expected head header : C4
// Expected head fast block: C4
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 0,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 4,
expSidechainBlocks: 0,
expFrozen: 5,
expHeadHeader: 4,
expHeadFastBlock: 4,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks where the fast
// sync pivot point - older than the ancient limit - was not yet committed, but the
// process crashed. In this case we expect the chain to detect that it was fast
// syncing and not delete anything, since we can just pick up directly where we
// left off.
func TestLongFastSyncingShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
//
// Frozen:
// G->C1->C2
//
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 0,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 0,
})
}
// Tests a recovery for a long canonical chain with frozen blocks where the fast
// sync pivot point - newer than the ancient limit - was not yet committed, but the
// process crashed. In this case we expect the chain to detect that it was fast
// syncing and not delete anything, since we can just pick up directly where we
// left off.
func TestLongFastSyncingDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Expected in leveldb:
// C8)->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24
//
// Expected head header : C24
// Expected head fast block: C24
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 0,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 24,
expSidechainBlocks: 0,
expFrozen: 9,
expHeadHeader: 24,
expHeadFastBlock: 24,
expHeadBlock: 0,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where a recent block - newer than the ancient limit - was already
// committed to disk and then the process crashed. In this test scenario the side
// chain is below the committed block. In this case we expect the chain to be
// rolled back to the committed block, with everything afterwads kept as fast
// sync data; the side chain completely nuked by the freezer.
func TestLongOldForkedShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
// └->S1->S2->S3
//
// Frozen:
// G->C1->C2
//
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 3,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where a recent block - older than the ancient limit - was already
// committed to disk and then the process crashed. In this test scenario the side
// chain is below the committed block. In this case we expect the canonical chain
// to be rolled back to the committed block, with everything afterwads deleted;
// the side chain completely nuked by the freezer.
func TestLongOldForkedDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
// └->S1->S2->S3
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4
//
// Expected in leveldb: none
//
// Expected head header : C4
// Expected head fast block: C4
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 3,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 4,
expSidechainBlocks: 0,
expFrozen: 5,
expHeadHeader: 4,
expHeadFastBlock: 4,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where the fast sync pivot point - newer than the ancient limit -
// was already committed to disk and then the process crashed. In this test scenario
// the side chain is below the committed block. In this case we expect the chain
// to be rolled back to the committed block, with everything afterwads kept as
// fast sync data; the side chain completely nuked by the freezer.
func TestLongOldForkedFastSyncedShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
// └->S1->S2->S3
//
// Frozen:
// G->C1->C2
//
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 3,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where the fast sync pivot point - older than the ancient limit -
// was already committed to disk and then the process crashed. In this test scenario
// the side chain is below the committed block. In this case we expect the canonical
// chain to be rolled back to the committed block, with everything afterwads deleted;
// the side chain completely nuked by the freezer.
func TestLongOldForkedFastSyncedDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
// └->S1->S2->S3
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4
//
// Expected in leveldb: none
//
// Expected head header : C4
// Expected head fast block: C4
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 3,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 4,
expSidechainBlocks: 0,
expFrozen: 5,
expHeadHeader: 4,
expHeadFastBlock: 4,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where the fast sync pivot point - older than the ancient limit -
// was not yet committed, but the process crashed. In this test scenario the side
// chain is below the committed block. In this case we expect the chain to detect
// that it was fast syncing and not delete anything. The side chain is completely
// nuked by the freezer.
func TestLongOldForkedFastSyncingShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
// └->S1->S2->S3
//
// Frozen:
// G->C1->C2
//
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 3,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 0,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where the fast sync pivot point - older than the ancient limit -
// was not yet committed, but the process crashed. In this test scenario the side
// chain is below the committed block. In this case we expect the chain to detect
// that it was fast syncing and not delete anything. The side chain is completely
// nuked by the freezer.
func TestLongOldForkedFastSyncingDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
// └->S1->S2->S3
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Expected in leveldb:
// C8)->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24
//
// Expected head header : C24
// Expected head fast block: C24
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 3,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 24,
expSidechainBlocks: 0,
expFrozen: 9,
expHeadHeader: 24,
expHeadFastBlock: 24,
expHeadBlock: 0,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where a recent block - newer than the ancient limit - was already
// committed to disk and then the process crashed. In this test scenario the side
// chain is above the committed block. In this case we expect the chain to be
// rolled back to the committed block, with everything afterwads kept as fast
// sync data; the side chain completely nuked by the freezer.
func TestLongNewerForkedShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12
//
// Frozen:
// G->C1->C2
//
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 12,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where a recent block - older than the ancient limit - was already
// committed to disk and then the process crashed. In this test scenario the side
// chain is above the committed block. In this case we expect the canonical chain
// to be rolled back to the committed block, with everything afterwads deleted;
// the side chain completely nuked by the freezer.
func TestLongNewerForkedDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4
//
// Expected in leveldb: none
//
// Expected head header : C4
// Expected head fast block: C4
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 12,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 4,
expSidechainBlocks: 0,
expFrozen: 5,
expHeadHeader: 4,
expHeadFastBlock: 4,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where the fast sync pivot point - newer than the ancient limit -
// was already committed to disk and then the process crashed. In this test scenario
// the side chain is above the committed block. In this case we expect the chain
// to be rolled back to the committed block, with everything afterwads kept as fast
// sync data; the side chain completely nuked by the freezer.
func TestLongNewerForkedFastSyncedShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12
//
// Frozen:
// G->C1->C2
//
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 12,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where the fast sync pivot point - older than the ancient limit -
// was already committed to disk and then the process crashed. In this test scenario
// the side chain is above the committed block. In this case we expect the canonical
// chain to be rolled back to the committed block, with everything afterwads deleted;
// the side chain completely nuked by the freezer.
func TestLongNewerForkedFastSyncedDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4
//
// Expected in leveldb: none
//
// Expected head header : C4
// Expected head fast block: C4
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 12,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 4,
expSidechainBlocks: 0,
expFrozen: 5,
expHeadHeader: 4,
expHeadFastBlock: 4,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where the fast sync pivot point - older than the ancient limit -
// was not yet committed, but the process crashed. In this test scenario the side
// chain is above the committed block. In this case we expect the chain to detect
// that it was fast syncing and not delete anything. The side chain is completely
// nuked by the freezer.
func TestLongNewerForkedFastSyncingShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12
//
// Frozen:
// G->C1->C2
//
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 12,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 0,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a shorter
// side chain, where the fast sync pivot point - older than the ancient limit -
// was not yet committed, but the process crashed. In this test scenario the side
// chain is above the committed block. In this case we expect the chain to detect
// that it was fast syncing and not delete anything. The side chain is completely
// nuked by the freezer.
func TestLongNewerForkedFastSyncingDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Expected in leveldb:
// C8)->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24
//
// Expected head header : C24
// Expected head fast block: C24
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 12,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 24,
expSidechainBlocks: 0,
expFrozen: 9,
expHeadHeader: 24,
expHeadFastBlock: 24,
expHeadBlock: 0,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a longer side
// chain, where a recent block - newer than the ancient limit - was already committed
// to disk and then the process crashed. In this case we expect the chain to be
// rolled back to the committed block, with everything afterwads kept as fast sync
// data. The side chain completely nuked by the freezer.
func TestLongReorgedShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26
//
// Frozen:
// G->C1->C2
//
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 26,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a longer side
// chain, where a recent block - older than the ancient limit - was already committed
// to disk and then the process crashed. In this case we expect the canonical chains
// to be rolled back to the committed block, with everything afterwads deleted. The
// side chain completely nuked by the freezer.
func TestLongReorgedDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G, C4
// Pivot : none
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4
//
// Expected in leveldb: none
//
// Expected head header : C4
// Expected head fast block: C4
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 26,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: nil,
expCanonicalBlocks: 4,
expSidechainBlocks: 0,
expFrozen: 5,
expHeadHeader: 4,
expHeadFastBlock: 4,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a longer
// side chain, where the fast sync pivot point - newer than the ancient limit -
// was already committed to disk and then the process crashed. In this case we
// expect the chain to be rolled back to the committed block, with everything
// afterwads kept as fast sync data. The side chain completely nuked by the
// freezer.
func TestLongReorgedFastSyncedShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26
//
// Frozen:
// G->C1->C2
//
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 26,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a longer
// side chain, where the fast sync pivot point - older than the ancient limit -
// was already committed to disk and then the process crashed. In this case we
// expect the canonical chains to be rolled back to the committed block, with
// everything afterwads deleted. The side chain completely nuked by the freezer.
func TestLongReorgedFastSyncedDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G, C4
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4
//
// Expected in leveldb: none
//
// Expected head header : C4
// Expected head fast block: C4
// Expected head block : C4
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 26,
freezeThreshold: 16,
commitBlock: 4,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 4,
expSidechainBlocks: 0,
expFrozen: 5,
expHeadHeader: 4,
expHeadFastBlock: 4,
expHeadBlock: 4,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a longer
// side chain, where the fast sync pivot point - newer than the ancient limit -
// was not yet committed, but the process crashed. In this case we expect the
// chain to detect that it was fast syncing and not delete anything, since we
// can just pick up directly where we left off.
func TestLongReorgedFastSyncingShallowRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26
//
// Frozen:
// G->C1->C2
//
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2
//
// Expected in leveldb:
// C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
//
// Expected head header : C18
// Expected head fast block: C18
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 18,
sidechainBlocks: 26,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 18,
expSidechainBlocks: 0,
expFrozen: 3,
expHeadHeader: 18,
expHeadFastBlock: 18,
expHeadBlock: 0,
})
}
// Tests a recovery for a long canonical chain with frozen blocks and a longer
// side chain, where the fast sync pivot point - older than the ancient limit -
// was not yet committed, but the process crashed. In this case we expect the
// chain to detect that it was fast syncing and not delete anything, since we
// can just pick up directly where we left off.
func TestLongReorgedFastSyncingDeepRepair(t *testing.T) {
// Chain:
// G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
// └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26
//
// Frozen:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Commit: G
// Pivot : C4
//
// CRASH
//
// ------------------------------
//
// Expected in freezer:
// G->C1->C2->C3->C4->C5->C6->C7->C8
//
// Expected in leveldb:
// C8)->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24
//
// Expected head header : C24
// Expected head fast block: C24
// Expected head block : G
testRepair(t, &rewindTest{
canonicalBlocks: 24,
sidechainBlocks: 26,
freezeThreshold: 16,
commitBlock: 0,
pivotBlock: uint64ptr(4),
expCanonicalBlocks: 24,
expSidechainBlocks: 0,
expFrozen: 9,
expHeadHeader: 24,
expHeadFastBlock: 24,
expHeadBlock: 0,
})
}
func testRepair(t *testing.T, tt *rewindTest) {
// 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(true))
// Create a temporary persistent database
datadir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Failed to create temporary datadir: %v", err)
}
os.RemoveAll(datadir)
db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "")
if err != nil {
t.Fatalf("Failed to create persistent database: %v", err)
}
defer db.Close() // Might double close, should be fine
// Initialize a fresh chain
var (
genesis = new(Genesis).MustCommit(db)
engine = ethash.NewFullFaker()
)
chain, err := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create chain: %v", err)
}
// If sidechain blocks are needed, make a light chain and import it
var sideblocks types.Blocks
if tt.sidechainBlocks > 0 {
sideblocks, _ = GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.sidechainBlocks, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{0x01})
})
if _, err := chain.InsertChain(sideblocks); err != nil {
t.Fatalf("Failed to import side chain: %v", err)
}
}
canonblocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{0x02})
b.SetDifficulty(big.NewInt(1000000))
})
if _, err := chain.InsertChain(canonblocks[:tt.commitBlock]); err != nil {
t.Fatalf("Failed to import canonical chain start: %v", err)
}
if tt.commitBlock > 0 {
chain.stateCache.TrieDB().Commit(canonblocks[tt.commitBlock-1].Root(), true, nil)
}
if _, err := chain.InsertChain(canonblocks[tt.commitBlock:]); err != nil {
t.Fatalf("Failed to import canonical chain tail: %v", err)
}
// Force run a freeze cycle
type freezer interface {
Freeze(threshold uint64)
Ancients() (uint64, error)
}
db.(freezer).Freeze(tt.freezeThreshold)
// Set the simulated pivot block
if tt.pivotBlock != nil {
rawdb.WriteLastPivotNumber(db, *tt.pivotBlock)
}
// Pull the plug on the database, simulating a hard crash
db.Close()
// Start a new blockchain back up and see where the repait leads us
db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "")
if err != nil {
t.Fatalf("Failed to reopen persistent database: %v", err)
}
defer db.Close()
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()
// Iterate over all the remaining blocks and ensure there are no gaps
verifyNoGaps(t, chain, true, canonblocks)
verifyNoGaps(t, chain, false, sideblocks)
verifyCutoff(t, chain, true, canonblocks, tt.expCanonicalBlocks)
verifyCutoff(t, chain, false, sideblocks, tt.expSidechainBlocks)
if head := chain.CurrentHeader(); head.Number.Uint64() != tt.expHeadHeader {
t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadHeader)
}
if head := chain.CurrentFastBlock(); head.NumberU64() != tt.expHeadFastBlock {
t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadFastBlock)
}
if head := chain.CurrentBlock(); head.NumberU64() != tt.expHeadBlock {
t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock)
}
if frozen, err := db.(freezer).Ancients(); err != nil {
t.Errorf("Failed to retrieve ancient count: %v\n", err)
} else if int(frozen) != tt.expFrozen {
t.Errorf("Frozen block count mismatch: have %d, want %d", frozen, tt.expFrozen)
}
}