Merge branch 'develop' of https://github.com/maticnetwork/bor into shivam/pos-636

This commit is contained in:
Shivam Sharma 2022-07-15 12:05:13 +05:30
commit 73054f8608
84 changed files with 2227 additions and 568 deletions

View file

@ -32,6 +32,9 @@ bor:
protoc: protoc:
protoc --go_out=. --go-grpc_out=. ./internal/cli/server/proto/*.proto 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: geth:
$(GORUN) build/ci.go install ./cmd/geth $(GORUN) build/ci.go install ./cmd/geth
@echo "Done building." @echo "Done building."

View file

@ -78,7 +78,7 @@ type SimulatedBackend struct {
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc} genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
genesis.MustCommit(database) 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{ backend := &SimulatedBackend{
database: database, database: database,

View file

@ -5,11 +5,12 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"gopkg.in/urfave/cli.v1"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"gopkg.in/urfave/cli.v1"
) )
var ( var (

View file

@ -33,6 +33,10 @@ import (
"text/template" "text/template"
"time" "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"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -68,9 +72,6 @@ import (
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/p2p/netutil"
"github.com/ethereum/go-ethereum/params" "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() { 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. // TODO(rjl493456442) disable snapshot generation/wiping if the chain is read only.
// Disable transaction indexing/unindexing by default. // 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 { if err != nil {
Fatalf("Can't create BlockChain: %v", err) Fatalf("Can't create BlockChain: %v", err)
} }

View file

@ -606,7 +606,7 @@ func (c *Bor) verifySeal(chain consensus.ChainHeaderReader, header *types.Header
return err return err
} }
if !snap.ValidatorSet.HasAddress(signer.Bytes()) { if !snap.ValidatorSet.HasAddress(signer) {
// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1 // Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
return &UnauthorizedSignerError{number - 1, signer.Bytes()} return &UnauthorizedSignerError{number - 1, signer.Bytes()}
} }
@ -623,13 +623,13 @@ func (c *Bor) verifySeal(chain consensus.ChainHeaderReader, header *types.Header
parent = chain.GetHeader(header.ParentHash, number-1) parent = chain.GetHeader(header.ParentHash, number-1)
} }
if parent != nil && header.Time < parent.Time+CalcProducerDelay(number, succession, c.config) { if IsBlockOnTime(parent, header, number, succession, c.config) {
return &BlockTooSoonError{number, succession} return &BlockTooSoonError{number, succession}
} }
// Ensure that the difficulty corresponds to the turn-ness of the signer // Ensure that the difficulty corresponds to the turn-ness of the signer
if !c.fakeDiff { if !c.fakeDiff {
difficulty := snap.Difficulty(signer) difficulty := Difficulty(snap.ValidatorSet, signer)
if header.Difficulty.Uint64() != difficulty { if header.Difficulty.Uint64() != difficulty {
return &WrongDifficultyError{number, difficulty, header.Difficulty.Uint64(), signer.Bytes()} return &WrongDifficultyError{number, difficulty, header.Difficulty.Uint64(), signer.Bytes()}
} }
@ -638,6 +638,10 @@ func (c *Bor) verifySeal(chain consensus.ChainHeaderReader, header *types.Header
return nil return nil
} }
func IsBlockOnTime(parent *types.Header, header *types.Header, number uint64, succession int, cfg *params.BorConfig) bool {
return parent != nil && header.Time < parent.Time+CalcProducerDelay(number, succession, cfg)
}
// Prepare implements consensus.Engine, preparing all the consensus fields of the // Prepare implements consensus.Engine, preparing all the consensus fields of the
// header for running the transactions on top. // header for running the transactions on top.
func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
@ -653,7 +657,7 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
} }
// Set the correct difficulty // Set the correct difficulty
header.Difficulty = new(big.Int).SetUint64(snap.Difficulty(c.signer)) header.Difficulty = new(big.Int).SetUint64(Difficulty(snap.ValidatorSet, c.signer))
// Ensure the extra data has all it's components // Ensure the extra data has all it's components
if len(header.Extra) < extraVanity { if len(header.Extra) < extraVanity {
@ -716,7 +720,7 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header,
headerNumber := header.Number.Uint64() headerNumber := header.Number.Uint64()
if headerNumber%c.config.Sprint == 0 { if IsSprintStart(headerNumber, c.config.Sprint) {
ctx := context.Background() ctx := context.Background()
cx := statefull.ChainContext{Chain: chain, Bor: c} cx := statefull.ChainContext{Chain: chain, Bor: c}
@ -727,7 +731,7 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header,
} }
if c.HeimdallClient != nil { if c.HeimdallClient != nil {
// commit statees // commit states
stateSyncData, err = c.CommitStates(ctx, state, header, cx) stateSyncData, err = c.CommitStates(ctx, state, header, cx)
if err != nil { if err != nil {
log.Error("Error while committing states", "error", err) log.Error("Error while committing states", "error", err)
@ -790,7 +794,7 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ
headerNumber := header.Number.Uint64() headerNumber := header.Number.Uint64()
if headerNumber%c.config.Sprint == 0 { if IsSprintStart(headerNumber, c.config.Sprint) {
ctx := context.Background() ctx := context.Background()
cx := statefull.ChainContext{Chain: chain, Bor: c} cx := statefull.ChainContext{Chain: chain, Bor: c}
@ -867,7 +871,7 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
} }
// Bail out if we're unauthorized to sign a block // Bail out if we're unauthorized to sign a block
if !snap.ValidatorSet.HasAddress(signer.Bytes()) { if !snap.ValidatorSet.HasAddress(signer) {
// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1 // Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
return &UnauthorizedSignerError{number - 1, signer.Bytes()} return &UnauthorizedSignerError{number - 1, signer.Bytes()}
} }
@ -945,7 +949,7 @@ func (c *Bor) CalcDifficulty(chain consensus.ChainHeaderReader, _ uint64, parent
return nil return nil
} }
return new(big.Int).SetUint64(snap.Difficulty(c.signer)) return new(big.Int).SetUint64(Difficulty(snap.ValidatorSet, c.signer))
} }
// SealHash returns the hash of a block prior to it being sealed. // SealHash returns the hash of a block prior to it being sealed.
@ -1059,7 +1063,6 @@ func (c *Bor) CommitStates(
header *types.Header, header *types.Header,
chain statefull.ChainContext, chain statefull.ChainContext,
) ([]*types.StateSyncData, error) { ) ([]*types.StateSyncData, error) {
stateSyncs := make([]*types.StateSyncData, 0)
number := header.Number.Uint64() number := header.Number.Uint64()
_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1) _lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
@ -1087,15 +1090,17 @@ func (c *Bor) CommitStates(
} }
totalGas := 0 /// limit on gas for state sync per block totalGas := 0 /// limit on gas for state sync per block
chainID := c.chainConfig.ChainID.String() chainID := c.chainConfig.ChainID.String()
stateSyncs := make([]*types.StateSyncData, len(eventRecords))
var gasUsed uint64
for _, eventRecord := range eventRecords { for _, eventRecord := range eventRecords {
if eventRecord.ID <= lastStateID { if eventRecord.ID <= lastStateID {
continue continue
} }
if err := validateEventRecord(eventRecord, number, to, lastStateID, chainID); err != nil { if err = validateEventRecord(eventRecord, number, to, lastStateID, chainID); err != nil {
log.Error("while validating event record", "block", number, "to", to, "stateID", lastStateID, "error", err.Error()) log.Error("while validating event record", "block", number, "to", to, "stateID", lastStateID, "error", err.Error())
break break
} }
@ -1109,7 +1114,10 @@ func (c *Bor) CommitStates(
stateSyncs = append(stateSyncs, &stateData) stateSyncs = append(stateSyncs, &stateData)
gasUsed, err := c.GenesisContractsClient.CommitState(eventRecord, state, header, chain) // we expect that this call MUST emit an event, otherwise we wouldn't make a receipt
// if the receiver address is not a contract then we'll skip the most of the execution and emitting an event as well
// https://github.com/maticnetwork/genesis-contracts/blob/master/contracts/StateReceiver.sol#L27
gasUsed, err = c.GenesisContractsClient.CommitState(eventRecord, state, header, chain)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -1,13 +1,15 @@
package bor package bor
import ( import (
"context"
"math/big" "math/big"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "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"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
@ -55,11 +57,11 @@ func TestGenesisContractChange(t *testing.T) {
genesis := genspec.MustCommit(db) genesis := genspec.MustCommit(db)
statedb, err := state.New(genesis.Root(), state.NewDatabase(db), nil) statedb, err := state.New(genesis.Root(), state.NewDatabase(db), nil)
assert.NoError(t, err) require.NoError(t, err)
config := params.ChainConfig{} 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)
assert.NoError(t, err) require.NoError(t, err)
addBlock := func(root common.Hash, num int64) (common.Hash, *state.StateDB) { addBlock := func(root common.Hash, num int64) (common.Hash, *state.StateDB) {
h := &types.Header{ h := &types.Header{
@ -70,37 +72,37 @@ func TestGenesisContractChange(t *testing.T) {
// write state to database // write state to database
root, err := statedb.Commit(false) root, err := statedb.Commit(false)
assert.NoError(t, err) require.NoError(t, err)
assert.NoError(t, statedb.Database().TrieDB().Commit(root, true, nil)) require.NoError(t, statedb.Database().TrieDB().Commit(root, true, nil))
statedb, err := state.New(h.Root, state.NewDatabase(db), nil) statedb, err := state.New(h.Root, state.NewDatabase(db), nil)
assert.NoError(t, err) require.NoError(t, err)
return root, statedb return root, statedb
} }
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1}) require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1})
root := genesis.Root() root := genesis.Root()
// code does not change // code does not change
root, statedb = addBlock(root, 1) root, statedb = addBlock(root, 1)
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1}) require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1})
// code changes 1st time // code changes 1st time
root, statedb = addBlock(root, 2) root, statedb = addBlock(root, 2)
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2}) require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2})
// code same as 1st change // code same as 1st change
root, statedb = addBlock(root, 3) root, statedb = addBlock(root, 3)
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2}) require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2})
// code changes 2nd time // code changes 2nd time
_, statedb = addBlock(root, 4) _, statedb = addBlock(root, 4)
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x3}) require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x3})
// make sure balance change DOES NOT take effect // make sure balance change DOES NOT take effect
assert.Equal(t, statedb.GetBalance(addr0), big.NewInt(0)) require.Equal(t, statedb.GetBalance(addr0), big.NewInt(0))
} }
func TestEncodeSigHeaderJaipur(t *testing.T) { func TestEncodeSigHeaderJaipur(t *testing.T) {
@ -124,19 +126,64 @@ func TestEncodeSigHeaderJaipur(t *testing.T) {
// Jaipur NOT enabled and BaseFee not set // Jaipur NOT enabled and BaseFee not set
hash := SealHash(h, &params.BorConfig{JaipurBlock: 10}) hash := SealHash(h, &params.BorConfig{JaipurBlock: 10})
assert.Equal(t, hash, hashWithoutBaseFee) require.Equal(t, hash, hashWithoutBaseFee)
// Jaipur enabled (Jaipur=0) and BaseFee not set // Jaipur enabled (Jaipur=0) and BaseFee not set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 0}) hash = SealHash(h, &params.BorConfig{JaipurBlock: 0})
assert.Equal(t, hash, hashWithoutBaseFee) require.Equal(t, hash, hashWithoutBaseFee)
h.BaseFee = big.NewInt(2) h.BaseFee = big.NewInt(2)
// Jaipur enabled (Jaipur=Header block) and BaseFee set // Jaipur enabled (Jaipur=Header block) and BaseFee set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 1}) hash = SealHash(h, &params.BorConfig{JaipurBlock: 1})
assert.Equal(t, hash, hashWithBaseFee) require.Equal(t, hash, hashWithBaseFee)
// Jaipur NOT enabled and BaseFee set // Jaipur NOT enabled and BaseFee set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 10}) hash = SealHash(h, &params.BorConfig{JaipurBlock: 10})
assert.Equal(t, hash, hashWithoutBaseFee) 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")
}
} }

View file

@ -102,7 +102,8 @@ func (gc *GenesisContractsClient) CommitState(
func (gc *GenesisContractsClient) LastStateId(snapshotNumber uint64) (*big.Int, error) { func (gc *GenesisContractsClient) LastStateId(snapshotNumber uint64) (*big.Int, error) {
blockNr := rpc.BlockNumber(snapshotNumber) blockNr := rpc.BlockNumber(snapshotNumber)
method := "lastStateId"
const method = "lastStateId"
data, err := gc.stateReceiverABI.Pack(method) data, err := gc.stateReceiverABI.Pack(method)
if err != nil { if err != nil {

View file

@ -12,6 +12,7 @@ import (
type IHeimdallClient interface { type IHeimdallClient interface {
StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error)
Span(ctx context.Context, spanID uint64) (*span.HeimdallSpan, 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() Close()
} }

View file

@ -20,3 +20,12 @@ type CheckpointResponse struct {
Height string `json:"height"` Height string `json:"height"`
Result Checkpoint `json:"result"` Result Checkpoint `json:"result"`
} }
type CheckpointCount struct {
Result int64 `json:"result"`
}
type CheckpointCountResponse struct {
Height string `json:"height"`
Result CheckpointCount `json:"result"`
}

View file

@ -5,7 +5,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"sort" "sort"
@ -59,7 +59,8 @@ func NewHeimdallClient(urlString string) *HeimdallClient {
const ( const (
fetchStateSyncEventsFormat = "from-id=%d&to-time=%d&limit=%d" fetchStateSyncEventsFormat = "from-id=%d&to-time=%d&limit=%d"
fetchStateSyncEventsPath = "clerk/event-record/list" fetchStateSyncEventsPath = "clerk/event-record/list"
fetchLatestCheckpoint = "/checkpoints/latest" fetchCheckpoint = "/checkpoints/%s"
fetchCheckpointCount = "/checkpoints/count"
fetchSpanFormat = "bor/span/%d" fetchSpanFormat = "bor/span/%d"
) )
@ -115,9 +116,9 @@ func (h *HeimdallClient) Span(ctx context.Context, spanID uint64) (*span.Heimdal
return &response.Result, nil return &response.Result, nil
} }
// FetchLatestCheckpoint fetches the latest bor submitted checkpoint from heimdall // FetchCheckpoint fetches the checkpoint from heimdall
func (h *HeimdallClient) FetchLatestCheckpoint(ctx context.Context) (*checkpoint.Checkpoint, error) { func (h *HeimdallClient) FetchCheckpoint(ctx context.Context, number int64) (*checkpoint.Checkpoint, error) {
url, err := latestCheckpointURL(h.urlString) url, err := checkpointURL(h.urlString, number)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -130,6 +131,21 @@ func (h *HeimdallClient) FetchLatestCheckpoint(ctx context.Context) (*checkpoint
return &response.Result, nil 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 // 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) { func FetchWithRetry[T any](ctx context.Context, client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) {
// request data once // request data once
@ -210,8 +226,19 @@ func stateSyncURL(urlString string, fromID uint64, to int64) (*url.URL, error) {
return makeURL(urlString, fetchStateSyncEventsPath, queryParams) return makeURL(urlString, fetchStateSyncEventsPath, queryParams)
} }
func latestCheckpointURL(urlString string) (*url.URL, error) { func checkpointURL(urlString string, number int64) (*url.URL, error) {
return makeURL(urlString, fetchLatestCheckpoint, "") 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) { func makeURL(urlString, rawPath, rawQuery string) (*url.URL, error) {
@ -251,7 +278,7 @@ func internalFetch(ctx context.Context, client http.Client, u *url.URL) ([]byte,
} }
// get response // get response
body, err := ioutil.ReadAll(res.Body) body, err := io.ReadAll(res.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -44,7 +44,7 @@ func (c *ChainSpanner) GetCurrentSpan(ctx context.Context, headerHash common.Has
blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false) blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false)
// method // method
method := "getCurrentSpan" const method = "getCurrentSpan"
data, err := c.validatorSet.Pack(method) data, err := c.validatorSet.Pack(method)
if err != nil { if err != nil {

View file

@ -39,7 +39,7 @@ func convertTo32(input []byte) (output [32]byte) {
return return
} }
func convert(input []([32]byte)) [][]byte { func convert(input [][32]byte) [][]byte {
output := make([][]byte, 0, len(input)) output := make([][]byte, 0, len(input))
for _, in := range input { for _, in := range input {

View file

@ -131,7 +131,7 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
} }
// check if signer is in validator set // check if signer is in validator set
if !snap.ValidatorSet.HasAddress(signer.Bytes()) { if !snap.ValidatorSet.HasAddress(signer) {
return nil, &UnauthorizedSignerError{number, signer.Bytes()} return nil, &UnauthorizedSignerError{number, signer.Bytes()}
} }
@ -201,18 +201,18 @@ func (s *Snapshot) signers() []common.Address {
} }
// Difficulty returns the difficulty for a particular signer at the current snapshot number // Difficulty returns the difficulty for a particular signer at the current snapshot number
func (s *Snapshot) Difficulty(signer common.Address) uint64 { func Difficulty(validatorSet *valset.ValidatorSet, signer common.Address) uint64 {
// if signer is empty // if signer is empty
if signer == (common.Address{}) { if signer == (common.Address{}) {
return 1 return 1
} }
validators := s.ValidatorSet.Validators validators := validatorSet.Validators
proposer := s.ValidatorSet.GetProposer().Address proposer := validatorSet.GetProposer().Address
totalValidators := len(validators) totalValidators := len(validators)
proposerIndex, _ := s.ValidatorSet.GetByAddress(proposer) proposerIndex, _ := validatorSet.GetByAddress(proposer)
signerIndex, _ := s.ValidatorSet.GetByAddress(signer) signerIndex, _ := validatorSet.GetByAddress(signer)
// temp index // temp index
tempIndex := signerIndex tempIndex := signerIndex

View file

@ -6,7 +6,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/require"
"pgregory.net/rapid" "pgregory.net/rapid"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -34,7 +34,7 @@ func TestGetSignerSuccessionNumber_ProposerIsSigner(t *testing.T) {
t.Fatalf("%s", err) t.Fatalf("%s", err)
} }
assert.Equal(t, 0, successionNumber) require.Equal(t, 0, successionNumber)
} }
func TestGetSignerSuccessionNumber_SignerIndexIsLarger(t *testing.T) { func TestGetSignerSuccessionNumber_SignerIndexIsLarger(t *testing.T) {
@ -60,7 +60,7 @@ func TestGetSignerSuccessionNumber_SignerIndexIsLarger(t *testing.T) {
t.Fatalf("%s", err) t.Fatalf("%s", err)
} }
assert.Equal(t, signerIndex-proposerIndex, successionNumber) require.Equal(t, signerIndex-proposerIndex, successionNumber)
} }
func TestGetSignerSuccessionNumber_SignerIndexIsSmaller(t *testing.T) { func TestGetSignerSuccessionNumber_SignerIndexIsSmaller(t *testing.T) {
@ -82,7 +82,7 @@ func TestGetSignerSuccessionNumber_SignerIndexIsSmaller(t *testing.T) {
t.Fatalf("%s", err) t.Fatalf("%s", err)
} }
assert.Equal(t, signerIndex+numVals-proposerIndex, successionNumber) require.Equal(t, signerIndex+numVals-proposerIndex, successionNumber)
} }
func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) { func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) {
@ -93,6 +93,8 @@ func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) {
ValidatorSet: valset.NewValidatorSet(validators), ValidatorSet: valset.NewValidatorSet(validators),
} }
require.Len(t, snap.ValidatorSet.Validators, numVals)
dummyProposerAddress := randomAddress() dummyProposerAddress := randomAddress()
snap.ValidatorSet.Proposer = &valset.Validator{Address: dummyProposerAddress} snap.ValidatorSet.Proposer = &valset.Validator{Address: dummyProposerAddress}
@ -100,11 +102,11 @@ func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) {
signer := snap.ValidatorSet.Validators[3].Address signer := snap.ValidatorSet.Validators[3].Address
_, err := snap.GetSignerSuccessionNumber(signer) _, err := snap.GetSignerSuccessionNumber(signer)
assert.NotNil(t, err) require.NotNil(t, err)
e, ok := err.(*UnauthorizedProposerError) e, ok := err.(*UnauthorizedProposerError)
assert.True(t, ok) require.True(t, ok)
assert.Equal(t, dummyProposerAddress.Bytes(), e.Proposer) require.Equal(t, dummyProposerAddress.Bytes(), e.Proposer)
} }
func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) { func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) {
@ -116,10 +118,10 @@ func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) {
} }
dummySignerAddress := randomAddress() dummySignerAddress := randomAddress()
_, err := snap.GetSignerSuccessionNumber(dummySignerAddress) _, err := snap.GetSignerSuccessionNumber(dummySignerAddress)
assert.NotNil(t, err) require.NotNil(t, err)
e, ok := err.(*UnauthorizedSignerError) e, ok := err.(*UnauthorizedSignerError)
assert.True(t, ok) require.True(t, ok)
assert.Equal(t, dummySignerAddress.Bytes(), e.Signer) require.Equal(t, dummySignerAddress.Bytes(), e.Signer)
} }
// nolint: unparam // nolint: unparam

View file

@ -47,21 +47,26 @@ func (v *Validator) Cmp(other *Validator) *Validator {
return v return v
} }
// nolint:nestif
if v.ProposerPriority > other.ProposerPriority { if v.ProposerPriority > other.ProposerPriority {
return v return v
} else if v.ProposerPriority < other.ProposerPriority {
return other
} else {
result := bytes.Compare(v.Address.Bytes(), other.Address.Bytes())
if result < 0 {
return v
} else if result > 0 {
return other
} else {
panic("Cannot compare identical validators")
}
} }
if v.ProposerPriority < other.ProposerPriority {
return other
}
result := bytes.Compare(v.Address.Bytes(), other.Address.Bytes())
if result == 0 {
panic("Cannot compare identical validators")
}
if result < 0 {
return v
}
// result > 0
return other
} }
func (v *Validator) String() string { func (v *Validator) String() string {

View file

@ -46,6 +46,7 @@ type ValidatorSet struct {
// cached (unexported) // cached (unexported)
totalVotingPower int64 totalVotingPower int64
validatorsMap map[common.Address]int // address -> index
} }
// NewValidatorSet initializes a ValidatorSet by copying over the // NewValidatorSet initializes a ValidatorSet by copying over the
@ -54,7 +55,7 @@ type ValidatorSet struct {
// The addresses of validators in `valz` must be unique otherwise the // The addresses of validators in `valz` must be unique otherwise the
// function panics. // function panics.
func NewValidatorSet(valz []*Validator) *ValidatorSet { func NewValidatorSet(valz []*Validator) *ValidatorSet {
vals := &ValidatorSet{} vals := &ValidatorSet{validatorsMap: make(map[common.Address]int)}
err := vals.updateWithChangeSet(valz, false) err := vals.updateWithChangeSet(valz, false)
if err != nil { if err != nil {
@ -232,30 +233,34 @@ func validatorListCopy(valsList []*Validator) []*Validator {
// Copy each validator into a new ValidatorSet. // Copy each validator into a new ValidatorSet.
func (vals *ValidatorSet) Copy() *ValidatorSet { func (vals *ValidatorSet) Copy() *ValidatorSet {
valCopy := validatorListCopy(vals.Validators)
validatorsMap := make(map[common.Address]int, len(vals.Validators))
for i, val := range valCopy {
validatorsMap[val.Address] = i
}
return &ValidatorSet{ return &ValidatorSet{
Validators: validatorListCopy(vals.Validators), Validators: validatorListCopy(vals.Validators),
Proposer: vals.Proposer, Proposer: vals.Proposer,
totalVotingPower: vals.totalVotingPower, totalVotingPower: vals.totalVotingPower,
validatorsMap: validatorsMap,
} }
} }
// HasAddress returns true if address given is in the validator set, false - // HasAddress returns true if address given is in the validator set, false -
// otherwise. // otherwise.
func (vals *ValidatorSet) HasAddress(address []byte) bool { func (vals *ValidatorSet) HasAddress(address common.Address) bool {
idx := sort.Search(len(vals.Validators), func(i int) bool { _, ok := vals.validatorsMap[address]
return bytes.Compare(address, vals.Validators[i].Address.Bytes()) <= 0
})
return idx < len(vals.Validators) && bytes.Equal(vals.Validators[idx].Address.Bytes(), address) return ok
} }
// GetByAddress returns an index of the validator with address and validator // GetByAddress returns an index of the validator with address and validator
// itself if found. Otherwise, -1 and nil are returned. // itself if found. Otherwise, -1 and nil are returned.
func (vals *ValidatorSet) GetByAddress(address common.Address) (index int, val *Validator) { func (vals *ValidatorSet) GetByAddress(address common.Address) (index int, val *Validator) {
idx := sort.Search(len(vals.Validators), func(i int) bool { idx, ok := vals.validatorsMap[address]
return bytes.Compare(address.Bytes(), vals.Validators[i].Address.Bytes()) <= 0 if ok {
})
if idx < len(vals.Validators) && vals.Validators[idx].Address == address {
return idx, vals.Validators[idx].Copy() return idx, vals.Validators[idx].Copy()
} }
@ -265,14 +270,14 @@ func (vals *ValidatorSet) GetByAddress(address common.Address) (index int, val *
// GetByIndex returns the validator's address and validator itself by index. // GetByIndex returns the validator's address and validator itself by index.
// It returns nil values if index is less than 0 or greater or equal to // It returns nil values if index is less than 0 or greater or equal to
// len(ValidatorSet.Validators). // len(ValidatorSet.Validators).
func (vals *ValidatorSet) GetByIndex(index int) (address []byte, val *Validator) { func (vals *ValidatorSet) GetByIndex(index int) (address common.Address, val *Validator) {
if index < 0 || index >= len(vals.Validators) { if index < 0 || index >= len(vals.Validators) {
return nil, nil return common.Address{}, nil
} }
val = vals.Validators[index] val = vals.Validators[index]
return val.Address.Bytes(), val.Copy() return val.Address, val.Copy()
} }
// Size returns the length of the validator set. // Size returns the length of the validator set.
@ -328,7 +333,7 @@ func (vals *ValidatorSet) GetProposer() (proposer *Validator) {
func (vals *ValidatorSet) findProposer() *Validator { func (vals *ValidatorSet) findProposer() *Validator {
var proposer *Validator var proposer *Validator
for _, val := range vals.Validators { for _, val := range vals.Validators {
if proposer == nil || !bytes.Equal(val.Address.Bytes(), proposer.Address.Bytes()) { if proposer == nil || val.Address != proposer.Address {
proposer = proposer.Cmp(val) proposer = proposer.Cmp(val)
} }
} }
@ -371,14 +376,19 @@ func processChanges(origChanges []*Validator) (updates, removals []*Validator, e
changes := validatorListCopy(origChanges) changes := validatorListCopy(origChanges)
sort.Sort(ValidatorsByAddress(changes)) sort.Sort(ValidatorsByAddress(changes))
removals = make([]*Validator, 0, len(changes)) sliceCap := len(changes) / 2
updates = make([]*Validator, 0, len(changes)) if sliceCap == 0 {
sliceCap = 1
}
removals = make([]*Validator, 0, sliceCap)
updates = make([]*Validator, 0, sliceCap)
var prevAddr common.Address var prevAddr common.Address
// Scan changes by address and append valid validators to updates or removals lists. // Scan changes by address and append valid validators to updates or removals lists.
for _, valUpdate := range changes { for _, valUpdate := range changes {
if bytes.Equal(valUpdate.Address.Bytes(), prevAddr.Bytes()) { if valUpdate.Address == prevAddr {
err = fmt.Errorf("duplicate entry %v in %v", valUpdate, changes) err = fmt.Errorf("duplicate entry %v in %v", valUpdate, changes)
return nil, nil, err return nil, nil, err
} }
@ -489,10 +499,11 @@ func (vals *ValidatorSet) applyUpdates(updates []*Validator) {
} else { } else {
// Apply add or update. // Apply add or update.
merged[i] = updates[0] merged[i] = updates[0]
if bytes.Equal(existing[0].Address.Bytes(), updates[0].Address.Bytes()) { if existing[0].Address == updates[0].Address {
// Validator is present in both, advance existing. // Validator is present in both, advance existing.
existing = existing[1:] existing = existing[1:]
} }
updates = updates[1:] updates = updates[1:]
} }
i++ i++
@ -503,6 +514,7 @@ func (vals *ValidatorSet) applyUpdates(updates []*Validator) {
merged[i] = existing[j] merged[i] = existing[j]
i++ i++
} }
// OR add updates which are left. // OR add updates which are left.
for j := 0; j < len(updates); j++ { for j := 0; j < len(updates); j++ {
merged[i] = updates[j] merged[i] = updates[j]
@ -541,7 +553,7 @@ func (vals *ValidatorSet) applyRemovals(deletes []*Validator) {
// Loop over deletes until we removed all of them. // Loop over deletes until we removed all of them.
for len(deletes) > 0 { for len(deletes) > 0 {
if bytes.Equal(existing[0].Address.Bytes(), deletes[0].Address.Bytes()) { if existing[0].Address == deletes[0].Address {
deletes = deletes[1:] deletes = deletes[1:]
} else { // Leave it in the resulting slice. } else { // Leave it in the resulting slice.
merged[i] = existing[0] merged[i] = existing[0]
@ -599,8 +611,7 @@ func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes
computeNewPriorities(updates, vals, updatedTotalVotingPower) computeNewPriorities(updates, vals, updatedTotalVotingPower)
// Apply updates and removals. // Apply updates and removals.
vals.applyUpdates(updates) vals.updateValidators(updates, deletes)
vals.applyRemovals(deletes)
if err := vals.UpdateTotalVotingPower(); err != nil { if err := vals.UpdateTotalVotingPower(); err != nil {
return err return err
@ -613,6 +624,17 @@ func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes
return nil return nil
} }
func (vals *ValidatorSet) updateValidators(updates []*Validator, deletes []*Validator) {
vals.applyUpdates(updates)
vals.applyRemovals(deletes)
vals.validatorsMap = make(map[common.Address]int, len(vals.Validators))
for i, val := range vals.Validators {
vals.validatorsMap[val.Address] = i
}
}
// UpdateWithChangeSet attempts to update the validator set with 'changes'. // UpdateWithChangeSet attempts to update the validator set with 'changes'.
// It performs the following steps: // It performs the following steps:
// - validates the changes making sure there are no duplicates and splits them in updates and deletes // - validates the changes making sure there are no duplicates and splits them in updates and deletes
@ -661,7 +683,7 @@ func (vals *ValidatorSet) StringIndented(indent string) string {
return "nil-ValidatorSet" return "nil-ValidatorSet"
} }
var valStrings []string valStrings := make([]string, 0, len(vals.Validators))
vals.Iterate(func(index int, val *Validator) bool { vals.Iterate(func(index int, val *Validator) bool {
valStrings = append(valStrings, val.String()) valStrings = append(valStrings, val.String())

View file

@ -0,0 +1,199 @@
package valset
import (
"testing"
"github.com/stretchr/testify/require"
"gotest.tools/assert"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
func NewValidatorFromKey(key string, votingPower int64) *Validator {
privKey, _ := crypto.HexToECDSA(key)
return NewValidator(crypto.PubkeyToAddress(privKey.PublicKey), votingPower)
}
func GetValidators() [4]*Validator {
const (
// addr0 = 0x96C42C56fdb78294F96B0cFa33c92bed7D75F96a
signer0 = "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd9"
// addr1 = 0x98925BE497f6dFF6A5a33dDA8B5933cA35262d69
signer1 = "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd8"
//addr2 = 0x648Cf2A5b119E2c04061021834F8f75735B1D36b
signer2 = "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd7"
//addr3 = 0x168f220B3b313D456eD4797520eFdFA9c57E6C45
signer3 = "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd6"
)
return [4]*Validator{
NewValidatorFromKey(signer0, 100),
NewValidatorFromKey(signer1, 200),
NewValidatorFromKey(signer2, 300),
NewValidatorFromKey(signer3, 400),
}
}
func TestIncrementProposerPriority(t *testing.T) {
t.Parallel()
vals := GetValidators()
// Validator set length = 1
valSet := NewValidatorSet(vals[:1])
expectedPropsers := []*Validator{vals[0], vals[0], vals[0], vals[0], vals[0], vals[0], vals[0], vals[0], vals[0], vals[0]}
for i := 0; i < 10; i++ {
valSet.IncrementProposerPriority(1)
require.Equal(t, expectedPropsers[i].Address, valSet.GetProposer().Address)
}
// Validator set length = 2
valSet = NewValidatorSet(vals[:2])
expectedPropsers = []*Validator{vals[0], vals[1], vals[1], vals[0], vals[1], vals[1], vals[0], vals[1], vals[1], vals[0]}
for i := 0; i < 10; i++ {
valSet.IncrementProposerPriority(1)
require.Equal(t, expectedPropsers[i].Address, valSet.GetProposer().Address)
}
// Validator set length = 3
valSet = NewValidatorSet(vals[:3])
expectedPropsers = []*Validator{vals[1], vals[2], vals[0], vals[1], vals[2], vals[2], vals[1], vals[2], vals[0], vals[1]}
for i := 0; i < 10; i++ {
valSet.IncrementProposerPriority(1)
require.Equal(t, expectedPropsers[i].Address, valSet.GetProposer().Address)
}
// Validator set length = 4
valSet = NewValidatorSet(vals[:4])
expectedPropsers = []*Validator{vals[2], vals[1], vals[3], vals[2], vals[0], vals[3], vals[1], vals[2], vals[3], vals[3]}
for i := 0; i < 10; i++ {
valSet.IncrementProposerPriority(1)
require.Equal(t, expectedPropsers[i].Address, valSet.GetProposer().Address)
}
}
func TestRescalePriorities(t *testing.T) {
t.Parallel()
vals := GetValidators()
// Validator set length = 1
valSet := NewValidatorSet(vals[:1])
valSet.RescalePriorities(10)
expectedPriorities := []int64{0}
for i, val := range valSet.Validators {
require.Equal(t, expectedPriorities[i], val.ProposerPriority)
}
// Validator set length = 2
valSet = NewValidatorSet(vals[:2])
valSet.RescalePriorities(100)
expectedPriorities = []int64{50, -50}
for i, val := range valSet.Validators {
require.Equal(t, expectedPriorities[i], val.ProposerPriority)
}
// Validator set length = 3
valSet = NewValidatorSet(vals[:3])
valSet.RescalePriorities(30)
expectedPriorities = []int64{-17, 5, 11}
for i, val := range valSet.Validators {
require.Equal(t, expectedPriorities[i], val.ProposerPriority)
}
// Validator set length = 4
valSet = NewValidatorSet(vals[:4])
valSet.RescalePriorities(10)
expectedPriorities = []int64{-6, 3, 1, 2}
for i, val := range valSet.Validators {
require.Equal(t, expectedPriorities[i], val.ProposerPriority)
}
}
func TestGetValidatorByAddressAndIndex(t *testing.T) {
t.Parallel()
vals := GetValidators()
valSet := NewValidatorSet(vals[:4])
for _, val := range valSet.Validators {
idx, valByAddress := valSet.GetByAddress(val.Address)
addr, valByIndex := valSet.GetByIndex(idx)
assert.DeepEqual(t, val, valByIndex)
assert.DeepEqual(t, val, valByAddress)
assert.DeepEqual(t, val.Address, addr)
}
tempAddress := common.HexToAddress("0x12345")
// Negative Testcase
idx, _ := valSet.GetByAddress(tempAddress)
require.Equal(t, idx, -1)
// checking for validator index out of range
addr, _ := valSet.GetByIndex(100)
require.Equal(t, addr, common.Address{})
}
func TestUpdateWithChangeSet(t *testing.T) {
t.Parallel()
vals := GetValidators()
valSet := NewValidatorSet(vals[:4])
// halved the power of vals[2] and doubled the power of vals[3]
vals[2].VotingPower = 150
vals[3].VotingPower = 800
// Adding new temp validator in the set
const tempSigner = "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd5"
tempVal := NewValidatorFromKey(tempSigner, 250)
// check totalVotingPower before updating validator set
require.Equal(t, int64(1000), valSet.TotalVotingPower())
err := valSet.UpdateWithChangeSet([]*Validator{vals[2], vals[3], tempVal})
require.NoError(t, err)
// check totalVotingPower after updating validator set
require.Equal(t, int64(1500), valSet.TotalVotingPower())
_, updatedVal2 := valSet.GetByAddress(vals[2].Address)
require.Equal(t, int64(150), updatedVal2.VotingPower)
_, updatedVal3 := valSet.GetByAddress(vals[3].Address)
require.Equal(t, int64(800), updatedVal3.VotingPower)
_, updatedTempVal := valSet.GetByAddress(tempVal.Address)
require.Equal(t, int64(250), updatedTempVal.VotingPower)
}

View file

@ -55,7 +55,7 @@ func TestReimportMirroredState(t *testing.T) {
genesis := genspec.MustCommit(db) genesis := genspec.MustCommit(db)
// Generate a batch of blocks, each properly signed // 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() defer chain.Stop()
blocks, _ := core.GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db, 3, func(i int, block *core.BlockGen) { 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() db = rawdb.NewMemoryDatabase()
genspec.MustCommit(db) 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() defer chain.Stop()
if _, err := chain.InsertChain(blocks[:2]); err != nil { 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 // 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 // flushing the dirty states out. Insert the last block, triggering a sidechain
// reimport. // 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() defer chain.Stop()
if _, err := chain.InsertChain(blocks[2:]); err != nil { if _, err := chain.InsertChain(blocks[2:]); err != nil {

View file

@ -450,7 +450,7 @@ func TestClique(t *testing.T) {
batches[len(batches)-1] = append(batches[len(batches)-1], block) batches[len(batches)-1] = append(batches[len(batches)-1], block)
} }
// Pass all the headers through clique and ensure tallying succeeds // 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 { if err != nil {
t.Errorf("test %d: failed to create test chain: %v", i, err) t.Errorf("test %d: failed to create test chain: %v", i, err)
continue continue

View file

@ -201,7 +201,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Time the insertion of the new chain. // Time the insertion of the new chain.
// State and blocks are stored in the same DB. // 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() defer chainman.Stop()
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
@ -317,7 +317,8 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil { if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err) 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 { if err != nil {
b.Fatalf("error creating chain: %v", err) b.Fatalf("error creating chain: %v", err)
} }

View file

@ -49,7 +49,7 @@ func TestHeaderVerification(t *testing.T) {
headers[i] = block.Header() headers[i] = block.Header()
} }
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces // 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() defer chain.Stop()
for i := 0; i < len(blocks); i++ { 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)) 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 // 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() defer chain.Stop()
// Verify the blocks before the merging // Verify the blocks before the merging
@ -279,11 +279,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
var results <-chan error var results <-chan error
if valid { 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) _, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop() chain.Stop()
} else { } 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) _, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop() chain.Stop()
} }
@ -346,7 +346,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
defer runtime.GOMAXPROCS(old) defer runtime.GOMAXPROCS(old)
// Start the verifications and immediately abort // 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() defer chain.Stop()
abort, results := chain.engine.VerifyHeaders(chain, headers, seals) abort, results := chain.engine.VerifyHeaders(chain, headers, seals)

View file

@ -29,6 +29,7 @@ import (
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/common/prque" "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/state/snapshot"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "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/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/syncx" "github.com/ethereum/go-ethereum/internal/syncx"
@ -221,7 +223,8 @@ type BlockChain struct {
// NewBlockChain returns a fully initialised block chain using information // NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialises the default Ethereum Validator // available in the database. It initialises the default Ethereum Validator
// and Processor. // 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 { if cacheConfig == nil {
cacheConfig = DefaultCacheConfig cacheConfig = DefaultCacheConfig
} }
@ -257,7 +260,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
borReceiptsCache: borReceiptsCache, borReceiptsCache: borReceiptsCache,
} }
bc.forker = NewForkChoice(bc, shouldPreserve) bc.forker = NewForkChoice(bc, shouldPreserve, checker)
bc.validator = NewBlockValidator(chainConfig, bc, engine) bc.validator = NewBlockValidator(chainConfig, bc, engine)
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine) bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
bc.processor = NewStateProcessor(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 ancientBlocks, liveBlocks types.Blocks
ancientReceipts, liveReceipts []types.Receipts ancientReceipts, liveReceipts []types.Receipts
) )
// Do a sanity check that the provided chain is actually ordered and linked // Do a sanity check that the provided chain is actually ordered and linked
for i := 0; i < len(blockChain); i++ { for i := 0; i < len(blockChain); i++ {
if i != 0 { 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 // updateHead updates the head fast sync block if the inserted blocks are better
// and returns an indicator whether the inserted blocks are canonical. // 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() { if !bc.chainmu.TryLock() {
return false return false
} }
@ -948,6 +952,14 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
} else if !reorg { } else if !reorg {
return false 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()) rawdb.WriteHeadFastBlockHash(bc.db, head.Hash())
bc.currentFastBlock.Store(head) bc.currentFastBlock.Store(head)
headFastBlockGauge.Update(int64(head.NumberU64())) 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]) 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{} borReceipts := []types.Receipts{}
var headers []*types.Header
for _, block := range blockChain { for _, block := range blockChain {
borReceipts = append(borReceipts, []*types.Receipt{bc.GetBorReceiptByHash(block.Hash())}) borReceipts = append(borReceipts, []*types.Receipt{bc.GetBorReceiptByHash(block.Hash())})
headers = append(headers, block.Header())
} }
// Write all chain data to ancients. // 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. // Update the current fast block because all block data is now present in DB.
previousFastBlock := bc.CurrentFastBlock().NumberU64() 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 // We end up here if the header chain has reorg'ed, and the blocks/receipts
// don't match the canonical chain. // don't match the canonical chain.
if err := bc.db.TruncateHead(previousFastBlock + 1); err != nil { 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) { writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
skipPresenceCheck := false skipPresenceCheck := false
batch := bc.db.NewBatch() batch := bc.db.NewBatch()
headers := make([]*types.Header, 0, len(blockChain))
for i, block := range 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 // Short circuit insertion if shutting down or processing failed
if bc.insertStopped() { if bc.insertStopped() {
return 0, errInsertionInterrupted return 0, errInsertionInterrupted
@ -1122,7 +1142,8 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
return 0, err return 0, err
} }
} }
updateHead(blockChain[len(blockChain)-1])
updateHead(blockChain[len(blockChain)-1], headers)
return 0, nil return 0, nil
} }
@ -1491,6 +1512,18 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
it := newInsertIterator(chain, results, bc.validator) it := newInsertIterator(chain, results, bc.validator)
block, err := it.next() 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 // Left-trim all the known blocks that don't need to build snapshot
if bc.skipBlock(err, it) { if bc.skipBlock(err, it) {
// First block (and state) is known // First block (and state) is known
@ -1833,6 +1866,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
externTd *big.Int externTd *big.Int
lastBlock = block lastBlock = block
current = bc.CurrentBlock() current = bc.CurrentBlock()
headers []*types.Header
) )
// The first sidechain block error is already verified to be ErrPrunedAncestor. // The first sidechain block error is already verified to be ErrPrunedAncestor.
// Since we don't import them here, we expect ErrUnknownAncestor for the remaining // 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. // to disk.
err := consensus.ErrPrunedAncestor err := consensus.ErrPrunedAncestor
for ; block != nil && errors.Is(err, consensus.ErrPrunedAncestor); block, err = it.next() { 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 // Check the canonical state root for that number
if number := block.NumberU64(); current.NumberU64() >= number { if number := block.NumberU64(); current.NumberU64() >= number {
canonical := bc.GetBlockByNumber(number) canonical := bc.GetBlockByNumber(number)
@ -1895,7 +1930,13 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
if err != nil { if err != nil {
return it.index, err 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()) 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) log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
return it.index, err return it.index, err

View file

@ -27,7 +27,7 @@ func TestChain2HeadEvent(t *testing.T) {
signer = types.LatestSigner(gspec.Config) 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() defer blockchain.Stop()
chain2HeadCh := make(chan Chain2HeadEvent, 64) chain2HeadCh := make(chan Chain2HeadEvent, 64)

View file

@ -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 // 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 // Create and inject the requested chain
if n == 0 { if n == 0 {
return db, blockchain, nil return db, blockchain, nil
@ -655,7 +655,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
blockchain.Stop() blockchain.Stop()
// Create a new BlockChain and check that it rolled back the state. // 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 { if err != nil {
t.Fatalf("failed to create new chain manager: %v", err) 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 // Import the chain as an archive node for the comparison baseline
archiveDb := rawdb.NewMemoryDatabase() archiveDb := rawdb.NewMemoryDatabase()
gspec.MustCommit(archiveDb) 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() defer archive.Stop()
if n, err := archive.InsertChain(blocks); err != nil { 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 // Fast import the chain as a non-archive node to test
fastDb := rawdb.NewMemoryDatabase() fastDb := rawdb.NewMemoryDatabase()
gspec.MustCommit(fastDb) 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() defer fast.Stop()
headers := make([]*types.Header, len(blocks)) 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) t.Fatalf("failed to create temp freezer db: %v", err)
} }
gspec.MustCommit(ancientDb) 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() defer ancient.Stop()
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil { if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
@ -923,7 +923,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
archiveCaching := *DefaultCacheConfig archiveCaching := *DefaultCacheConfig
archiveCaching.TrieDirtyDisabled = true 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 { if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err) 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 // Import the chain as a non-archive node and ensure all pointers are updated
fastDb, delfn := makeDb() fastDb, delfn := makeDb()
defer delfn() 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() defer fast.Stop()
headers := make([]*types.Header, len(blocks)) 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 // Import the chain as a ancient-first node and ensure all pointers are updated
ancientDb, delfn := makeDb() ancientDb, delfn := makeDb()
defer delfn() 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() defer ancient.Stop()
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil { 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 // Import the chain as a light node and ensure all pointers are updated
lightDb, delfn := makeDb() lightDb, delfn := makeDb()
defer delfn() 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 { if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err) 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. // 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 { if i, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err) 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) 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() defer blockchain.Stop()
rmLogsCh := make(chan RemovedLogsEvent) rmLogsCh := make(chan RemovedLogsEvent)
@ -1167,7 +1170,7 @@ func TestLogRebirth(t *testing.T) {
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
signer = types.LatestSigner(gspec.Config) signer = types.LatestSigner(gspec.Config)
engine = ethash.NewFaker() 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() defer blockchain.Stop()
@ -1230,7 +1233,7 @@ func TestSideLogRebirth(t *testing.T) {
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}} gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
signer = types.LatestSigner(gspec.Config) 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() defer blockchain.Stop()
@ -1305,7 +1308,7 @@ func TestReorgSideEvent(t *testing.T) {
signer = types.LatestSigner(gspec.Config) 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() defer blockchain.Stop()
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) 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) 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() defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) { 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) 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() defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) { 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() diskdb := rawdb.NewMemoryDatabase()
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) (&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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -1664,7 +1668,7 @@ func TestTrieForkGC(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) (&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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -1703,7 +1707,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) (&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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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) t.Fatalf("failed to create temp freezer db: %v", err)
} }
gspec.MustCommit(ancientDb) 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)) headers := make([]*types.Header, len(blocks))
for i, block := range blocks { for i, block := range blocks {
@ -1784,7 +1788,7 @@ func TestBlockchainRecovery(t *testing.T) {
rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash()) rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())
// Reopen broken blockchain again // 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() defer ancient.Stop()
if num := ancient.CurrentBlock().NumberU64(); num != 0 { if num := ancient.CurrentBlock().NumberU64(); num != 0 {
t.Errorf("head block mismatch: have #%v, want #%v", 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 := Genesis{Config: params.AllEthashProtocolChanges}
gspec.MustCommit(ancientDb) 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() defer ancientChain.Stop()
// Import the canonical header chain. // Import the canonical header chain.
@ -1897,7 +1901,7 @@ func TestLowDiffLongChain(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) (&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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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 // Generate and import the canonical chain
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb) 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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) (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb)
defer os.RemoveAll(dir) 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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) (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb)
defer os.RemoveAll(dir) 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -2384,7 +2388,7 @@ func getLongAndShortChains() (bc *BlockChain, longChain []*types.Block, heavyCha
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) (&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 { if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err) 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 // Import all blocks into ancient db
l := uint64(0) 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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) t.Fatalf("failed to create temp freezer db: %v", err)
} }
gspec.MustCommit(ancientDb) 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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 */} 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} tails := []uint64{0, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */, 69 /* 132 - 64 + 1 */, 0}
for i, l := range limit { 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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. // Import all blocks into ancient db, only HEAD-32 indices are kept.
l := uint64(32) 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -2768,7 +2772,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb) 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 { if err != nil {
b.Fatalf("failed to create tester chain: %v", err) b.Fatalf("failed to create tester chain: %v", err)
} }
@ -2851,7 +2855,7 @@ func TestSideImportPrunedBlocks(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) (&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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -2945,7 +2949,7 @@ func TestDeleteCreateRevert(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb) 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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{ chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
Debug: true, Debug: true,
Tracer: logger.NewJSONLogger(nil, os.Stdout), Tracer: logger.NewJSONLogger(nil, os.Stdout),
}, nil, nil) }, nil, nil, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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{ chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
Debug: true, Debug: true,
Tracer: logger.NewJSONLogger(nil, os.Stdout), Tracer: logger.NewJSONLogger(nil, os.Stdout),
}, nil, nil) }, nil, nil, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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{ chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
//Debug: true, //Debug: true,
//Tracer: vm.NewJSONLogger(nil, os.Stdout), //Tracer: vm.NewJSONLogger(nil, os.Stdout),
}, nil, nil) }, nil, nil, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) 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{ chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
//Debug: true, //Debug: true,
//Tracer: vm.NewJSONLogger(nil, os.Stdout), //Tracer: vm.NewJSONLogger(nil, os.Stdout),
}, nil, nil) }, nil, nil, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -3533,7 +3537,7 @@ func TestEIP2718Transition(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb) 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -3628,7 +3632,7 @@ func TestEIP1559Transition(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb) 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }

