mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
RFC35/Common Ancestor: Modifying the forkchoice rule (#425)
* initial * update * fix: add validator to NewBlockchain function calls * handle past chain reorg * fix: only check with last checkpoint * rm logs * add: handle future chain import case * fix: handle single block case * add unit tests for past and future chain * modularise forker tests * minor fixes * add: overlapping chain test case, minor fixes * minor fixes * add: isolated unit test for IsValidChain * add more test case for IsValidChain * fix: use index for header time * add: fetch last N checkpoints in first run * fix: change checkpoint count to int64 * fix: handle edge case * fix: handle no checkpoint case separately * fix: consider offset for future chain calculation * re-write test case for split chain * fix: typo * add: split chain properties test * separate reorg checks, validate chain before inserting * fix: handle err incase of invalid chain * fix: use error from whitelist service * split chain property tests * remove duplicate test cases * cleanup * clean up * fix linters * fix: fetch checkpoint count bug, add tests * fix more linters * fix: handle nil chain validator in downloader * fix: mock bor tests Co-authored-by: Evgeny Danienko <6655321@bk.ru>
This commit is contained in:
parent
3df1d6f017
commit
c44693706a
58 changed files with 1098 additions and 224 deletions
3
Makefile
3
Makefile
|
|
@ -32,6 +32,9 @@ bor:
|
|||
protoc:
|
||||
protoc --go_out=. --go-grpc_out=. ./internal/cli/server/proto/*.proto
|
||||
|
||||
generate-mocks:
|
||||
go generate mockgen -destination=./tests/bor/mocks/IHeimdallClient.go -package=mocks ./consensus/bor IHeimdallClient
|
||||
|
||||
geth:
|
||||
$(GORUN) build/ci.go install ./cmd/geth
|
||||
@echo "Done building."
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ type SimulatedBackend struct {
|
|||
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
||||
genesis.MustCommit(database)
|
||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
|
||||
backend := &SimulatedBackend{
|
||||
database: database,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ import (
|
|||
"text/template"
|
||||
"time"
|
||||
|
||||
pcsclite "github.com/gballet/go-libpcsclite"
|
||||
gopsutil "github.com/shirou/gopsutil/mem"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -68,9 +72,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
pcsclite "github.com/gballet/go-libpcsclite"
|
||||
gopsutil "github.com/shirou/gopsutil/mem"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -2072,7 +2073,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
|
|||
|
||||
// TODO(rjl493456442) disable snapshot generation/wiping if the chain is read only.
|
||||
// Disable transaction indexing/unindexing by default.
|
||||
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil)
|
||||
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil, nil)
|
||||
if err != nil {
|
||||
Fatalf("Can't create BlockChain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package bor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
|
|
@ -8,6 +9,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall" //nolint:typecheck
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
|
|
@ -58,7 +60,7 @@ func TestGenesisContractChange(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
config := params.ChainConfig{}
|
||||
chain, err := core.NewBlockChain(db, nil, &config, b, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(db, nil, &config, b, vm.Config{}, nil, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
addBlock := func(root common.Hash, num int64) (common.Hash, *state.StateDB) {
|
||||
|
|
@ -140,3 +142,48 @@ func TestEncodeSigHeaderJaipur(t *testing.T) {
|
|||
hash = SealHash(h, ¶ms.BorConfig{JaipurBlock: 10})
|
||||
require.Equal(t, hash, hashWithoutBaseFee)
|
||||
}
|
||||
|
||||
// TestCheckpoint can be used for to fetch checkpoint
|
||||
// count and checkpoint for debugging purpose.
|
||||
// Also, this is kept only for local use.
|
||||
func TestCheckpoint(t *testing.T) {
|
||||
t.Skip()
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// TODO: For testing, add heimdall url here
|
||||
h := heimdall.NewHeimdallClient("http://localhost:1317")
|
||||
|
||||
count, err := h.FetchCheckpointCount(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Log("Count:", count)
|
||||
|
||||
checkpoint1, err := h.FetchCheckpoint(ctx, count)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Log("Checkpoint1:", checkpoint1)
|
||||
|
||||
checkpoint2, err := h.FetchCheckpoint(ctx, 10000)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Log("Checkpoint2:", checkpoint2)
|
||||
|
||||
checkpoint3, err := h.FetchCheckpoint(ctx, -1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Log("Checkpoint3:", checkpoint3)
|
||||
|
||||
if checkpoint3.RootHash != checkpoint1.RootHash {
|
||||
t.Fatal("Invalid root hash")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
type IHeimdallClient interface {
|
||||
StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error)
|
||||
Span(ctx context.Context, spanID uint64) (*span.HeimdallSpan, error)
|
||||
FetchLatestCheckpoint(ctx context.Context) (*checkpoint.Checkpoint, error)
|
||||
FetchCheckpoint(ctx context.Context, number int64) (*checkpoint.Checkpoint, error)
|
||||
FetchCheckpointCount(ctx context.Context) (int64, error)
|
||||
Close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,3 +20,12 @@ type CheckpointResponse struct {
|
|||
Height string `json:"height"`
|
||||
Result Checkpoint `json:"result"`
|
||||
}
|
||||
|
||||
type CheckpointCount struct {
|
||||
Result int64 `json:"result"`
|
||||
}
|
||||
|
||||
type CheckpointCountResponse struct {
|
||||
Height string `json:"height"`
|
||||
Result CheckpointCount `json:"result"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ func NewHeimdallClient(urlString string) *HeimdallClient {
|
|||
const (
|
||||
fetchStateSyncEventsFormat = "from-id=%d&to-time=%d&limit=%d"
|
||||
fetchStateSyncEventsPath = "clerk/event-record/list"
|
||||
fetchLatestCheckpoint = "/checkpoints/latest"
|
||||
fetchCheckpoint = "/checkpoints/%s"
|
||||
fetchCheckpointCount = "/checkpoints/count"
|
||||
|
||||
fetchSpanFormat = "bor/span/%d"
|
||||
)
|
||||
|
|
@ -115,9 +116,9 @@ func (h *HeimdallClient) Span(ctx context.Context, spanID uint64) (*span.Heimdal
|
|||
return &response.Result, nil
|
||||
}
|
||||
|
||||
// FetchLatestCheckpoint fetches the latest bor submitted checkpoint from heimdall
|
||||
func (h *HeimdallClient) FetchLatestCheckpoint(ctx context.Context) (*checkpoint.Checkpoint, error) {
|
||||
url, err := latestCheckpointURL(h.urlString)
|
||||
// FetchCheckpoint fetches the checkpoint from heimdall
|
||||
func (h *HeimdallClient) FetchCheckpoint(ctx context.Context, number int64) (*checkpoint.Checkpoint, error) {
|
||||
url, err := checkpointURL(h.urlString, number)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -130,6 +131,21 @@ func (h *HeimdallClient) FetchLatestCheckpoint(ctx context.Context) (*checkpoint
|
|||
return &response.Result, nil
|
||||
}
|
||||
|
||||
// FetchCheckpointCount fetches the checkpoint count from heimdall
|
||||
func (h *HeimdallClient) FetchCheckpointCount(ctx context.Context) (int64, error) {
|
||||
url, err := checkpointCountURL(h.urlString)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
response, err := FetchWithRetry[checkpoint.CheckpointCountResponse](ctx, h.client, url, h.closeCh)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return response.Result.Result, nil
|
||||
}
|
||||
|
||||
// FetchWithRetry returns data from heimdall with retry
|
||||
func FetchWithRetry[T any](ctx context.Context, client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) {
|
||||
// request data once
|
||||
|
|
@ -210,8 +226,19 @@ func stateSyncURL(urlString string, fromID uint64, to int64) (*url.URL, error) {
|
|||
return makeURL(urlString, fetchStateSyncEventsPath, queryParams)
|
||||
}
|
||||
|
||||
func latestCheckpointURL(urlString string) (*url.URL, error) {
|
||||
return makeURL(urlString, fetchLatestCheckpoint, "")
|
||||
func checkpointURL(urlString string, number int64) (*url.URL, error) {
|
||||
url := ""
|
||||
if number == -1 {
|
||||
url = fmt.Sprintf(fetchCheckpoint, "latest")
|
||||
} else {
|
||||
url = fmt.Sprintf(fetchCheckpoint, fmt.Sprint(number))
|
||||
}
|
||||
|
||||
return makeURL(urlString, url, "")
|
||||
}
|
||||
|
||||
func checkpointCountURL(urlString string) (*url.URL, error) {
|
||||
return makeURL(urlString, fetchCheckpointCount, "")
|
||||
}
|
||||
|
||||
func makeURL(urlString, rawPath, rawQuery string) (*url.URL, error) {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ func TestReimportMirroredState(t *testing.T) {
|
|||
genesis := genspec.MustCommit(db)
|
||||
|
||||
// Generate a batch of blocks, each properly signed
|
||||
chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
blocks, _ := core.GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db, 3, func(i int, block *core.BlockGen) {
|
||||
|
|
@ -89,7 +89,7 @@ func TestReimportMirroredState(t *testing.T) {
|
|||
db = rawdb.NewMemoryDatabase()
|
||||
genspec.MustCommit(db)
|
||||
|
||||
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
if _, err := chain.InsertChain(blocks[:2]); err != nil {
|
||||
|
|
@ -102,7 +102,7 @@ func TestReimportMirroredState(t *testing.T) {
|
|||
// Simulate a crash by creating a new chain on top of the database, without
|
||||
// flushing the dirty states out. Insert the last block, triggering a sidechain
|
||||
// reimport.
|
||||
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
if _, err := chain.InsertChain(blocks[2:]); err != nil {
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ func TestClique(t *testing.T) {
|
|||
batches[len(batches)-1] = append(batches[len(batches)-1], block)
|
||||
}
|
||||
// Pass all the headers through clique and ensure tallying succeeds
|
||||
chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Errorf("test %d: failed to create test chain: %v", i, err)
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
|||
|
||||
// Time the insertion of the new chain.
|
||||
// State and blocks are stored in the same DB.
|
||||
chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer chainman.Stop()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
|
@ -317,7 +317,8 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
|||
if err != nil {
|
||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||
}
|
||||
chain, err := NewBlockChain(db, &cacheConfig, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
|
||||
chain, err := NewBlockChain(db, &cacheConfig, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("error creating chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func TestHeaderVerification(t *testing.T) {
|
|||
headers[i] = block.Header()
|
||||
}
|
||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
|
|
@ -168,7 +168,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
|||
t.Logf("Log header after the merging %d: %v", block.NumberU64(), string(blob))
|
||||
}
|
||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||
chain, _ := NewBlockChain(testdb, nil, chainConfig, runEngine, vm.Config{}, nil, nil)
|
||||
chain, _ := NewBlockChain(testdb, nil, chainConfig, runEngine, vm.Config{}, nil, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
// Verify the blocks before the merging
|
||||
|
|
@ -279,11 +279,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
|||
var results <-chan error
|
||||
|
||||
if valid {
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
chain.Stop()
|
||||
} else {
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil)
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil, nil)
|
||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
chain.Stop()
|
||||
}
|
||||
|
|
@ -346,7 +346,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
|||
defer runtime.GOMAXPROCS(old)
|
||||
|
||||
// Start the verifications and immediately abort
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil)
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
"github.com/ethereum/go-ethereum/common/prque"
|
||||
|
|
@ -38,6 +39,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader/whitelist"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/internal/syncx"
|
||||
|
|
@ -221,7 +223,8 @@ type BlockChain struct {
|
|||
// NewBlockChain returns a fully initialised block chain using information
|
||||
// available in the database. It initialises the default Ethereum Validator
|
||||
// and Processor.
|
||||
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(header *types.Header) bool, txLookupLimit *uint64) (*BlockChain, error) {
|
||||
//nolint:gocognit
|
||||
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(header *types.Header) bool, txLookupLimit *uint64, checker ethereum.ChainValidator) (*BlockChain, error) {
|
||||
if cacheConfig == nil {
|
||||
cacheConfig = DefaultCacheConfig
|
||||
}
|
||||
|
|
@ -257,7 +260,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||
|
||||
borReceiptsCache: borReceiptsCache,
|
||||
}
|
||||
bc.forker = NewForkChoice(bc, shouldPreserve)
|
||||
bc.forker = NewForkChoice(bc, shouldPreserve, checker)
|
||||
bc.validator = NewBlockValidator(chainConfig, bc, engine)
|
||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
|
||||
bc.processor = NewStateProcessor(chainConfig, bc, engine)
|
||||
|
|
@ -908,6 +911,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
ancientBlocks, liveBlocks types.Blocks
|
||||
ancientReceipts, liveReceipts []types.Receipts
|
||||
)
|
||||
|
||||
// Do a sanity check that the provided chain is actually ordered and linked
|
||||
for i := 0; i < len(blockChain); i++ {
|
||||
if i != 0 {
|
||||
|
|
@ -933,7 +937,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
|
||||
// updateHead updates the head fast sync block if the inserted blocks are better
|
||||
// and returns an indicator whether the inserted blocks are canonical.
|
||||
updateHead := func(head *types.Block) bool {
|
||||
updateHead := func(head *types.Block, headers []*types.Header) bool {
|
||||
if !bc.chainmu.TryLock() {
|
||||
return false
|
||||
}
|
||||
|
|
@ -948,6 +952,14 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
} else if !reorg {
|
||||
return false
|
||||
}
|
||||
|
||||
isValid, err := bc.forker.ValidateReorg(bc.CurrentFastBlock().Header(), headers)
|
||||
if err != nil {
|
||||
log.Warn("Reorg failed", "err", err)
|
||||
return false
|
||||
} else if !isValid {
|
||||
return false
|
||||
}
|
||||
rawdb.WriteHeadFastBlockHash(bc.db, head.Hash())
|
||||
bc.currentFastBlock.Store(head)
|
||||
headFastBlockGauge.Update(int64(head.NumberU64()))
|
||||
|
|
@ -985,10 +997,14 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
return 0, fmt.Errorf("containing header #%d [%x..] unknown", last.Number(), last.Hash().Bytes()[:4])
|
||||
}
|
||||
|
||||
// BOR: Retrieve all the bor receipts.
|
||||
// BOR: Retrieve all the bor receipts and also maintain the array of headers
|
||||
// for bor specific reorg check.
|
||||
borReceipts := []types.Receipts{}
|
||||
|
||||
var headers []*types.Header
|
||||
for _, block := range blockChain {
|
||||
borReceipts = append(borReceipts, []*types.Receipt{bc.GetBorReceiptByHash(block.Hash())})
|
||||
headers = append(headers, block.Header())
|
||||
}
|
||||
|
||||
// Write all chain data to ancients.
|
||||
|
|
@ -1039,7 +1055,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
}
|
||||
// Update the current fast block because all block data is now present in DB.
|
||||
previousFastBlock := bc.CurrentFastBlock().NumberU64()
|
||||
if !updateHead(blockChain[len(blockChain)-1]) {
|
||||
if !updateHead(blockChain[len(blockChain)-1], headers) {
|
||||
// We end up here if the header chain has reorg'ed, and the blocks/receipts
|
||||
// don't match the canonical chain.
|
||||
if err := bc.db.TruncateHead(previousFastBlock + 1); err != nil {
|
||||
|
|
@ -1075,7 +1091,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
||||
skipPresenceCheck := false
|
||||
batch := bc.db.NewBatch()
|
||||
headers := make([]*types.Header, 0, len(blockChain))
|
||||
for i, block := range blockChain {
|
||||
// Update the headers for bor specific reorg check
|
||||
headers = append(headers, block.Header())
|
||||
|
||||
// Short circuit insertion if shutting down or processing failed
|
||||
if bc.insertStopped() {
|
||||
return 0, errInsertionInterrupted
|
||||
|
|
@ -1122,7 +1142,8 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
return 0, err
|
||||
}
|
||||
}
|
||||
updateHead(blockChain[len(blockChain)-1])
|
||||
|
||||
updateHead(blockChain[len(blockChain)-1], headers)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
|
|
@ -1491,6 +1512,18 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
|
|||
it := newInsertIterator(chain, results, bc.validator)
|
||||
block, err := it.next()
|
||||
|
||||
// Check the validity of incoming chain
|
||||
isValid, err1 := bc.forker.ValidateReorg(bc.CurrentBlock().Header(), headers)
|
||||
if err1 != nil {
|
||||
return it.index, err1
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
// The chain to be imported is invalid as the blocks doesn't match with
|
||||
// the whitelisted checkpoints.
|
||||
return it.index, whitelist.ErrCheckpointMismatch
|
||||
}
|
||||
|
||||
// Left-trim all the known blocks that don't need to build snapshot
|
||||
if bc.skipBlock(err, it) {
|
||||
// First block (and state) is known
|
||||
|
|
@ -1833,6 +1866,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
|||
externTd *big.Int
|
||||
lastBlock = block
|
||||
current = bc.CurrentBlock()
|
||||
headers []*types.Header
|
||||
)
|
||||
// The first sidechain block error is already verified to be ErrPrunedAncestor.
|
||||
// Since we don't import them here, we expect ErrUnknownAncestor for the remaining
|
||||
|
|
@ -1840,6 +1874,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
|||
// to disk.
|
||||
err := consensus.ErrPrunedAncestor
|
||||
for ; block != nil && errors.Is(err, consensus.ErrPrunedAncestor); block, err = it.next() {
|
||||
headers = append(headers, block.Header())
|
||||
// Check the canonical state root for that number
|
||||
if number := block.NumberU64(); current.NumberU64() >= number {
|
||||
canonical := bc.GetBlockByNumber(number)
|
||||
|
|
@ -1895,7 +1930,13 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
|||
if err != nil {
|
||||
return it.index, err
|
||||
}
|
||||
if !reorg {
|
||||
|
||||
isValid, err := bc.forker.ValidateReorg(current.Header(), headers)
|
||||
if err != nil {
|
||||
return it.index, err
|
||||
}
|
||||
|
||||
if !reorg || !isValid {
|
||||
localTd := bc.GetTd(current.Hash(), current.NumberU64())
|
||||
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
|
||||
return it.index, err
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func TestChain2HeadEvent(t *testing.T) {
|
|||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
chain2HeadCh := make(chan Chain2HeadEvent, 64)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *B
|
|||
)
|
||||
|
||||
// Initialize a fresh chain with only a genesis block
|
||||
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||
// Create and inject the requested chain
|
||||
if n == 0 {
|
||||
return db, blockchain, nil
|
||||
|
|
@ -655,7 +655,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
|
|||
blockchain.Stop()
|
||||
|
||||
// Create a new BlockChain and check that it rolled back the state.
|
||||
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new chain manager: %v", err)
|
||||
}
|
||||
|
|
@ -768,7 +768,7 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
// Import the chain as an archive node for the comparison baseline
|
||||
archiveDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(archiveDb)
|
||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer archive.Stop()
|
||||
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
|
|
@ -777,7 +777,7 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
// Fast import the chain as a non-archive node to test
|
||||
fastDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(fastDb)
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer fast.Stop()
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
|
|
@ -801,7 +801,7 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
gspec.MustCommit(ancientDb)
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer ancient.Stop()
|
||||
|
||||
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
|
||||
|
|
@ -923,7 +923,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
archiveCaching := *DefaultCacheConfig
|
||||
archiveCaching.TrieDirtyDisabled = true
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to process block %d: %v", n, err)
|
||||
}
|
||||
|
|
@ -936,7 +936,8 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||
fastDb, delfn := makeDb()
|
||||
defer delfn()
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer fast.Stop()
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
|
|
@ -956,7 +957,8 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
// Import the chain as a ancient-first node and ensure all pointers are updated
|
||||
ancientDb, delfn := makeDb()
|
||||
defer delfn()
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer ancient.Stop()
|
||||
|
||||
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
|
||||
|
|
@ -975,7 +977,8 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
// Import the chain as a light node and ensure all pointers are updated
|
||||
lightDb, delfn := makeDb()
|
||||
defer delfn()
|
||||
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
|
||||
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
||||
}
|
||||
|
|
@ -1044,7 +1047,7 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
}
|
||||
})
|
||||
// Import the chain. This runs all block validation rules.
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
||||
}
|
||||
|
|
@ -1114,7 +1117,7 @@ func TestLogReorgs(t *testing.T) {
|
|||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
rmLogsCh := make(chan RemovedLogsEvent)
|
||||
|
|
@ -1167,7 +1170,7 @@ func TestLogRebirth(t *testing.T) {
|
|||
genesis = gspec.MustCommit(db)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
engine = ethash.NewFaker()
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
|
||||
)
|
||||
|
||||
defer blockchain.Stop()
|
||||
|
|
@ -1230,7 +1233,7 @@ func TestSideLogRebirth(t *testing.T) {
|
|||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
)
|
||||
|
||||
defer blockchain.Stop()
|
||||
|
|
@ -1305,7 +1308,7 @@ func TestReorgSideEvent(t *testing.T) {
|
|||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
|
||||
|
|
@ -1437,7 +1440,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
genesis = gspec.MustCommit(db)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
|
||||
|
|
@ -1545,7 +1548,8 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
|||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
|
||||
|
|
@ -1620,7 +1624,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -1664,7 +1668,7 @@ func TestTrieForkGC(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -1703,7 +1707,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -1764,7 +1768,7 @@ func TestBlockchainRecovery(t *testing.T) {
|
|||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
gspec.MustCommit(ancientDb)
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
|
|
@ -1784,7 +1788,7 @@ func TestBlockchainRecovery(t *testing.T) {
|
|||
rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())
|
||||
|
||||
// Reopen broken blockchain again
|
||||
ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer ancient.Stop()
|
||||
if num := ancient.CurrentBlock().NumberU64(); num != 0 {
|
||||
t.Errorf("head block mismatch: have #%v, want #%v", num, 0)
|
||||
|
|
@ -1836,7 +1840,7 @@ func TestInsertReceiptChainRollback(t *testing.T) {
|
|||
}
|
||||
gspec := Genesis{Config: params.AllEthashProtocolChanges}
|
||||
gspec.MustCommit(ancientDb)
|
||||
ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer ancientChain.Stop()
|
||||
|
||||
// Import the canonical header chain.
|
||||
|
|
@ -1897,7 +1901,7 @@ func TestLowDiffLongChain(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -1963,7 +1967,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
|||
// Generate and import the canonical chain
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
chain, err := NewBlockChain(diskdb, nil, &chainConfig, runEngine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, &chainConfig, runEngine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2102,7 +2106,7 @@ func testInsertKnownChainData(t *testing.T, typ string) {
|
|||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2266,7 +2270,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
|||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
chain, err := NewBlockChain(chaindb, nil, &chainConfig, runEngine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(chaindb, nil, &chainConfig, runEngine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2384,7 +2388,7 @@ func getLongAndShortChains() (bc *BlockChain, longChain []*types.Block, heavyCha
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2577,7 +2581,7 @@ func TestTransactionIndices(t *testing.T) {
|
|||
|
||||
// Import all blocks into ancient db
|
||||
l := uint64(0)
|
||||
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
||||
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2602,7 +2606,7 @@ func TestTransactionIndices(t *testing.T) {
|
|||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
gspec.MustCommit(ancientDb)
|
||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2626,7 +2630,7 @@ func TestTransactionIndices(t *testing.T) {
|
|||
limit = []uint64{0, 64 /* drop stale */, 32 /* shorten history */, 64 /* extend history */, 0 /* restore all */}
|
||||
tails := []uint64{0, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */, 69 /* 132 - 64 + 1 */, 0}
|
||||
for i, l := range limit {
|
||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2704,7 +2708,7 @@ func TestSkipStaleTxIndicesInSnapSync(t *testing.T) {
|
|||
|
||||
// Import all blocks into ancient db, only HEAD-32 indices are kept.
|
||||
l := uint64(32)
|
||||
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
||||
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2768,7 +2772,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2851,7 +2855,7 @@ func TestSideImportPrunedBlocks(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2945,7 +2949,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -3059,7 +3063,7 @@ func TestDeleteRecreateSlots(t *testing.T) {
|
|||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
||||
}, nil, nil)
|
||||
}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -3139,7 +3143,7 @@ func TestDeleteRecreateAccount(t *testing.T) {
|
|||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
||||
}, nil, nil)
|
||||
}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -3312,7 +3316,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
|
|||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
||||
//Debug: true,
|
||||
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
||||
}, nil, nil)
|
||||
}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -3446,7 +3450,7 @@ func TestInitThenFailCreateContract(t *testing.T) {
|
|||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
||||
//Debug: true,
|
||||
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
||||
}, nil, nil)
|
||||
}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -3533,7 +3537,7 @@ func TestEIP2718Transition(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -3628,7 +3632,7 @@ func TestEIP1559Transition(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ func ExampleGenerateChain() {
|
|||
})
|
||||
|
||||
// Import the chain. This runs all block validation rules.
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
proConf.DAOForkBlock = forkBlock
|
||||
proConf.DAOForkSupport = true
|
||||
|
||||
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer proBc.Stop()
|
||||
|
||||
conDb := rawdb.NewMemoryDatabase()
|
||||
|
|
@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
conConf.DAOForkBlock = forkBlock
|
||||
conConf.DAOForkSupport = false
|
||||
|
||||
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer conBc.Stop()
|
||||
|
||||
if _, err := proBc.InsertChain(prefix); err != nil {
|
||||
|
|
@ -69,7 +69,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
// Create a pro-fork block, and try to feed into the no-fork chain
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
|
||||
|
|
@ -94,7 +94,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
// Create a no-fork block, and try to feed into the pro-fork chain
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
|
||||
|
|
@ -120,7 +120,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
|
||||
|
|
@ -140,7 +140,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"math/big"
|
||||
mrand "math/rand"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -54,18 +55,21 @@ type ForkChoice struct {
|
|||
// local td is equal to the extern one. It can be nil for light
|
||||
// client
|
||||
preserve func(header *types.Header) bool
|
||||
|
||||
validator ethereum.ChainValidator
|
||||
}
|
||||
|
||||
func NewForkChoice(chainReader ChainReader, preserve func(header *types.Header) bool) *ForkChoice {
|
||||
func NewForkChoice(chainReader ChainReader, preserve func(header *types.Header) bool, validator ethereum.ChainValidator) *ForkChoice {
|
||||
// Seed a fast but crypto originating random generator
|
||||
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
|
||||
if err != nil {
|
||||
log.Crit("Failed to initialize random seed", "err", err)
|
||||
}
|
||||
return &ForkChoice{
|
||||
chain: chainReader,
|
||||
rand: mrand.New(mrand.NewSource(seed.Int64())),
|
||||
preserve: preserve,
|
||||
chain: chainReader,
|
||||
rand: mrand.New(mrand.NewSource(seed.Int64())), //nolint:gosec
|
||||
preserve: preserve,
|
||||
validator: validator,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,3 +110,15 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, header *types.Header) (b
|
|||
}
|
||||
return reorg, nil
|
||||
}
|
||||
|
||||
// ValidateReorg calls the chain validator service to check if the reorg is valid or not
|
||||
func (f *ForkChoice) ValidateReorg(current *types.Header, chain []*types.Header) (bool, error) {
|
||||
// Call the bor chain validator service
|
||||
if f.validator != nil {
|
||||
if isValid := f.validator.IsValidChain(current, chain); !isValid {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
|||
240
core/forkchoice_test.go
Normal file
240
core/forkchoice_test.go
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"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/params"
|
||||
)
|
||||
|
||||
// chainValidatorFake is a mock for the chain validator service
|
||||
type chainValidatorFake struct {
|
||||
validate func(currentHeader *types.Header, chain []*types.Header) bool
|
||||
}
|
||||
|
||||
// chainReaderFake is a mock for the chain reader service
|
||||
type chainReaderFake struct {
|
||||
getTd func(hash common.Hash, number uint64) *big.Int
|
||||
}
|
||||
|
||||
func newChainValidatorFake(validate func(currentHeader *types.Header, chain []*types.Header) bool) *chainValidatorFake {
|
||||
return &chainValidatorFake{validate: validate}
|
||||
}
|
||||
|
||||
func newChainReaderFake(getTd func(hash common.Hash, number uint64) *big.Int) *chainReaderFake {
|
||||
return &chainReaderFake{getTd: getTd}
|
||||
}
|
||||
|
||||
func TestPastChainInsert(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||
)
|
||||
|
||||
hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create mocks for forker
|
||||
getTd := func(hash common.Hash, number uint64) *big.Int {
|
||||
return big.NewInt(int64(number))
|
||||
}
|
||||
validate := func(currentHeader *types.Header, chain []*types.Header) bool {
|
||||
// Put all explicit conditions here
|
||||
// If canonical chain is empty and we're importing a chain of 64 blocks
|
||||
if currentHeader.Number.Uint64() == uint64(0) && len(chain) == 64 {
|
||||
return true
|
||||
}
|
||||
// If canonical chain is of len 64 and we're importing a past chain from 54-64, then accept it
|
||||
if currentHeader.Number.Uint64() == uint64(64) && chain[0].Number.Uint64() == 55 && len(chain) == 10 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
mockChainReader := newChainReaderFake(getTd)
|
||||
mockChainValidator := newChainValidatorFake(validate)
|
||||
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||
|
||||
// chain A: G->A1->A2...A64
|
||||
chainA := makeHeaderChain(genesis.Header(), 64, ethash.NewFaker(), db, 10)
|
||||
|
||||
// Inserting 64 headers on an empty chain
|
||||
// expecting 1 write status with no error
|
||||
testInsert(t, hc, chainA, CanonStatTy, nil, mockForker)
|
||||
|
||||
// The current chain is: G->A1->A2...A64
|
||||
// chain B: G->A1->A2...A44->B45->B46...B64
|
||||
chainB := makeHeaderChain(chainA[43], 20, ethash.NewFaker(), db, 10)
|
||||
|
||||
// The current chain is: G->A1->A2...A64
|
||||
// chain C: G->A1->A2...A54->C55->C56...C64
|
||||
chainC := makeHeaderChain(chainA[53], 10, ethash.NewFaker(), db, 10)
|
||||
|
||||
// Update the function to consider chainC with higher difficulty
|
||||
getTd = func(hash common.Hash, number uint64) *big.Int {
|
||||
td := big.NewInt(int64(number))
|
||||
if hash == chainB[len(chainB)-1].Hash() || hash == chainC[len(chainC)-1].Hash() {
|
||||
td = big.NewInt(65)
|
||||
}
|
||||
|
||||
return td
|
||||
}
|
||||
mockChainReader = newChainReaderFake(getTd)
|
||||
mockForker = NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||
|
||||
// Inserting 20 blocks from chainC on canonical chain
|
||||
// expecting 2 write status with no error
|
||||
testInsert(t, hc, chainB, SideStatTy, nil, mockForker)
|
||||
|
||||
// Inserting 10 blocks from chainB on canonical chain
|
||||
// expecting 1 write status with no error
|
||||
testInsert(t, hc, chainC, CanonStatTy, nil, mockForker)
|
||||
}
|
||||
|
||||
func TestFutureChainInsert(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||
)
|
||||
|
||||
hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create mocks for forker
|
||||
getTd := func(hash common.Hash, number uint64) *big.Int {
|
||||
return big.NewInt(int64(number))
|
||||
}
|
||||
validate := func(currentHeader *types.Header, chain []*types.Header) bool {
|
||||
// Put all explicit conditions here
|
||||
// If canonical chain is empty and we're importing a chain of 64 blocks
|
||||
if currentHeader.Number.Uint64() == uint64(0) && len(chain) == 64 {
|
||||
return true
|
||||
}
|
||||
// If length of future chains > some value, they should not be accepted
|
||||
if currentHeader.Number.Uint64() == uint64(64) && len(chain) <= 10 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
mockChainReader := newChainReaderFake(getTd)
|
||||
mockChainValidator := newChainValidatorFake(validate)
|
||||
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||
|
||||
// chain A: G->A1->A2...A64
|
||||
chainA := makeHeaderChain(genesis.Header(), 64, ethash.NewFaker(), db, 10)
|
||||
|
||||
// Inserting 64 headers on an empty chain
|
||||
// expecting 1 write status with no error
|
||||
testInsert(t, hc, chainA, CanonStatTy, nil, mockForker)
|
||||
|
||||
// The current chain is: G->A1->A2...A64
|
||||
// chain B: G->A1->A2...A64->B65->B66...B84
|
||||
chainB := makeHeaderChain(chainA[63], 20, ethash.NewFaker(), db, 10)
|
||||
|
||||
// Inserting 20 headers on the canonical chain
|
||||
// expecting 0 write status with no error
|
||||
testInsert(t, hc, chainB, SideStatTy, nil, mockForker)
|
||||
|
||||
// The current chain is: G->A1->A2...A64
|
||||
// chain C: G->A1->A2...A64->C65->C66...C74
|
||||
chainC := makeHeaderChain(chainA[63], 10, ethash.NewFaker(), db, 10)
|
||||
|
||||
// Inserting 10 headers on the canonical chain
|
||||
// expecting 0 write status with no error
|
||||
testInsert(t, hc, chainC, CanonStatTy, nil, mockForker)
|
||||
}
|
||||
|
||||
func TestOverlappingChainInsert(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||
)
|
||||
|
||||
hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create mocks for forker
|
||||
getTd := func(hash common.Hash, number uint64) *big.Int {
|
||||
return big.NewInt(int64(number))
|
||||
}
|
||||
validate := func(currentHeader *types.Header, chain []*types.Header) bool {
|
||||
// Put all explicit conditions here
|
||||
// If canonical chain is empty and we're importing a chain of 64 blocks
|
||||
if currentHeader.Number.Uint64() == uint64(0) && len(chain) == 64 {
|
||||
return true
|
||||
}
|
||||
// If length of chain is > some fixed value then don't accept it
|
||||
if currentHeader.Number.Uint64() == uint64(64) && len(chain) <= 20 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
mockChainReader := newChainReaderFake(getTd)
|
||||
mockChainValidator := newChainValidatorFake(validate)
|
||||
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||
|
||||
// chain A: G->A1->A2...A64
|
||||
chainA := makeHeaderChain(genesis.Header(), 64, ethash.NewFaker(), db, 10)
|
||||
|
||||
// Inserting 64 headers on an empty chain
|
||||
// expecting 1 write status with no error
|
||||
testInsert(t, hc, chainA, CanonStatTy, nil, mockForker)
|
||||
|
||||
// The current chain is: G->A1->A2...A64
|
||||
// chain B: G->A1->A2...A54->B55->B56...B84
|
||||
chainB := makeHeaderChain(chainA[53], 30, ethash.NewFaker(), db, 10)
|
||||
|
||||
// Inserting 20 blocks on canonical chain
|
||||
// expecting 2 write status with no error
|
||||
testInsert(t, hc, chainB, SideStatTy, nil, mockForker)
|
||||
|
||||
// The current chain is: G->A1->A2...A64
|
||||
// chain C: G->A1->A2...A54->C55->C56...C74
|
||||
chainC := makeHeaderChain(chainA[53], 20, ethash.NewFaker(), db, 10)
|
||||
|
||||
// Inserting 10 blocks on canonical chain
|
||||
// expecting 1 write status with no error
|
||||
testInsert(t, hc, chainC, CanonStatTy, nil, mockForker)
|
||||
}
|
||||
|
||||
// Mock chain reader functions
|
||||
func (c *chainReaderFake) Config() *params.ChainConfig {
|
||||
return ¶ms.ChainConfig{TerminalTotalDifficulty: nil}
|
||||
}
|
||||
func (c *chainReaderFake) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||
return c.getTd(hash, number)
|
||||
}
|
||||
|
||||
// Mock chain validator functions
|
||||
func (w *chainValidatorFake) IsValidPeer(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
func (w *chainValidatorFake) IsValidChain(current *types.Header, headers []*types.Header) bool {
|
||||
return w.validate(current, headers)
|
||||
}
|
||||
func (w *chainValidatorFake) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {}
|
||||
func (w *chainValidatorFake) GetCheckpointWhitelist() map[uint64]common.Hash {
|
||||
return nil
|
||||
}
|
||||
func (w *chainValidatorFake) PurgeCheckpointWhitelist() {}
|
||||
func (w *chainValidatorFake) GetCheckpoints(current, sidechainHeader *types.Header, sidechainCheckpoints []*types.Header) (map[uint64]*types.Header, error) {
|
||||
return map[uint64]*types.Header{}, nil
|
||||
}
|
||||
|
|
@ -117,7 +117,7 @@ func TestSetupGenesis(t *testing.T) {
|
|||
// Advance to block #4, past the homestead transition block of customg.
|
||||
genesis := oldcustomg.MustCommit(db)
|
||||
|
||||
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)
|
||||
|
|
|
|||
|
|
@ -283,8 +283,10 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *F
|
|||
lastHeader: lastHeader,
|
||||
}
|
||||
)
|
||||
|
||||
// Ask the fork choicer if the reorg is necessary
|
||||
if reorg, err := forker.ReorgNeeded(hc.CurrentHeader(), lastHeader); err != nil {
|
||||
reorg, err := forker.ReorgNeeded(hc.CurrentHeader(), lastHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !reorg {
|
||||
if inserted != 0 {
|
||||
|
|
@ -292,6 +294,16 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *F
|
|||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
isValid, err := forker.ValidateReorg(hc.CurrentHeader(), headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !isValid {
|
||||
if inserted != 0 {
|
||||
result.status = SideStatTy
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
// Special case, all the inserted headers are already on the canonical
|
||||
// header chain, skip the reorg operation.
|
||||
if hc.GetCanonicalHash(lastHeader.Number.Uint64()) == lastHash && lastHeader.Number.Uint64() <= hc.CurrentHeader().Number.Uint64() {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ func TestHeaderInsertion(t *testing.T) {
|
|||
chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10)
|
||||
log.Root().SetHandler(log.StdoutHandler)
|
||||
|
||||
forker := NewForkChoice(hc, nil)
|
||||
forker := NewForkChoice(hc, nil, nil)
|
||||
// Inserting 64 headers on an empty chain, expecting
|
||||
// 1 callbacks, 1 canon-status, 0 sidestatus,
|
||||
testInsert(t, hc, chainA[:64], CanonStatTy, nil, forker)
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||
|
|
@ -235,7 +235,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
for i, tt := range []struct {
|
||||
|
|
@ -275,7 +275,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
for i, tt := range []struct {
|
||||
|
|
|
|||
|
|
@ -1891,7 +1891,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
|||
|
||||
defer db.Close()
|
||||
|
||||
newChain, err := core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
newChain, err := core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -1969,7 +1969,7 @@ func TestIssue23496(t *testing.T) {
|
|||
}
|
||||
)
|
||||
|
||||
chain, err := core.NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
|
|
@ -2024,7 +2024,7 @@ func TestIssue23496(t *testing.T) {
|
|||
|
||||
defer db.Close()
|
||||
|
||||
chain, err = core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, err = core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1987,7 +1987,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
|
|||
config.SnapshotWait = true
|
||||
}
|
||||
|
||||
chain, err := core.NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*core.BlockChain, []*type
|
|||
cacheConfig = core.DefaultCacheConfig
|
||||
)
|
||||
|
||||
chain, err := core.NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
|
|
@ -246,7 +246,7 @@ func (snaptest *snapshotTest) test(t *testing.T) {
|
|||
// Restart the chain normally
|
||||
chain.Stop()
|
||||
|
||||
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -283,13 +283,13 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
|||
// the crash, we do restart twice here: one after the crash and one
|
||||
// after the normal stop. It's used to ensure the broken snapshot
|
||||
// can be detected all the time.
|
||||
newchain, err := core.NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := core.NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
newchain.Stop()
|
||||
|
||||
newchain, err = core.NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err = core.NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -327,7 +327,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
|||
SnapshotLimit: 0,
|
||||
}
|
||||
|
||||
newchain, err := core.NewBlockChain(snaptest.db, cacheConfig, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := core.NewBlockChain(snaptest.db, cacheConfig, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -336,7 +336,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
|||
newchain.Stop()
|
||||
|
||||
// Restart the chain with enabling the snapshot
|
||||
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -365,7 +365,7 @@ func (snaptest *setHeadSnapshotTest) test(t *testing.T) {
|
|||
chain.SetHead(snaptest.setHead)
|
||||
chain.Stop()
|
||||
|
||||
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -396,7 +396,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
|
|||
// and state committed.
|
||||
chain.Stop()
|
||||
|
||||
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -414,7 +414,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
|
|||
// journal and latest state will be committed
|
||||
|
||||
// Restart the chain after the crash
|
||||
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -449,8 +449,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
|||
TrieTimeLimit: 5 * time.Minute,
|
||||
SnapshotLimit: 0,
|
||||
}
|
||||
|
||||
newchain, err := core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -467,14 +466,13 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
|||
SnapshotLimit: 256,
|
||||
SnapshotWait: false, // Don't wait rebuild
|
||||
}
|
||||
|
||||
_, err = core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
_, err = core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
// Simulate the blockchain crash.
|
||||
|
||||
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader/whitelist"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||
|
|
@ -219,7 +220,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||
Preimages: config.Preimages,
|
||||
}
|
||||
)
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit)
|
||||
|
||||
checker := whitelist.NewService(10)
|
||||
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, checker)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -260,6 +264,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||
Checkpoint: checkpoint,
|
||||
EthAPI: ethAPI,
|
||||
PeerRequiredBlocks: config.PeerRequiredBlocks,
|
||||
checker: checker,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -622,6 +627,13 @@ func (s *Ethereum) Start() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNotBorConsensus = errors.New("not bor consensus was given")
|
||||
ErrBorConsensusWithoutHeimdall = errors.New("bor consensus without heimdall")
|
||||
|
||||
whitelistTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
// StartCheckpointWhitelistService starts the goroutine to fetch checkpoints and update the
|
||||
// checkpoint whitelist map.
|
||||
func (s *Ethereum) startCheckpointWhitelistService() {
|
||||
|
|
@ -633,8 +645,11 @@ func (s *Ethereum) startCheckpointWhitelistService() {
|
|||
}
|
||||
|
||||
// first run the checkpoint whitelist
|
||||
// TODO: add context timeout if needed
|
||||
err := s.handleWhitelistCheckpoint(context.Background())
|
||||
firstCtx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
|
||||
err := s.handleWhitelistCheckpoint(firstCtx, true)
|
||||
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrBorConsensusWithoutHeimdall) || errors.Is(err, ErrNotBorConsensus) {
|
||||
return
|
||||
|
|
@ -649,8 +664,11 @@ func (s *Ethereum) startCheckpointWhitelistService() {
|
|||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// TODO: add context timeout if needed
|
||||
err = s.handleWhitelistCheckpoint(context.Background())
|
||||
ctx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
|
||||
err := s.handleWhitelistCheckpoint(ctx, false)
|
||||
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
log.Warn("unable to whitelist checkpoint", "err", err)
|
||||
}
|
||||
|
|
@ -660,13 +678,8 @@ func (s *Ethereum) startCheckpointWhitelistService() {
|
|||
}
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNotBorConsensus = errors.New("not bor consensus was given")
|
||||
ErrBorConsensusWithoutHeimdall = errors.New("bor consensus without heimdall")
|
||||
)
|
||||
|
||||
// handleWhitelistCheckpoint handles the checkpoint whitelist mechanism.
|
||||
func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context) error {
|
||||
func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context, first bool) error {
|
||||
ethHandler := (*ethHandler)(s.handler)
|
||||
|
||||
bor, ok := ethHandler.chain.Engine().(*bor.Bor)
|
||||
|
|
@ -678,13 +691,18 @@ func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context) error {
|
|||
return ErrBorConsensusWithoutHeimdall
|
||||
}
|
||||
|
||||
endBlockNum, endBlockHash, err := ethHandler.fetchWhitelistCheckpoint(ctx, bor)
|
||||
if err != nil {
|
||||
blockNums, blockHashes, err := ethHandler.fetchWhitelistCheckpoints(ctx, bor, first)
|
||||
// If the array is empty, we're bound to receive an error. Non-nill error and non-empty array
|
||||
// means that array has partial elements and it failed for some block. We'll add those partial
|
||||
// elements anyway.
|
||||
if len(blockNums) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the checkpoint whitelist map.
|
||||
ethHandler.downloader.ProcessCheckpoint(endBlockNum, endBlockHash)
|
||||
for i := 0; i < len(blockNums); i++ {
|
||||
ethHandler.downloader.ProcessCheckpoint(blockNums[i], blockHashes[i])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ type Downloader struct {
|
|||
quitCh chan struct{} // Quit channel to signal termination
|
||||
quitLock sync.Mutex // Lock to prevent double closes
|
||||
|
||||
ChainValidator
|
||||
ethereum.ChainValidator
|
||||
|
||||
// Testing hooks
|
||||
syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run
|
||||
|
|
@ -153,14 +153,6 @@ type Downloader struct {
|
|||
chainInsertHook func([]*fetchResult) // Method to call upon inserting a chain of blocks (possibly in multiple invocations)
|
||||
}
|
||||
|
||||
// interface for whitelist service
|
||||
type ChainValidator interface {
|
||||
IsValidChain(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error)
|
||||
ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash)
|
||||
GetCheckpointWhitelist() map[uint64]common.Hash
|
||||
PurgeCheckpointWhitelist()
|
||||
}
|
||||
|
||||
// LightChain encapsulates functions required to synchronise a light chain.
|
||||
type LightChain interface {
|
||||
// HasHeader verifies a header's presence in the local chain.
|
||||
|
|
@ -216,7 +208,7 @@ type BlockChain interface {
|
|||
|
||||
// New creates a new downloader to fetch hashes and blocks from remote peers.
|
||||
//nolint: staticcheck
|
||||
func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn, success func(), whitelistService ChainValidator) *Downloader {
|
||||
func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn, success func(), whitelistService ethereum.ChainValidator) *Downloader {
|
||||
if lightchain == nil {
|
||||
lightchain = chain
|
||||
}
|
||||
|
|
@ -799,9 +791,11 @@ func (d *Downloader) getFetchHeadersByNumber(p *peerConnection) func(number uint
|
|||
// In the rare scenario when we ended up on a long reorganisation (i.e. none of
|
||||
// the head links match), we do a binary search to find the common ancestor.
|
||||
func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) (uint64, error) {
|
||||
// Check the validity of chain to be downloaded
|
||||
if _, err := d.IsValidChain(remoteHeader, d.getFetchHeadersByNumber(p)); err != nil {
|
||||
return 0, err
|
||||
// Check the validity of peer from which the chain is to be downloaded
|
||||
if d.ChainValidator != nil {
|
||||
if _, err := d.IsValidPeer(remoteHeader, d.getFetchHeadersByNumber(p)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out the valid ancestor range to prevent rewrite attacks
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ func newTester() *downloadTester {
|
|||
|
||||
core.GenesisBlockForTesting(db, testAddress, big.NewInt(1000000000000000))
|
||||
|
||||
chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ func newTester() *downloadTester {
|
|||
return tester
|
||||
}
|
||||
|
||||
func (dl *downloadTester) setWhitelist(w ChainValidator) {
|
||||
func (dl *downloadTester) setWhitelist(w ethereum.ChainValidator) {
|
||||
dl.downloader.ChainValidator = w
|
||||
}
|
||||
|
||||
|
|
@ -1416,9 +1416,9 @@ func newWhitelistFake(validate func(count int) (bool, error)) *whitelistFake {
|
|||
return &whitelistFake{0, validate}
|
||||
}
|
||||
|
||||
// IsValidChain is the mock function which the downloader will use to validate the chain
|
||||
// IsValidPeer is the mock function which the downloader will use to validate the chain
|
||||
// to be received from a peer.
|
||||
func (w *whitelistFake) IsValidChain(_ *types.Header, _ func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
|
||||
func (w *whitelistFake) IsValidPeer(_ *types.Header, _ func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
|
||||
defer func() {
|
||||
w.count++
|
||||
}()
|
||||
|
|
@ -1426,13 +1426,18 @@ func (w *whitelistFake) IsValidChain(_ *types.Header, _ func(number uint64, amou
|
|||
return w.validate(w.count)
|
||||
}
|
||||
|
||||
func (w *whitelistFake) IsValidChain(current *types.Header, headers []*types.Header) bool {
|
||||
return true
|
||||
}
|
||||
func (w *whitelistFake) ProcessCheckpoint(_ uint64, _ common.Hash) {}
|
||||
|
||||
func (w *whitelistFake) GetCheckpointWhitelist() map[uint64]common.Hash {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *whitelistFake) PurgeCheckpointWhitelist() {}
|
||||
func (w *whitelistFake) GetCheckpoints(current, sidechainHeader *types.Header, sidechainCheckpoints []*types.Header) (map[uint64]*types.Header, error) {
|
||||
return map[uint64]*types.Header{}, nil
|
||||
}
|
||||
|
||||
// TestFakedSyncProgress66WhitelistMismatch tests if in case of whitelisted
|
||||
// checkpoint mismatch with opposite peer, the sync should fail.
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ func newTestBlockchain(blocks []*types.Block) *core.BlockChain {
|
|||
db := rawdb.NewMemoryDatabase()
|
||||
core.GenesisBlockForTesting(db, testAddress, big.NewInt(1000000000000000))
|
||||
|
||||
chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ type Service struct {
|
|||
m sync.Mutex
|
||||
checkpointWhitelist map[uint64]common.Hash // Checkpoint whitelist, populated by reaching out to heimdall
|
||||
checkpointOrder []uint64 // Checkpoint order, populated by reaching out to heimdall
|
||||
maxCapacity uint
|
||||
maxCapacity uint // Max capacity of the whitelist
|
||||
checkpointInterval uint64 // Checkpoint interval, until which we can allow importing
|
||||
}
|
||||
|
||||
func NewService(maxCapacity uint) *Service {
|
||||
|
|
@ -23,6 +24,7 @@ func NewService(maxCapacity uint) *Service {
|
|||
checkpointWhitelist: make(map[uint64]common.Hash),
|
||||
checkpointOrder: []uint64{},
|
||||
maxCapacity: maxCapacity,
|
||||
checkpointInterval: 256, // TODO: make it configurable through params?
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -31,9 +33,9 @@ var (
|
|||
ErrNoRemoteCheckoint = errors.New("remote peer doesn't have a checkoint")
|
||||
)
|
||||
|
||||
// IsValidChain checks if the chain we're about to receive from this peer is valid or not
|
||||
// IsValidPeer checks if the chain we're about to receive from a peer is valid or not
|
||||
// in terms of reorgs. We won't reorg beyond the last bor checkpoint submitted to mainchain.
|
||||
func (w *Service) IsValidChain(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
|
||||
func (w *Service) IsValidPeer(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
|
||||
// We want to validate the chain by comparing the last checkpointed block
|
||||
// we're storing in `checkpointWhitelist` with the peer's block.
|
||||
//
|
||||
|
|
@ -70,6 +72,84 @@ func (w *Service) IsValidChain(remoteHeader *types.Header, fetchHeadersByNumber
|
|||
return false, ErrCheckpointMismatch
|
||||
}
|
||||
|
||||
// IsValidChain checks the validity of chain by comparing it
|
||||
// against the local checkpoint entries
|
||||
func (w *Service) IsValidChain(currentHeader *types.Header, chain []*types.Header) bool {
|
||||
// Check if we have checkpoints to validate incoming chain in memory
|
||||
if len(w.checkpointWhitelist) == 0 {
|
||||
// We don't have any entries, no additional validation will be possible
|
||||
return true
|
||||
}
|
||||
|
||||
// Return if we've received empty chain
|
||||
if len(chain) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var (
|
||||
oldestCheckpointNumber uint64 = w.checkpointOrder[0]
|
||||
current uint64 = currentHeader.Number.Uint64()
|
||||
)
|
||||
|
||||
// Check if we have whitelist entries in required range
|
||||
if chain[len(chain)-1].Number.Uint64() < oldestCheckpointNumber {
|
||||
// We have future whitelisted entries, so no additional validation will be possible
|
||||
// This case will occur when bor is in middle of sync, but heimdall is ahead/fully synced.
|
||||
return true
|
||||
}
|
||||
|
||||
// Split the chain into past and future chain
|
||||
pastChain, futureChain := splitChain(current, chain)
|
||||
|
||||
// Add an offset to future chain if it's not in continuity
|
||||
offset := 0
|
||||
if len(futureChain) != 0 {
|
||||
offset += int(futureChain[0].Number.Uint64()-currentHeader.Number.Uint64()) - 1
|
||||
}
|
||||
|
||||
// Don't accept future chain of unacceptable length (from current block)
|
||||
if len(futureChain)+offset > int(w.checkpointInterval) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Iterate over the chain and validate against the last checkpoint
|
||||
// It will handle all cases where the incoming chain has atleast one checkpoint
|
||||
for i := len(pastChain) - 1; i >= 0; i-- {
|
||||
if _, ok := w.checkpointWhitelist[pastChain[i].Number.Uint64()]; ok {
|
||||
return pastChain[i].Hash() == w.checkpointWhitelist[pastChain[i].Number.Uint64()]
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func splitChain(current uint64, chain []*types.Header) ([]*types.Header, []*types.Header) {
|
||||
var (
|
||||
pastChain []*types.Header
|
||||
futureChain []*types.Header
|
||||
first uint64 = chain[0].Number.Uint64()
|
||||
last uint64 = chain[len(chain)-1].Number.Uint64()
|
||||
)
|
||||
|
||||
if current >= first {
|
||||
if len(chain) == 1 || current >= last {
|
||||
pastChain = chain
|
||||
} else {
|
||||
pastChain = chain[:current-first+1]
|
||||
}
|
||||
}
|
||||
|
||||
if current < last {
|
||||
if len(chain) == 1 || current < first {
|
||||
futureChain = chain
|
||||
} else {
|
||||
futureChain = chain[current-first+1:]
|
||||
}
|
||||
}
|
||||
|
||||
return pastChain, futureChain
|
||||
}
|
||||
|
||||
func (w *Service) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {
|
||||
w.m.Lock()
|
||||
defer w.m.Unlock()
|
||||
|
|
@ -116,7 +196,7 @@ func (w *Service) dequeueCheckpointWhitelist() {
|
|||
log.Debug("Dequeing checkpoint whitelist", "block number", w.checkpointOrder[0], "block hash", w.checkpointWhitelist[w.checkpointOrder[0]])
|
||||
|
||||
delete(w.checkpointWhitelist, w.checkpointOrder[0])
|
||||
w.checkpointOrder = w.checkpointOrder[1:]
|
||||
w.checkpointOrder = w.checkpointOrder[1:] // fixme: this slice is growing infinitely and never will be released. also a panic is possible if the last element is going to be removed
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@ package whitelist
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
|
@ -12,11 +16,12 @@ import (
|
|||
)
|
||||
|
||||
// NewMockService creates a new mock whitelist service
|
||||
func NewMockService(maxCapacity uint) *Service {
|
||||
func NewMockService(maxCapacity uint, checkpointInterval uint64) *Service {
|
||||
return &Service{
|
||||
checkpointWhitelist: make(map[uint64]common.Hash),
|
||||
checkpointOrder: []uint64{},
|
||||
maxCapacity: maxCapacity,
|
||||
checkpointInterval: checkpointInterval,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +29,7 @@ func NewMockService(maxCapacity uint) *Service {
|
|||
func TestWhitelistCheckpoint(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := NewMockService(10)
|
||||
s := NewMockService(10, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
s.enqueueCheckpointWhitelist(uint64(i), common.Hash{})
|
||||
}
|
||||
|
|
@ -35,15 +40,15 @@ func TestWhitelistCheckpoint(t *testing.T) {
|
|||
require.Equal(t, s.length(), 10, "expected 10 items in whitelist")
|
||||
}
|
||||
|
||||
// TestIsValidChain checks che IsValidChain function in isolation
|
||||
// TestIsValidPeer checks the IsValidPeer function in isolation
|
||||
// for different cases by providing a mock fetchHeadersByNumber function
|
||||
func TestIsValidChain(t *testing.T) {
|
||||
func TestIsValidPeer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := NewMockService(10)
|
||||
s := NewMockService(10, 10)
|
||||
|
||||
// case1: no checkpoint whitelist, should consider the chain as valid
|
||||
res, err := s.IsValidChain(nil, nil)
|
||||
res, err := s.IsValidPeer(nil, nil)
|
||||
require.NoError(t, err, "expected no error")
|
||||
require.Equal(t, res, true, "expected chain to be valid")
|
||||
|
||||
|
|
@ -60,7 +65,7 @@ func TestIsValidChain(t *testing.T) {
|
|||
|
||||
// case2: false fetchHeadersByNumber function provided, should consider the chain as invalid
|
||||
// and throw `ErrNoRemoteCheckoint` error
|
||||
res, err = s.IsValidChain(nil, falseFetchHeadersByNumber)
|
||||
res, err = s.IsValidPeer(nil, falseFetchHeadersByNumber)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
|
@ -91,7 +96,7 @@ func TestIsValidChain(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
res, err = s.IsValidChain(nil, fetchHeadersByNumber)
|
||||
res, err = s.IsValidPeer(nil, fetchHeadersByNumber)
|
||||
require.NoError(t, err, "expected no error")
|
||||
require.Equal(t, res, true, "expected chain to be valid")
|
||||
|
||||
|
|
@ -101,7 +106,306 @@ func TestIsValidChain(t *testing.T) {
|
|||
|
||||
// case4: correct fetchHeadersByNumber function provided with wrong header
|
||||
// for block number 2. Should consider the chain as invalid and throw an error
|
||||
res, err = s.IsValidChain(nil, fetchHeadersByNumber)
|
||||
res, err = s.IsValidPeer(nil, fetchHeadersByNumber)
|
||||
require.Equal(t, err, ErrCheckpointMismatch, "expected checkpoint mismatch error")
|
||||
require.Equal(t, res, false, "expected chain to be invalid")
|
||||
}
|
||||
|
||||
// TestIsValidChain checks the IsValidChain function in isolation
|
||||
// for different cases by providing a mock current header and chain
|
||||
func TestIsValidChain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := NewMockService(10, 10)
|
||||
chainA := createMockChain(1, 20) // A1->A2...A19->A20
|
||||
// case1: no checkpoint whitelist, should consider the chain as valid
|
||||
res := s.IsValidChain(nil, chainA)
|
||||
require.Equal(t, res, true, "expected chain to be valid")
|
||||
|
||||
tempChain := createMockChain(21, 22) // A21->A22
|
||||
|
||||
// add mock checkpoint entries
|
||||
s.ProcessCheckpoint(tempChain[0].Number.Uint64(), tempChain[0].Hash())
|
||||
s.ProcessCheckpoint(tempChain[1].Number.Uint64(), tempChain[1].Hash())
|
||||
|
||||
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
|
||||
|
||||
// case2: We're behind the oldest whitelisted block entry, should consider
|
||||
// the chain as valid as we're still far behind the latest blocks
|
||||
res = s.IsValidChain(chainA[len(chainA)-1], chainA)
|
||||
require.Equal(t, res, true, "expected chain to be valid")
|
||||
|
||||
// Clear checkpoint whitelist and add blocks A5 and A15 in whitelist
|
||||
s.PurgeCheckpointWhitelist()
|
||||
s.ProcessCheckpoint(chainA[5].Number.Uint64(), chainA[5].Hash())
|
||||
s.ProcessCheckpoint(chainA[15].Number.Uint64(), chainA[15].Hash())
|
||||
|
||||
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
|
||||
|
||||
// case3: Try importing a past chain having valid checkpoint, should
|
||||
// consider the chain as valid
|
||||
res = s.IsValidChain(chainA[len(chainA)-1], chainA)
|
||||
require.Equal(t, res, true, "expected chain to be valid")
|
||||
|
||||
// Clear checkpoint whitelist and mock blocks in whitelist
|
||||
tempChain = createMockChain(20, 20) // A20
|
||||
|
||||
s.PurgeCheckpointWhitelist()
|
||||
s.ProcessCheckpoint(tempChain[0].Number.Uint64(), tempChain[0].Hash())
|
||||
|
||||
require.Equal(t, s.length(), 1, "expected 1 items in whitelist")
|
||||
|
||||
// case4: Try importing a past chain having invalid checkpoint
|
||||
res = s.IsValidChain(chainA[len(chainA)-1], chainA)
|
||||
require.Equal(t, res, false, "expected chain to be invalid")
|
||||
|
||||
// create a future chain to be imported of length <= `checkpointInterval`
|
||||
chainB := createMockChain(21, 30) // B21->B22...B29->B30
|
||||
|
||||
// case5: Try importing a future chain of acceptable length
|
||||
res = s.IsValidChain(chainA[len(chainA)-1], chainB)
|
||||
require.Equal(t, res, true, "expected chain to be valid")
|
||||
|
||||
// create a future chain to be imported of length > `checkpointInterval`
|
||||
chainB = createMockChain(21, 40) // C21->C22...C39->C40
|
||||
|
||||
// case5: Try importing a future chain of unacceptable length
|
||||
res = s.IsValidChain(chainA[len(chainA)-1], chainB)
|
||||
require.Equal(t, res, false, "expected chain to be invalid")
|
||||
}
|
||||
|
||||
func TestSplitChain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Result struct {
|
||||
pastStart uint64
|
||||
pastEnd uint64
|
||||
futureStart uint64
|
||||
futureEnd uint64
|
||||
pastLength int
|
||||
futureLength int
|
||||
}
|
||||
|
||||
// Current chain is at block: X
|
||||
// Incoming chain is represented as [N, M]
|
||||
testCases := []struct {
|
||||
name string
|
||||
current uint64
|
||||
chain []*types.Header
|
||||
result Result
|
||||
}{
|
||||
{name: "X = 10, N = 11, M = 20", current: uint64(10), chain: createMockChain(11, 20), result: Result{futureStart: 11, futureEnd: 20, futureLength: 10}},
|
||||
{name: "X = 10, N = 13, M = 20", current: uint64(10), chain: createMockChain(13, 20), result: Result{futureStart: 13, futureEnd: 20, futureLength: 8}},
|
||||
{name: "X = 10, N = 2, M = 10", current: uint64(10), chain: createMockChain(2, 10), result: Result{pastStart: 2, pastEnd: 10, pastLength: 9}},
|
||||
{name: "X = 10, N = 2, M = 9", current: uint64(10), chain: createMockChain(2, 9), result: Result{pastStart: 2, pastEnd: 9, pastLength: 8}},
|
||||
{name: "X = 10, N = 2, M = 8", current: uint64(10), chain: createMockChain(2, 8), result: Result{pastStart: 2, pastEnd: 8, pastLength: 7}},
|
||||
{name: "X = 10, N = 5, M = 15", current: uint64(10), chain: createMockChain(5, 15), result: Result{pastStart: 5, pastEnd: 10, pastLength: 6, futureStart: 11, futureEnd: 15, futureLength: 5}},
|
||||
{name: "X = 10, N = 10, M = 20", current: uint64(10), chain: createMockChain(10, 20), result: Result{pastStart: 10, pastEnd: 10, pastLength: 1, futureStart: 11, futureEnd: 20, futureLength: 10}},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
past, future := splitChain(tc.current, tc.chain)
|
||||
require.Equal(t, len(past), tc.result.pastLength)
|
||||
require.Equal(t, len(future), tc.result.futureLength)
|
||||
|
||||
if len(past) > 0 {
|
||||
// Check if we have expected block/s
|
||||
require.Equal(t, past[0].Number.Uint64(), tc.result.pastStart)
|
||||
require.Equal(t, past[len(past)-1].Number.Uint64(), tc.result.pastEnd)
|
||||
}
|
||||
|
||||
if len(future) > 0 {
|
||||
// Check if we have expected block/s
|
||||
require.Equal(t, future[0].Number.Uint64(), tc.result.futureStart)
|
||||
require.Equal(t, future[len(future)-1].Number.Uint64(), tc.result.futureEnd)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:gocognit
|
||||
func TestSplitChainProperties(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Current chain is at block: X
|
||||
// Incoming chain is represented as [N, M]
|
||||
|
||||
currentChain := []int{0, 1, 2, 3, 10, 100} // blocks starting from genesis
|
||||
blockDiffs := []int{0, 1, 2, 3, 4, 5, 9, 10, 11, 12, 90, 100, 101, 102}
|
||||
|
||||
caseParams := make(map[int]map[int]map[int]struct{}) // X -> N -> M
|
||||
|
||||
for _, current := range currentChain {
|
||||
// past cases only + past to current
|
||||
for _, diff := range blockDiffs {
|
||||
from := current - diff
|
||||
|
||||
// use int type for everything to not care about underflow
|
||||
if from < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, diff := range blockDiffs {
|
||||
to := current - diff
|
||||
|
||||
if to >= from {
|
||||
addTestCaseParams(caseParams, current, from, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// future only + current to future
|
||||
for _, diff := range blockDiffs {
|
||||
from := current + diff
|
||||
|
||||
if from < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, diff := range blockDiffs {
|
||||
to := current + diff
|
||||
|
||||
if to >= from {
|
||||
addTestCaseParams(caseParams, current, from, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// past-current-future
|
||||
for _, diff := range blockDiffs {
|
||||
from := current - diff
|
||||
|
||||
if from < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, diff := range blockDiffs {
|
||||
to := current + diff
|
||||
|
||||
if to >= from {
|
||||
addTestCaseParams(caseParams, current, from, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
current int
|
||||
remoteStart int
|
||||
remoteEnd int
|
||||
}
|
||||
|
||||
var ts []testCase
|
||||
|
||||
// X -> N -> M
|
||||
for x, nm := range caseParams {
|
||||
for n, mMap := range nm {
|
||||
for m := range mMap {
|
||||
ts = append(ts, testCase{x, n, m})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:paralleltest
|
||||
for i, tc := range ts {
|
||||
tc := tc
|
||||
|
||||
name := fmt.Sprintf("test case: index = %d, X = %d, N = %d, M = %d", i, tc.current, tc.remoteStart, tc.remoteEnd)
|
||||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
chain := createMockChain(uint64(tc.remoteStart), uint64(tc.remoteEnd))
|
||||
|
||||
past, future := splitChain(uint64(tc.current), chain)
|
||||
|
||||
// properties
|
||||
if len(past) > 0 {
|
||||
// Check if the chain is ordered
|
||||
isOrdered := sort.SliceIsSorted(past, func(i, j int) bool {
|
||||
return past[i].Number.Uint64() < past[j].Number.Uint64()
|
||||
})
|
||||
|
||||
require.True(t, isOrdered, "an ordered past chain expected: %v", past)
|
||||
|
||||
isSequential := sort.SliceIsSorted(past, func(i, j int) bool {
|
||||
return past[i].Number.Uint64() == past[j].Number.Uint64()-1
|
||||
})
|
||||
|
||||
require.True(t, isSequential, "a sequential past chain expected: %v", past)
|
||||
|
||||
// Check if current block >= past chain's last block
|
||||
require.Equal(t, past[len(past)-1].Number.Uint64() <= uint64(tc.current), true)
|
||||
}
|
||||
|
||||
if len(future) > 0 {
|
||||
// Check if the chain is ordered
|
||||
isOrdered := sort.SliceIsSorted(future, func(i, j int) bool {
|
||||
return future[i].Number.Uint64() < future[j].Number.Uint64()
|
||||
})
|
||||
|
||||
require.True(t, isOrdered, "an ordered future chain expected: %v", future)
|
||||
|
||||
isSequential := sort.SliceIsSorted(future, func(i, j int) bool {
|
||||
return future[i].Number.Uint64() == future[j].Number.Uint64()-1
|
||||
})
|
||||
|
||||
require.True(t, isSequential, "a sequential future chain expected: %v", future)
|
||||
|
||||
// Check if future chain's first block > current block
|
||||
require.Equal(t, future[len(future)-1].Number.Uint64() > uint64(tc.current), true)
|
||||
}
|
||||
|
||||
// Check if both chains are continuous
|
||||
if len(past) > 0 && len(future) > 0 {
|
||||
require.Equal(t, past[len(past)-1].Number.Uint64(), future[0].Number.Uint64()-1)
|
||||
}
|
||||
|
||||
// Check if we get the original chain on appending both
|
||||
gotChain := append(past, future...)
|
||||
require.Equal(t, reflect.DeepEqual(gotChain, chain), true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// createMockChain returns a chain with dummy headers
|
||||
// starting from `start` to `end` (inclusive)
|
||||
func createMockChain(start, end uint64) []*types.Header {
|
||||
var (
|
||||
i uint64
|
||||
idx uint64
|
||||
chain []*types.Header = make([]*types.Header, end-start+1)
|
||||
)
|
||||
|
||||
for i = start; i <= end; i++ {
|
||||
header := &types.Header{
|
||||
Number: big.NewInt(int64(i)),
|
||||
Time: uint64(time.Now().UnixMicro()) + i,
|
||||
}
|
||||
chain[idx] = header
|
||||
idx++
|
||||
}
|
||||
|
||||
return chain
|
||||
}
|
||||
|
||||
// mXNM should be initialized
|
||||
func addTestCaseParams(mXNM map[int]map[int]map[int]struct{}, x, n, m int) {
|
||||
//nolint:ineffassign
|
||||
mNM, ok := mXNM[x]
|
||||
if !ok {
|
||||
mNM = make(map[int]map[int]struct{})
|
||||
mXNM[x] = mNM
|
||||
}
|
||||
|
||||
//nolint:ineffassign
|
||||
_, ok = mNM[n]
|
||||
if !ok {
|
||||
mM := make(map[int]struct{})
|
||||
mNM[n] = mM
|
||||
}
|
||||
|
||||
mXNM[x][n][m] = struct{}{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/contract"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall" //nolint:typecheck
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
||||
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
|
|||
// Construct testing chain
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.Commit(diskdb)
|
||||
chain, err := core.NewBlockChain(diskdb, &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(diskdb, &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec.Config, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create local chain, %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||
|
|
@ -31,7 +32,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/forkid"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader/whitelist"
|
||||
"github.com/ethereum/go-ethereum/eth/fetcher"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/snap"
|
||||
|
|
@ -91,6 +91,7 @@ type handlerConfig struct {
|
|||
EthAPI *ethapi.PublicBlockChainAPI // EthAPI to interact
|
||||
|
||||
PeerRequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges
|
||||
checker ethereum.ChainValidator
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
|
|
@ -202,7 +203,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
|
|||
// sync is requested. The downloader is responsible for deallocating the state
|
||||
// bloom when it's done.
|
||||
// todo: it'd better to extract maxCapacity into config
|
||||
h.downloader = downloader.New(h.checkpointNumber, config.Database, h.eventMux, h.chain, nil, h.removePeer, success, whitelist.NewService(10))
|
||||
h.downloader = downloader.New(h.checkpointNumber, config.Database, h.eventMux, h.chain, nil, h.removePeer, success, config.checker)
|
||||
|
||||
// Construct the fetcher (short sync)
|
||||
validator := func(header *types.Header) error {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,14 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// errCheckpointCount is returned when we are unable to fetch
|
||||
// the checkpoint count from local heimdall.
|
||||
errCheckpointCount = errors.New("failed to fetch checkpoint count")
|
||||
|
||||
// errNoCheckpoint is returned when there is not checkpoint proposed
|
||||
// by heimdall yet or heimdall is not in sync
|
||||
errNoCheckpoint = errors.New("no checkpoint proposed")
|
||||
|
||||
// errCheckpoint is returned when we are unable to fetch the
|
||||
// latest checkpoint from the local heimdall.
|
||||
errCheckpoint = errors.New("failed to fetch latest checkpoint")
|
||||
|
|
@ -33,43 +41,78 @@ var (
|
|||
errEndBlock = errors.New("failed to get end block")
|
||||
)
|
||||
|
||||
// fetchWhitelistCheckpoint fetched the latest checkpoint from it's local heimdall
|
||||
// fetchWhitelistCheckpoints fetches the latest checkpoint/s from it's local heimdall
|
||||
// and verifies the data against bor data.
|
||||
func (h *ethHandler) fetchWhitelistCheckpoint(ctx context.Context, bor *bor.Bor) (uint64, common.Hash, error) {
|
||||
// check for checkpoint whitelisting: bor
|
||||
checkpoint, err := bor.HeimdallClient.FetchLatestCheckpoint(ctx)
|
||||
func (h *ethHandler) fetchWhitelistCheckpoints(ctx context.Context, bor *bor.Bor, first bool) ([]uint64, []common.Hash, error) {
|
||||
// Create an array for block number and block hashes
|
||||
//nolint:prealloc
|
||||
var (
|
||||
blockNums []uint64 = make([]uint64, 0)
|
||||
blockHashes []common.Hash = make([]common.Hash, 0)
|
||||
)
|
||||
|
||||
// Fetch the checkpoint count from heimdall
|
||||
count, err := bor.HeimdallClient.FetchCheckpointCount(ctx)
|
||||
if err != nil {
|
||||
log.Debug("Failed to fetch latest checkpoint for whitelisting")
|
||||
return 0, common.Hash{}, errCheckpoint
|
||||
log.Debug("Failed to fetch checkpoint count for whitelisting", "err", err)
|
||||
return blockNums, blockHashes, errCheckpointCount
|
||||
}
|
||||
|
||||
// check if we have the checkpoint blocks
|
||||
head := h.ethAPI.BlockNumber()
|
||||
if head < hexutil.Uint64(checkpoint.EndBlock.Uint64()) {
|
||||
log.Debug("Head block behind checkpoint block", "head", head, "checkpoint end block", checkpoint.EndBlock)
|
||||
return 0, common.Hash{}, errMissingCheckpoint
|
||||
if count == 0 {
|
||||
return blockNums, blockHashes, errNoCheckpoint
|
||||
}
|
||||
|
||||
// verify the root hash of checkpoint
|
||||
roothash, err := h.ethAPI.GetRootHash(ctx, checkpoint.StartBlock.Uint64(), checkpoint.EndBlock.Uint64())
|
||||
if err != nil {
|
||||
log.Debug("Failed to get root hash of checkpoint while whitelisting")
|
||||
return 0, common.Hash{}, errRootHash
|
||||
// If we're in the first iteration, we'll fetch last 10 checkpoints, else only the latest one
|
||||
iterations := 1
|
||||
if first {
|
||||
iterations = 10
|
||||
}
|
||||
|
||||
if roothash != checkpoint.RootHash.String()[2:] {
|
||||
log.Warn("Checkpoint root hash mismatch while whitelisting", "expected", checkpoint.RootHash.String()[2:], "got", roothash)
|
||||
return 0, common.Hash{}, errCheckpointRootHashMismatch
|
||||
for i := 0; i < iterations; i++ {
|
||||
// If we don't have any checkpoints in heimdall, break
|
||||
if count == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// fetch `count` indexed checkpoint from heimdall
|
||||
checkpoint, err := bor.HeimdallClient.FetchCheckpoint(ctx, count)
|
||||
if err != nil {
|
||||
log.Debug("Failed to fetch latest checkpoint for whitelisting", "err", err)
|
||||
return blockNums, blockHashes, errCheckpoint
|
||||
}
|
||||
|
||||
// check if we have the checkpoint blocks
|
||||
head := h.ethAPI.BlockNumber()
|
||||
if head < hexutil.Uint64(checkpoint.EndBlock.Uint64()) {
|
||||
log.Debug("Head block behind checkpoint block", "head", head, "checkpoint end block", checkpoint.EndBlock)
|
||||
return blockNums, blockHashes, errMissingCheckpoint
|
||||
}
|
||||
|
||||
// verify the root hash of checkpoint
|
||||
roothash, err := h.ethAPI.GetRootHash(ctx, checkpoint.StartBlock.Uint64(), checkpoint.EndBlock.Uint64())
|
||||
if err != nil {
|
||||
log.Debug("Failed to get root hash of checkpoint while whitelisting", "err", err)
|
||||
return blockNums, blockHashes, errRootHash
|
||||
}
|
||||
|
||||
if roothash != checkpoint.RootHash.String()[2:] {
|
||||
log.Warn("Checkpoint root hash mismatch while whitelisting", "expected", checkpoint.RootHash.String()[2:], "got", roothash)
|
||||
return blockNums, blockHashes, errCheckpointRootHashMismatch
|
||||
}
|
||||
|
||||
// fetch the end checkpoint block hash
|
||||
block, err := h.ethAPI.GetBlockByNumber(ctx, rpc.BlockNumber(checkpoint.EndBlock.Uint64()), false)
|
||||
if err != nil {
|
||||
log.Debug("Failed to get end block hash of checkpoint while whitelisting", "err", err)
|
||||
return blockNums, blockHashes, errEndBlock
|
||||
}
|
||||
|
||||
hash := fmt.Sprintf("%v", block["hash"])
|
||||
|
||||
blockNums = append(blockNums, checkpoint.EndBlock.Uint64())
|
||||
blockHashes = append(blockHashes, common.HexToHash(hash))
|
||||
count--
|
||||
}
|
||||
|
||||
// fetch the end checkpoint block hash
|
||||
block, err := h.ethAPI.GetBlockByNumber(context.Background(), rpc.BlockNumber(checkpoint.EndBlock.Uint64()), false)
|
||||
if err != nil {
|
||||
log.Debug("Failed to get end block hash of checkpoint while whitelisting")
|
||||
return 0, common.Hash{}, errEndBlock
|
||||
}
|
||||
|
||||
hash := fmt.Sprintf("%v", block["hash"])
|
||||
|
||||
return checkpoint.EndBlock.Uint64(), common.HexToHash(hash), nil
|
||||
return blockNums, blockHashes, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ func testForkIDSplit(t *testing.T, protocol uint) {
|
|||
genesisNoFork = gspecNoFork.MustCommit(dbNoFork)
|
||||
genesisProFork = gspecProFork.MustCommit(dbProFork)
|
||||
|
||||
chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil)
|
||||
chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil)
|
||||
chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil, nil)
|
||||
chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil, nil)
|
||||
|
||||
blocksNoFork, _ = core.GenerateChain(configNoFork, genesisNoFork, engine, dbNoFork, 2, nil)
|
||||
blocksProFork, _ = core.GenerateChain(configProFork, genesisProFork, engine, dbProFork, 2, nil)
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ func newTestHandlerWithBlocks(blocks int) *testHandler {
|
|||
Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}},
|
||||
}).MustCommit(db)
|
||||
|
||||
chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
|
||||
bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, nil)
|
||||
if _, err := chain.InsertChain(bs); err != nil {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ func newTestBackendWithGenerator(blocks int, generator func(int, *core.BlockGen)
|
|||
Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}},
|
||||
}).MustCommit(db)
|
||||
|
||||
chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
|
||||
bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, generator)
|
||||
if _, err := chain.InsertChain(bs); err != nil {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i i
|
|||
SnapshotLimit: 0,
|
||||
TrieDirtyDisabled: true, // Archive mode
|
||||
}
|
||||
chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, backend.chainConfig, backend.engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, backend.chainConfig, backend.engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,3 +238,12 @@ type StateSyncFilter struct {
|
|||
ID uint64
|
||||
Contract common.Address
|
||||
}
|
||||
|
||||
// interface for whitelist service
|
||||
type ChainValidator interface {
|
||||
IsValidPeer(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error)
|
||||
IsValidChain(currentHeader *types.Header, chain []*types.Header) bool
|
||||
ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash)
|
||||
GetCheckpointWhitelist() map[uint64]common.Hash
|
||||
PurgeCheckpointWhitelist()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor"
|
||||
"github.com/ethereum/go-ethereum/consensus/beacon" //nolint:typecheck
|
||||
"github.com/ethereum/go-ethereum/consensus/bor" //nolint:typecheck
|
||||
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader/whitelist"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||
|
|
@ -145,7 +146,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
|
|||
}
|
||||
// Note: NewLightChain adds the trusted checkpoint so it needs an ODR with
|
||||
// indexers already set but not started yet
|
||||
if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint); err != nil {
|
||||
if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint, whitelist.NewService(10)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
leth.chainReader = leth.blockchain
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, index
|
|||
oracle *checkpointoracle.CheckpointOracle
|
||||
)
|
||||
genesis := gspec.MustCommit(db)
|
||||
chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil)
|
||||
chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil, nil)
|
||||
if indexers != nil {
|
||||
checkpointConfig := ¶ms.CheckpointOracleConfig{
|
||||
Address: crypto.CreateAddress(bankAddr, 0),
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
|
|
@ -37,7 +40,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -81,7 +83,7 @@ type LightChain struct {
|
|||
// NewLightChain returns a fully initialised light chain using information
|
||||
// available in the database. It initialises the default Ethereum header
|
||||
// validator.
|
||||
func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine, checkpoint *params.TrustedCheckpoint) (*LightChain, error) {
|
||||
func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine, checkpoint *params.TrustedCheckpoint, checker ethereum.ChainValidator) (*LightChain, error) {
|
||||
bodyCache, _ := lru.New(bodyCacheLimit)
|
||||
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
||||
blockCache, _ := lru.New(blockCacheLimit)
|
||||
|
|
@ -96,7 +98,7 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
|
|||
blockCache: blockCache,
|
||||
engine: engine,
|
||||
}
|
||||
bc.forker = core.NewForkChoice(bc, nil)
|
||||
bc.forker = core.NewForkChoice(bc, nil, checker)
|
||||
var err error
|
||||
bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func newCanonical(n int) (ethdb.Database, *LightChain, error) {
|
|||
db := rawdb.NewMemoryDatabase()
|
||||
gspec := core.Genesis{Config: params.TestChainConfig}
|
||||
genesis := gspec.MustCommit(db)
|
||||
blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker(), nil)
|
||||
blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker(), nil, nil)
|
||||
|
||||
// Create and inject the requested chain
|
||||
if n == 0 {
|
||||
|
|
@ -76,7 +76,7 @@ func newTestLightChain() *LightChain {
|
|||
Config: params.TestChainConfig,
|
||||
}
|
||||
gspec.MustCommit(db)
|
||||
lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker(), nil)
|
||||
lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker(), nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -347,7 +347,7 @@ func TestReorgBadHeaderHashes(t *testing.T) {
|
|||
defer func() { delete(core.BadHashes, headers[3].Hash()) }()
|
||||
|
||||
// Create a new LightChain and check that it rolled back the state.
|
||||
ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker(), nil)
|
||||
ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker(), nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new chain manager: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,14 +261,14 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
|
|||
)
|
||||
gspec.MustCommit(ldb)
|
||||
// Assemble the test environment
|
||||
blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
|
||||
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen)
|
||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig}
|
||||
lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil)
|
||||
lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ func TestNodeIterator(t *testing.T) {
|
|||
genesis = gspec.MustCommit(fulldb)
|
||||
)
|
||||
gspec.MustCommit(lightdb)
|
||||
blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
||||
|
||||
blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
|
||||
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), fulldb, 4, testChainGen)
|
||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||
panic(err)
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ func TestTxPool(t *testing.T) {
|
|||
)
|
||||
gspec.MustCommit(ldb)
|
||||
// Assemble the test environment
|
||||
blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
|
||||
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen)
|
||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||
panic(err)
|
||||
|
|
@ -103,7 +103,7 @@ func TestTxPool(t *testing.T) {
|
|||
discard: make(chan int, 1),
|
||||
mined: make(chan int, 1),
|
||||
}
|
||||
lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil)
|
||||
lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil, nil)
|
||||
txPermanent = 50
|
||||
pool := NewTxPool(params.TestChainConfig, lightchain, relay)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ func createBorMiner(t *testing.T, ethAPIMock api.Caller, spanner bor.Spanner, he
|
|||
engine := NewFakeBor(t, chainDB, chainConfig, ethAPIMock, spanner, heimdallClientMock, contractMock)
|
||||
|
||||
// Create Ethereum backend
|
||||
bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil)
|
||||
bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("can't create new chain %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ func newTestWorkerBackend(t TensingObject, chainConfig *params.ChainConfig, engi
|
|||
|
||||
genesis := gspec.MustCommit(db)
|
||||
|
||||
chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil, nil)
|
||||
txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain)
|
||||
|
||||
// Generate a small n-block chain and an uncle block for it
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool, isBor bool) {
|
|||
db2 := rawdb.NewMemoryDatabase()
|
||||
b.Genesis.MustCommit(db2)
|
||||
|
||||
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
// Ignore empty commit here for less noise.
|
||||
|
|
@ -650,7 +650,7 @@ func BenchmarkBorMining(b *testing.B) {
|
|||
db2 := rawdb.NewMemoryDatabase()
|
||||
back.Genesis.MustCommit(db2)
|
||||
|
||||
chain, _ := core.NewBlockChain(db2, nil, back.chain.Config(), engine, vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db2, nil, back.chain.Config(), engine, vm.Config{}, nil, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
// Ignore empty commit here for less noise.
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@ func (t *BlockTest) Run(snapshotter bool) error {
|
|||
cache.SnapshotLimit = 1
|
||||
cache.SnapshotWait = true
|
||||
}
|
||||
chain, err := core.NewBlockChain(db, cache, config, engine, vm.Config{}, nil, nil)
|
||||
|
||||
chain, err := core.NewBlockChain(db, cache, config, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func TestGetTransactionReceiptsByBlock(t *testing.T) {
|
|||
|
||||
h.EXPECT().Span(gomock.Any(), uint64(1)).Return(&res.Result, nil).MinTimes(1)
|
||||
h.EXPECT().Close().MinTimes(1)
|
||||
h.EXPECT().FetchLatestCheckpoint(gomock.Any()).Return(&checkpoint.Checkpoint{
|
||||
h.EXPECT().FetchCheckpoint(gomock.Any(), int64(-1)).Return(&checkpoint.Checkpoint{
|
||||
Proposer: res.Result.SelectedProducers[0].Address,
|
||||
StartBlock: big.NewInt(0),
|
||||
EndBlock: big.NewInt(int64(spanSize)),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func TestInsertingSpanSizeBlocks(t *testing.T) {
|
|||
defer ctrl.Finish()
|
||||
|
||||
h.EXPECT().Close().AnyTimes()
|
||||
h.EXPECT().FetchLatestCheckpoint(gomock.Any()).Return(&checkpoint.Checkpoint{
|
||||
h.EXPECT().FetchCheckpoint(gomock.Any(), int64(-1)).Return(&checkpoint.Checkpoint{
|
||||
Proposer: currentSpan.SelectedProducers[0].Address,
|
||||
StartBlock: big.NewInt(0),
|
||||
EndBlock: big.NewInt(int64(spanSize)),
|
||||
|
|
@ -418,7 +418,7 @@ func TestEIP1559Transition(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -711,7 +711,7 @@ func TestTransitionWithoutEIP155(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall" //nolint:typecheck
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/valset"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
|
|
|
|||
|
|
@ -49,19 +49,34 @@ func (mr *MockIHeimdallClientMockRecorder) Close() *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockIHeimdallClient)(nil).Close))
|
||||
}
|
||||
|
||||
// FetchLatestCheckpoint mocks base method.
|
||||
func (m *MockIHeimdallClient) FetchLatestCheckpoint(arg0 context.Context) (*checkpoint.Checkpoint, error) {
|
||||
// FetchCheckpoint mocks base method.
|
||||
func (m *MockIHeimdallClient) FetchCheckpoint(arg0 context.Context, arg1 int64) (*checkpoint.Checkpoint, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FetchLatestCheckpoint", arg0)
|
||||
ret := m.ctrl.Call(m, "FetchCheckpoint", arg0, arg1)
|
||||
ret0, _ := ret[0].(*checkpoint.Checkpoint)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FetchLatestCheckpoint indicates an expected call of FetchLatestCheckpoint.
|
||||
func (mr *MockIHeimdallClientMockRecorder) FetchLatestCheckpoint(arg0 interface{}) *gomock.Call {
|
||||
// FetchCheckpoint indicates an expected call of FetchCheckpoint.
|
||||
func (mr *MockIHeimdallClientMockRecorder) FetchCheckpoint(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchLatestCheckpoint", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchLatestCheckpoint), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchCheckpoint", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchCheckpoint), arg0, arg1)
|
||||
}
|
||||
|
||||
// FetchCheckpointCount mocks base method.
|
||||
func (m *MockIHeimdallClient) FetchCheckpointCount(arg0 context.Context) (int64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FetchCheckpointCount", arg0)
|
||||
ret0, _ := ret[0].(int64)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FetchCheckpointCount indicates an expected call of FetchCheckpointCount.
|
||||
func (mr *MockIHeimdallClientMockRecorder) FetchCheckpointCount(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchCheckpointCount", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchCheckpointCount), arg0)
|
||||
}
|
||||
|
||||
// Span mocks base method.
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func makechain() (bc *core.BlockChain, addrHashes, txHashes []common.Hash) {
|
|||
addrHashes = append(addrHashes, crypto.Keccak256Hash(addr[:]))
|
||||
txHashes = append(txHashes, tx.Hash())
|
||||
})
|
||||
bc, _ = core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ = core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
if _, err := bc.InsertChain(blocks); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ func getChain() *core.BlockChain {
|
|||
SnapshotWait: true,
|
||||
}
|
||||
trieRoot = blocks[len(blocks)-1].Root()
|
||||
bc, _ := core.NewBlockChain(db, cacheConf, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ := core.NewBlockChain(db, cacheConf, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
if _, err := bc.InsertChain(blocks); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue