1654 lines
49 KiB
Go
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)
|
|
}
|
|
}
|