View file

@ -79,7 +79,7 @@ func ExampleGenerateChain() {
}) })
// Import the chain. This runs all block validation rules. // 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() defer blockchain.Stop()
if i, err := blockchain.InsertChain(chain); err != nil { if i, err := blockchain.InsertChain(chain); err != nil {

View file

@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
proConf.DAOForkBlock = forkBlock proConf.DAOForkBlock = forkBlock
proConf.DAOForkSupport = true 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() defer proBc.Stop()
conDb := rawdb.NewMemoryDatabase() conDb := rawdb.NewMemoryDatabase()
@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
conConf.DAOForkBlock = forkBlock conConf.DAOForkBlock = forkBlock
conConf.DAOForkSupport = false 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() defer conBc.Stop()
if _, err := proBc.InsertChain(prefix); err != nil { 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 // Create a pro-fork block, and try to feed into the no-fork chain
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
gspec.MustCommit(db) 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() defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) 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 // Create a no-fork block, and try to feed into the pro-fork chain
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
gspec.MustCommit(db) 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() defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) 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 // Verify that contra-forkers accept pro-fork extra-datas after forking finishes
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
gspec.MustCommit(db) 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() defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) 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 // Verify that pro-forkers accept contra-fork extra-datas after forking finishes
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
gspec.MustCommit(db) 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() defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))

