2020-08-20 10:01:24 +00:00
|
|
|
// 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"
|
2020-10-29 19:01:58 +00:00
|
|
|
"time"
|
2020-08-20 10:01:24 +00:00
|
|
|
|
|
|
|
"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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestShortRepair(t *testing.T) { testShortRepair(t, false) }
|
|
|
|
func TestShortRepairWithSnapshots(t *testing.T) { testShortRepair(t, true) }
|
|
|
|
|
|
|
|
func testShortRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestShortFastSyncedRepair(t *testing.T) { testShortFastSyncedRepair(t, false) }
|
|
|
|
func TestShortFastSyncedRepairWithSnapshots(t *testing.T) { testShortFastSyncedRepair(t, true) }
|
|
|
|
|
|
|
|
func testShortFastSyncedRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestShortFastSyncingRepair(t *testing.T) { testShortFastSyncingRepair(t, false) }
|
|
|
|
func TestShortFastSyncingRepairWithSnapshots(t *testing.T) { testShortFastSyncingRepair(t, true) }
|
|
|
|
|
|
|
|
func testShortFastSyncingRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestShortOldForkedRepair(t *testing.T) { testShortOldForkedRepair(t, false) }
|
|
|
|
func TestShortOldForkedRepairWithSnapshots(t *testing.T) { testShortOldForkedRepair(t, true) }
|
|
|
|
|
|
|
|
func testShortOldForkedRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testShortOldForkedFastSyncedRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestShortOldForkedFastSyncedRepairWithSnapshots(t *testing.T) {
|
|
|
|
testShortOldForkedFastSyncedRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testShortOldForkedFastSyncedRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testShortOldForkedFastSyncingRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestShortOldForkedFastSyncingRepairWithSnapshots(t *testing.T) {
|
|
|
|
testShortOldForkedFastSyncingRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testShortOldForkedFastSyncingRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestShortNewlyForkedRepair(t *testing.T) { testShortNewlyForkedRepair(t, false) }
|
|
|
|
func TestShortNewlyForkedRepairWithSnapshots(t *testing.T) { testShortNewlyForkedRepair(t, true) }
|
|
|
|
|
|
|
|
func testShortNewlyForkedRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testShortNewlyForkedFastSyncedRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestShortNewlyForkedFastSyncedRepairWithSnapshots(t *testing.T) {
|
|
|
|
testShortNewlyForkedFastSyncedRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testShortNewlyForkedFastSyncedRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testShortNewlyForkedFastSyncingRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestShortNewlyForkedFastSyncingRepairWithSnapshots(t *testing.T) {
|
|
|
|
testShortNewlyForkedFastSyncingRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testShortNewlyForkedFastSyncingRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestShortReorgedRepair(t *testing.T) { testShortReorgedRepair(t, false) }
|
|
|
|
func TestShortReorgedRepairWithSnapshots(t *testing.T) { testShortReorgedRepair(t, true) }
|
|
|
|
|
|
|
|
func testShortReorgedRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testShortReorgedFastSyncedRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestShortReorgedFastSyncedRepairWithSnapshots(t *testing.T) {
|
|
|
|
testShortReorgedFastSyncedRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testShortReorgedFastSyncedRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testShortReorgedFastSyncingRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestShortReorgedFastSyncingRepairWithSnapshots(t *testing.T) {
|
|
|
|
testShortReorgedFastSyncingRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testShortReorgedFastSyncingRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestLongShallowRepair(t *testing.T) { testLongShallowRepair(t, false) }
|
|
|
|
func TestLongShallowRepairWithSnapshots(t *testing.T) { testLongShallowRepair(t, true) }
|
|
|
|
|
|
|
|
func testLongShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestLongDeepRepair(t *testing.T) { testLongDeepRepair(t, false) }
|
|
|
|
func TestLongDeepRepairWithSnapshots(t *testing.T) { testLongDeepRepair(t, true) }
|
|
|
|
|
|
|
|
func testLongDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongFastSyncedShallowRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongFastSyncedShallowRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongFastSyncedShallowRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongFastSyncedShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestLongFastSyncedDeepRepair(t *testing.T) { testLongFastSyncedDeepRepair(t, false) }
|
|
|
|
func TestLongFastSyncedDeepRepairWithSnapshots(t *testing.T) { testLongFastSyncedDeepRepair(t, true) }
|
|
|
|
|
|
|
|
func testLongFastSyncedDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongFastSyncingShallowRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongFastSyncingShallowRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongFastSyncingShallowRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongFastSyncingShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestLongFastSyncingDeepRepair(t *testing.T) { testLongFastSyncingDeepRepair(t, false) }
|
|
|
|
func TestLongFastSyncingDeepRepairWithSnapshots(t *testing.T) { testLongFastSyncingDeepRepair(t, true) }
|
|
|
|
|
|
|
|
func testLongFastSyncingDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongOldForkedShallowRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongOldForkedShallowRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongOldForkedShallowRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongOldForkedShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestLongOldForkedDeepRepair(t *testing.T) { testLongOldForkedDeepRepair(t, false) }
|
|
|
|
func TestLongOldForkedDeepRepairWithSnapshots(t *testing.T) { testLongOldForkedDeepRepair(t, true) }
|
|
|
|
|
|
|
|
func testLongOldForkedDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongOldForkedFastSyncedShallowRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongOldForkedFastSyncedShallowRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongOldForkedFastSyncedShallowRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongOldForkedFastSyncedShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongOldForkedFastSyncedDeepRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongOldForkedFastSyncedDeepRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongOldForkedFastSyncedDeepRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongOldForkedFastSyncedDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongOldForkedFastSyncingShallowRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongOldForkedFastSyncingShallowRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongOldForkedFastSyncingShallowRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongOldForkedFastSyncingShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongOldForkedFastSyncingDeepRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongOldForkedFastSyncingDeepRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongOldForkedFastSyncingDeepRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongOldForkedFastSyncingDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongNewerForkedShallowRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongNewerForkedShallowRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongNewerForkedShallowRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongNewerForkedShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestLongNewerForkedDeepRepair(t *testing.T) { testLongNewerForkedDeepRepair(t, false) }
|
|
|
|
func TestLongNewerForkedDeepRepairWithSnapshots(t *testing.T) { testLongNewerForkedDeepRepair(t, true) }
|
|
|
|
|
|
|
|
func testLongNewerForkedDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongNewerForkedFastSyncedShallowRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongNewerForkedFastSyncedShallowRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongNewerForkedFastSyncedShallowRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongNewerForkedFastSyncedShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongNewerForkedFastSyncedDeepRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongNewerForkedFastSyncedDeepRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongNewerForkedFastSyncedDeepRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongNewerForkedFastSyncedDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongNewerForkedFastSyncingShallowRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongNewerForkedFastSyncingShallowRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongNewerForkedFastSyncingShallowRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongNewerForkedFastSyncingShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongNewerForkedFastSyncingDeepRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongNewerForkedFastSyncingDeepRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongNewerForkedFastSyncingDeepRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongNewerForkedFastSyncingDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestLongReorgedShallowRepair(t *testing.T) { testLongReorgedShallowRepair(t, false) }
|
|
|
|
func TestLongReorgedShallowRepairWithSnapshots(t *testing.T) { testLongReorgedShallowRepair(t, true) }
|
|
|
|
|
|
|
|
func testLongReorgedShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-29 19:01:58 +00:00
|
|
|
func TestLongReorgedDeepRepair(t *testing.T) { testLongReorgedDeepRepair(t, false) }
|
|
|
|
func TestLongReorgedDeepRepairWithSnapshots(t *testing.T) { testLongReorgedDeepRepair(t, true) }
|
|
|
|
|
|
|
|
func testLongReorgedDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongReorgedFastSyncedShallowRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongReorgedFastSyncedShallowRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongReorgedFastSyncedShallowRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongReorgedFastSyncedShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongReorgedFastSyncedDeepRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongReorgedFastSyncedDeepRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongReorgedFastSyncedDeepRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongReorgedFastSyncedDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongReorgedFastSyncingShallowRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongReorgedFastSyncingShallowRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongReorgedFastSyncingShallowRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongReorgedFastSyncingShallowRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-10-29 19:01:58 +00:00
|
|
|
testLongReorgedFastSyncingDeepRepair(t, false)
|
|
|
|
}
|
|
|
|
func TestLongReorgedFastSyncingDeepRepairWithSnapshots(t *testing.T) {
|
|
|
|
testLongReorgedFastSyncingDeepRepair(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLongReorgedFastSyncingDeepRepair(t *testing.T, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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,
|
2020-10-29 19:01:58 +00:00
|
|
|
}, snapshots)
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
|
2020-10-29 19:01:58 +00:00
|
|
|
func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
2020-08-20 10:01:24 +00:00
|
|
|
// 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))))
|
2020-10-29 19:01:58 +00:00
|
|
|
// fmt.Println(tt.dump(true))
|
2020-08-20 10:01:24 +00:00
|
|
|
|
|
|
|
// Create a temporary persistent database
|
|
|
|
datadir, err := ioutil.TempDir("", "")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to create temporary datadir: %v", err)
|
|
|
|
}
|
|
|
|
os.RemoveAll(datadir)
|
|
|
|
|
2021-03-22 18:06:30 +00:00
|
|
|
db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false)
|
2020-08-20 10:01:24 +00:00
|
|
|
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 (
|
2021-06-15 10:56:14 +00:00
|
|
|
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
2020-08-20 10:01:24 +00:00
|
|
|
engine = ethash.NewFullFaker()
|
2020-10-29 19:01:58 +00:00
|
|
|
config = &CacheConfig{
|
|
|
|
TrieCleanLimit: 256,
|
|
|
|
TrieDirtyLimit: 256,
|
|
|
|
TrieTimeLimit: 5 * time.Minute,
|
|
|
|
SnapshotLimit: 0, // Disable snapshot by default
|
|
|
|
}
|
2020-08-20 10:01:24 +00:00
|
|
|
)
|
2020-10-29 19:01:58 +00:00
|
|
|
if snapshots {
|
|
|
|
config.SnapshotLimit = 256
|
|
|
|
config.SnapshotWait = true
|
|
|
|
}
|
|
|
|
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
2020-08-20 10:01:24 +00:00
|
|
|
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)
|
2020-10-29 19:01:58 +00:00
|
|
|
if snapshots {
|
|
|
|
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
|
|
|
t.Fatalf("Failed to flatten snapshots: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2020-08-20 10:01:24 +00:00
|
|
|
}
|
|
|
|
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 {
|
2021-03-22 18:06:30 +00:00
|
|
|
Freeze(threshold uint64) error
|
2020-08-20 10:01:24 +00:00
|
|
|
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()
|
|
|
|
|
all: core rework for the merge transition (#23761)
* all: work for eth1/2 transtition
* consensus/beacon, eth: change beacon difficulty to 0
* eth: updates
* all: add terminalBlockDifficulty config, fix rebasing issues
* eth: implemented merge interop spec
* internal/ethapi: update to v1.0.0.alpha.2
This commit updates the code to the new spec, moving payloadId into
it's own object. It also fixes an issue with finalizing an empty blockhash.
It also properly sets the basefee
* all: sync polishes, other fixes + refactors
* core, eth: correct semantics for LeavePoW, EnterPoS
* core: fixed rebasing artifacts
* core: light: performance improvements
* core: use keyed field (f)
* core: eth: fix compilation issues + tests
* eth/catalyst: dbetter error codes
* all: move Merger to consensus/, remove reliance on it in bc
* all: renamed EnterPoS and LeavePoW to ReachTDD and FinalizePoS
* core: make mergelogs a function
* core: use InsertChain instead of InsertBlock
* les: drop merger from lightchain object
* consensus: add merger
* core: recoverAncestors in catalyst mode
* core: fix nitpick
* all: removed merger from beacon, use TTD, nitpicks
* consensus: eth: add docstring, removed unnecessary code duplication
* consensus/beacon: better comment
* all: easy to fix nitpicks by karalabe
* consensus/beacon: verify known headers to be sure
* core: comments
* core: eth: don't drop peers who advertise blocks, nitpicks
* core: never add beacon blocks to the future queue
* core: fixed nitpicks
* consensus/beacon: simplify IsTTDReached check
* consensus/beacon: correct IsTTDReached check
Co-authored-by: rjl493456442 <garyrong0905@gmail.com>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2021-11-26 11:23:02 +00:00
|
|
|
// Start a new blockchain back up and see where the repair leads us
|
2021-03-22 18:06:30 +00:00
|
|
|
db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false)
|
2020-08-20 10:01:24 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2021-11-01 13:09:36 +00:00
|
|
|
|
|
|
|
// TestIssue23496 tests scenario described in https://github.com/ethereum/go-ethereum/pull/23496#issuecomment-926393893
|
|
|
|
// Credits to @zzyalbert for finding the issue.
|
|
|
|
//
|
|
|
|
// Local chain owns these blocks:
|
|
|
|
// G B1 B2 B3 B4
|
|
|
|
// B1: state committed
|
|
|
|
// B2: snapshot disk layer
|
|
|
|
// B3: state committed
|
|
|
|
// B4: head block
|
|
|
|
//
|
|
|
|
// Crash happens without fully persisting snapshot and in-memory states,
|
|
|
|
// chain rewinds itself to the B1 (skip B3 in order to recover snapshot)
|
|
|
|
// In this case the snapshot layer of B3 is not created because of existent
|
|
|
|
// state.
|
|
|
|
func TestIssue23496(t *testing.T) {
|
|
|
|
// 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))))
|
|
|
|
|
|
|
|
// 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, "", false)
|
|
|
|
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 = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
|
|
|
engine = ethash.NewFullFaker()
|
|
|
|
config = &CacheConfig{
|
|
|
|
TrieCleanLimit: 256,
|
|
|
|
TrieDirtyLimit: 256,
|
|
|
|
TrieTimeLimit: 5 * time.Minute,
|
|
|
|
SnapshotLimit: 256,
|
|
|
|
SnapshotWait: true,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to create chain: %v", err)
|
|
|
|
}
|
|
|
|
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), 4, func(i int, b *BlockGen) {
|
|
|
|
b.SetCoinbase(common.Address{0x02})
|
|
|
|
b.SetDifficulty(big.NewInt(1000000))
|
|
|
|
})
|
|
|
|
|
|
|
|
// Insert block B1 and commit the state into disk
|
|
|
|
if _, err := chain.InsertChain(blocks[:1]); err != nil {
|
|
|
|
t.Fatalf("Failed to import canonical chain start: %v", err)
|
|
|
|
}
|
|
|
|
chain.stateCache.TrieDB().Commit(blocks[0].Root(), true, nil)
|
|
|
|
|
|
|
|
// Insert block B2 and commit the snapshot into disk
|
|
|
|
if _, err := chain.InsertChain(blocks[1:2]); err != nil {
|
|
|
|
t.Fatalf("Failed to import canonical chain start: %v", err)
|
|
|
|
}
|
|
|
|
if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
|
|
|
|
t.Fatalf("Failed to flatten snapshots: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert block B3 and commit the state into disk
|
|
|
|
if _, err := chain.InsertChain(blocks[2:3]); err != nil {
|
|
|
|
t.Fatalf("Failed to import canonical chain start: %v", err)
|
|
|
|
}
|
|
|
|
chain.stateCache.TrieDB().Commit(blocks[2].Root(), true, nil)
|
|
|
|
|
|
|
|
// Insert the remaining blocks
|
|
|
|
if _, err := chain.InsertChain(blocks[3:]); err != nil {
|
|
|
|
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pull the plug on the database, simulating a hard crash
|
|
|
|
db.Close()
|
|
|
|
|
|
|
|
// Start a new blockchain back up and see where the repair leads us
|
|
|
|
db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false)
|
|
|
|
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()
|
|
|
|
|
|
|
|
if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
|
|
|
|
t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
|
|
|
|
}
|
|
|
|
if head := chain.CurrentFastBlock(); head.NumberU64() != uint64(4) {
|
|
|
|
t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), uint64(4))
|
|
|
|
}
|
|
|
|
if head := chain.CurrentBlock(); head.NumberU64() != uint64(1) {
|
|
|
|
t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), uint64(1))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reinsert B2-B4
|
|
|
|
if _, err := chain.InsertChain(blocks[1:]); err != nil {
|
|
|
|
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
|
|
|
}
|
|
|
|
if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
|
|
|
|
t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
|
|
|
|
}
|
|
|
|
if head := chain.CurrentFastBlock(); head.NumberU64() != uint64(4) {
|
|
|
|
t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), uint64(4))
|
|
|
|
}
|
|
|
|
if head := chain.CurrentBlock(); head.NumberU64() != uint64(4) {
|
|
|
|
t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), uint64(4))
|
|
|
|
}
|
|
|
|
if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
|
|
|
|
t.Error("Failed to regenerate the snapshot of known state")
|
|
|
|
}
|
|
|
|
}
|