go-ethereum/eth/handler_bor.go
Manav Darji c44693706a
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>
2022-07-15 10:37:35 +05:30

118 lines
4.1 KiB
Go

package eth
import (
"context"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)
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")
// errMissingCheckpoint is returned when we don't have the
// checkpoint blocks locally, yet.
errMissingCheckpoint = errors.New("missing checkpoint blocks")
// errRootHash is returned when we aren't able to calculate the root hash
// locally for a range of blocks.
errRootHash = errors.New("failed to get local root hash")
// errCheckpointRootHashMismatch is returned when the local root hash
// doesn't match with the root hash in checkpoint.
errCheckpointRootHashMismatch = errors.New("checkpoint roothash mismatch")
// errEndBlock is returned when we're unable to fetch a block locally.
errEndBlock = errors.New("failed to get end block")
)
// fetchWhitelistCheckpoints fetches the latest checkpoint/s from it's local heimdall
// and verifies the data against bor data.
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 checkpoint count for whitelisting", "err", err)
return blockNums, blockHashes, errCheckpointCount
}
if count == 0 {
return blockNums, blockHashes, errNoCheckpoint
}
// If we're in the first iteration, we'll fetch last 10 checkpoints, else only the latest one
iterations := 1
if first {
iterations = 10
}
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--
}
return blockNums, blockHashes, nil
}