View file

@ -22,6 +22,7 @@ import (
"math/big" "math/big"
mrand "math/rand" mrand "math/rand"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types" "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 // local td is equal to the extern one. It can be nil for light
// client // client
preserve func(header *types.Header) bool 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 a fast but crypto originating random generator
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
if err != nil { if err != nil {
log.Crit("Failed to initialize random seed", "err", err) log.Crit("Failed to initialize random seed", "err", err)
} }
return &ForkChoice{ return &ForkChoice{
chain: chainReader, chain: chainReader,
rand: mrand.New(mrand.NewSource(seed.Int64())), rand: mrand.New(mrand.NewSource(seed.Int64())), //nolint:gosec
preserve: preserve, preserve: preserve,
validator: validator,
} }
} }
@ -106,3 +110,15 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, header *types.Header) (b
} }
return reorg, nil 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
View 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 &params.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
}

View file

@ -117,7 +117,7 @@ func TestSetupGenesis(t *testing.T) {
// Advance to block #4, past the homestead transition block of customg. // Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db) 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() defer bc.Stop()
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil) blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)

View file

@ -283,8 +283,10 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *F
lastHeader: lastHeader, lastHeader: lastHeader,
} }
) )
// Ask the fork choicer if the reorg is necessary // 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 return nil, err
} else if !reorg { } else if !reorg {
if inserted != 0 { if inserted != 0 {
@ -292,6 +294,16 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *F
} }
return result, nil 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 // Special case, all the inserted headers are already on the canonical
// header chain, skip the reorg operation. // header chain, skip the reorg operation.
if hc.GetCanonicalHash(lastHeader.Number.Uint64()) == lastHash && lastHeader.Number.Uint64() <= hc.CurrentHeader().Number.Uint64() { if hc.GetCanonicalHash(lastHeader.Number.Uint64()) == lastHash && lastHeader.Number.Uint64() <= hc.CurrentHeader().Number.Uint64() {

View file

@ -84,7 +84,7 @@ func TestHeaderInsertion(t *testing.T) {
chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10) chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10)
log.Root().SetHandler(log.StdoutHandler) log.Root().SetHandler(log.StdoutHandler)
forker := NewForkChoice(hc, nil) forker := NewForkChoice(hc, nil, nil)
// Inserting 64 headers on an empty chain, expecting // Inserting 64 headers on an empty chain, expecting
// 1 callbacks, 1 canon-status, 0 sidestatus, // 1 callbacks, 1 canon-status, 0 sidestatus,
testInsert(t, hc, chainA[:64], CanonStatTy, nil, forker) testInsert(t, hc, chainA[:64], CanonStatTy, nil, forker)

View file

@ -18,7 +18,11 @@ var (
getDerivedBorTxHash = types.GetDerivedBorTxHash getDerivedBorTxHash = types.GetDerivedBorTxHash
// borTxLookupPrefix + hash -> transaction/receipt lookup metadata // borTxLookupPrefix + hash -> transaction/receipt lookup metadata
borTxLookupPrefix = []byte("matic-bor-tx-lookup-") borTxLookupPrefix = []byte(borTxLookupPrefixStr)
)
const (
borTxLookupPrefixStr = "matic-bor-tx-lookup-"
// freezerBorReceiptTable indicates the name of the freezer bor receipts table. // freezerBorReceiptTable indicates the name of the freezer bor receipts table.
freezerBorReceiptTable = "matic-bor-receipts" freezerBorReceiptTable = "matic-bor-receipts"

View file

@ -601,8 +601,8 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
// CreateAccount is called during the EVM CREATE operation. The situation might arise that // CreateAccount is called during the EVM CREATE operation. The situation might arise that
// a contract does the following: // a contract does the following:
// //
// 1. sends funds to sha(account ++ (nonce + 1)) // 1. sends funds to sha(account ++ (nonce + 1))
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) // 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
// //
// Carrying over the balance ensures that Ether doesn't disappear. // Carrying over the balance ensures that Ether doesn't disappear.
func (s *StateDB) CreateAccount(addr common.Address) { func (s *StateDB) CreateAccount(addr common.Address) {

View file

@ -94,7 +94,7 @@ func TestStateProcessorErrors(t *testing.T) {
}, },
} }
genesis = gspec.MustCommit(db) 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() defer blockchain.Stop()
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
@ -235,7 +235,7 @@ func TestStateProcessorErrors(t *testing.T) {
}, },
} }
genesis = gspec.MustCommit(db) 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() defer blockchain.Stop()
for i, tt := range []struct { for i, tt := range []struct {
@ -275,7 +275,7 @@ func TestStateProcessorErrors(t *testing.T) {
}, },
} }
genesis = gspec.MustCommit(db) 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() defer blockchain.Stop()
for i, tt := range []struct { for i, tt := range []struct {

View file

@ -1891,7 +1891,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
defer db.Close() 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) 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 { if err != nil {
t.Fatalf("Failed to create chain: %v", err) t.Fatalf("Failed to create chain: %v", err)
} }
@ -2024,7 +2024,7 @@ func TestIssue23496(t *testing.T) {
defer db.Close() 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }

View file

@ -1987,7 +1987,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
config.SnapshotWait = true 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 { if err != nil {
t.Fatalf("Failed to create chain: %v", err) t.Fatalf("Failed to create chain: %v", err)
} }

View file

@ -84,7 +84,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*core.BlockChain, []*type
cacheConfig = core.DefaultCacheConfig 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 { if err != nil {
t.Fatalf("Failed to create chain: %v", err) t.Fatalf("Failed to create chain: %v", err)
} }
@ -246,7 +246,7 @@ func (snaptest *snapshotTest) test(t *testing.T) {
// Restart the chain normally // Restart the chain normally
chain.Stop() 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) 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 // 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 // after the normal stop. It's used to ensure the broken snapshot
// can be detected all the time. // 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
newchain.Stop() 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -327,7 +327,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
SnapshotLimit: 0, 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -336,7 +336,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
newchain.Stop() newchain.Stop()
// Restart the chain with enabling the snapshot // 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -365,7 +365,7 @@ func (snaptest *setHeadSnapshotTest) test(t *testing.T) {
chain.SetHead(snaptest.setHead) chain.SetHead(snaptest.setHead)
chain.Stop() 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -396,7 +396,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
// and state committed. // and state committed.
chain.Stop() 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) 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 // journal and latest state will be committed
// Restart the chain after the crash // 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -449,8 +449,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
TrieTimeLimit: 5 * time.Minute, TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0, SnapshotLimit: 0,
} }
newchain, err := core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
newchain, err := core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -467,14 +466,13 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
SnapshotLimit: 256, SnapshotLimit: 256,
SnapshotWait: false, // Don't wait rebuild SnapshotWait: false, // Don't wait rebuild
} }
_, err = core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
_, err = core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
// Simulate the blockchain crash. // 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 { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }

View file

@ -6,7 +6,7 @@ The ```bor server``` command runs the Bor client.
- ```chain```: Name of the chain to sync - ```chain```: Name of the chain to sync
- ```name```: Name/Identity of the node - ```identity```: Name/Identity of the node
- ```log-level```: Set log level for the server - ```log-level```: Set log level for the server
@ -22,7 +22,7 @@ The ```bor server``` command runs the Bor client.
- ```requiredblocks```: Comma separated block number-to-hash mappings to enforce (<number>=<hash>) - ```requiredblocks```: Comma separated block number-to-hash mappings to enforce (<number>=<hash>)
- ```no-snapshot```: Disables the snapshot-database mode (default = false) - ```snapshot```: Disables/Enables the snapshot-database mode (default = true)
- ```bor.heimdall```: URL of Heimdall service - ```bor.heimdall```: URL of Heimdall service
@ -88,9 +88,17 @@ The ```bor server``` command runs the Bor client.
- ```ipcpath```: Filename for IPC socket/pipe within the datadir (explicit paths escape it) - ```ipcpath```: Filename for IPC socket/pipe within the datadir (explicit paths escape it)
- ```jsonrpc.corsdomain```: Comma separated list of domains from which to accept cross origin requests (browser enforced) - ```http.corsdomain```: Comma separated list of domains from which to accept cross origin requests (browser enforced)
- ```jsonrpc.vhosts```: Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. - ```http.vhosts```: Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.
- ```ws.corsdomain```: Comma separated list of domains from which to accept cross origin requests (browser enforced)
- ```ws.vhosts```: Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.
- ```graphql.corsdomain```: Comma separated list of domains from which to accept cross origin requests (browser enforced)
- ```graphql.vhosts```: Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.
- ```http```: Enable the HTTP-RPC server - ```http```: Enable the HTTP-RPC server
@ -100,7 +108,7 @@ The ```bor server``` command runs the Bor client.
- ```http.rpcprefix```: HTTP path path prefix on which JSON-RPC is served. Use '/' to serve on all paths. - ```http.rpcprefix```: HTTP path path prefix on which JSON-RPC is served. Use '/' to serve on all paths.
- ```http.modules```: API's offered over the HTTP-RPC interface - ```http.api```: API's offered over the HTTP-RPC interface
- ```ws```: Enable the WS-RPC server - ```ws```: Enable the WS-RPC server
@ -110,7 +118,7 @@ The ```bor server``` command runs the Bor client.
- ```ws.rpcprefix```: HTTP path prefix on which JSON-RPC is served. Use '/' to serve on all paths. - ```ws.rpcprefix```: HTTP path prefix on which JSON-RPC is served. Use '/' to serve on all paths.
- ```ws.modules```: API's offered over the WS-RPC interface - ```ws.api```: API's offered over the WS-RPC interface
- ```graphql```: Enable GraphQL on the HTTP-RPC server. Note that GraphQL can only be started if an HTTP server is started as well. - ```graphql```: Enable GraphQL on the HTTP-RPC server. Note that GraphQL can only be started if an HTTP server is started as well.

View file

@ -41,6 +41,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader" "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/ethconfig"
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
@ -219,7 +220,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
Preimages: config.Preimages, 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 { if err != nil {
return nil, err return nil, err
} }
@ -260,6 +264,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
Checkpoint: checkpoint, Checkpoint: checkpoint,
EthAPI: ethAPI, EthAPI: ethAPI,
PeerRequiredBlocks: config.PeerRequiredBlocks, PeerRequiredBlocks: config.PeerRequiredBlocks,
checker: checker,
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
@ -622,6 +627,13 @@ func (s *Ethereum) Start() error {
return nil 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 // StartCheckpointWhitelistService starts the goroutine to fetch checkpoints and update the
// checkpoint whitelist map. // checkpoint whitelist map.
func (s *Ethereum) startCheckpointWhitelistService() { func (s *Ethereum) startCheckpointWhitelistService() {
@ -633,8 +645,11 @@ func (s *Ethereum) startCheckpointWhitelistService() {
} }
// first run the checkpoint whitelist // first run the checkpoint whitelist
// TODO: add context timeout if needed firstCtx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
err := s.handleWhitelistCheckpoint(context.Background()) err := s.handleWhitelistCheckpoint(firstCtx, true)
cancel()
if err != nil { if err != nil {
if errors.Is(err, ErrBorConsensusWithoutHeimdall) || errors.Is(err, ErrNotBorConsensus) { if errors.Is(err, ErrBorConsensusWithoutHeimdall) || errors.Is(err, ErrNotBorConsensus) {
return return
@ -649,8 +664,11 @@ func (s *Ethereum) startCheckpointWhitelistService() {
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
// TODO: add context timeout if needed ctx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
err = s.handleWhitelistCheckpoint(context.Background()) err := s.handleWhitelistCheckpoint(ctx, false)
cancel()
if err != nil { if err != nil {
log.Warn("unable to whitelist checkpoint", "err", err) 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. // 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) ethHandler := (*ethHandler)(s.handler)
bor, ok := ethHandler.chain.Engine().(*bor.Bor) bor, ok := ethHandler.chain.Engine().(*bor.Bor)
@ -678,13 +691,18 @@ func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context) error {
return ErrBorConsensusWithoutHeimdall return ErrBorConsensusWithoutHeimdall
} }
endBlockNum, endBlockHash, err := ethHandler.fetchWhitelistCheckpoint(ctx, bor) blockNums, blockHashes, err := ethHandler.fetchWhitelistCheckpoints(ctx, bor, first)
if err != nil { // 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 return err
} }
// Update the checkpoint whitelist map. // 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 return nil
} }

View file

@ -144,7 +144,7 @@ type Downloader struct {
quitCh chan struct{} // Quit channel to signal termination quitCh chan struct{} // Quit channel to signal termination
quitLock sync.Mutex // Lock to prevent double closes quitLock sync.Mutex // Lock to prevent double closes
ChainValidator ethereum.ChainValidator
// Testing hooks // Testing hooks
syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run 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) 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. // LightChain encapsulates functions required to synchronise a light chain.
type LightChain interface { type LightChain interface {
// HasHeader verifies a header's presence in the local chain. // 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. // New creates a new downloader to fetch hashes and blocks from remote peers.
//nolint: staticcheck //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 { if lightchain == nil {
lightchain = chain 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 // 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. // 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) { func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) (uint64, error) {
// Check the validity of chain to be downloaded // Check the validity of peer from which the chain is to be downloaded
if _, err := d.IsValidChain(remoteHeader, d.getFetchHeadersByNumber(p)); err != nil { if d.ChainValidator != nil {
return 0, err if _, err := d.IsValidPeer(remoteHeader, d.getFetchHeadersByNumber(p)); err != nil {
return 0, err
}
} }
// Figure out the valid ancestor range to prevent rewrite attacks // Figure out the valid ancestor range to prevent rewrite attacks

View file

@ -71,7 +71,7 @@ func newTester() *downloadTester {
core.GenesisBlockForTesting(db, testAddress, big.NewInt(1000000000000000)) 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 { if err != nil {
panic(err) panic(err)
} }
@ -88,7 +88,7 @@ func newTester() *downloadTester {
return tester return tester
} }
func (dl *downloadTester) setWhitelist(w ChainValidator) { func (dl *downloadTester) setWhitelist(w ethereum.ChainValidator) {
dl.downloader.ChainValidator = w dl.downloader.ChainValidator = w
} }
@ -1416,9 +1416,9 @@ func newWhitelistFake(validate func(count int) (bool, error)) *whitelistFake {
return &whitelistFake{0, validate} 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. // 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() { defer func() {
w.count++ w.count++
}() }()
@ -1426,13 +1426,18 @@ func (w *whitelistFake) IsValidChain(_ *types.Header, _ func(number uint64, amou
return w.validate(w.count) 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) ProcessCheckpoint(_ uint64, _ common.Hash) {}
func (w *whitelistFake) GetCheckpointWhitelist() map[uint64]common.Hash { func (w *whitelistFake) GetCheckpointWhitelist() map[uint64]common.Hash {
return nil return nil
} }
func (w *whitelistFake) PurgeCheckpointWhitelist() {} 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 // TestFakedSyncProgress66WhitelistMismatch tests if in case of whitelisted
// checkpoint mismatch with opposite peer, the sync should fail. // checkpoint mismatch with opposite peer, the sync should fail.

View file

@ -214,7 +214,7 @@ func newTestBlockchain(blocks []*types.Block) *core.BlockChain {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
core.GenesisBlockForTesting(db, testAddress, big.NewInt(1000000000000000)) 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 { if err != nil {
panic(err) panic(err)
} }

View file

@ -15,7 +15,8 @@ type Service struct {
m sync.Mutex m sync.Mutex
checkpointWhitelist map[uint64]common.Hash // Checkpoint whitelist, populated by reaching out to heimdall checkpointWhitelist map[uint64]common.Hash // Checkpoint whitelist, populated by reaching out to heimdall
checkpointOrder []uint64 // Checkpoint order, 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 { func NewService(maxCapacity uint) *Service {
@ -23,6 +24,7 @@ func NewService(maxCapacity uint) *Service {
checkpointWhitelist: make(map[uint64]common.Hash), checkpointWhitelist: make(map[uint64]common.Hash),
checkpointOrder: []uint64{}, checkpointOrder: []uint64{},
maxCapacity: maxCapacity, 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") 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. // 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 want to validate the chain by comparing the last checkpointed block
// we're storing in `checkpointWhitelist` with the peer's 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 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) { func (w *Service) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {
w.m.Lock() w.m.Lock()
defer w.m.Unlock() 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]]) log.Debug("Dequeing checkpoint whitelist", "block number", w.checkpointOrder[0], "block hash", w.checkpointWhitelist[w.checkpointOrder[0]])
delete(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
} }
} }

View file

@ -2,21 +2,26 @@ package whitelist
import ( import (
"errors" "errors"
"fmt"
"math/big" "math/big"
"reflect"
"sort"
"testing" "testing"
"time"
"gotest.tools/assert" "github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
) )
// NewMockService creates a new mock whitelist service // NewMockService creates a new mock whitelist service
func NewMockService(maxCapacity uint) *Service { func NewMockService(maxCapacity uint, checkpointInterval uint64) *Service {
return &Service{ return &Service{
checkpointWhitelist: make(map[uint64]common.Hash), checkpointWhitelist: make(map[uint64]common.Hash),
checkpointOrder: []uint64{}, checkpointOrder: []uint64{},
maxCapacity: maxCapacity, maxCapacity: maxCapacity,
checkpointInterval: checkpointInterval,
} }
} }
@ -24,34 +29,34 @@ func NewMockService(maxCapacity uint) *Service {
func TestWhitelistCheckpoint(t *testing.T) { func TestWhitelistCheckpoint(t *testing.T) {
t.Parallel() t.Parallel()
s := NewMockService(10) s := NewMockService(10, 10)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
s.enqueueCheckpointWhitelist(uint64(i), common.Hash{}) s.enqueueCheckpointWhitelist(uint64(i), common.Hash{})
} }
assert.Equal(t, s.length(), 10, "expected 10 items in whitelist") require.Equal(t, s.length(), 10, "expected 10 items in whitelist")
s.enqueueCheckpointWhitelist(11, common.Hash{}) s.enqueueCheckpointWhitelist(11, common.Hash{})
s.dequeueCheckpointWhitelist() s.dequeueCheckpointWhitelist()
assert.Equal(t, s.length(), 10, "expected 10 items in whitelist") 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 // for different cases by providing a mock fetchHeadersByNumber function
func TestIsValidChain(t *testing.T) { func TestIsValidPeer(t *testing.T) {
t.Parallel() t.Parallel()
s := NewMockService(10) s := NewMockService(10, 10)
// case1: no checkpoint whitelist, should consider the chain as valid // case1: no checkpoint whitelist, should consider the chain as valid
res, err := s.IsValidChain(nil, nil) res, err := s.IsValidPeer(nil, nil)
assert.NilError(t, err, "expected no error") require.NoError(t, err, "expected no error")
assert.Equal(t, res, true, "expected chain to be valid") require.Equal(t, res, true, "expected chain to be valid")
// add checkpoint entries and mock fetchHeadersByNumber function // add checkpoint entries and mock fetchHeadersByNumber function
s.ProcessCheckpoint(uint64(0), common.Hash{}) s.ProcessCheckpoint(uint64(0), common.Hash{})
s.ProcessCheckpoint(uint64(1), common.Hash{}) s.ProcessCheckpoint(uint64(1), common.Hash{})
assert.Equal(t, s.length(), 2, "expected 2 items in whitelist") require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
// create a false function, returning absolutely nothing // create a false function, returning absolutely nothing
falseFetchHeadersByNumber := func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error) { falseFetchHeadersByNumber := func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error) {
@ -60,7 +65,7 @@ func TestIsValidChain(t *testing.T) {
// case2: false fetchHeadersByNumber function provided, should consider the chain as invalid // case2: false fetchHeadersByNumber function provided, should consider the chain as invalid
// and throw `ErrNoRemoteCheckoint` error // and throw `ErrNoRemoteCheckoint` error
res, err = s.IsValidChain(nil, falseFetchHeadersByNumber) res, err = s.IsValidPeer(nil, falseFetchHeadersByNumber)
if err == nil { if err == nil {
t.Fatal("expected error, got nil") t.Fatal("expected error, got nil")
} }
@ -69,7 +74,7 @@ func TestIsValidChain(t *testing.T) {
t.Fatalf("expected error ErrNoRemoteCheckoint, got %v", err) t.Fatalf("expected error ErrNoRemoteCheckoint, got %v", err)
} }
assert.Equal(t, res, false, "expected chain to be invalid") require.Equal(t, res, false, "expected chain to be invalid")
// case3: correct fetchHeadersByNumber function provided, should consider the chain as valid // case3: correct fetchHeadersByNumber function provided, should consider the chain as valid
// create a mock function, returning a the required header // create a mock function, returning a the required header
@ -91,17 +96,316 @@ func TestIsValidChain(t *testing.T) {
} }
} }
res, err = s.IsValidChain(nil, fetchHeadersByNumber) res, err = s.IsValidPeer(nil, fetchHeadersByNumber)
assert.NilError(t, err, "expected no error") require.NoError(t, err, "expected no error")
assert.Equal(t, res, true, "expected chain to be valid") require.Equal(t, res, true, "expected chain to be valid")
// add one more checkpoint whitelist entry // add one more checkpoint whitelist entry
s.ProcessCheckpoint(uint64(2), common.Hash{}) s.ProcessCheckpoint(uint64(2), common.Hash{})
assert.Equal(t, s.length(), 3, "expected 3 items in whitelist") require.Equal(t, s.length(), 3, "expected 3 items in whitelist")
// case4: correct fetchHeadersByNumber function provided with wrong header // case4: correct fetchHeadersByNumber function provided with wrong header
// for block number 2. Should consider the chain as invalid and throw an error // 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)
assert.Equal(t, err, ErrCheckpointMismatch, "expected checkpoint mismatch error") require.Equal(t, err, ErrCheckpointMismatch, "expected checkpoint mismatch error")
assert.Equal(t, res, false, "expected chain to be invalid") 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{}{}
} }

View file

@ -30,7 +30,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/bor" "github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/bor/contract" "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/bor/heimdall/span"
"github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"

View file

@ -59,8 +59,9 @@ func (api *PublicFilterAPI) NewDeposits(ctx context.Context, crit ethereum.State
} }
rpcSub := notifier.CreateSubscription() rpcSub := notifier.CreateSubscription()
go func() { go func() {
stateSyncData := make(chan *types.StateSyncData) stateSyncData := make(chan *types.StateSyncData, 10)
stateSyncSub := api.events.SubscribeNewDeposits(stateSyncData) stateSyncSub := api.events.SubscribeNewDeposits(stateSyncData)
for { for {

View file

@ -144,7 +144,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
// Construct testing chain // Construct testing chain
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
gspec.Commit(diskdb) 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 { if err != nil {
t.Fatalf("Failed to create local chain, %v", err) t.Fatalf("Failed to create local chain, %v", err)
} }

View file

@ -24,6 +24,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/beacon" "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/forkid"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader" "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/fetcher"
"github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/eth/protocols/snap"
@ -91,6 +91,7 @@ type handlerConfig struct {
EthAPI *ethapi.PublicBlockChainAPI // EthAPI to interact EthAPI *ethapi.PublicBlockChainAPI // EthAPI to interact
PeerRequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges PeerRequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges
checker ethereum.ChainValidator
} }
type handler struct { type handler struct {
@ -202,7 +203,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
// sync is requested. The downloader is responsible for deallocating the state // sync is requested. The downloader is responsible for deallocating the state
// bloom when it's done. // bloom when it's done.
// todo: it'd better to extract maxCapacity into config // 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) // Construct the fetcher (short sync)
validator := func(header *types.Header) error { validator := func(header *types.Header) error {

View file

@ -13,6 +13,14 @@ import (
) )
var ( 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 // errCheckpoint is returned when we are unable to fetch the
// latest checkpoint from the local heimdall. // latest checkpoint from the local heimdall.
errCheckpoint = errors.New("failed to fetch latest checkpoint") errCheckpoint = errors.New("failed to fetch latest checkpoint")
@ -33,43 +41,78 @@ var (
errEndBlock = errors.New("failed to get end block") 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. // and verifies the data against bor data.
func (h *ethHandler) fetchWhitelistCheckpoint(ctx context.Context, bor *bor.Bor) (uint64, common.Hash, error) { func (h *ethHandler) fetchWhitelistCheckpoints(ctx context.Context, bor *bor.Bor, first bool) ([]uint64, []common.Hash, error) {
// check for checkpoint whitelisting: bor // Create an array for block number and block hashes
checkpoint, err := bor.HeimdallClient.FetchLatestCheckpoint(ctx) //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 { if err != nil {
log.Debug("Failed to fetch latest checkpoint for whitelisting") log.Debug("Failed to fetch checkpoint count for whitelisting", "err", err)
return 0, common.Hash{}, errCheckpoint return blockNums, blockHashes, errCheckpointCount
} }
// check if we have the checkpoint blocks if count == 0 {
head := h.ethAPI.BlockNumber() return blockNums, blockHashes, errNoCheckpoint
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
} }
// verify the root hash of checkpoint // If we're in the first iteration, we'll fetch last 10 checkpoints, else only the latest one
roothash, err := h.ethAPI.GetRootHash(ctx, checkpoint.StartBlock.Uint64(), checkpoint.EndBlock.Uint64()) iterations := 1
if err != nil { if first {
log.Debug("Failed to get root hash of checkpoint while whitelisting") iterations = 10
return 0, common.Hash{}, errRootHash
} }
if roothash != checkpoint.RootHash.String()[2:] { for i := 0; i < iterations; i++ {
log.Warn("Checkpoint root hash mismatch while whitelisting", "expected", checkpoint.RootHash.String()[2:], "got", roothash) // If we don't have any checkpoints in heimdall, break
return 0, common.Hash{}, errCheckpointRootHashMismatch 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 return blockNums, blockHashes, nil
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
} }

View file

@ -105,8 +105,8 @@ func testForkIDSplit(t *testing.T, protocol uint) {
genesisNoFork = gspecNoFork.MustCommit(dbNoFork) genesisNoFork = gspecNoFork.MustCommit(dbNoFork)
genesisProFork = gspecProFork.MustCommit(dbProFork) genesisProFork = gspecProFork.MustCommit(dbProFork)
chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, configNoFork, 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) chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil, nil)
blocksNoFork, _ = core.GenerateChain(configNoFork, genesisNoFork, engine, dbNoFork, 2, nil) blocksNoFork, _ = core.GenerateChain(configNoFork, genesisNoFork, engine, dbNoFork, 2, nil)
blocksProFork, _ = core.GenerateChain(configProFork, genesisProFork, engine, dbProFork, 2, nil) blocksProFork, _ = core.GenerateChain(configProFork, genesisProFork, engine, dbProFork, 2, nil)

View file

@ -138,7 +138,7 @@ func newTestHandlerWithBlocks(blocks int) *testHandler {
Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}}, Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}},
}).MustCommit(db) }).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) bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, nil)
if _, err := chain.InsertChain(bs); err != nil { if _, err := chain.InsertChain(bs); err != nil {

View file

@ -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)}}, Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}},
}).MustCommit(db) }).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) bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, generator)
if _, err := chain.InsertChain(bs); err != nil { if _, err := chain.InsertChain(bs); err != nil {

View file

@ -81,7 +81,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i i
SnapshotLimit: 0, SnapshotLimit: 0,
TrieDirtyDisabled: true, // Archive mode 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }

View file

@ -238,3 +238,12 @@ type StateSyncFilter struct {
ID uint64 ID uint64
Contract common.Address 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()
}

View file

@ -4,7 +4,6 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"os" "os"
"os/signal" "os/signal"
@ -162,7 +161,7 @@ func (b *BootnodeCommand) Run(args []string) int {
} }
// save the public key // save the public key
pubRaw := fmt.Sprintf("%x", crypto.FromECDSAPub(&nodeKey.PublicKey)[1:]) pubRaw := fmt.Sprintf("%x", crypto.FromECDSAPub(&nodeKey.PublicKey)[1:])
if err := ioutil.WriteFile(filepath.Join(path, "pub.key"), []byte(pubRaw), 0600); err != nil { if err := os.WriteFile(filepath.Join(path, "pub.key"), []byte(pubRaw), 0600); err != nil {
b.UI.Error(fmt.Sprintf("failed to write node pub key: %v", err)) b.UI.Error(fmt.Sprintf("failed to write node pub key: %v", err))
return 1 return 1
} }

View file

@ -8,7 +8,7 @@ import (
"time" "time"
"github.com/mitchellh/cli" "github.com/mitchellh/cli"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/internal/cli/server" "github.com/ethereum/go-ethereum/internal/cli/server"
) )
@ -30,7 +30,7 @@ func TestCommand_DebugBlock(t *testing.T) {
// start the mock server // start the mock server
srv, err := server.CreateMockServer(config) srv, err := server.CreateMockServer(config)
assert.NoError(t, err) require.NoError(t, err)
defer server.CloseMockServer(srv) defer server.CloseMockServer(srv)
@ -53,7 +53,7 @@ func TestCommand_DebugBlock(t *testing.T) {
start := time.Now() start := time.Now()
dst1 := path.Join(output, prefix+time.Now().UTC().Format("2006-01-02-150405Z"), "block.json") dst1 := path.Join(output, prefix+time.Now().UTC().Format("2006-01-02-150405Z"), "block.json")
res := traceBlock(port, 1, output) res := traceBlock(port, 1, output)
assert.Equal(t, 0, res) require.Equal(t, 0, res)
t.Logf("Completed trace of block %d in %d ms at %s", 1, time.Since(start).Milliseconds(), dst1) t.Logf("Completed trace of block %d in %d ms at %s", 1, time.Since(start).Milliseconds(), dst1)
// adding this to avoid debug directory name conflicts // adding this to avoid debug directory name conflicts
@ -64,14 +64,14 @@ func TestCommand_DebugBlock(t *testing.T) {
latestBlock := srv.GetLatestBlockNumber().Int64() latestBlock := srv.GetLatestBlockNumber().Int64()
dst2 := path.Join(output, prefix+time.Now().UTC().Format("2006-01-02-150405Z"), "block.json") dst2 := path.Join(output, prefix+time.Now().UTC().Format("2006-01-02-150405Z"), "block.json")
res = traceBlock(port, latestBlock, output) res = traceBlock(port, latestBlock, output)
assert.Equal(t, 0, res) require.Equal(t, 0, res)
t.Logf("Completed trace of block %d in %d ms at %s", latestBlock, time.Since(start).Milliseconds(), dst2) t.Logf("Completed trace of block %d in %d ms at %s", latestBlock, time.Since(start).Milliseconds(), dst2)
// verify if the trace files are created // verify if the trace files are created
done := verify(dst1) done := verify(dst1)
assert.Equal(t, true, done) require.Equal(t, true, done)
done = verify(dst2) done = verify(dst2)
assert.Equal(t, true, done) require.Equal(t, true, done)
// delete the traces // delete the traces
deleteTraces(output) deleteTraces(output)

View file

@ -202,10 +202,11 @@ func (f *Flagset) BigIntFlag(b *BigIntFlag) {
} }
type SliceStringFlag struct { type SliceStringFlag struct {
Name string Name string
Usage string Usage string
Value *[]string Value *[]string
Group string Default []string
Group string
} }
func (i *SliceStringFlag) String() string { func (i *SliceStringFlag) String() string {
@ -217,8 +218,8 @@ func (i *SliceStringFlag) String() string {
} }
func (i *SliceStringFlag) Set(value string) error { func (i *SliceStringFlag) Set(value string) error {
*i.Value = append(*i.Value, strings.Split(value, ",")...) // overwritting insted of appending
*i.Value = strings.Split(value, ",")
return nil return nil
} }

View file

@ -23,14 +23,17 @@ func TestFlagsetBool(t *testing.T) {
func TestFlagsetSliceString(t *testing.T) { func TestFlagsetSliceString(t *testing.T) {
f := NewFlagSet("") f := NewFlagSet("")
value := []string{} value := []string{"a", "b", "c"}
f.SliceStringFlag(&SliceStringFlag{ f.SliceStringFlag(&SliceStringFlag{
Name: "flag", Name: "flag",
Value: &value, Value: &value,
Default: value,
}) })
assert.NoError(t, f.Parse([]string{"--flag", "a,b", "--flag", "c"})) assert.NoError(t, f.Parse([]string{}))
assert.Equal(t, value, []string{"a", "b", "c"}) assert.Equal(t, value, []string{"a", "b", "c"})
assert.NoError(t, f.Parse([]string{"--flag", "a,b"}))
assert.Equal(t, value, []string{"a", "b"})
} }
func TestFlagsetDuration(t *testing.T) { func TestFlagsetDuration(t *testing.T) {

View file

@ -3,12 +3,12 @@ package cli
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/require"
) )
func TestCodeBlock(t *testing.T) { func TestCodeBlock(t *testing.T) {
t.Parallel() t.Parallel()
assert := assert.New(t) assert := require.New(t)
lines := []string{ lines := []string{
"abc", "abc",

View file

@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -50,7 +49,7 @@ func GetChain(name string) (*Chain, error) {
} }
func ImportFromFile(filename string) (*Chain, error) { func ImportFromFile(filename string) (*Chain, error) {
data, err := ioutil.ReadFile(filename) data, err := os.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -40,8 +40,8 @@ type Config struct {
// Chain is the chain to sync with // Chain is the chain to sync with
Chain string `hcl:"chain,optional"` Chain string `hcl:"chain,optional"`
// Name, or identity of the node // Identity of the node
Name string `hcl:"name,optional"` Identity string `hcl:"identity,optional"`
// RequiredBlocks is a list of required (block number, hash) pairs to accept // RequiredBlocks is a list of required (block number, hash) pairs to accept
RequiredBlocks map[string]string `hcl:"requiredblocks,optional"` RequiredBlocks map[string]string `hcl:"requiredblocks,optional"`
@ -61,8 +61,8 @@ type Config struct {
// GcMode selects the garbage collection mode for the trie // GcMode selects the garbage collection mode for the trie
GcMode string `hcl:"gc-mode,optional"` GcMode string `hcl:"gc-mode,optional"`
// NoSnapshot disables the snapshot database mode // Snapshot disables/enables the snapshot database mode
NoSnapshot bool `hcl:"no-snapshot,optional"` Snapshot bool `hcl:"snapshot,optional"`
// Ethstats is the address of the ethstats server to send telemetry // Ethstats is the address of the ethstats server to send telemetry
Ethstats string `hcl:"ethstats,optional"` Ethstats string `hcl:"ethstats,optional"`
@ -217,12 +217,6 @@ type JsonRPCConfig struct {
// IPCPath is the path of the ipc endpoint // IPCPath is the path of the ipc endpoint
IPCPath string `hcl:"ipc-path,optional"` IPCPath string `hcl:"ipc-path,optional"`
// VHost is the list of valid virtual hosts
VHost []string `hcl:"vhost,optional"`
// Cors is the list of Cors endpoints
Cors []string `hcl:"cors,optional"`
// GasCap is the global gas cap for eth-call variants. // GasCap is the global gas cap for eth-call variants.
GasCap uint64 `hcl:"gas-cap,optional"` GasCap uint64 `hcl:"gas-cap,optional"`
@ -232,10 +226,10 @@ type JsonRPCConfig struct {
// Http has the json-rpc http related settings // Http has the json-rpc http related settings
Http *APIConfig `hcl:"http,block"` Http *APIConfig `hcl:"http,block"`
// Http has the json-rpc websocket related settings // Ws has the json-rpc websocket related settings
Ws *APIConfig `hcl:"ws,block"` Ws *APIConfig `hcl:"ws,block"`
// Http has the json-rpc graphql related settings // Graphql has the json-rpc graphql related settings
Graphql *APIConfig `hcl:"graphql,block"` Graphql *APIConfig `hcl:"graphql,block"`
} }
@ -258,7 +252,13 @@ type APIConfig struct {
Host string `hcl:"host,optional"` Host string `hcl:"host,optional"`
// Modules is the list of enabled api modules // Modules is the list of enabled api modules
Modules []string `hcl:"modules,optional"` API []string `hcl:"modules,optional"`
// VHost is the list of valid virtual hosts
VHost []string `hcl:"vhost,optional"`
// Cors is the list of Cors endpoints
Cors []string `hcl:"cors,optional"`
} }
type GpoConfig struct { type GpoConfig struct {
@ -387,7 +387,7 @@ type DeveloperConfig struct {
func DefaultConfig() *Config { func DefaultConfig() *Config {
return &Config{ return &Config{
Chain: "mainnet", Chain: "mainnet",
Name: Hostname(), Identity: Hostname(),
RequiredBlocks: map[string]string{}, RequiredBlocks: map[string]string{},
LogLevel: "INFO", LogLevel: "INFO",
DataDir: defaultDataDir(), DataDir: defaultDataDir(),
@ -412,9 +412,9 @@ func DefaultConfig() *Config {
URL: "http://localhost:1317", URL: "http://localhost:1317",
Without: false, Without: false,
}, },
SyncMode: "full", SyncMode: "full",
GcMode: "full", GcMode: "full",
NoSnapshot: false, Snapshot: true,
TxPool: &TxPoolConfig{ TxPool: &TxPoolConfig{
Locals: []string{}, Locals: []string{},
NoLocals: false, NoLocals: false,
@ -444,8 +444,6 @@ func DefaultConfig() *Config {
JsonRPC: &JsonRPCConfig{ JsonRPC: &JsonRPCConfig{
IPCDisable: false, IPCDisable: false,
IPCPath: "", IPCPath: "",
Cors: []string{"*"},
VHost: []string{"*"},
GasCap: ethconfig.Defaults.RPCGasCap, GasCap: ethconfig.Defaults.RPCGasCap,
TxFeeCap: ethconfig.Defaults.RPCTxFeeCap, TxFeeCap: ethconfig.Defaults.RPCTxFeeCap,
Http: &APIConfig{ Http: &APIConfig{
@ -453,17 +451,23 @@ func DefaultConfig() *Config {
Port: 8545, Port: 8545,
Prefix: "", Prefix: "",
Host: "localhost", Host: "localhost",
Modules: []string{"eth", "net", "web3", "txpool", "bor"}, API: []string{"eth", "net", "web3", "txpool", "bor"},
Cors: []string{"*"},
VHost: []string{"*"},
}, },
Ws: &APIConfig{ Ws: &APIConfig{
Enabled: false, Enabled: false,
Port: 8546, Port: 8546,
Prefix: "", Prefix: "",
Host: "localhost", Host: "localhost",
Modules: []string{"web3", "net"}, API: []string{"web3", "net"},
Cors: []string{"*"},
VHost: []string{"*"},
}, },
Graphql: &APIConfig{ Graphql: &APIConfig{
Enabled: false, Enabled: false,
Cors: []string{"*"},
VHost: []string{"*"},
}, },
}, },
Ethstats: "", Ethstats: "",
@ -864,7 +868,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
} }
// snapshot disable check // snapshot disable check
if c.NoSnapshot { if !c.Snapshot {
if n.SyncMode == downloader.SnapSync { if n.SyncMode == downloader.SnapSync {
log.Info("Snap sync requested, enabling --snapshot") log.Info("Snap sync requested, enabling --snapshot")
} else { } else {
@ -908,15 +912,15 @@ func (c *Config) buildNode() (*node.Config, error) {
ListenAddr: c.P2P.Bind + ":" + strconv.Itoa(int(c.P2P.Port)), ListenAddr: c.P2P.Bind + ":" + strconv.Itoa(int(c.P2P.Port)),
DiscoveryV5: c.P2P.Discovery.V5Enabled, DiscoveryV5: c.P2P.Discovery.V5Enabled,
}, },
HTTPModules: c.JsonRPC.Http.Modules, HTTPModules: c.JsonRPC.Http.API,
HTTPCors: c.JsonRPC.Cors, HTTPCors: c.JsonRPC.Http.Cors,
HTTPVirtualHosts: c.JsonRPC.VHost, HTTPVirtualHosts: c.JsonRPC.Http.VHost,
HTTPPathPrefix: c.JsonRPC.Http.Prefix, HTTPPathPrefix: c.JsonRPC.Http.Prefix,
WSModules: c.JsonRPC.Ws.Modules, WSModules: c.JsonRPC.Ws.API,
WSOrigins: c.JsonRPC.Cors, WSOrigins: c.JsonRPC.Ws.Cors,
WSPathPrefix: c.JsonRPC.Ws.Prefix, WSPathPrefix: c.JsonRPC.Ws.Prefix,
GraphQLCors: c.JsonRPC.Cors, GraphQLCors: c.JsonRPC.Graphql.Cors,
GraphQLVirtualHosts: c.JsonRPC.VHost, GraphQLVirtualHosts: c.JsonRPC.Graphql.VHost,
} }
// dev mode // dev mode
@ -999,7 +1003,7 @@ func (c *Config) buildNode() (*node.Config, error) {
func (c *Config) Merge(cc ...*Config) error { func (c *Config) Merge(cc ...*Config) error {
for _, elem := range cc { for _, elem := range cc {
if err := mergo.Merge(c, elem, mergo.WithOverwriteWithEmptyValue, mergo.WithAppendSlice); err != nil { if err := mergo.Merge(c, elem, mergo.WithOverwriteWithEmptyValue); err != nil {
return fmt.Errorf("failed to merge configurations: %v", err) return fmt.Errorf("failed to merge configurations: %v", err)
} }
} }

View file

@ -22,8 +22,8 @@ func TestConfigDefault(t *testing.T) {
func TestConfigMerge(t *testing.T) { func TestConfigMerge(t *testing.T) {
c0 := &Config{ c0 := &Config{
Chain: "0", Chain: "0",
NoSnapshot: true, Snapshot: true,
RequiredBlocks: map[string]string{ RequiredBlocks: map[string]string{
"a": "b", "a": "b",
}, },
@ -54,8 +54,8 @@ func TestConfigMerge(t *testing.T) {
} }
expected := &Config{ expected := &Config{
Chain: "1", Chain: "1",
NoSnapshot: false, Snapshot: false,
RequiredBlocks: map[string]string{ RequiredBlocks: map[string]string{
"a": "b", "a": "b",
"b": "c", "b": "c",
@ -64,7 +64,6 @@ func TestConfigMerge(t *testing.T) {
MaxPeers: 10, MaxPeers: 10,
Discovery: &P2PDiscovery{ Discovery: &P2PDiscovery{
StaticNodes: []string{ StaticNodes: []string{
"a",
"b", "b",
}, },
}, },

View file

@ -16,10 +16,10 @@ func (c *Command) Flags() *flagset.Flagset {
Default: c.cliConfig.Chain, Default: c.cliConfig.Chain,
}) })
f.StringFlag(&flagset.StringFlag{ f.StringFlag(&flagset.StringFlag{
Name: "name", Name: "identity",
Usage: "Name/Identity of the node", Usage: "Name/Identity of the node",
Value: &c.cliConfig.Name, Value: &c.cliConfig.Identity,
Default: c.cliConfig.Name, Default: c.cliConfig.Identity,
}) })
f.StringFlag(&flagset.StringFlag{ f.StringFlag(&flagset.StringFlag{
Name: "log-level", Name: "log-level",
@ -61,10 +61,10 @@ func (c *Command) Flags() *flagset.Flagset {
Value: &c.cliConfig.RequiredBlocks, Value: &c.cliConfig.RequiredBlocks,
}) })
f.BoolFlag(&flagset.BoolFlag{ f.BoolFlag(&flagset.BoolFlag{
Name: "no-snapshot", Name: "snapshot",
Usage: `Disables the snapshot-database mode (default = false)`, Usage: `Disables/Enables the snapshot-database mode (default = true)`,
Value: &c.cliConfig.NoSnapshot, Value: &c.cliConfig.Snapshot,
Default: c.cliConfig.NoSnapshot, Default: c.cliConfig.Snapshot,
}) })
// heimdall // heimdall
@ -83,10 +83,11 @@ func (c *Command) Flags() *flagset.Flagset {
// txpool options // txpool options
f.SliceStringFlag(&flagset.SliceStringFlag{ f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "txpool.locals", Name: "txpool.locals",
Usage: "Comma separated accounts to treat as locals (no flush, priority inclusion)", Usage: "Comma separated accounts to treat as locals (no flush, priority inclusion)",
Value: &c.cliConfig.TxPool.Locals, Value: &c.cliConfig.TxPool.Locals,
Group: "Transaction Pool", Default: c.cliConfig.TxPool.Locals,
Group: "Transaction Pool",
}) })
f.BoolFlag(&flagset.BoolFlag{ f.BoolFlag(&flagset.BoolFlag{
Name: "txpool.nolocals", Name: "txpool.nolocals",
@ -329,16 +330,46 @@ func (c *Command) Flags() *flagset.Flagset {
Group: "JsonRPC", Group: "JsonRPC",
}) })
f.SliceStringFlag(&flagset.SliceStringFlag{ f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "jsonrpc.corsdomain", Name: "http.corsdomain",
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: &c.cliConfig.JsonRPC.Cors, Value: &c.cliConfig.JsonRPC.Http.Cors,
Group: "JsonRPC", Default: c.cliConfig.JsonRPC.Http.Cors,
Group: "JsonRPC",
}) })
f.SliceStringFlag(&flagset.SliceStringFlag{ f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "jsonrpc.vhosts", Name: "http.vhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.", Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: &c.cliConfig.JsonRPC.VHost, Value: &c.cliConfig.JsonRPC.Http.VHost,
Group: "JsonRPC", Default: c.cliConfig.JsonRPC.Http.VHost,
Group: "JsonRPC",
})
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "ws.corsdomain",
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: &c.cliConfig.JsonRPC.Ws.Cors,
Default: c.cliConfig.JsonRPC.Ws.Cors,
Group: "JsonRPC",
})
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "ws.vhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: &c.cliConfig.JsonRPC.Ws.VHost,
Default: c.cliConfig.JsonRPC.Ws.VHost,
Group: "JsonRPC",
})
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "graphql.corsdomain",
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: &c.cliConfig.JsonRPC.Graphql.Cors,
Default: c.cliConfig.JsonRPC.Graphql.Cors,
Group: "JsonRPC",
})
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "graphql.vhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: &c.cliConfig.JsonRPC.Graphql.VHost,
Default: c.cliConfig.JsonRPC.Graphql.VHost,
Group: "JsonRPC",
}) })
// http options // http options
@ -371,10 +402,11 @@ func (c *Command) Flags() *flagset.Flagset {
Group: "JsonRPC", Group: "JsonRPC",
}) })
f.SliceStringFlag(&flagset.SliceStringFlag{ f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "http.modules", Name: "http.api",
Usage: "API's offered over the HTTP-RPC interface", Usage: "API's offered over the HTTP-RPC interface",
Value: &c.cliConfig.JsonRPC.Http.Modules, Value: &c.cliConfig.JsonRPC.Http.API,
Group: "JsonRPC", Default: c.cliConfig.JsonRPC.Http.API,
Group: "JsonRPC",
}) })
// ws options // ws options
@ -407,10 +439,11 @@ func (c *Command) Flags() *flagset.Flagset {
Group: "JsonRPC", Group: "JsonRPC",
}) })
f.SliceStringFlag(&flagset.SliceStringFlag{ f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "ws.modules", Name: "ws.api",
Usage: "API's offered over the WS-RPC interface", Usage: "API's offered over the WS-RPC interface",
Value: &c.cliConfig.JsonRPC.Ws.Modules, Value: &c.cliConfig.JsonRPC.Ws.API,
Group: "JsonRPC", Default: c.cliConfig.JsonRPC.Ws.API,
Group: "JsonRPC",
}) })
// graphql options // graphql options
@ -438,10 +471,11 @@ func (c *Command) Flags() *flagset.Flagset {
Group: "P2P", Group: "P2P",
}) })
f.SliceStringFlag(&flagset.SliceStringFlag{ f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "bootnodes", Name: "bootnodes",
Usage: "Comma separated enode URLs for P2P discovery bootstrap", Usage: "Comma separated enode URLs for P2P discovery bootstrap",
Value: &c.cliConfig.P2P.Discovery.Bootnodes, Value: &c.cliConfig.P2P.Discovery.Bootnodes,
Group: "P2P", Default: c.cliConfig.P2P.Discovery.Bootnodes,
Group: "P2P",
}) })
f.Uint64Flag(&flagset.Uint64Flag{ f.Uint64Flag(&flagset.Uint64Flag{
Name: "maxpeers", Name: "maxpeers",
@ -581,10 +615,11 @@ func (c *Command) Flags() *flagset.Flagset {
// account // account
f.SliceStringFlag(&flagset.SliceStringFlag{ f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "unlock", Name: "unlock",
Usage: "Comma separated list of accounts to unlock", Usage: "Comma separated list of accounts to unlock",
Value: &c.cliConfig.Accounts.Unlock, Value: &c.cliConfig.Accounts.Unlock,
Group: "Account Management", Default: c.cliConfig.Accounts.Unlock,
Group: "Account Management",
}) })
f.StringFlag(&flagset.StringFlag{ f.StringFlag(&flagset.StringFlag{
Name: "password", Name: "password",

View file

@ -2,7 +2,6 @@ package server
import ( import (
"fmt" "fmt"
"io/ioutil"
"math/rand" "math/rand"
"net" "net"
"os" "os"
@ -55,7 +54,7 @@ func CreateMockServer(config *Config) (*Server, error) {
config.GRPC.Addr = fmt.Sprintf(":%d", port) config.GRPC.Addr = fmt.Sprintf(":%d", port)
// datadir // datadir
datadir, _ := ioutil.TempDir("/tmp", "bor-cli-test") datadir, _ := os.MkdirTemp("/tmp", "bor-cli-test")
config.DataDir = datadir config.DataDir = datadir
// find available port for http server // find available port for http server

View file

@ -23,8 +23,8 @@ import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/beacon" //nolint:typecheck
"github.com/ethereum/go-ethereum/consensus/bor" "github.com/ethereum/go-ethereum/consensus/bor" //nolint:typecheck
"github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers"
@ -182,7 +182,7 @@ func NewServer(config *Config) (*Server, error) {
// graphql is started from another place // graphql is started from another place
if config.JsonRPC.Graphql.Enabled { if config.JsonRPC.Graphql.Enabled {
if err := graphql.New(stack, srv.backend.APIBackend, config.JsonRPC.Cors, config.JsonRPC.VHost); err != nil { if err := graphql.New(stack, srv.backend.APIBackend, config.JsonRPC.Graphql.Cors, config.JsonRPC.Graphql.VHost); err != nil {
return nil, fmt.Errorf("failed to register the GraphQL service: %v", err) return nil, fmt.Errorf("failed to register the GraphQL service: %v", err)
} }
} }
@ -201,7 +201,7 @@ func NewServer(config *Config) (*Server, error) {
} }
} }
if err := srv.setupMetrics(config.Telemetry, config.Name); err != nil { if err := srv.setupMetrics(config.Telemetry, config.Identity); err != nil {
return nil, err return nil, err
} }

View file

@ -25,6 +25,8 @@ import (
"time" "time"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/tyler-smith/go-bip39"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
@ -47,7 +49,6 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/tyler-smith/go-bip39"
) )
// PublicEthereumAPI provides an API to access Ethereum related information. // PublicEthereumAPI provides an API to access Ethereum related information.
@ -829,10 +830,10 @@ func (s *PublicBlockChainAPI) getAuthor(head *types.Header) *common.Address {
} }
// GetBlockByNumber returns the requested canonical block. // GetBlockByNumber returns the requested canonical block.
// * When blockNr is -1 the chain head is returned. // - When blockNr is -1 the chain head is returned.
// * When blockNr is -2 the pending chain head is returned. // - When blockNr is -2 the pending chain head is returned.
// * When fullTx is true all transactions in the block are returned, otherwise // - When fullTx is true all transactions in the block are returned, otherwise
// only the transaction hash is returned. // only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
block, err := s.b.BlockByNumber(ctx, number) block, err := s.b.BlockByNumber(ctx, number)
if block != nil && err == nil { if block != nil && err == nil {

View file

@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "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/ethconfig"
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice" "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 // Note: NewLightChain adds the trusted checkpoint so it needs an ODR with
// indexers already set but not started yet // 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 return nil, err
} }
leth.chainReader = leth.blockchain leth.chainReader = leth.blockchain

View file

@ -203,7 +203,7 @@ func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, index
oracle *checkpointoracle.CheckpointOracle oracle *checkpointoracle.CheckpointOracle
) )
genesis := gspec.MustCommit(db) genesis := gspec.MustCommit(db)
chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil) chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil, nil)
if indexers != nil { if indexers != nil {
checkpointConfig := &params.CheckpointOracleConfig{ checkpointConfig := &params.CheckpointOracleConfig{
Address: crypto.CreateAddress(bankAddr, 0), Address: crypto.CreateAddress(bankAddr, 0),

View file

@ -26,6 +26,9 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
lru "github.com/hashicorp/golang-lru"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@ -37,7 +40,6 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
lru "github.com/hashicorp/golang-lru"
) )
var ( var (
@ -81,7 +83,7 @@ type LightChain struct {
// NewLightChain returns a fully initialised light chain using information // NewLightChain returns a fully initialised light chain using information
// available in the database. It initialises the default Ethereum header // available in the database. It initialises the default Ethereum header
// validator. // 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) bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit)
blockCache, _ := lru.New(blockCacheLimit) blockCache, _ := lru.New(blockCacheLimit)
@ -96,7 +98,7 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
blockCache: blockCache, blockCache: blockCache,
engine: engine, engine: engine,
} }
bc.forker = core.NewForkChoice(bc, nil) bc.forker = core.NewForkChoice(bc, nil, checker)
var err error var err error
bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt) bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt)
if err != nil { if err != nil {

View file

@ -56,7 +56,7 @@ func newCanonical(n int) (ethdb.Database, *LightChain, error) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
gspec := core.Genesis{Config: params.TestChainConfig} gspec := core.Genesis{Config: params.TestChainConfig}
genesis := gspec.MustCommit(db) 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 // Create and inject the requested chain
if n == 0 { if n == 0 {
@ -76,7 +76,7 @@ func newTestLightChain() *LightChain {
Config: params.TestChainConfig, Config: params.TestChainConfig,
} }
gspec.MustCommit(db) 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 { if err != nil {
panic(err) panic(err)
} }
@ -347,7 +347,7 @@ func TestReorgBadHeaderHashes(t *testing.T) {
defer func() { delete(core.BadHashes, headers[3].Hash()) }() defer func() { delete(core.BadHashes, headers[3].Hash()) }()
// Create a new LightChain and check that it rolled back the state. // 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 { if err != nil {
t.Fatalf("failed to create new chain manager: %v", err) t.Fatalf("failed to create new chain manager: %v", err)
} }

View file

@ -261,14 +261,14 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
) )
gspec.MustCommit(ldb) gspec.MustCommit(ldb)
// Assemble the test environment // 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) gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil { if _, err := blockchain.InsertChain(gchain); err != nil {
t.Fatal(err) t.Fatal(err)
} }
odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig} 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -44,7 +44,8 @@ func TestNodeIterator(t *testing.T) {
genesis = gspec.MustCommit(fulldb) genesis = gspec.MustCommit(fulldb)
) )
gspec.MustCommit(lightdb) 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) gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), fulldb, 4, testChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil { if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err) panic(err)

View file

@ -91,7 +91,7 @@ func TestTxPool(t *testing.T) {
) )
gspec.MustCommit(ldb) gspec.MustCommit(ldb)
// Assemble the test environment // 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) gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil { if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err) panic(err)
@ -103,7 +103,7 @@ func TestTxPool(t *testing.T) {
discard: make(chan int, 1), discard: make(chan int, 1),
mined: 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 txPermanent = 50
pool := NewTxPool(params.TestChainConfig, lightchain, relay) pool := NewTxPool(params.TestChainConfig, lightchain, relay)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)

View file

@ -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) engine := NewFakeBor(t, chainDB, chainConfig, ethAPIMock, spanner, heimdallClientMock, contractMock)
// Create Ethereum backend // 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 { if err != nil {
t.Fatalf("can't create new chain %v", err) t.Fatalf("can't create new chain %v", err)
} }

View file

@ -91,7 +91,7 @@ func newTestWorkerBackend(t TensingObject, chainConfig *params.ChainConfig, engi
genesis := gspec.MustCommit(db) 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) txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain)
// Generate a small n-block chain and an uncle block for it // Generate a small n-block chain and an uncle block for it

View file

@ -114,7 +114,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool, isBor bool) {
db2 := rawdb.NewMemoryDatabase() db2 := rawdb.NewMemoryDatabase()
b.Genesis.MustCommit(db2) 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() defer chain.Stop()
// Ignore empty commit here for less noise. // Ignore empty commit here for less noise.
@ -650,7 +650,7 @@ func BenchmarkBorMining(b *testing.B) {
db2 := rawdb.NewMemoryDatabase() db2 := rawdb.NewMemoryDatabase()
back.Genesis.MustCommit(db2) 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() defer chain.Stop()
// Ignore empty commit here for less noise. // Ignore empty commit here for less noise.

View file

@ -128,7 +128,8 @@ func (t *BlockTest) Run(snapshotter bool) error {
cache.SnapshotLimit = 1 cache.SnapshotLimit = 1
cache.SnapshotWait = true 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 { if err != nil {
return err return err
} }

168
tests/bor/bor_api_test.go Normal file
View file

@ -0,0 +1,168 @@
//go:build integration
package bor
import (
"context"
"math/big"
"testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/tests/bor/mocks"
)
func TestGetTransactionReceiptsByBlock(t *testing.T) {
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
chain := init.ethereum.BlockChain()
engine := init.ethereum.Engine()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
_bor := engine.(*bor.Bor)
defer _bor.Close()
// Mock /bor/span/1
res, _ := loadSpanFromFile(t)
h := mocks.NewMockIHeimdallClient(ctrl)
h.EXPECT().Span(gomock.Any(), uint64(1)).Return(&res.Result, nil).MinTimes(1)
h.EXPECT().Close().MinTimes(1)
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)),
}, nil).AnyTimes()
// Mock State Sync events
// at # sprintSize, events are fetched for [fromID, (block-sprint).Time)
fromID := uint64(1)
to := int64(chain.GetHeaderByNumber(0).Time)
sample := getSampleEventRecord(t)
// First query will be from [id=1, (block-sprint).Time]
eventRecords := []*clerk.EventRecordWithTime{
buildStateEvent(sample, 1, 1),
buildStateEvent(sample, 2, 2),
buildStateEvent(sample, 3, 3),
}
h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).MinTimes(1)
_bor.SetHeimdallClient(h)
// Insert blocks for 0th sprint
db := init.ethereum.ChainDb()
block := init.genesis.ToBlock(db)
signer := types.LatestSigner(init.genesis.Config)
toAddress := common.HexToAddress("0x000000000000000000000000000000000000aaaa")
currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)}
txHashes := map[int]common.Hash{} // blockNumber -> txHash
var (
err error
nonce uint64
tx *types.Transaction
txs []*types.Transaction
)
for i := uint64(1); i <= sprintSize; i++ {
if IsSpanEnd(i) {
currentValidators = []*valset.Validator{valset.NewValidator(addr, 10)}
}
if i%3 == 0 {
txdata := &types.LegacyTx{
Nonce: nonce,
To: &toAddress,
Gas: 30000,
GasPrice: newGwei(5),
}
nonce++
tx = types.NewTx(txdata)
tx, err = types.SignTx(tx, signer, key)
require.Nil(t, err, "an incorrect transaction or signer")
txs = []*types.Transaction{tx}
} else {
txs = nil
}
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, txs, currentValidators)
insertNewBlock(t, chain, block)
if len(txs) != 0 {
txHashes[int(block.Number().Uint64())] = tx.Hash()
}
}
// state 6 was not written
//
fromID = uint64(4)
to = int64(chain.GetHeaderByNumber(sprintSize).Time)
eventRecords = []*clerk.EventRecordWithTime{
buildStateEvent(sample, 4, 4),
buildStateEvent(sample, 5, 5),
}
h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).MinTimes(1)
for i := sprintSize + 1; i <= spanSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, currentValidators)
insertNewBlock(t, chain, block)
}
ethAPI := ethapi.NewPublicBlockChainAPI(init.ethereum.APIBackend)
txPoolAPI := ethapi.NewPublicTransactionPoolAPI(init.ethereum.APIBackend, nil)
for n := 0; n < int(spanSize)+1; n++ {
rpcNumber := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(n))
txs, err := ethAPI.GetTransactionReceiptsByBlock(context.Background(), rpcNumber)
require.Nil(t, err)
tx := txPoolAPI.GetTransactionByBlockNumberAndIndex(context.Background(), rpc.BlockNumber(n), 0)
blockMap, err := ethAPI.GetBlockByNumber(context.Background(), rpc.BlockNumber(n), true)
require.Nil(t, err)
expectedTxHash, ok := txHashes[n]
// FIXME: add `IsSprintStart(uint64(n)) || IsSpanStart(uint64(n))` after adding a full state receiver contract
if ok {
require.Len(t, txs, 1)
require.NotNil(t, tx, "not nil receipt expected")
require.Equal(t, expectedTxHash, tx.Hash, "got different from expected receipt")
blockTxs, ok := blockMap["transactions"].([]interface{})
require.Len(t, blockTxs, 1)
blockTx, ok := blockTxs[0].(*ethapi.RPCTransaction)
require.True(t, ok)
require.Equal(t, expectedTxHash, blockTx.Hash)
} else {
require.Len(t, txs, 0)
require.Nil(t, tx, "nil receipt expected")
blockTxs, _ := blockMap["transactions"].([]interface{})
require.Len(t, blockTxs, 0)
}
}
}

View file

@ -11,14 +11,15 @@ import (
"time" "time"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/require"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor" "github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/bor/clerk" "github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint" "github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span" "github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
@ -38,13 +39,13 @@ func TestInsertingSpanSizeBlocks(t *testing.T) {
defer _bor.Close() defer _bor.Close()
h, heimdallSpan, ctrl := getMockedHeimdallClient(t)
defer ctrl.Finish()
_, currentSpan := loadSpanFromFile(t) _, currentSpan := loadSpanFromFile(t)
h, ctrl := getMockedHeimdallClient(t, currentSpan)
defer ctrl.Finish()
h.EXPECT().Close().AnyTimes() 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, Proposer: currentSpan.SelectedProducers[0].Address,
StartBlock: big.NewInt(0), StartBlock: big.NewInt(0),
EndBlock: big.NewInt(int64(spanSize)), EndBlock: big.NewInt(int64(spanSize)),
@ -56,9 +57,11 @@ func TestInsertingSpanSizeBlocks(t *testing.T) {
block := init.genesis.ToBlock(db) block := init.genesis.ToBlock(db)
// to := int64(block.Header().Time) // to := int64(block.Header().Time)
currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)}
// Insert sprintSize # of blocks so that span is fetched at the start of a new sprint // Insert sprintSize # of blocks so that span is fetched at the start of a new sprint
for i := uint64(1); i <= spanSize; i++ { for i := uint64(1); i <= spanSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor) block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, currentValidators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
} }
@ -67,10 +70,10 @@ func TestInsertingSpanSizeBlocks(t *testing.T) {
t.Fatalf("%s", err) t.Fatalf("%s", err)
} }
assert.Equal(t, 3, len(validators)) require.Equal(t, 3, len(validators))
for i, validator := range validators { for i, validator := range validators {
assert.Equal(t, validator.Address.Bytes(), heimdallSpan.SelectedProducers[i].Address.Bytes()) require.Equal(t, validator.Address.Bytes(), currentSpan.SelectedProducers[i].Address.Bytes())
assert.Equal(t, validator.VotingPower, heimdallSpan.SelectedProducers[i].VotingPower) require.Equal(t, validator.VotingPower, currentSpan.SelectedProducers[i].VotingPower)
} }
} }
@ -85,16 +88,23 @@ func TestFetchStateSyncEvents(t *testing.T) {
// A. Insert blocks for 0th sprint // A. Insert blocks for 0th sprint
db := init.ethereum.ChainDb() db := init.ethereum.ChainDb()
block := init.genesis.ToBlock(db) block := init.genesis.ToBlock(db)
// B.1 Mock /bor/span/1
res, _ := loadSpanFromFile(t)
currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)}
// Insert sprintSize # of blocks so that span is fetched at the start of a new sprint // Insert sprintSize # of blocks so that span is fetched at the start of a new sprint
for i := uint64(1); i < sprintSize; i++ { for i := uint64(1); i < sprintSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor) if IsSpanEnd(i) {
currentValidators = res.Result.ValidatorSet.Validators
}
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, currentValidators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
} }
// B. Before inserting 1st block of the next sprint, mock heimdall deps // B. Before inserting 1st block of the next sprint, mock heimdall deps
// B.1 Mock /bor/span/1
res, _ := loadSpanFromFile(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
@ -115,7 +125,7 @@ func TestFetchStateSyncEvents(t *testing.T) {
h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).AnyTimes() h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).AnyTimes()
_bor.SetHeimdallClient(h) _bor.SetHeimdallClient(h)
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor) block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, res.Result.ValidatorSet.Validators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
} }
@ -130,6 +140,9 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
// Mock /bor/span/1 // Mock /bor/span/1
res, _ := loadSpanFromFile(t) res, _ := loadSpanFromFile(t)
// add the block producer
res.Result.ValidatorSet.Validators = append(res.Result.ValidatorSet.Validators, valset.NewValidator(addr, 4500))
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
@ -160,15 +173,24 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
// Insert blocks for 0th sprint // Insert blocks for 0th sprint
db := init.ethereum.ChainDb() db := init.ethereum.ChainDb()
block := init.genesis.ToBlock(db) block := init.genesis.ToBlock(db)
var currentValidators []*valset.Validator
for i := uint64(1); i <= sprintSize; i++ { for i := uint64(1); i <= sprintSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor) if IsSpanEnd(i) {
currentValidators = res.Result.ValidatorSet.Validators
} else {
currentValidators = []*valset.Validator{valset.NewValidator(addr, 10)}
}
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, currentValidators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
} }
lastStateID, _ := _bor.GenesisContractsClient.LastStateId(sprintSize) lastStateID, _ := _bor.GenesisContractsClient.LastStateId(sprintSize)
// state 6 was not written // state 6 was not written
assert.Equal(t, uint64(4), lastStateID.Uint64()) require.Equal(t, uint64(4), lastStateID.Uint64())
// //
fromID = uint64(5) fromID = uint64(5)
@ -181,12 +203,18 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).AnyTimes() h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).AnyTimes()
for i := sprintSize + 1; i <= spanSize; i++ { for i := sprintSize + 1; i <= spanSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor) if IsSpanEnd(i) {
currentValidators = res.Result.ValidatorSet.Validators
} else {
currentValidators = []*valset.Validator{valset.NewValidator(addr, 10)}
}
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, res.Result.ValidatorSet.Validators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
} }
lastStateID, _ = _bor.GenesisContractsClient.LastStateId(spanSize) lastStateID, _ = _bor.GenesisContractsClient.LastStateId(spanSize)
assert.Equal(t, uint64(6), lastStateID.Uint64()) require.Equal(t, uint64(6), lastStateID.Uint64())
} }
func TestOutOfTurnSigning(t *testing.T) { func TestOutOfTurnSigning(t *testing.T) {
@ -197,17 +225,29 @@ func TestOutOfTurnSigning(t *testing.T) {
defer _bor.Close() defer _bor.Close()
h, _, ctrl := getMockedHeimdallClient(t) _, heimdallSpan := loadSpanFromFile(t)
proposer := valset.NewValidator(addr, 10)
heimdallSpan.ValidatorSet.Validators = append(heimdallSpan.ValidatorSet.Validators, proposer)
// add the block producer
h, ctrl := getMockedHeimdallClient(t, heimdallSpan)
defer ctrl.Finish() defer ctrl.Finish()
h.EXPECT().Close().AnyTimes() h.EXPECT().Close().AnyTimes()
_bor.SetHeimdallClient(h) _bor.SetHeimdallClient(h)
db := init.ethereum.ChainDb() db := init.ethereum.ChainDb()
block := init.genesis.ToBlock(db) block := init.genesis.ToBlock(db)
setDifficulty := func(header *types.Header) {
if IsSprintStart(header.Number.Uint64()) {
header.Difficulty = big.NewInt(int64(len(heimdallSpan.ValidatorSet.Validators)))
}
}
for i := uint64(1); i < spanSize; i++ { for i := uint64(1); i < spanSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor) block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, heimdallSpan.ValidatorSet.Validators, setDifficulty)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
} }
@ -215,36 +255,50 @@ func TestOutOfTurnSigning(t *testing.T) {
// This account is one the out-of-turn validators for 1st (0-indexed) span // This account is one the out-of-turn validators for 1st (0-indexed) span
signer := "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd9" signer := "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd9"
signerKey, _ := hex.DecodeString(signer) signerKey, _ := hex.DecodeString(signer)
key, _ = crypto.HexToECDSA(signer) newKey, _ := crypto.HexToECDSA(signer)
addr = crypto.PubkeyToAddress(key.PublicKey) newAddr := crypto.PubkeyToAddress(newKey.PublicKey)
expectedSuccessionNumber := 2 expectedSuccessionNumber := 2
block = buildNextBlock(t, _bor, chain, block, signerKey, init.genesis.Config.Bor) parentTime := block.Time()
_, err := chain.InsertChain([]*types.Block{block})
assert.Equal(t,
*err.(*bor.BlockTooSoonError),
bor.BlockTooSoonError{Number: spanSize, Succession: expectedSuccessionNumber})
expectedDifficulty := uint64(3 - expectedSuccessionNumber) // len(validators) - succession setParentTime := func(header *types.Header) {
header.Time = parentTime + 1
}
const turn = 1
setDifficulty = func(header *types.Header) {
header.Difficulty = big.NewInt(int64(len(heimdallSpan.ValidatorSet.Validators)) - turn)
}
block = buildNextBlock(t, _bor, chain, block, signerKey, init.genesis.Config.Bor, nil, heimdallSpan.ValidatorSet.Validators, setParentTime, setDifficulty)
_, err := chain.InsertChain([]*types.Block{block})
require.Equal(t,
bor.BlockTooSoonError{Number: spanSize, Succession: expectedSuccessionNumber},
*err.(*bor.BlockTooSoonError))
expectedDifficulty := uint64(len(heimdallSpan.ValidatorSet.Validators) - expectedSuccessionNumber - turn) // len(validators) - succession
header := block.Header() header := block.Header()
header.Time += bor.CalcProducerDelay(header.Number.Uint64(), expectedSuccessionNumber, init.genesis.Config.Bor) - diff := bor.CalcProducerDelay(header.Number.Uint64(), expectedSuccessionNumber, init.genesis.Config.Bor)
bor.CalcProducerDelay(header.Number.Uint64(), 0, init.genesis.Config.Bor) header.Time += diff
sign(t, header, signerKey, init.genesis.Config.Bor) sign(t, header, signerKey, init.genesis.Config.Bor)
block = types.NewBlockWithHeader(header) block = types.NewBlockWithHeader(header)
_, err = chain.InsertChain([]*types.Block{block}) _, err = chain.InsertChain([]*types.Block{block})
assert.Equal(t, require.NotNil(t, err)
*err.(*bor.WrongDifficultyError), require.Equal(t,
bor.WrongDifficultyError{Number: spanSize, Expected: expectedDifficulty, Actual: 3, Signer: addr.Bytes()}) bor.WrongDifficultyError{Number: spanSize, Expected: expectedDifficulty, Actual: 3, Signer: newAddr.Bytes()},
*err.(*bor.WrongDifficultyError))
header.Difficulty = new(big.Int).SetUint64(expectedDifficulty) header.Difficulty = new(big.Int).SetUint64(expectedDifficulty)
sign(t, header, signerKey, init.genesis.Config.Bor) sign(t, header, signerKey, init.genesis.Config.Bor)
block = types.NewBlockWithHeader(header) block = types.NewBlockWithHeader(header)
_, err = chain.InsertChain([]*types.Block{block}) _, err = chain.InsertChain([]*types.Block{block})
assert.Nil(t, err) require.Nil(t, err)
} }
func TestSignerNotFound(t *testing.T) { func TestSignerNotFound(t *testing.T) {
@ -255,7 +309,9 @@ func TestSignerNotFound(t *testing.T) {
defer _bor.Close() defer _bor.Close()
h, _, ctrl := getMockedHeimdallClient(t) _, heimdallSpan := loadSpanFromFile(t)
h, ctrl := getMockedHeimdallClient(t, heimdallSpan)
defer ctrl.Finish() defer ctrl.Finish()
h.EXPECT().Close().AnyTimes() h.EXPECT().Close().AnyTimes()
@ -266,62 +322,21 @@ func TestSignerNotFound(t *testing.T) {
block := init.genesis.ToBlock(db) block := init.genesis.ToBlock(db)
// random signer account that is not a part of the validator set // random signer account that is not a part of the validator set
signer := "3714d99058cd64541433d59c6b391555b2fd9b54629c2b717a6c9c00d1127b6b" const signer = "3714d99058cd64541433d59c6b391555b2fd9b54629c2b717a6c9c00d1127b6b"
signerKey, _ := hex.DecodeString(signer) signerKey, _ := hex.DecodeString(signer)
key, _ = crypto.HexToECDSA(signer) newKey, _ := crypto.HexToECDSA(signer)
addr = crypto.PubkeyToAddress(key.PublicKey) newAddr := crypto.PubkeyToAddress(newKey.PublicKey)
_bor.Authorize(newAddr, func(account accounts.Account, s string, data []byte) ([]byte, error) {
return crypto.Sign(crypto.Keccak256(data), newKey)
})
block = buildNextBlock(t, _bor, chain, block, signerKey, init.genesis.Config.Bor, nil, heimdallSpan.ValidatorSet.Validators)
block = buildNextBlock(t, _bor, chain, block, signerKey, init.genesis.Config.Bor)
_, err := chain.InsertChain([]*types.Block{block}) _, err := chain.InsertChain([]*types.Block{block})
assert.Equal(t, require.Equal(t,
*err.(*bor.UnauthorizedSignerError), *err.(*bor.UnauthorizedSignerError),
bor.UnauthorizedSignerError{Number: 0, Signer: addr.Bytes()}) bor.UnauthorizedSignerError{Number: 0, Signer: newAddr.Bytes()})
}
func getMockedHeimdallClient(t *testing.T) (*mocks.MockIHeimdallClient, *span.HeimdallSpan, *gomock.Controller) {
ctrl := gomock.NewController(t)
h := mocks.NewMockIHeimdallClient(ctrl)
_, heimdallSpan := loadSpanFromFile(t)
h.EXPECT().Span(gomock.Any(), uint64(1)).Return(heimdallSpan, nil).AnyTimes()
h.EXPECT().StateSyncEvents(gomock.Any(), gomock.Any(), gomock.Any()).
Return([]*clerk.EventRecordWithTime{getSampleEventRecord(t)}, nil).AnyTimes()
return h, heimdallSpan, ctrl
}
func generateFakeStateSyncEvents(sample *clerk.EventRecordWithTime, count int) []*clerk.EventRecordWithTime {
events := make([]*clerk.EventRecordWithTime, count)
event := *sample
event.ID = 1
events[0] = &clerk.EventRecordWithTime{}
*events[0] = event
for i := 1; i < count; i++ {
event.ID = uint64(i)
event.Time = event.Time.Add(1 * time.Second)
events[i] = &clerk.EventRecordWithTime{}
*events[i] = event
}
return events
}
func buildStateEvent(sample *clerk.EventRecordWithTime, id uint64, timeStamp int64) *clerk.EventRecordWithTime {
event := *sample
event.ID = id
event.Time = time.Unix(timeStamp, 0)
return &event
}
func getSampleEventRecord(t *testing.T) *clerk.EventRecordWithTime {
eventRecords := stateSyncEventsPayload(t)
eventRecords.Result[0].Time = time.Unix(1, 0)
return eventRecords.Result[0]
}
func getEventRecords(t *testing.T) []*clerk.EventRecordWithTime {
return stateSyncEventsPayload(t).Result
} }
// TestEIP1559Transition tests the following: // TestEIP1559Transition tests the following:
@ -351,7 +366,7 @@ func TestEIP1559Transition(t *testing.T) {
addr3 = crypto.PubkeyToAddress(key3.PublicKey) addr3 = crypto.PubkeyToAddress(key3.PublicKey)
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
gspec = &core.Genesis{ gspec = &core.Genesis{
Config: params.BorTestChainConfig, Config: params.BorUnittestChainConfig,
Alloc: core.GenesisAlloc{ Alloc: core.GenesisAlloc{
addr1: {Balance: funds}, addr1: {Balance: funds},
addr2: {Balance: funds}, addr2: {Balance: funds},
@ -403,7 +418,7 @@ func TestEIP1559Transition(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb) 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 { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -433,7 +448,7 @@ func TestEIP1559Transition(t *testing.T) {
} }
// check burnt contract balance // check burnt contract balance
actual = state.GetBalance(common.HexToAddress(params.BorTestChainConfig.Bor.CalculateBurntContract(block.NumberU64()))) actual = state.GetBalance(common.HexToAddress(params.BorUnittestChainConfig.Bor.CalculateBurntContract(block.NumberU64())))
expected = new(big.Int).Mul(new(big.Int).SetUint64(block.GasUsed()), block.BaseFee()) expected = new(big.Int).Mul(new(big.Int).SetUint64(block.GasUsed()), block.BaseFee())
burntContractBalance := expected burntContractBalance := expected
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
@ -481,7 +496,7 @@ func TestEIP1559Transition(t *testing.T) {
} }
// check burnt contract balance // check burnt contract balance
actual = state.GetBalance(common.HexToAddress(params.BorTestChainConfig.Bor.CalculateBurntContract(block.NumberU64()))) actual = state.GetBalance(common.HexToAddress(params.BorUnittestChainConfig.Bor.CalculateBurntContract(block.NumberU64())))
expected = new(big.Int).Add(burntContractBalance, new(big.Int).Mul(new(big.Int).SetUint64(block.GasUsed()), block.BaseFee())) expected = new(big.Int).Add(burntContractBalance, new(big.Int).Mul(new(big.Int).SetUint64(block.GasUsed()), block.BaseFee()))
burntContractBalance = expected burntContractBalance = expected
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
@ -539,7 +554,7 @@ func TestEIP1559Transition(t *testing.T) {
state, _ = chain.State() state, _ = chain.State()
// check burnt contract balance // check burnt contract balance
actual = state.GetBalance(common.HexToAddress(params.BorTestChainConfig.Bor.CalculateBurntContract(block.NumberU64()))) actual = state.GetBalance(common.HexToAddress(params.BorUnittestChainConfig.Bor.CalculateBurntContract(block.NumberU64())))
burntAmount := new(big.Int).Mul( burntAmount := new(big.Int).Mul(
block.BaseFee(), block.BaseFee(),
big.NewInt(int64(block.GasUsed())), big.NewInt(int64(block.GasUsed())),
@ -550,25 +565,188 @@ func TestEIP1559Transition(t *testing.T) {
} }
} }
func newGwei(n int64) *big.Int { // EIP1559 is not supported without EIP155. An error is expected
return new(big.Int).Mul(big.NewInt(n), big.NewInt(params.GWei)) func TestEIP1559TransitionWithEIP155(t *testing.T) {
var (
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
// Generate a canonical chain to act as the main dataset
db = rawdb.NewMemoryDatabase()
engine = ethash.NewFaker()
// A sender who makes transactions, has some funds
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
key3, _ = crypto.HexToECDSA("225171aed3793cba1c029832886d69785b7e77a54a44211226b447aa2d16b058")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
gspec = &core.Genesis{
Config: params.BorUnittestChainConfig,
Alloc: core.GenesisAlloc{
addr1: {Balance: funds},
addr2: {Balance: funds},
addr3: {Balance: funds},
// The address 0xAAAA sloads 0x00 and 0x01
aa: {
Code: []byte{
byte(vm.PC),
byte(vm.PC),
byte(vm.SLOAD),
byte(vm.SLOAD),
},
Nonce: 0,
Balance: big.NewInt(0),
},
},
}
)
genesis := gspec.MustCommit(db)
// Use signer without chain ID
signer := types.HomesteadSigner{}
_, _ = core.GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})
// One transaction to 0xAAAA
accesses := types.AccessList{types.AccessTuple{
Address: aa,
StorageKeys: []common.Hash{{0}},
}}
txdata := &types.DynamicFeeTx{
ChainID: gspec.Config.ChainID,
Nonce: 0,
To: &aa,
Gas: 30000,
GasFeeCap: newGwei(5),
GasTipCap: big.NewInt(2),
AccessList: accesses,
Data: []byte{},
}
var err error
tx := types.NewTx(txdata)
tx, err = types.SignTx(tx, signer, key1)
require.ErrorIs(t, err, types.ErrTxTypeNotSupported)
})
}
// it is up to a user to use protected transactions. so if a transaction is unprotected no errors related to chainID are expected.
// transactions are checked in 2 places: transaction pool and blockchain processor.
func TestTransitionWithoutEIP155(t *testing.T) {
var (
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
// Generate a canonical chain to act as the main dataset
db = rawdb.NewMemoryDatabase()
engine = ethash.NewFaker()
// A sender who makes transactions, has some funds
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
key3, _ = crypto.HexToECDSA("225171aed3793cba1c029832886d69785b7e77a54a44211226b447aa2d16b058")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
gspec = &core.Genesis{
Config: params.BorUnittestChainConfig,
Alloc: core.GenesisAlloc{
addr1: {Balance: funds},
addr2: {Balance: funds},
addr3: {Balance: funds},
// The address 0xAAAA sloads 0x00 and 0x01
aa: {
Code: []byte{
byte(vm.PC),
byte(vm.PC),
byte(vm.SLOAD),
byte(vm.SLOAD),
},
Nonce: 0,
Balance: big.NewInt(0),
},
},
}
)
genesis := gspec.MustCommit(db)
// Use signer without chain ID
signer := types.HomesteadSigner{}
//signer := types.FrontierSigner{}
blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})
txdata := &types.LegacyTx{
Nonce: 0,
To: &aa,
Gas: 30000,
GasPrice: newGwei(5),
}
var err error
tx := types.NewTx(txdata)
tx, err = types.SignTx(tx, signer, key1)
require.Nil(t, err)
require.False(t, tx.Protected())
from, err := types.Sender(types.EIP155Signer{}, tx)
require.Equal(t, addr1, from)
require.Nil(t, err)
b.AddTx(tx)
})
diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb)
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)
}
if n, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
block := chain.GetBlockByNumber(1)
require.Len(t, block.Transactions(), 1)
} }
func TestJaipurFork(t *testing.T) { func TestJaipurFork(t *testing.T) {
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase()) init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
chain := init.ethereum.BlockChain() chain := init.ethereum.BlockChain()
engine := init.ethereum.Engine() engine := init.ethereum.Engine()
_bor := engine.(*bor.Bor) _bor := engine.(*bor.Bor)
defer _bor.Close()
db := init.ethereum.ChainDb() db := init.ethereum.ChainDb()
block := init.genesis.ToBlock(db) block := init.genesis.ToBlock(db)
res, _ := loadSpanFromFile(t)
for i := uint64(1); i < sprintSize; i++ { for i := uint64(1); i < sprintSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor) block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, res.Result.ValidatorSet.Validators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock-1 { if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock-1 {
assert.Equal(t, testSealHash(block.Header(), init.genesis.Config.Bor), bor.SealHash(block.Header(), init.genesis.Config.Bor)) require.Equal(t, testSealHash(block.Header(), init.genesis.Config.Bor), bor.SealHash(block.Header(), init.genesis.Config.Bor))
} }
if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock { if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock {
assert.Equal(t, testSealHash(block.Header(), init.genesis.Config.Bor), bor.SealHash(block.Header(), init.genesis.Config.Bor)) require.Equal(t, testSealHash(block.Header(), init.genesis.Config.Bor), bor.SealHash(block.Header(), init.genesis.Config.Bor))
} }
} }
} }

View file

@ -1,46 +1,63 @@
//go:build integration
package bor package bor
import ( import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"sort" "sort"
"testing" "testing"
"time"
"github.com/golang/mock/gomock"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/bor" "github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall" "github.com/ethereum/go-ethereum/consensus/bor/clerk"
"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/heimdall/span"
"github.com/ethereum/go-ethereum/consensus/bor/valset" "github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/tests/bor/mocks"
) )
var ( var (
// Only this account is a validator for the 0th span
key, _ = crypto.HexToECDSA(privKey)
addr = crypto.PubkeyToAddress(key.PublicKey) // 0x71562b71999873DB5b286dF957af199Ec94617F7
// This account is one the validators for 1st span (0-indexed)
key2, _ = crypto.HexToECDSA(privKey2)
addr2 = crypto.PubkeyToAddress(key2.PublicKey) // 0x9fB29AAc15b9A4B7F17c3385939b007540f4d791
)
const (
privKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
privKey2 = "9b28f36fbd67381120752d6172ecdcf10e06ab2d9a1367aac00cdcd6ac7855d3"
// The genesis for tests was generated with following parameters // The genesis for tests was generated with following parameters
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
// Only this account is a validator for the 0th span sprintSize uint64 = 4
privKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" spanSize uint64 = 8
key, _ = crypto.HexToECDSA(privKey)
addr = crypto.PubkeyToAddress(key.PublicKey) // 0x71562b71999873DB5b286dF957af199Ec94617F7
// This account is one the validators for 1st span (0-indexed) validatorHeaderBytesLength = common.AddressLength + 20 // address + power
privKey2 = "9b28f36fbd67381120752d6172ecdcf10e06ab2d9a1367aac00cdcd6ac7855d3"
key2, _ = crypto.HexToECDSA(privKey2)
addr2 = crypto.PubkeyToAddress(key2.PublicKey) // 0x9fB29AAc15b9A4B7F17c3385939b007540f4d791
validatorHeaderBytesLength = common.AddressLength + 20 // address + power
sprintSize uint64 = 4
spanSize uint64 = 8
) )
type initializeData struct { type initializeData struct {
@ -49,8 +66,6 @@ type initializeData struct {
} }
func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData { func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData {
t.Helper()
genesisData, err := ioutil.ReadFile("./testdata/genesis.json") genesisData, err := ioutil.ReadFile("./testdata/genesis.json")
if err != nil { if err != nil {
t.Fatalf("%s", err) t.Fatalf("%s", err)
@ -64,6 +79,7 @@ func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData {
ethConf := &eth.Config{ ethConf := &eth.Config{
Genesis: gen, Genesis: gen,
BorLogs: true,
} }
ethConf.Genesis.MustCommit(db) ethConf.Genesis.MustCommit(db)
@ -75,6 +91,10 @@ func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData {
ethConf.Genesis.MustCommit(ethereum.ChainDb()) ethConf.Genesis.MustCommit(ethereum.ChainDb())
ethereum.Engine().(*bor.Bor).Authorize(addr, func(account accounts.Account, s string, data []byte) ([]byte, error) {
return crypto.Sign(crypto.Keccak256(data), key)
})
return &initializeData{ return &initializeData{
genesis: gen, genesis: gen,
ethereum: ethereum, ethereum: ethereum,
@ -89,33 +109,31 @@ func insertNewBlock(t *testing.T, chain *core.BlockChain, block *types.Block) {
} }
} }
func buildNextBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, block *types.Block, signer []byte, borConfig *params.BorConfig) *types.Block { type Option func(header *types.Header)
func buildNextBlock(t *testing.T, _bor consensus.Engine, chain *core.BlockChain, parentBlock *types.Block, signer []byte, borConfig *params.BorConfig, txs []*types.Transaction, currentValidators []*valset.Validator, opts ...Option) *types.Block {
t.Helper() t.Helper()
header := block.Header() header := &types.Header{
header.Number.Add(header.Number, big.NewInt(1)) Number: big.NewInt(int64(parentBlock.Number().Uint64() + 1)),
Difficulty: big.NewInt(int64(parentBlock.Difficulty().Uint64())),
GasLimit: parentBlock.GasLimit(),
ParentHash: parentBlock.Hash(),
}
number := header.Number.Uint64() number := header.Number.Uint64()
if signer == nil { if signer == nil {
signer = getSignerKey(header.Number.Uint64()) signer = getSignerKey(header.Number.Uint64())
} }
header.ParentHash = block.Hash() header.Time = parentBlock.Time() + bor.CalcProducerDelay(header.Number.Uint64(), 0, borConfig)
header.Time += bor.CalcProducerDelay(header.Number.Uint64(), 0, borConfig)
header.Extra = make([]byte, 32+65) // vanity + extraSeal header.Extra = make([]byte, 32+65) // vanity + extraSeal
currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)} isSpanStart := IsSpanStart(number)
isSprintEnd := IsSprintEnd(number)
isSpanEnd := (number+1)%spanSize == 0 if isSpanStart {
isSpanStart := number%spanSize == 0 header.Difficulty = new(big.Int).SetInt64(int64(len(currentValidators)))
isSprintEnd := (header.Number.Uint64()+1)%sprintSize == 0
if isSpanEnd {
_, heimdallSpan := loadSpanFromFile(t)
// this is to stash the validator bytes in the header
currentValidators = heimdallSpan.ValidatorSet.Validators
} else if isSpanStart {
header.Difficulty = new(big.Int).SetInt64(3)
} }
if isSprintEnd { if isSprintEnd {
@ -132,27 +150,87 @@ func buildNextBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, block *
} }
if chain.Config().IsLondon(header.Number) { if chain.Config().IsLondon(header.Number) {
header.BaseFee = misc.CalcBaseFee(chain.Config(), block.Header()) header.BaseFee = misc.CalcBaseFee(chain.Config(), parentBlock.Header())
if !chain.Config().IsLondon(block.Number()) { if !chain.Config().IsLondon(parentBlock.Number()) {
parentGasLimit := block.GasLimit() * params.ElasticityMultiplier parentGasLimit := parentBlock.GasLimit() * params.ElasticityMultiplier
header.GasLimit = core.CalcGasLimit(parentGasLimit, parentGasLimit) header.GasLimit = core.CalcGasLimit(parentGasLimit, parentGasLimit)
} }
} }
for _, opt := range opts {
opt(header)
}
state, err := chain.State() state, err := chain.State()
if err != nil { if err != nil {
t.Fatalf("%s", err) t.Fatalf("%s", err)
} }
_, err = _bor.FinalizeAndAssemble(chain, header, state, nil, nil, nil) b := &blockGen{header: header}
if err != nil { for _, tx := range txs {
t.Fatalf("%s", err) b.addTxWithChain(chain, state, tx, addr)
} }
sign(t, header, signer, borConfig) // Finalize and seal the block
block, _ := _bor.FinalizeAndAssemble(chain, b.header, state, b.txs, nil, b.receipts)
return types.NewBlockWithHeader(header) // Write state changes to db
root, err := state.Commit(chain.Config().IsEIP158(b.header.Number))
if err != nil {
panic(fmt.Sprintf("state write error: %v", err))
}
if err := state.Database().TrieDB().Commit(root, false, nil); err != nil {
panic(fmt.Sprintf("trie write error: %v", err))
}
res := make(chan *types.Block, 1)
err = _bor.Seal(chain, block, res, nil)
if err != nil {
// an error case - sign manually
sign(t, header, signer, borConfig)
return types.NewBlockWithHeader(header)
}
return <-res
}
type blockGen struct {
txs []*types.Transaction
receipts []*types.Receipt
gasPool *core.GasPool
header *types.Header
}
func (b *blockGen) addTxWithChain(bc *core.BlockChain, statedb *state.StateDB, tx *types.Transaction, coinbase common.Address) {
if b.gasPool == nil {
b.setCoinbase(coinbase)
}
statedb.Prepare(tx.Hash(), len(b.txs))
receipt, err := core.ApplyTransaction(bc.Config(), bc, &b.header.Coinbase, b.gasPool, statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}
b.txs = append(b.txs, tx)
b.receipts = append(b.receipts, receipt)
}
func (b *blockGen) setCoinbase(addr common.Address) {
if b.gasPool != nil {
if len(b.txs) > 0 {
panic("coinbase must be set before adding transactions")
}
panic("coinbase can only be set once")
}
b.header.Coinbase = addr
b.gasPool = new(core.GasPool).AddGas(b.header.GasLimit)
} }
func sign(t *testing.T, header *types.Header, signer []byte, c *params.BorConfig) { func sign(t *testing.T, header *types.Header, signer []byte, c *params.BorConfig) {
@ -204,13 +282,80 @@ func loadSpanFromFile(t *testing.T) (*heimdall.SpanResponse, *span.HeimdallSpan)
func getSignerKey(number uint64) []byte { func getSignerKey(number uint64) []byte {
signerKey := privKey signerKey := privKey
isSpanStart := number%spanSize == 0 if IsSpanStart(number) {
if isSpanStart {
// validator set in the new span has changed // validator set in the new span has changed
signerKey = privKey2 signerKey = privKey2
} }
_key, _ := hex.DecodeString(signerKey) newKey, _ := hex.DecodeString(signerKey)
return _key return newKey
}
func getMockedHeimdallClient(t *testing.T, heimdallSpan *span.HeimdallSpan) (*mocks.MockIHeimdallClient, *gomock.Controller) {
t.Helper()
ctrl := gomock.NewController(t)
h := mocks.NewMockIHeimdallClient(ctrl)
h.EXPECT().Span(gomock.Any(), uint64(1)).Return(heimdallSpan, nil).AnyTimes()
h.EXPECT().StateSyncEvents(gomock.Any(), gomock.Any(), gomock.Any()).
Return([]*clerk.EventRecordWithTime{getSampleEventRecord(t)}, nil).AnyTimes()
return h, ctrl
}
func generateFakeStateSyncEvents(sample *clerk.EventRecordWithTime, count int) []*clerk.EventRecordWithTime {
events := make([]*clerk.EventRecordWithTime, count)
event := *sample
event.ID = 1
events[0] = &clerk.EventRecordWithTime{}
*events[0] = event
for i := 1; i < count; i++ {
event.ID = uint64(i)
event.Time = event.Time.Add(1 * time.Second)
events[i] = &clerk.EventRecordWithTime{}
*events[i] = event
}
return events
}
func buildStateEvent(sample *clerk.EventRecordWithTime, id uint64, timeStamp int64) *clerk.EventRecordWithTime {
event := *sample
event.ID = id
event.Time = time.Unix(timeStamp, 0)
return &event
}
func getSampleEventRecord(t *testing.T) *clerk.EventRecordWithTime {
t.Helper()
eventRecords := stateSyncEventsPayload(t)
eventRecords.Result[0].Time = time.Unix(1, 0)
return eventRecords.Result[0]
}
func newGwei(n int64) *big.Int {
return new(big.Int).Mul(big.NewInt(n), big.NewInt(params.GWei))
}
func IsSpanEnd(number uint64) bool {
return (number+1)%spanSize == 0
}
func IsSpanStart(number uint64) bool {
return number%spanSize == 0
}
func IsSprintStart(number uint64) bool {
return number%sprintSize == 0
}
func IsSprintEnd(number uint64) bool {
return (number+1)%sprintSize == 0
} }

View file

@ -49,19 +49,34 @@ func (mr *MockIHeimdallClientMockRecorder) Close() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockIHeimdallClient)(nil).Close)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockIHeimdallClient)(nil).Close))
} }
// FetchLatestCheckpoint mocks base method. // FetchCheckpoint mocks base method.
func (m *MockIHeimdallClient) FetchLatestCheckpoint(arg0 context.Context) (*checkpoint.Checkpoint, error) { func (m *MockIHeimdallClient) FetchCheckpoint(arg0 context.Context, arg1 int64) (*checkpoint.Checkpoint, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FetchLatestCheckpoint", arg0) ret := m.ctrl.Call(m, "FetchCheckpoint", arg0, arg1)
ret0, _ := ret[0].(*checkpoint.Checkpoint) ret0, _ := ret[0].(*checkpoint.Checkpoint)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// FetchLatestCheckpoint indicates an expected call of FetchLatestCheckpoint. // FetchCheckpoint indicates an expected call of FetchCheckpoint.
func (mr *MockIHeimdallClientMockRecorder) FetchLatestCheckpoint(arg0 interface{}) *gomock.Call { func (mr *MockIHeimdallClientMockRecorder) FetchCheckpoint(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() 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. // Span mocks base method.

View file

@ -52,6 +52,9 @@
}, },
"71562b71999873DB5b286dF957af199Ec94617F7": { "71562b71999873DB5b286dF957af199Ec94617F7": {
"balance": "0x3635c9adc5dea00000" "balance": "0x3635c9adc5dea00000"
},
"9fB29AAc15b9A4B7F17c3385939b007540f4d791": {
"balance": "0x3635c9adc5dea00000"
} }
}, },
"number": "0x0", "number": "0x0",

View file

@ -80,7 +80,7 @@ func makechain() (bc *core.BlockChain, addrHashes, txHashes []common.Hash) {
addrHashes = append(addrHashes, crypto.Keccak256Hash(addr[:])) addrHashes = append(addrHashes, crypto.Keccak256Hash(addr[:]))
txHashes = append(txHashes, tx.Hash()) 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 { if _, err := bc.InsertChain(blocks); err != nil {
panic(err) panic(err)
} }

View file

@ -79,7 +79,7 @@ func getChain() *core.BlockChain {
SnapshotWait: true, SnapshotWait: true,
} }
trieRoot = blocks[len(blocks)-1].Root() 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 { if _, err := bc.InsertChain(blocks); err != nil {
panic(err) panic(err)
} }