Prepare Bor package for testing (#416)

* Limit state sync by gas

* Added logging for state-sync total gas usage

* Added number of event-records in log

* Minor Changes

* Minor Fix

* Adding individual gasUsed

* Minor Fix

* it works

* fix tests

* log wiggle and delay with block number

* log delays as numbers

* linters

* fix tests

* restore linters for the project

* fix linters

* fix

* fix

* fix

* linters

* generation

* fix tests

* remove heimdall wrapper response

* linters

* remove possible collisions

* remove possible collisions

* remove possible collisions

* tests for unique address generation

* generalize set

* bor miner tests got restored

* fixes after CR

* final step and mining test

* fix

* fix e2e

* more tests for Heimdall requests

* fix linters

Co-authored-by: Ferran <ferranbt@protonmail.com>
Co-authored-by: Shivam Sharma <shivam691999@gmail.com>
This commit is contained in:
Evgeny Danilenko 2022-06-08 16:39:30 +03:00 committed by GitHub
parent 8808c231df
commit 9c8bf51f57
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
68 changed files with 2517 additions and 1181 deletions

View file

@ -1,6 +1,7 @@
# This file configures github.com/golangci/golangci-lint.
run:
go: '1.18'
timeout: 20m
tests: true
# default is true. Enables skipping of directories:
@ -183,4 +184,4 @@ issues:
max-issues-per-linter: 0
max-same-issues: 0
#new: true
# new-from-rev: origin/master
new-from-rev: origin/master

View file

@ -65,9 +65,7 @@ escape:
cd $(path) && go test -gcflags "-m -m" -run none -bench=BenchmarkJumpdest* -benchmem -memprofile mem.out
lint:
@./build/bin/golangci-lint run --config ./.golangci.yml \
internal/cli \
consensus/bor
@./build/bin/golangci-lint run --config ./.golangci.yml
lintci-deps:
rm -f ./build/bin/golangci-lint
@ -87,16 +85,20 @@ clean:
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
devtools:
env GOBIN= go install golang.org/x/tools/cmd/stringer@latest
env GOBIN= go install github.com/kevinburke/go-bindata/go-bindata@latest
env GOBIN= go install github.com/fjl/gencodec@latest
env GOBIN= go install github.com/golang/protobuf/protoc-gen-go@latest
env GOBIN= go install ./cmd/abigen
# Notice! If you adding new binary - add it also to tests/deps/fake.go file
$(GOBUILD) -o $(GOBIN)/stringer github.com/golang.org/x/tools/cmd/stringer
$(GOBUILD) -o $(GOBIN)/go-bindata github.com/kevinburke/go-bindata/go-bindata
$(GOBUILD) -o $(GOBIN)/codecgen github.com/ugorji/go/codec/codecgen
$(GOBUILD) -o $(GOBIN)/abigen ./cmd/abigen
$(GOBUILD) -o $(GOBIN)/mockgen github.com/golang/mock/mockgen
$(GOBUILD) -o $(GOBIN)/protoc-gen-go github.com/golang/protobuf/protoc-gen-go
PATH=$(GOBIN):$(PATH) go generate ./common
PATH=$(GOBIN):$(PATH) go generate ./core/types
PATH=$(GOBIN):$(PATH) go generate ./consensus/bor
@type "solc" 2> /dev/null || echo 'Please install solc'
@type "protoc" 2> /dev/null || echo 'Please install protoc'
# Cross Compilation Targets (xgo)
geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios
@echo "Full cross compilation done:"
@ls -ld $(GOBIN)/geth-*

View file

@ -17,7 +17,6 @@ const (
)
func main() {
commands := cli.Commands()
dest := flag.String("d", DefaultDir, "Destination directory where the docs will be generated")
@ -35,10 +34,12 @@ func main() {
keys := make([]string, len(commands))
i := 0
for k := range commands {
keys[i] = k
i++
}
sort.Strings(keys)
for _, name := range keys {
@ -60,12 +61,17 @@ func main() {
func overwriteFile(filePath string, text string) {
log.Printf("Writing to page: %s\n", filePath)
f, err := os.Create(filePath)
if err != nil {
log.Fatalln(err)
}
f.WriteString(text)
if err := f.Close(); err != nil {
if _, err = f.WriteString(text); err != nil {
log.Fatalln(err)
}
if err = f.Close(); err != nil {
log.Fatalln(err)
}
}

28
common/debug/debug.go Normal file
View file

@ -0,0 +1,28 @@
package debug
import (
"runtime"
)
// Callers returns given number of callers with packages
func Callers(show int) []string {
fpcs := make([]uintptr, show)
n := runtime.Callers(2, fpcs)
if n == 0 {
return nil
}
callers := make([]string, 0, len(fpcs))
for _, p := range fpcs {
caller := runtime.FuncForPC(p - 1)
if caller == nil {
continue
}
callers = append(callers, caller.Name())
}
return callers
}

11
common/set/slice.go Normal file
View file

@ -0,0 +1,11 @@
package set
func New[T comparable](slice []T) map[T]struct{} {
m := make(map[T]struct{}, len(slice))
for _, el := range slice {
m[el] = struct{}{}
}
return m
}

View file

@ -0,0 +1,6 @@
package abi
type ABI interface {
Pack(name string, args ...interface{}) ([]byte, error)
UnpackIntoInterface(v interface{}, name string, data []byte) error
}

View file

@ -10,6 +10,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rpc"
@ -206,10 +207,10 @@ func (api *API) GetCurrentProposer() (common.Address, error) {
}
// GetCurrentValidators gets the current validators
func (api *API) GetCurrentValidators() ([]*Validator, error) {
func (api *API) GetCurrentValidators() ([]*valset.Validator, error) {
snap, err := api.GetSnapshot(nil)
if err != nil {
return make([]*Validator, 0), err
return make([]*valset.Validator, 0), err
}
return snap.ValidatorSet.Validators, nil
@ -236,7 +237,7 @@ func (api *API) GetRootHash(start uint64, end uint64) (string, error) {
currentHeaderNumber := api.chain.CurrentHeader().Number.Uint64()
if start > end || end > currentHeaderNumber {
return "", &InvalidStartEndBlockError{start, end, currentHeaderNumber}
return "", &valset.InvalidStartEndBlockError{Start: start, End: end, CurrentHeader: currentHeaderNumber}
}
blockHeaders := make([]*types.Header, end-start+1)

View file

@ -0,0 +1,14 @@
package api
import (
"context"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc"
)
//go:generate mockgen -destination=./caller_mock.go -package=api . Caller
type Caller interface {
Call(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *ethapi.StateOverride) (hexutil.Bytes, error)
}

View file

@ -0,0 +1,53 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/ethereum/go-ethereum/consensus/bor/api (interfaces: Caller)
// Package api is a generated GoMock package.
package api
import (
context "context"
reflect "reflect"
hexutil "github.com/ethereum/go-ethereum/common/hexutil"
ethapi "github.com/ethereum/go-ethereum/internal/ethapi"
rpc "github.com/ethereum/go-ethereum/rpc"
gomock "github.com/golang/mock/gomock"
)
// MockCaller is a mock of Caller interface.
type MockCaller struct {
ctrl *gomock.Controller
recorder *MockCallerMockRecorder
}
// MockCallerMockRecorder is the mock recorder for MockCaller.
type MockCallerMockRecorder struct {
mock *MockCaller
}
// NewMockCaller creates a new mock instance.
func NewMockCaller(ctrl *gomock.Controller) *MockCaller {
mock := &MockCaller{ctrl: ctrl}
mock.recorder = &MockCallerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockCaller) EXPECT() *MockCallerMockRecorder {
return m.recorder
}
// Call mocks base method.
func (m *MockCaller) Call(arg0 context.Context, arg1 ethapi.TransactionArgs, arg2 rpc.BlockNumberOrHash, arg3 *ethapi.StateOverride) (hexutil.Bytes, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Call", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(hexutil.Bytes)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Call indicates an expected call of Call.
func (mr *MockCallerMockRecorder) Call(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Call", reflect.TypeOf((*MockCaller)(nil).Call), arg0, arg1, arg2, arg3)
}

View file

@ -2,37 +2,34 @@ package bor
import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"math"
"math/big"
"sort"
"strconv"
"strings"
"sync"
"time"
lru "github.com/hashicorp/golang-lru"
"golang.org/x/crypto/sha3"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/bor/api"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/consensus/misc"
"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/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
@ -56,7 +53,6 @@ var (
uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
validatorHeaderBytesLength = common.AddressLength + 20 // address + power
systemAddress = common.HexToAddress("0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE")
)
// Various error messages to mark blocks invalid. These should be private to
@ -100,9 +96,6 @@ var (
// errOutOfRangeChain is returned if an authorization list is attempted to
// be modified via out-of-range or non-contiguous headers.
errOutOfRangeChain = errors.New("out of range or non-contiguous chain")
// errShutdownDetected is returned if a shutdown was detected
errShutdownDetected = errors.New("shutdown detected")
)
// SignerFn is a signer callback function to request a header to be signed by a
@ -220,24 +213,25 @@ type Bor struct {
signFn SignerFn // Signer function to authorize hashes with
lock sync.RWMutex // Protects the signer fields
ethAPI *ethapi.PublicBlockChainAPI
GenesisContractsClient *GenesisContractsClient
validatorSetABI abi.ABI
stateReceiverABI abi.ABI
ethAPI api.Caller
spanner Spanner
GenesisContractsClient GenesisContract
HeimdallClient IHeimdallClient
WithoutHeimdall bool
// The fields below are for testing only
fakeDiff bool // Skip difficulty verifications
closeOnce sync.Once
}
// New creates a Matic Bor consensus engine.
func New(
chainConfig *params.ChainConfig,
db ethdb.Database,
ethAPI *ethapi.PublicBlockChainAPI,
heimdallURL string,
withoutHeimdall bool,
ethAPI api.Caller,
spanner Spanner,
heimdallClient IHeimdallClient,
genesisContracts GenesisContract,
) *Bor {
// get bor config
borConfig := chainConfig.Bor
@ -246,14 +240,10 @@ func New(
if borConfig != nil && borConfig.Sprint == 0 {
borConfig.Sprint = defaultSprintLength
}
// Allocate the snapshot caches and create the engine
recents, _ := lru.NewARC(inmemorySnapshots)
signatures, _ := lru.NewARC(inmemorySignatures)
vABI, _ := abi.JSON(strings.NewReader(validatorsetABI))
sABI, _ := abi.JSON(strings.NewReader(stateReceiverABI))
heimdallClient, _ := NewHeimdallClient(heimdallURL)
genesisContractsClient := NewGenesisContractsClient(chainConfig, borConfig.ValidatorContract, borConfig.StateReceiverContract, ethAPI)
c := &Bor{
chainConfig: chainConfig,
config: borConfig,
@ -261,11 +251,9 @@ func New(
ethAPI: ethAPI,
recents: recents,
signatures: signatures,
validatorSetABI: vABI,
stateReceiverABI: sABI,
GenesisContractsClient: genesisContractsClient,
spanner: spanner,
GenesisContractsClient: genesisContracts,
HeimdallClient: heimdallClient,
WithoutHeimdall: withoutHeimdall,
}
// make sure we can decode all the GenesisAlloc in the BorConfig.
@ -332,7 +320,7 @@ func (c *Bor) verifyHeader(chain consensus.ChainHeaderReader, header *types.Head
}
// check extr adata
isSprintEnd := (number+1)%c.config.Sprint == 0
isSprintEnd := IsSprintStart(number+1, c.config.Sprint)
// Ensure that the extra-data contains a signer list on checkpoint, but none otherwise
signersBytes := len(header.Extra) - extraVanity - extraSeal
@ -441,19 +429,18 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainHeaderReader, header *t
// Retrieve the snapshot needed to verify this header and cache it
snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
if err != nil {
return err
}
// verify the validator list in the last sprint block
if isSprintStart(number, c.config.Sprint) {
if IsSprintStart(number, c.config.Sprint) {
parentValidatorBytes := parent.Extra[extraVanity : len(parent.Extra)-extraSeal]
validatorsBytes := make([]byte, len(snap.ValidatorSet.Validators)*validatorHeaderBytesLength)
currentValidators := snap.ValidatorSet.Copy().Validators
// sort validator by address
sort.Sort(ValidatorsByAddress(currentValidators))
sort.Sort(valset.ValidatorsByAddress(currentValidators))
for i, validator := range currentValidators {
copy(validatorsBytes[i*validatorHeaderBytesLength:], validator.HeaderBytes())
@ -487,7 +474,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co
// If an on-disk checkpoint snapshot can be found, use that
if number%checkpointInterval == 0 {
if s, err := loadSnapshot(c.config, c.signatures, c.db, hash, c.ethAPI); err == nil {
if s, err := loadSnapshot(c.config, c.signatures, c.db, hash); err == nil {
log.Trace("Loaded snapshot from disk", "number", number, "hash", hash)
snap = s
@ -509,13 +496,13 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co
hash := checkpoint.Hash()
// get validators and current span
validators, err := c.GetCurrentValidators(hash, number+1)
validators, err := c.spanner.GetCurrentValidators(hash, number+1)
if err != nil {
return nil, err
}
// new snap shot
snap = newSnapshot(c.config, c.signatures, number, hash, validators, c.ethAPI)
snap = newSnapshot(c.config, c.signatures, number, hash, validators)
if err := snap.store(c.db); err != nil {
return nil, err
}
@ -674,14 +661,14 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
header.Extra = header.Extra[:extraVanity]
// get validator set if number
if (number+1)%c.config.Sprint == 0 {
newValidators, err := c.GetCurrentValidators(header.ParentHash, number+1)
if IsSprintStart(number+1, c.config.Sprint) {
newValidators, err := c.spanner.GetCurrentValidators(header.ParentHash, number+1)
if err != nil {
return errors.New("unknown validators")
}
// sort validator by address
sort.Sort(ValidatorsByAddress(newValidators))
sort.Sort(valset.ValidatorsByAddress(newValidators))
for _, validator := range newValidators {
header.Extra = append(header.Extra, validator.HeaderBytes()...)
@ -727,14 +714,14 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header,
headerNumber := header.Number.Uint64()
if headerNumber%c.config.Sprint == 0 {
cx := chainContext{Chain: chain, Bor: c}
cx := statefull.ChainContext{Chain: chain, Bor: c}
// check and commit span
if err := c.checkAndCommitSpan(state, header, cx); err != nil {
log.Error("Error while committing span", "error", err)
return
}
if !c.WithoutHeimdall {
if c.HeimdallClient != nil {
// commit statees
stateSyncData, err = c.CommitStates(state, header, cx)
if err != nil {
@ -762,7 +749,6 @@ func decodeGenesisAlloc(i interface{}) (core.GenesisAlloc, error) {
var alloc core.GenesisAlloc
b, err := json.Marshal(i)
if err != nil {
return nil, err
}
@ -800,7 +786,7 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ
headerNumber := header.Number.Uint64()
if headerNumber%c.config.Sprint == 0 {
cx := chainContext{Chain: chain, Bor: c}
cx := statefull.ChainContext{Chain: chain, Bor: c}
// check and commit span
err := c.checkAndCommitSpan(state, header, cx)
@ -809,7 +795,7 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ
return nil, err
}
if !c.WithoutHeimdall {
if c.HeimdallClient != nil {
// commit states
stateSyncData, err = c.CommitStates(state, header, cx)
if err != nil {
@ -832,7 +818,7 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ
block := types.NewBlock(header, txs, nil, receipts, new(trie.Trie))
// set state sync
bc := chain.(*core.BlockChain)
bc := chain.(core.BorStateSyncer)
bc.SetStateSync(stateSyncData)
// return the final block for sealing
@ -890,15 +876,13 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
wiggle := time.Duration(successionNumber) * time.Duration(c.config.CalculateBackupMultiplier(number)) * time.Second
// Sign all the things!
sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeBor, BorRLP(header, c.config))
err = Sign(signFn, signer, header, c.config)
if err != nil {
return err
}
copy(header.Extra[len(header.Extra)-extraSeal:], sighash)
// Wait until sealing is terminated or delay timeout.
log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay))
log.Info("Waiting for slot to sign and propagate", "number", number, "hash", header.Hash, "delay-in-sec", uint(delay), "delay", common.PrettyDuration(delay))
go func() {
select {
@ -910,6 +894,8 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
log.Info(
"Sealing out-of-turn",
"number", number,
"hash", header.Hash,
"wiggle-in-sec", uint(wiggle),
"wiggle", common.PrettyDuration(wiggle),
"in-turn-signer", snap.ValidatorSet.GetProposer().Address.Hex(),
)
@ -932,6 +918,17 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
return nil
}
func Sign(signFn SignerFn, signer common.Address, header *types.Header, c *params.BorConfig) error {
sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeBor, BorRLP(header, c))
if err != nil {
return err
}
copy(header.Extra[len(header.Extra)-extraSeal:], sighash)
return nil
}
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have based on the previous blocks in the chain and the
// current signer.
@ -962,114 +959,11 @@ func (c *Bor) APIs(chain consensus.ChainHeaderReader) []rpc.API {
// Close implements consensus.Engine. It's a noop for bor as there are no background threads.
func (c *Bor) Close() error {
c.HeimdallClient.Close()
return nil
}
// GetCurrentSpan get current span from contract
func (c *Bor) GetCurrentSpan(headerHash common.Hash) (*Span, error) {
// block
blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false)
// method
method := "getCurrentSpan"
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
data, err := c.validatorSetABI.Pack(method)
if err != nil {
log.Error("Unable to pack tx for getCurrentSpan", "error", err)
return nil, err
}
msgData := (hexutil.Bytes)(data)
toAddress := common.HexToAddress(c.config.ValidatorContract)
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
result, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{
Gas: &gas,
To: &toAddress,
Data: &msgData,
}, blockNr, nil)
if err != nil {
return nil, err
}
// span result
ret := new(struct {
Number *big.Int
StartBlock *big.Int
EndBlock *big.Int
c.closeOnce.Do(func() {
c.HeimdallClient.Close()
})
if err := c.validatorSetABI.UnpackIntoInterface(ret, method, result); err != nil {
return nil, err
}
// create new span
span := Span{
ID: ret.Number.Uint64(),
StartBlock: ret.StartBlock.Uint64(),
EndBlock: ret.EndBlock.Uint64(),
}
return &span, nil
}
// GetCurrentValidators get current validators
func (c *Bor) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*Validator, error) {
// block
blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false)
// method
method := "getBorValidators"
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
data, err := c.validatorSetABI.Pack(method, big.NewInt(0).SetUint64(blockNumber))
if err != nil {
log.Error("Unable to pack tx for getValidator", "error", err)
return nil, err
}
// call
msgData := (hexutil.Bytes)(data)
toAddress := common.HexToAddress(c.config.ValidatorContract)
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
result, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{
Gas: &gas,
To: &toAddress,
Data: &msgData,
}, blockNr, nil)
if err != nil {
panic(err)
}
var (
ret0 = new([]common.Address)
ret1 = new([]*big.Int)
)
out := &[]interface{}{
ret0,
ret1,
}
if err := c.validatorSetABI.UnpackIntoInterface(out, method, result); err != nil {
return nil, err
}
valz := make([]*Validator, len(*ret0))
for i, a := range *ret0 {
valz[i] = &Validator{
Address: a,
VotingPower: (*ret1)[i].Int64(),
}
}
return valz, nil
return nil
}
func (c *Bor) checkAndCommitSpan(
@ -1078,21 +972,20 @@ func (c *Bor) checkAndCommitSpan(
chain core.ChainContext,
) error {
headerNumber := header.Number.Uint64()
span, err := c.GetCurrentSpan(header.ParentHash)
span, err := c.spanner.GetCurrentSpan(header.ParentHash)
if err != nil {
return err
}
if c.needToCommitSpan(span, headerNumber) {
err := c.fetchAndCommitSpan(span.ID+1, state, header, chain)
return err
return c.FetchAndCommitSpan(span.ID+1, state, header, chain)
}
return nil
}
func (c *Bor) needToCommitSpan(span *Span, headerNumber uint64) bool {
func (c *Bor) needToCommitSpan(span *span.Span, headerNumber uint64) bool {
// if span is nil
if span == nil {
return false
@ -1111,15 +1004,16 @@ func (c *Bor) needToCommitSpan(span *Span, headerNumber uint64) bool {
return false
}
func (c *Bor) fetchAndCommitSpan(
func (c *Bor) FetchAndCommitSpan(
newSpanID uint64,
state *state.StateDB,
header *types.Header,
chain core.ChainContext,
) error {
var heimdallSpan HeimdallSpan
var heimdallSpan span.HeimdallSpan
if c.WithoutHeimdall {
if c.HeimdallClient == nil {
// fixme: move to a new mock or fake and remove c.HeimdallClient completely
s, err := c.getNextHeimdallSpanForTest(newSpanID, header, chain)
if err != nil {
return err
@ -1127,105 +1021,51 @@ func (c *Bor) fetchAndCommitSpan(
heimdallSpan = *s
} else {
response, err := c.HeimdallClient.FetchWithRetry(fmt.Sprintf("bor/span/%d", newSpanID), "")
response, err := c.HeimdallClient.Span(newSpanID)
if err != nil {
return err
}
if err := json.Unmarshal(response.Result, &heimdallSpan); err != nil {
return err
}
heimdallSpan = *response
}
// check if chain id matches with heimdall span
// check if chain id matches with Heimdall span
if heimdallSpan.ChainID != c.chainConfig.ChainID.String() {
return fmt.Errorf(
"Chain id proposed span, %s, and bor chain id, %s, doesn't match",
"chain id proposed span, %s, and bor chain id, %s, doesn't match",
heimdallSpan.ChainID,
c.chainConfig.ChainID,
)
}
// get validators bytes
validators := make([]MinimalVal, 0, len(heimdallSpan.ValidatorSet.Validators))
for _, val := range heimdallSpan.ValidatorSet.Validators {
validators = append(validators, val.MinimalVal())
}
validatorBytes, err := rlp.EncodeToBytes(validators)
if err != nil {
return err
}
// get producers bytes
producers := make([]MinimalVal, 0, len(heimdallSpan.SelectedProducers))
for _, val := range heimdallSpan.SelectedProducers {
producers = append(producers, val.MinimalVal())
}
producerBytes, err := rlp.EncodeToBytes(producers)
if err != nil {
return err
}
// method
method := "commitSpan"
log.Info("✅ Committing new span",
"id", heimdallSpan.ID,
"startBlock", heimdallSpan.StartBlock,
"endBlock", heimdallSpan.EndBlock,
"validatorBytes", hex.EncodeToString(validatorBytes),
"producerBytes", hex.EncodeToString(producerBytes),
)
// get packed data
data, err := c.validatorSetABI.Pack(method,
big.NewInt(0).SetUint64(heimdallSpan.ID),
big.NewInt(0).SetUint64(heimdallSpan.StartBlock),
big.NewInt(0).SetUint64(heimdallSpan.EndBlock),
validatorBytes,
producerBytes,
)
if err != nil {
log.Error("Unable to pack tx for commitSpan", "error", err)
return err
}
// get system message
msg := getSystemMessage(common.HexToAddress(c.config.ValidatorContract), data)
// apply message
return applyMessage(msg, state, header, c.chainConfig, chain)
return c.spanner.CommitSpan(heimdallSpan, state, header, chain)
}
// CommitStates commit states
func (c *Bor) CommitStates(
state *state.StateDB,
header *types.Header,
chain chainContext,
chain statefull.ChainContext,
) ([]*types.StateSyncData, error) {
stateSyncs := make([]*types.StateSyncData, 0)
number := header.Number.Uint64()
_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
if err != nil {
return nil, err
}
to := time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.Sprint).Time), 0)
lastStateID := _lastStateID.Uint64()
log.Info(
"Fetching state updates from Heimdall",
"fromID", lastStateID+1,
"to", to.Format(time.RFC3339))
eventRecords, err := c.HeimdallClient.FetchStateSyncEvents(lastStateID+1, to.Unix())
eventRecords, err := c.HeimdallClient.StateSyncEvents(lastStateID+1, to.Unix())
if err != nil {
log.Error("Error occurred when fetching state sync events", err)
log.Error("Error occurred when fetching state sync events", "stateID", lastStateID+1, "error", err)
}
if c.config.OverrideStateSyncRecords != nil {
@ -1234,6 +1074,8 @@ func (c *Bor) CommitStates(
}
}
totalGas := 0 /// limit on gas for state sync per block
chainID := c.chainConfig.ChainID.String()
for _, eventRecord := range eventRecords {
@ -1242,7 +1084,7 @@ func (c *Bor) CommitStates(
}
if err := validateEventRecord(eventRecord, number, to, lastStateID, chainID); err != nil {
log.Error(err.Error())
log.Error("while validating event record", "block", number, "to", to, "stateID", lastStateID, "error", err.Error())
break
}
@ -1252,18 +1094,23 @@ func (c *Bor) CommitStates(
Data: hex.EncodeToString(eventRecord.Data),
TxHash: eventRecord.TxHash,
}
stateSyncs = append(stateSyncs, &stateData)
if err := c.GenesisContractsClient.CommitState(eventRecord, state, header, chain); err != nil {
gasUsed, err := c.GenesisContractsClient.CommitState(eventRecord, state, header, chain)
if err != nil {
return nil, err
}
totalGas += int(gasUsed)
lastStateID++
}
return stateSyncs, nil
}
func validateEventRecord(eventRecord *EventRecordWithTime, number uint64, to time.Time, lastStateID uint64, chainID string) error {
func validateEventRecord(eventRecord *clerk.EventRecordWithTime, number uint64, to time.Time, lastStateID uint64, chainID string) error {
// event id should be sequential and event.Time should lie in the range [from, to)
if lastStateID+1 != eventRecord.ID || eventRecord.ChainID != chainID || !eventRecord.Time.Before(to) {
return &InvalidStateReceivedError{number, lastStateID, &to, eventRecord}
@ -1276,6 +1123,10 @@ func (c *Bor) SetHeimdallClient(h IHeimdallClient) {
c.HeimdallClient = h
}
func (c *Bor) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
return c.spanner.GetCurrentValidators(headerHash, blockNumber)
}
//
// Private methods
//
@ -1284,16 +1135,16 @@ func (c *Bor) getNextHeimdallSpanForTest(
newSpanID uint64,
header *types.Header,
chain core.ChainContext,
) (*HeimdallSpan, error) {
) (*span.HeimdallSpan, error) {
headerNumber := header.Number.Uint64()
span, err := c.GetCurrentSpan(header.ParentHash)
spanBor, err := c.spanner.GetCurrentSpan(header.ParentHash)
if err != nil {
return nil, err
}
// get local chain context object
localContext := chain.(chainContext)
localContext := chain.(statefull.ChainContext)
// Retrieve the snapshot needed to verify this header and cache it
snap, err := c.snapshot(localContext.Chain, headerNumber-1, header.ParentHash, nil)
if err != nil {
@ -1301,22 +1152,22 @@ func (c *Bor) getNextHeimdallSpanForTest(
}
// new span
span.ID = newSpanID
if span.EndBlock == 0 {
span.StartBlock = 256
spanBor.ID = newSpanID
if spanBor.EndBlock == 0 {
spanBor.StartBlock = 256
} else {
span.StartBlock = span.EndBlock + 1
spanBor.StartBlock = spanBor.EndBlock + 1
}
span.EndBlock = span.StartBlock + (100 * c.config.Sprint) - 1
spanBor.EndBlock = spanBor.StartBlock + (100 * c.config.Sprint) - 1
selectedProducers := make([]Validator, len(snap.ValidatorSet.Validators))
selectedProducers := make([]valset.Validator, len(snap.ValidatorSet.Validators))
for i, v := range snap.ValidatorSet.Validators {
selectedProducers[i] = *v
}
heimdallSpan := &HeimdallSpan{
Span: *span,
heimdallSpan := &span.HeimdallSpan{
Span: *spanBor,
ValidatorSet: *snap.ValidatorSet,
SelectedProducers: selectedProducers,
ChainID: c.chainConfig.ChainID.String(),
@ -1325,82 +1176,7 @@ func (c *Bor) getNextHeimdallSpanForTest(
return heimdallSpan, nil
}
//
// Chain context
//
// chain context
type chainContext struct {
Chain consensus.ChainHeaderReader
Bor consensus.Engine
}
func (c chainContext) Engine() consensus.Engine {
return c.Bor
}
func (c chainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
return c.Chain.GetHeader(hash, number)
}
// callmsg implements core.Message to allow passing it as a transaction simulator.
type callmsg struct {
ethereum.CallMsg
}
func (m callmsg) From() common.Address { return m.CallMsg.From }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.CallMsg.To }
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
func (m callmsg) Data() []byte { return m.CallMsg.Data }
// get system message
func getSystemMessage(toAddress common.Address, data []byte) callmsg {
return callmsg{
ethereum.CallMsg{
From: systemAddress,
Gas: math.MaxUint64 / 2,
GasPrice: big.NewInt(0),
Value: big.NewInt(0),
To: &toAddress,
Data: data,
},
}
}
// apply message
func applyMessage(
msg callmsg,
state *state.StateDB,
header *types.Header,
chainConfig *params.ChainConfig,
chainContext core.ChainContext,
) error {
// Create a new context to be used in the EVM environment
blockContext := core.NewEVMBlockContext(header, chainContext, &header.Coinbase)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, state, chainConfig, vm.Config{})
// Apply the transaction to the current state (included in the env)
_, _, err := vmenv.Call(
vm.AccountRef(msg.From()),
*msg.To(),
msg.Data(),
msg.Gas(),
msg.Value(),
)
// Update the state with pending changes
if err != nil {
state.Finalise(true)
}
return nil
}
func validatorContains(a []*Validator, x *Validator) (*Validator, bool) {
func validatorContains(a []*valset.Validator, x *valset.Validator) (*valset.Validator, bool) {
for _, n := range a {
if n.Address == x.Address {
return n, true
@ -1410,11 +1186,11 @@ func validatorContains(a []*Validator, x *Validator) (*Validator, bool) {
return nil, false
}
func getUpdatedValidatorSet(oldValidatorSet *ValidatorSet, newVals []*Validator) *ValidatorSet {
func getUpdatedValidatorSet(oldValidatorSet *valset.ValidatorSet, newVals []*valset.Validator) *valset.ValidatorSet {
v := oldValidatorSet
oldVals := v.Validators
changes := make([]*Validator, 0, len(oldVals))
changes := make([]*valset.Validator, 0, len(oldVals))
for _, ov := range oldVals {
if f, ok := validatorContains(newVals, ov); ok {
@ -1433,12 +1209,12 @@ func getUpdatedValidatorSet(oldValidatorSet *ValidatorSet, newVals []*Validator)
}
if err := v.UpdateWithChangeSet(changes); err != nil {
log.Error("Error while updating change set", err)
log.Error("Error while updating change set", "error", err)
}
return v
}
func isSprintStart(number, sprint uint64) bool {
func IsSprintStart(number, sprint uint64) bool {
return number%sprint == 0
}

View file

@ -1,4 +1,4 @@
package bor
package clerk
import (
"fmt"
@ -23,10 +23,10 @@ type EventRecordWithTime struct {
Time time.Time `json:"record_time" yaml:"record_time"`
}
// String returns the string representations of span
func (e *EventRecordWithTime) String() string {
// String returns the string representation of EventRecord
func (e *EventRecordWithTime) String(gasUsed uint64) string {
return fmt.Sprintf(
"id %v, contract %v, data: %v, txHash: %v, logIndex: %v, chainId: %v, time %s",
"id %v, contract %v, data: %v, txHash: %v, logIndex: %v, chainId: %v, time %s, gasUsed %d",
e.ID,
e.Contract.String(),
e.Data.String(),
@ -34,6 +34,7 @@ func (e *EventRecordWithTime) String() string {
e.LogIndex,
e.ChainID,
e.Time.Format(time.RFC3339),
gasUsed,
)
}

File diff suppressed because one or more lines are too long

View file

@ -3,37 +3,10 @@ package bor
import (
"fmt"
"time"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
)
// TotalVotingPowerExceededError is returned when the maximum allowed total voting power is exceeded
type TotalVotingPowerExceededError struct {
Sum int64
Validators []*Validator
}
func (e *TotalVotingPowerExceededError) Error() string {
return fmt.Sprintf(
"Total voting power should be guarded to not exceed %v; got: %v; for validator set: %v",
MaxTotalVotingPower,
e.Sum,
e.Validators,
)
}
type InvalidStartEndBlockError struct {
Start uint64
End uint64
CurrentHeader uint64
}
func (e *InvalidStartEndBlockError) Error() string {
return fmt.Sprintf(
"Invalid parameters start: %d and end block: %d params",
e.Start,
e.End,
)
}
type MaxCheckpointLengthExceededError struct {
Start uint64
End uint64
@ -129,12 +102,12 @@ type InvalidStateReceivedError struct {
Number uint64
LastStateID uint64
To *time.Time
Event *EventRecordWithTime
Event *clerk.EventRecordWithTime
}
func (e *InvalidStateReceivedError) Error() string {
return fmt.Sprintf(
"Received invalid event %s at block %d. Requested events until %s. Last state id was %d",
"Received invalid event %v at block %d. Requested events until %s. Last state id was %d",
e.Event,
e.Number,
e.To.Format(time.RFC3339),

16
consensus/bor/genesis.go Normal file
View file

@ -0,0 +1,16 @@
package bor
import (
"math/big"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
)
//go:generate mockgen -destination=./genesis_contract_mock.go -package=bor . GenesisContract
type GenesisContract interface {
CommitState(event *clerk.EventRecordWithTime, state *state.StateDB, header *types.Header, chCtx statefull.ChainContext) (uint64, error)
LastStateId(snapshotNumber uint64) (*big.Int, error)
}

View file

@ -0,0 +1,69 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/ethereum/go-ethereum/consensus/bor (interfaces: GenesisContract)
// Package bor is a generated GoMock package.
package bor
import (
big "math/big"
reflect "reflect"
clerk "github.com/ethereum/go-ethereum/consensus/bor/clerk"
statefull "github.com/ethereum/go-ethereum/consensus/bor/statefull"
state "github.com/ethereum/go-ethereum/core/state"
types "github.com/ethereum/go-ethereum/core/types"
gomock "github.com/golang/mock/gomock"
)
// MockGenesisContract is a mock of GenesisContract interface.
type MockGenesisContract struct {
ctrl *gomock.Controller
recorder *MockGenesisContractMockRecorder
}
// MockGenesisContractMockRecorder is the mock recorder for MockGenesisContract.
type MockGenesisContractMockRecorder struct {
mock *MockGenesisContract
}
// NewMockGenesisContract creates a new mock instance.
func NewMockGenesisContract(ctrl *gomock.Controller) *MockGenesisContract {
mock := &MockGenesisContract{ctrl: ctrl}
mock.recorder = &MockGenesisContractMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockGenesisContract) EXPECT() *MockGenesisContractMockRecorder {
return m.recorder
}
// CommitState mocks base method.
func (m *MockGenesisContract) CommitState(arg0 *clerk.EventRecordWithTime, arg1 *state.StateDB, arg2 *types.Header, arg3 statefull.ChainContext) (uint64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CommitState", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(uint64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CommitState indicates an expected call of CommitState.
func (mr *MockGenesisContractMockRecorder) CommitState(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitState", reflect.TypeOf((*MockGenesisContract)(nil).CommitState), arg0, arg1, arg2, arg3)
}
// LastStateId mocks base method.
func (m *MockGenesisContract) LastStateId(arg0 uint64) (*big.Int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LastStateId", arg0)
ret0, _ := ret[0].(*big.Int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// LastStateId indicates an expected call of LastStateId.
func (mr *MockGenesisContractMockRecorder) LastStateId(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastStateId", reflect.TypeOf((*MockGenesisContract)(nil).LastStateId), arg0)
}

File diff suppressed because one or more lines are too long

13
consensus/bor/heimdall.go Normal file
View file

@ -0,0 +1,13 @@
package bor
import (
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
)
//go:generate mockgen -destination=../../tests/bor/mocks/IHeimdallClient.go -package=mocks . IHeimdallClient
type IHeimdallClient interface {
StateSyncEvents(fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error)
Span(spanID uint64) (*span.HeimdallSpan, error)
Close()
}

View file

@ -0,0 +1,222 @@
package heimdall
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"sort"
"time"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
"github.com/ethereum/go-ethereum/log"
)
// errShutdownDetected is returned if a shutdown was detected
var errShutdownDetected = errors.New("shutdown detected")
const (
stateFetchLimit = 50
apiHeimdallTimeout = 5 * time.Second
)
type StateSyncEventsResponse struct {
Height string `json:"height"`
Result []*clerk.EventRecordWithTime `json:"result"`
}
type SpanResponse struct {
Height string `json:"height"`
Result span.HeimdallSpan `json:"result"`
}
type HeimdallClient struct {
urlString string
client http.Client
closeCh chan struct{}
}
func NewHeimdallClient(urlString string) *HeimdallClient {
return &HeimdallClient{
urlString: urlString,
client: http.Client{
Timeout: apiHeimdallTimeout,
},
closeCh: make(chan struct{}),
}
}
const (
fetchStateSyncEventsFormat = "from-id=%d&to-time=%d&limit=%d"
fetchStateSyncEventsPath = "clerk/event-record/list"
fetchSpanFormat = "bor/span/%d"
)
func (h *HeimdallClient) StateSyncEvents(fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) {
eventRecords := make([]*clerk.EventRecordWithTime, 0)
for {
url, err := stateSyncURL(h.urlString, fromID, to)
if err != nil {
return nil, err
}
log.Info("Fetching state sync events", "queryParams", url.RawQuery)
response, err := FetchWithRetry[StateSyncEventsResponse](h.client, url, h.closeCh)
if err != nil {
return nil, err
}
if response == nil || response.Result == nil {
// status 204
break
}
eventRecords = append(eventRecords, response.Result...)
if len(response.Result) < stateFetchLimit {
break
}
fromID += uint64(stateFetchLimit)
}
sort.SliceStable(eventRecords, func(i, j int) bool {
return eventRecords[i].ID < eventRecords[j].ID
})
return eventRecords, nil
}
func (h *HeimdallClient) Span(spanID uint64) (*span.HeimdallSpan, error) {
url, err := spanURL(h.urlString, spanID)
if err != nil {
return nil, err
}
response, err := FetchWithRetry[SpanResponse](h.client, url, h.closeCh)
if err != nil {
return nil, err
}
return &response.Result, nil
}
// FetchWithRetry returns data from heimdall with retry
func FetchWithRetry[T any](client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) {
// attempt counter
attempt := 1
result := new(T)
ctx, cancel := context.WithTimeout(context.Background(), apiHeimdallTimeout)
// request data once
body, err := internalFetch(ctx, client, url)
cancel()
if err == nil && body != nil {
err = json.Unmarshal(body, result)
if err != nil {
return nil, err
}
return result, nil
}
// create a new ticker for retrying the request
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
log.Info("Retrying again in 5 seconds to fetch data from Heimdall", "path", url.Path, "attempt", attempt)
attempt++
select {
case <-closeCh:
log.Debug("Shutdown detected, terminating request")
return nil, errShutdownDetected
case <-ticker.C:
ctx, cancel = context.WithTimeout(context.Background(), apiHeimdallTimeout)
body, err = internalFetch(ctx, client, url)
cancel()
if err == nil && body != nil {
err = json.Unmarshal(body, result)
if err != nil {
return nil, err
}
return result, nil
}
}
}
}
func spanURL(urlString string, spanID uint64) (*url.URL, error) {
return makeURL(urlString, fmt.Sprintf(fetchSpanFormat, spanID), "")
}
func stateSyncURL(urlString string, fromID uint64, to int64) (*url.URL, error) {
queryParams := fmt.Sprintf(fetchStateSyncEventsFormat, fromID, to, stateFetchLimit)
return makeURL(urlString, fetchStateSyncEventsPath, queryParams)
}
func makeURL(urlString, rawPath, rawQuery string) (*url.URL, error) {
u, err := url.Parse(urlString)
if err != nil {
return nil, err
}
u.Path = rawPath
u.RawQuery = rawQuery
return u, err
}
// internal fetch method
func internalFetch(ctx context.Context, client http.Client, u *url.URL) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
return nil, err
}
res, err := client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
// check status code
if res.StatusCode != 200 && res.StatusCode != 204 {
return nil, fmt.Errorf("Error while fetching data from Heimdall")
}
// unmarshall data from buffer
if res.StatusCode == 204 {
return nil, nil
}
// get response
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
return body, nil
}
// Close sends a signal to stop the running process
func (h *HeimdallClient) Close() {
close(h.closeCh)
h.client.CloseIdleConnections()
}

View file

@ -0,0 +1,35 @@
package heimdall
import (
"testing"
)
func TestSpanURL(t *testing.T) {
t.Parallel()
url, err := spanURL("http://bor0", 1)
if err != nil {
t.Fatal("got an error", err)
}
const expected = "http://bor0/bor/span/1"
if url.String() != expected {
t.Fatalf("expected URL %q, got %q", url.String(), expected)
}
}
func TestStateSyncURL(t *testing.T) {
t.Parallel()
url, err := stateSyncURL("http://bor0", 10, 100)
if err != nil {
t.Fatal("got an error", err)
}
const expected = "http://bor0/clerk/event-record/list?from-id=10&to-time=100&limit=50"
if url.String() != expected {
t.Fatalf("expected URL %q, got %q", url.String(), expected)
}
}

View file

@ -0,0 +1,20 @@
package span
import (
"github.com/ethereum/go-ethereum/consensus/bor/valset"
)
// Span Bor represents a current bor span
type Span struct {
ID uint64 `json:"span_id" yaml:"span_id"`
StartBlock uint64 `json:"start_block" yaml:"start_block"`
EndBlock uint64 `json:"end_block" yaml:"end_block"`
}
// HeimdallSpan represents span from heimdall APIs
type HeimdallSpan struct {
Span
ValidatorSet valset.ValidatorSet `json:"validator_set" yaml:"validator_set"`
SelectedProducers []valset.Validator `json:"selected_producers" yaml:"selected_producers"`
ChainID string `json:"bor_chain_id" yaml:"bor_chain_id"`
}

View file

@ -0,0 +1,200 @@
package span
import (
"context"
"encoding/hex"
"math"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/bor/abi"
"github.com/ethereum/go-ethereum/consensus/bor/api"
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"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/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
)
type ChainSpanner struct {
ethAPI api.Caller
validatorSet abi.ABI
chainConfig *params.ChainConfig
validatorContractAddress common.Address
}
func NewChainSpanner(ethAPI api.Caller, validatorSet abi.ABI, chainConfig *params.ChainConfig, validatorContractAddress common.Address) *ChainSpanner {
return &ChainSpanner{
ethAPI: ethAPI,
validatorSet: validatorSet,
chainConfig: chainConfig,
validatorContractAddress: validatorContractAddress,
}
}
// GetCurrentSpan get current span from contract
func (c *ChainSpanner) GetCurrentSpan(headerHash common.Hash) (*Span, error) {
// block
blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false)
// method
method := "getCurrentSpan"
data, err := c.validatorSet.Pack(method)
if err != nil {
log.Error("Unable to pack tx for getCurrentSpan", "error", err)
return nil, err
}
msgData := (hexutil.Bytes)(data)
toAddress := c.validatorContractAddress
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
// todo: would we like to have a timeout here?
result, err := c.ethAPI.Call(context.Background(), ethapi.TransactionArgs{
Gas: &gas,
To: &toAddress,
Data: &msgData,
}, blockNr, nil)
if err != nil {
return nil, err
}
// span result
ret := new(struct {
Number *big.Int
StartBlock *big.Int
EndBlock *big.Int
})
if err := c.validatorSet.UnpackIntoInterface(ret, method, result); err != nil {
return nil, err
}
// create new span
span := Span{
ID: ret.Number.Uint64(),
StartBlock: ret.StartBlock.Uint64(),
EndBlock: ret.EndBlock.Uint64(),
}
return &span, nil
}
// GetCurrentValidators get current validators
func (c *ChainSpanner) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// method
const method = "getBorValidators"
data, err := c.validatorSet.Pack(method, big.NewInt(0).SetUint64(blockNumber))
if err != nil {
log.Error("Unable to pack tx for getValidator", "error", err)
return nil, err
}
// call
msgData := (hexutil.Bytes)(data)
toAddress := c.validatorContractAddress
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
// block
blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false)
result, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{
Gas: &gas,
To: &toAddress,
Data: &msgData,
}, blockNr, nil)
if err != nil {
panic(err)
}
var (
ret0 = new([]common.Address)
ret1 = new([]*big.Int)
)
out := &[]interface{}{
ret0,
ret1,
}
if err := c.validatorSet.UnpackIntoInterface(out, method, result); err != nil {
return nil, err
}
valz := make([]*valset.Validator, len(*ret0))
for i, a := range *ret0 {
valz[i] = &valset.Validator{
Address: a,
VotingPower: (*ret1)[i].Int64(),
}
}
return valz, nil
}
const method = "commitSpan"
func (c *ChainSpanner) CommitSpan(heimdallSpan HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error {
// get validators bytes
validators := make([]valset.MinimalVal, 0, len(heimdallSpan.ValidatorSet.Validators))
for _, val := range heimdallSpan.ValidatorSet.Validators {
validators = append(validators, val.MinimalVal())
}
validatorBytes, err := rlp.EncodeToBytes(validators)
if err != nil {
return err
}
// get producers bytes
producers := make([]valset.MinimalVal, 0, len(heimdallSpan.SelectedProducers))
for _, val := range heimdallSpan.SelectedProducers {
producers = append(producers, val.MinimalVal())
}
producerBytes, err := rlp.EncodeToBytes(producers)
if err != nil {
return err
}
log.Info("✅ Committing new span",
"id", heimdallSpan.ID,
"startBlock", heimdallSpan.StartBlock,
"endBlock", heimdallSpan.EndBlock,
"validatorBytes", hex.EncodeToString(validatorBytes),
"producerBytes", hex.EncodeToString(producerBytes),
)
data, err := c.validatorSet.Pack(method,
big.NewInt(0).SetUint64(heimdallSpan.ID),
big.NewInt(0).SetUint64(heimdallSpan.StartBlock),
big.NewInt(0).SetUint64(heimdallSpan.EndBlock),
validatorBytes,
producerBytes,
)
if err != nil {
log.Error("Unable to pack tx for commitSpan", "error", err)
return err
}
// get system message
msg := statefull.GetSystemMessage(c.validatorContractAddress, data)
// apply message
_, err = statefull.ApplyMessage(msg, state, header, c.chainConfig, chainContext)
return err
}

View file

@ -1,177 +0,0 @@
package bor
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"sort"
"time"
"github.com/ethereum/go-ethereum/log"
)
var (
stateFetchLimit = 50
)
// ResponseWithHeight defines a response object type that wraps an original
// response with a height.
type ResponseWithHeight struct {
Height string `json:"height"`
Result json.RawMessage `json:"result"`
}
type IHeimdallClient interface {
Fetch(path string, query string) (*ResponseWithHeight, error)
FetchWithRetry(path string, query string) (*ResponseWithHeight, error)
FetchStateSyncEvents(fromID uint64, to int64) ([]*EventRecordWithTime, error)
Close()
}
type HeimdallClient struct {
urlString string
client http.Client
closeCh chan struct{}
}
func NewHeimdallClient(urlString string) (*HeimdallClient, error) {
h := &HeimdallClient{
urlString: urlString,
client: http.Client{
Timeout: 5 * time.Second,
},
closeCh: make(chan struct{}),
}
return h, nil
}
func (h *HeimdallClient) FetchStateSyncEvents(fromID uint64, to int64) ([]*EventRecordWithTime, error) {
eventRecords := make([]*EventRecordWithTime, 0)
for {
queryParams := fmt.Sprintf("from-id=%d&to-time=%d&limit=%d", fromID, to, stateFetchLimit)
log.Info("Fetching state sync events", "queryParams", queryParams)
response, err := h.FetchWithRetry("clerk/event-record/list", queryParams)
if err != nil {
return nil, err
}
var _eventRecords []*EventRecordWithTime
if response.Result == nil { // status 204
break
}
if err := json.Unmarshal(response.Result, &_eventRecords); err != nil {
return nil, err
}
eventRecords = append(eventRecords, _eventRecords...)
if len(_eventRecords) < stateFetchLimit {
break
}
fromID += uint64(stateFetchLimit)
}
sort.SliceStable(eventRecords, func(i, j int) bool {
return eventRecords[i].ID < eventRecords[j].ID
})
return eventRecords, nil
}
// Fetch fetches response from heimdall
func (h *HeimdallClient) Fetch(rawPath string, rawQuery string) (*ResponseWithHeight, error) {
u, err := url.Parse(h.urlString)
if err != nil {
return nil, err
}
u.Path = rawPath
u.RawQuery = rawQuery
return h.internalFetch(u)
}
// FetchWithRetry returns data from heimdall with retry
func (h *HeimdallClient) FetchWithRetry(rawPath string, rawQuery string) (*ResponseWithHeight, error) {
u, err := url.Parse(h.urlString)
if err != nil {
return nil, err
}
u.Path = rawPath
u.RawQuery = rawQuery
// attempt counter
attempt := 1
// request data once
res, err := h.internalFetch(u)
if err == nil && res != nil {
return res, nil
}
// create a new ticker for retrying the request
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
log.Info("Retrying again in 5 seconds to fetch data from Heimdall", "path", u.Path, "attempt", attempt)
attempt++
select {
case <-h.closeCh:
log.Debug("Shutdown detected, terminating request")
return nil, errShutdownDetected
case <-ticker.C:
res, err := h.internalFetch(u)
if err == nil && res != nil {
return res, nil
}
}
}
}
// internal fetch method
func (h *HeimdallClient) internalFetch(u *url.URL) (*ResponseWithHeight, error) {
res, err := h.client.Get(u.String()) // nolint: noctx
if err != nil {
return nil, err
}
defer res.Body.Close()
// check status code
if res.StatusCode != 200 && res.StatusCode != 204 {
return nil, fmt.Errorf("Error while fetching data from Heimdall")
}
// unmarshall data from buffer
var response ResponseWithHeight
if res.StatusCode == 204 {
return &response, nil
}
// get response
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
if err := json.Unmarshal(body, &response); err != nil {
return nil, err
}
return &response, nil
}
// Close sends a signal to stop the running process
func (h *HeimdallClient) Close() {
close(h.closeCh)
h.client.CloseIdleConnections()
}

View file

@ -3,24 +3,24 @@ package bor
import (
"encoding/json"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
lru "github.com/hashicorp/golang-lru"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/params"
)
// Snapshot is the state of the authorization voting at a given point in time.
type Snapshot struct {
config *params.BorConfig // Consensus engine parameters to fine tune behavior
ethAPI *ethapi.PublicBlockChainAPI
sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
ValidatorSet *ValidatorSet `json:"validatorSet"` // Validator set at this moment
ValidatorSet *valset.ValidatorSet `json:"validatorSet"` // Validator set at this moment
Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections
}
@ -32,16 +32,14 @@ func newSnapshot(
sigcache *lru.ARCCache,
number uint64,
hash common.Hash,
validators []*Validator,
ethAPI *ethapi.PublicBlockChainAPI,
validators []*valset.Validator,
) *Snapshot {
snap := &Snapshot{
config: config,
ethAPI: ethAPI,
sigcache: sigcache,
Number: number,
Hash: hash,
ValidatorSet: NewValidatorSet(validators),
ValidatorSet: valset.NewValidatorSet(validators),
Recents: make(map[uint64]common.Address),
}
@ -49,7 +47,7 @@ func newSnapshot(
}
// loadSnapshot loads an existing snapshot from the database.
func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash, ethAPI *ethapi.PublicBlockChainAPI) (*Snapshot, error) {
func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
blob, err := db.Get(append([]byte("bor-"), hash[:]...))
if err != nil {
return nil, err
@ -63,10 +61,9 @@ func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Dat
snap.config = config
snap.sigcache = sigcache
snap.ethAPI = ethAPI
// update total voting power
if err := snap.ValidatorSet.updateTotalVotingPower(); err != nil {
if err := snap.ValidatorSet.UpdateTotalVotingPower(); err != nil {
return nil, err
}
@ -87,7 +84,6 @@ func (s *Snapshot) store(db ethdb.Database) error {
func (s *Snapshot) copy() *Snapshot {
cpy := &Snapshot{
config: s.config,
ethAPI: s.ethAPI,
sigcache: s.sigcache,
Number: s.Number,
Hash: s.Hash,
@ -155,7 +151,7 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
validatorBytes := header.Extra[extraVanity : len(header.Extra)-extraSeal]
// get validators from headers and use that for new validator set
newVals, _ := ParseValidators(validatorBytes)
newVals, _ := valset.ParseValidators(validatorBytes)
v := getUpdatedValidatorSet(snap.ValidatorSet.Copy(), newVals)
v.IncrementProposerPriority(1)
snap.ValidatorSet = v

View file

@ -7,8 +7,11 @@ import (
"time"
"github.com/stretchr/testify/assert"
"pgregory.net/rapid"
"github.com/ethereum/go-ethereum/common"
unique "github.com/ethereum/go-ethereum/common/set"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
)
const (
@ -19,7 +22,7 @@ func TestGetSignerSuccessionNumber_ProposerIsSigner(t *testing.T) {
t.Parallel()
validators := buildRandomValidatorSet(numVals)
validatorSet := NewValidatorSet(validators)
validatorSet := valset.NewValidatorSet(validators)
snap := Snapshot{
ValidatorSet: validatorSet,
}
@ -27,7 +30,6 @@ func TestGetSignerSuccessionNumber_ProposerIsSigner(t *testing.T) {
// proposer is signer
signer := validatorSet.Proposer.Address
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
if err != nil {
t.Fatalf("%s", err)
}
@ -41,20 +43,19 @@ func TestGetSignerSuccessionNumber_SignerIndexIsLarger(t *testing.T) {
validators := buildRandomValidatorSet(numVals)
// sort validators by address, which is what NewValidatorSet also does
sort.Sort(ValidatorsByAddress(validators))
sort.Sort(valset.ValidatorsByAddress(validators))
proposerIndex := 32
signerIndex := 56
// give highest ProposerPriority to a particular val, so that they become the proposer
validators[proposerIndex].VotingPower = 200
snap := Snapshot{
ValidatorSet: NewValidatorSet(validators),
ValidatorSet: valset.NewValidatorSet(validators),
}
// choose a signer at an index greater than proposer index
signer := snap.ValidatorSet.Validators[signerIndex].Address
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
if err != nil {
t.Fatalf("%s", err)
}
@ -71,13 +72,12 @@ func TestGetSignerSuccessionNumber_SignerIndexIsSmaller(t *testing.T) {
// give highest ProposerPriority to a particular val, so that they become the proposer
validators[proposerIndex].VotingPower = 200
snap := Snapshot{
ValidatorSet: NewValidatorSet(validators),
ValidatorSet: valset.NewValidatorSet(validators),
}
// choose a signer at an index greater than proposer index
signer := snap.ValidatorSet.Validators[signerIndex].Address
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
if err != nil {
t.Fatalf("%s", err)
}
@ -90,14 +90,18 @@ func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) {
validators := buildRandomValidatorSet(numVals)
snap := Snapshot{
ValidatorSet: NewValidatorSet(validators),
ValidatorSet: valset.NewValidatorSet(validators),
}
dummyProposerAddress := randomAddress()
snap.ValidatorSet.Proposer = &Validator{Address: dummyProposerAddress}
snap.ValidatorSet.Proposer = &valset.Validator{Address: dummyProposerAddress}
// choose any signer
signer := snap.ValidatorSet.Validators[3].Address
_, err := snap.GetSignerSuccessionNumber(signer)
assert.NotNil(t, err)
e, ok := err.(*UnauthorizedProposerError)
assert.True(t, ok)
assert.Equal(t, dummyProposerAddress.Bytes(), e.Proposer)
@ -108,7 +112,7 @@ func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) {
validators := buildRandomValidatorSet(numVals)
snap := Snapshot{
ValidatorSet: NewValidatorSet(validators),
ValidatorSet: valset.NewValidatorSet(validators),
}
dummySignerAddress := randomAddress()
_, err := snap.GetSignerSuccessionNumber(dummySignerAddress)
@ -119,21 +123,22 @@ func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) {
}
// nolint: unparam
func buildRandomValidatorSet(numVals int) []*Validator {
func buildRandomValidatorSet(numVals int) []*valset.Validator {
rand.Seed(time.Now().Unix())
validators := make([]*Validator, numVals)
validators := make([]*valset.Validator, numVals)
valAddrs := randomAddresses(numVals)
for i := 0; i < numVals; i++ {
validators[i] = &Validator{
Address: randomAddress(),
validators[i] = &valset.Validator{
Address: valAddrs[i],
// cannot process validators with voting power 0, hence +1
VotingPower: int64(rand.Intn(99) + 1),
}
}
// sort validators by address, which is what NewValidatorSet also does
sort.Sort(ValidatorsByAddress(validators))
sort.Sort(valset.ValidatorsByAddress(validators))
return validators
}
@ -144,3 +149,51 @@ func randomAddress() common.Address {
return common.BytesToAddress(bytes)
}
func randomAddresses(n int) []common.Address {
if n <= 0 {
return []common.Address{}
}
addrs := make([]common.Address, 0, n)
addrsSet := make(map[common.Address]struct{}, n)
var (
addr common.Address
exist bool
)
bytes := make([]byte, 32)
for {
rand.Read(bytes)
addr = common.BytesToAddress(bytes)
_, exist = addrsSet[addr]
if !exist {
addrs = append(addrs, addr)
addrsSet[addr] = struct{}{}
}
if len(addrs) == n {
return addrs
}
}
}
func TestRandomAddresses(t *testing.T) {
t.Parallel()
rapid.Check(t, func(t *rapid.T) {
length := rapid.IntMax(100).Draw(t, "length").(int)
addrs := randomAddresses(length)
addressSet := unique.New(addrs)
if len(addrs) != len(addressSet) {
t.Fatalf("length of unique addresses %d, expected %d", len(addressSet), len(addrs))
}
})
}

View file

@ -1,16 +1,17 @@
package bor
// Span represents a current bor span
type Span struct {
ID uint64 `json:"span_id" yaml:"span_id"`
StartBlock uint64 `json:"start_block" yaml:"start_block"`
EndBlock uint64 `json:"end_block" yaml:"end_block"`
}
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
)
// HeimdallSpan represents span from heimdall APIs
type HeimdallSpan struct {
Span
ValidatorSet ValidatorSet `json:"validator_set" yaml:"validator_set"`
SelectedProducers []Validator `json:"selected_producers" yaml:"selected_producers"`
ChainID string `json:"bor_chain_id" yaml:"bor_chain_id"`
//go:generate mockgen -destination=./span_mock.go -package=bor . Spanner
type Spanner interface {
GetCurrentSpan(headerHash common.Hash) (*span.Span, error)
GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error)
CommitSpan(heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error
}

View file

@ -0,0 +1,84 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/ethereum/go-ethereum/consensus/bor (interfaces: Spanner)
// Package bor is a generated GoMock package.
package bor
import (
reflect "reflect"
common "github.com/ethereum/go-ethereum/common"
span "github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
valset "github.com/ethereum/go-ethereum/consensus/bor/valset"
core "github.com/ethereum/go-ethereum/core"
state "github.com/ethereum/go-ethereum/core/state"
types "github.com/ethereum/go-ethereum/core/types"
gomock "github.com/golang/mock/gomock"
)
// MockSpanner is a mock of Spanner interface.
type MockSpanner struct {
ctrl *gomock.Controller
recorder *MockSpannerMockRecorder
}
// MockSpannerMockRecorder is the mock recorder for MockSpanner.
type MockSpannerMockRecorder struct {
mock *MockSpanner
}
// NewMockSpanner creates a new mock instance.
func NewMockSpanner(ctrl *gomock.Controller) *MockSpanner {
mock := &MockSpanner{ctrl: ctrl}
mock.recorder = &MockSpannerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockSpanner) EXPECT() *MockSpannerMockRecorder {
return m.recorder
}
// CommitSpan mocks base method.
func (m *MockSpanner) CommitSpan(arg0 span.HeimdallSpan, arg1 *state.StateDB, arg2 *types.Header, arg3 core.ChainContext) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CommitSpan", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(error)
return ret0
}
// CommitSpan indicates an expected call of CommitSpan.
func (mr *MockSpannerMockRecorder) CommitSpan(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), arg0, arg1, arg2, arg3)
}
// GetCurrentSpan mocks base method.
func (m *MockSpanner) GetCurrentSpan(arg0 common.Hash) (*span.Span, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCurrentSpan", arg0)
ret0, _ := ret[0].(*span.Span)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetCurrentSpan indicates an expected call of GetCurrentSpan.
func (mr *MockSpannerMockRecorder) GetCurrentSpan(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSpan", reflect.TypeOf((*MockSpanner)(nil).GetCurrentSpan), arg0)
}
// GetCurrentValidators mocks base method.
func (m *MockSpanner) GetCurrentValidators(arg0 common.Hash, arg1 uint64) ([]*valset.Validator, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1)
ret0, _ := ret[0].([]*valset.Validator)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetCurrentValidators indicates an expected call of GetCurrentValidators.
func (mr *MockSpannerMockRecorder) GetCurrentValidators(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidators", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidators), arg0, arg1)
}

View file

@ -0,0 +1,93 @@
package statefull
import (
"math"
"math/big"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"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/vm"
"github.com/ethereum/go-ethereum/params"
)
var systemAddress = common.HexToAddress("0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE")
type ChainContext struct {
Chain consensus.ChainHeaderReader
Bor consensus.Engine
}
func (c ChainContext) Engine() consensus.Engine {
return c.Bor
}
func (c ChainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
return c.Chain.GetHeader(hash, number)
}
// callmsg implements core.Message to allow passing it as a transaction simulator.
type callmsg struct {
ethereum.CallMsg
}
func (m callmsg) From() common.Address { return m.CallMsg.From }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.CallMsg.To }
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
func (m callmsg) Data() []byte { return m.CallMsg.Data }
// get system message
func GetSystemMessage(toAddress common.Address, data []byte) callmsg {
return callmsg{
ethereum.CallMsg{
From: systemAddress,
Gas: math.MaxUint64 / 2,
GasPrice: big.NewInt(0),
Value: big.NewInt(0),
To: &toAddress,
Data: data,
},
}
}
// apply message
func ApplyMessage(
msg callmsg,
state *state.StateDB,
header *types.Header,
chainConfig *params.ChainConfig,
chainContext core.ChainContext,
) (uint64, error) {
initialGas := msg.Gas()
// Create a new context to be used in the EVM environment
blockContext := core.NewEVMBlockContext(header, chainContext, &header.Coinbase)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, state, chainConfig, vm.Config{})
// Apply the transaction to the current state (included in the env)
_, gasLeft, err := vmenv.Call(
vm.AccountRef(msg.From()),
*msg.To(),
msg.Data(),
msg.Gas(),
msg.Value(),
)
// Update the state with pending changes
if err != nil {
state.Finalise(true)
}
gasUsed := initialGas - gasLeft
return gasUsed, nil
}

View file

@ -0,0 +1,11 @@
package bor
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
)
//go:generate mockgen -destination=./validators_getter_mock.go -package=bor . ValidatorsGetter
type ValidatorsGetter interface {
GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error)
}

View file

@ -0,0 +1,51 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/ethereum/go-ethereum/consensus/bor (interfaces: ValidatorsGetter)
// Package bor is a generated GoMock package.
package bor
import (
reflect "reflect"
common "github.com/ethereum/go-ethereum/common"
valset "github.com/ethereum/go-ethereum/consensus/bor/valset"
gomock "github.com/golang/mock/gomock"
)
// MockValidatorsGetter is a mock of ValidatorsGetter interface.
type MockValidatorsGetter struct {
ctrl *gomock.Controller
recorder *MockValidatorsGetterMockRecorder
}
// MockValidatorsGetterMockRecorder is the mock recorder for MockValidatorsGetter.
type MockValidatorsGetterMockRecorder struct {
mock *MockValidatorsGetter
}
// NewMockValidatorsGetter creates a new mock instance.
func NewMockValidatorsGetter(ctrl *gomock.Controller) *MockValidatorsGetter {
mock := &MockValidatorsGetter{ctrl: ctrl}
mock.recorder = &MockValidatorsGetterMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockValidatorsGetter) EXPECT() *MockValidatorsGetterMockRecorder {
return m.recorder
}
// GetCurrentValidators mocks base method.
func (m *MockValidatorsGetter) GetCurrentValidators(arg0 common.Hash, arg1 uint64) ([]*valset.Validator, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1)
ret0, _ := ret[0].([]*valset.Validator)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetCurrentValidators indicates an expected call of GetCurrentValidators.
func (mr *MockValidatorsGetterMockRecorder) GetCurrentValidators(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidators", reflect.TypeOf((*MockValidatorsGetter)(nil).GetCurrentValidators), arg0, arg1)
}

View file

@ -0,0 +1,32 @@
package valset
import "fmt"
// TotalVotingPowerExceededError is returned when the maximum allowed total voting power is exceeded
type TotalVotingPowerExceededError struct {
Sum int64
Validators []*Validator
}
func (e *TotalVotingPowerExceededError) Error() string {
return fmt.Sprintf(
"Total voting power should be guarded to not exceed %v; got: %v; for validator set: %v",
MaxTotalVotingPower,
e.Sum,
e.Validators,
)
}
type InvalidStartEndBlockError struct {
Start uint64
End uint64
CurrentHeader uint64
}
func (e *InvalidStartEndBlockError) Error() string {
return fmt.Sprintf(
"Invalid parameters start: %d and end block: %d params",
e.Start,
e.End,
)
}

View file

@ -1,8 +1,7 @@
package bor
package valset
import (
"bytes"
// "encoding/json"
"errors"
"fmt"
"math/big"

View file

@ -1,4 +1,4 @@
package bor
package valset
// Tendermint leader selection algorithm
@ -55,8 +55,8 @@ type ValidatorSet struct {
// function panics.
func NewValidatorSet(valz []*Validator) *ValidatorSet {
vals := &ValidatorSet{}
err := vals.updateWithChangeSet(valz, false)
err := vals.updateWithChangeSet(valz, false)
if err != nil {
panic(fmt.Sprintf("cannot create validator set: %s", err))
}
@ -281,7 +281,7 @@ func (vals *ValidatorSet) Size() int {
}
// Force recalculation of the set's total voting power.
func (vals *ValidatorSet) updateTotalVotingPower() error {
func (vals *ValidatorSet) UpdateTotalVotingPower() error {
sum := int64(0)
for _, val := range vals.Validators {
// mind overflow
@ -302,7 +302,7 @@ func (vals *ValidatorSet) TotalVotingPower() int64 {
if vals.totalVotingPower == 0 {
log.Info("invoking updateTotalVotingPower before returning it")
if err := vals.updateTotalVotingPower(); err != nil {
if err := vals.UpdateTotalVotingPower(); err != nil {
// Can/should we do better?
panic(err)
}
@ -602,7 +602,7 @@ func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes
vals.applyUpdates(updates)
vals.applyRemovals(deletes)
if err := vals.updateTotalVotingPower(); err != nil {
if err := vals.UpdateTotalVotingPower(); err != nil {
return err
}

View file

@ -45,12 +45,14 @@ type Merger struct {
// NewMerger creates a new Merger which stores its transition status in the provided db.
func NewMerger(db ethdb.KeyValueStore) *Merger {
var status transitionStatus
blob := rawdb.ReadTransitionStatus(db)
if len(blob) != 0 {
if err := rlp.DecodeBytes(blob, &status); err != nil {
log.Crit("Failed to decode the transition status", "err", err)
}
}
return &Merger{
db: db,
status: status,

View file

@ -305,7 +305,8 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
}
makeChainForBench(db, full, count)
db.Close()
cacheConfig := *defaultCacheConfig
cacheConfig := *DefaultCacheConfig
cacheConfig.TrieDirtyDisabled = true
b.ReportAllocs()

View file

@ -27,6 +27,8 @@ import (
"sync/atomic"
"time"
lru "github.com/hashicorp/golang-lru"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/common/prque"
@ -43,7 +45,6 @@ import (
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru"
)
var (
@ -133,9 +134,9 @@ type CacheConfig struct {
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
}
// defaultCacheConfig are the default caching values if none are specified by the
// DefaultCacheConfig are the default caching values if none are specified by the
// user (also used during testing).
var defaultCacheConfig = &CacheConfig{
var DefaultCacheConfig = &CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
@ -222,7 +223,7 @@ type BlockChain struct {
// 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) {
if cacheConfig == nil {
cacheConfig = defaultCacheConfig
cacheConfig = DefaultCacheConfig
}
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)

View file

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
@ -397,10 +398,25 @@ func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscr
return bc.scope.Track(bc.blockProcFeed.Subscribe(ch))
}
// Snaps retrieves the snapshot tree.
func (bc *BlockChain) Snaps() *snapshot.Tree {
return bc.snaps
}
// DB retrieves the blockchain database.
func (bc *BlockChain) DB() ethdb.Database {
return bc.db
}
//
// Bor related changes
//
type BorStateSyncer interface {
SetStateSync(stateData []*types.StateSyncData)
SubscribeStateSyncEvent(ch chan<- StateSyncEvent) event.Subscription
}
// SetStateSync set sync data in state_data
func (bc *BlockChain) SetStateSync(stateData []*types.StateSyncData) {
bc.stateSyncData = stateData

View file

@ -920,7 +920,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
archiveDb, delfn := makeDb()
defer delfn()
archiveCaching := *defaultCacheConfig
archiveCaching := *DefaultCacheConfig
archiveCaching.TrieDirtyDisabled = true
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
)
@ -334,7 +335,10 @@ func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethd
}
type fakeChainReader struct {
config *params.ChainConfig
config *params.ChainConfig
stateSyncData []*types.StateSyncData
stateSyncFeed event.Feed
scope event.SubscriptionScope
}
// Config returns the chain configuration.
@ -348,3 +352,13 @@ func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header
func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil }
func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil }
func (cr *fakeChainReader) GetTd(hash common.Hash, number uint64) *big.Int { return nil }
// SetStateSync set sync data in state_data
func (cr *fakeChainReader) SetStateSync(stateData []*types.StateSyncData) {
cr.stateSyncData = stateData
}
// SubscribeStateSyncEvent registers a subscription of StateSyncEvent.
func (cr *fakeChainReader) SubscribeStateSyncEvent(ch chan<- StateSyncEvent) event.Subscription {
return cr.scope.Track(cr.stateSyncFeed.Subscribe(ch))
}

View file

@ -21,6 +21,8 @@ import (
"math/big"
"testing"
"golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus"
@ -32,7 +34,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"golang.org/x/crypto/sha3"
)
// TestStateProcessorErrors tests the output from the 'core' errors
@ -308,7 +309,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
header := &types.Header{
ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(),
Difficulty: engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{
Difficulty: engine.CalcDifficulty(&fakeChainReader{config: config}, parent.Time()+10, &types.Header{
Number: parent.Number(),
Time: parent.Time(),
Difficulty: parent.Difficulty(),

View file

@ -18,7 +18,7 @@
// the database in some strange state with gaps in the chain, nor with block data
// dangling in the future.
package core
package tests
import (
"io/ioutil"
@ -27,12 +27,22 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/bor/api"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/tests/bor/mocks"
)
// Tests a recovery for a short canonical chain where a recent block was already
@ -1750,7 +1760,17 @@ func testLongReorgedSnapSyncingDeepRepair(t *testing.T, snapshots bool) {
}, snapshots)
}
var (
testKey1, _ = crypto.GenerateKey()
testAddress1 = crypto.PubkeyToAddress(testKey1.PublicKey)
testKey2, _ = crypto.GenerateKey()
testAddress2 = crypto.PubkeyToAddress(testKey2.PublicKey) //nolint:unused,varcheck
)
func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
t.Skip("need to add a proper signer for Bor consensus")
// It's hard to follow the test case, visualize the input
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
// fmt.Println(tt.dump(true))
@ -1768,47 +1788,104 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
}
defer db.Close() // Might double close, should be fine
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ethAPIMock := api.NewMockCaller(ctrl)
ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
spanner := bor.NewMockSpanner(ctrl)
spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any()).Return([]*valset.Validator{
{
ID: 0,
Address: common.Address{0x1},
VotingPower: 100,
ProposerPriority: 0,
},
}, nil).AnyTimes()
heimdallClientMock := mocks.NewMockIHeimdallClient(ctrl)
heimdallClientMock.EXPECT().Close().Times(1)
contractMock := bor.NewMockGenesisContract(ctrl)
// Initialize a fresh chain
var (
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
engine = ethash.NewFullFaker()
config = &CacheConfig{
gspec = &core.Genesis{
Config: params.BorUnittestChainConfig,
BaseFee: big.NewInt(params.InitialBaseFee),
}
config = &core.CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0, // Disable snapshot by default
}
)
engine := miner.NewFakeBor(t, db, params.BorUnittestChainConfig, ethAPIMock, spanner, heimdallClientMock, contractMock)
defer engine.Close()
engineBorInternal, ok := engine.(*bor.Bor)
if ok {
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
copy(gspec.ExtraData[32:32+common.AddressLength], testAddress1.Bytes())
engineBorInternal.Authorize(testAddress1, func(account accounts.Account, s string, data []byte) ([]byte, error) {
return crypto.Sign(crypto.Keccak256(data), testKey1)
})
}
genesis := gspec.MustCommit(db)
if snapshots {
config.SnapshotLimit = 256
config.SnapshotWait = true
}
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
chain, err := core.NewBlockChain(db, config, params.BorUnittestChainConfig, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create chain: %v", err)
}
// If sidechain blocks are needed, make a light chain and import it
var sideblocks types.Blocks
if tt.sidechainBlocks > 0 {
sideblocks, _ = GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.sidechainBlocks, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{0x01})
sideblocks, _ = core.GenerateChain(params.BorUnittestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.sidechainBlocks, func(i int, b *core.BlockGen) {
b.SetCoinbase(testAddress1)
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.Sprint) {
b.SetExtra(gspec.ExtraData)
} else {
b.SetExtra(make([]byte, 32+crypto.SignatureLength))
}
})
if _, err := chain.InsertChain(sideblocks); err != nil {
t.Fatalf("Failed to import side chain: %v", err)
}
}
canonblocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, func(i int, b *BlockGen) {
canonblocks, _ := core.GenerateChain(params.BorUnittestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{0x02})
b.SetDifficulty(big.NewInt(1000000))
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.Sprint) {
b.SetExtra(gspec.ExtraData)
} else {
b.SetExtra(make([]byte, 32+crypto.SignatureLength))
}
})
if _, err := chain.InsertChain(canonblocks[:tt.commitBlock]); err != nil {
t.Fatalf("Failed to import canonical chain start: %v", err)
}
if tt.commitBlock > 0 {
chain.stateCache.TrieDB().Commit(canonblocks[tt.commitBlock-1].Root(), true, nil)
err = chain.StateCache().TrieDB().Commit(canonblocks[tt.commitBlock-1].Root(), true, nil)
if err != nil {
t.Fatal("on trieDB.Commit", err)
}
if snapshots {
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
if err := chain.Snaps().Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
t.Fatalf("Failed to flatten snapshots: %v", err)
}
}
@ -1837,7 +1914,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
}
defer db.Close()
newChain, err := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
newChain, err := core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
@ -1888,19 +1965,21 @@ func TestIssue23496(t *testing.T) {
if err != nil {
t.Fatalf("Failed to create temporary datadir: %v", err)
}
os.RemoveAll(datadir)
db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false)
if err != nil {
t.Fatalf("Failed to create persistent database: %v", err)
}
defer db.Close() // Might double close, should be fine
// Initialize a fresh chain
var (
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
genesis = (&core.Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
engine = ethash.NewFullFaker()
config = &CacheConfig{
config = &core.CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
@ -1908,11 +1987,13 @@ func TestIssue23496(t *testing.T) {
SnapshotWait: true,
}
)
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
chain, err := core.NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create chain: %v", err)
}
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), 4, func(i int, b *BlockGen) {
blocks, _ := core.GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), 4, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{0x02})
b.SetDifficulty(big.NewInt(1000000))
})
@ -1921,13 +2002,18 @@ func TestIssue23496(t *testing.T) {
if _, err := chain.InsertChain(blocks[:1]); err != nil {
t.Fatalf("Failed to import canonical chain start: %v", err)
}
chain.stateCache.TrieDB().Commit(blocks[0].Root(), true, nil)
err = chain.StateCache().TrieDB().Commit(blocks[0].Root(), true, nil)
if err != nil {
t.Fatal("on trieDB.Commit", err)
}
// Insert block B2 and commit the snapshot into disk
if _, err := chain.InsertChain(blocks[1:2]); err != nil {
t.Fatalf("Failed to import canonical chain start: %v", err)
}
if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
if err := chain.Snaps().Cap(blocks[1].Root(), 0); err != nil {
t.Fatalf("Failed to flatten snapshots: %v", err)
}
@ -1935,7 +2021,11 @@ func TestIssue23496(t *testing.T) {
if _, err := chain.InsertChain(blocks[2:3]); err != nil {
t.Fatalf("Failed to import canonical chain start: %v", err)
}
chain.stateCache.TrieDB().Commit(blocks[2].Root(), true, nil)
err = chain.StateCache().TrieDB().Commit(blocks[2].Root(), true, nil)
if err != nil {
t.Fatal("on trieDB.Commit", err)
}
// Insert the remaining blocks
if _, err := chain.InsertChain(blocks[3:]); err != nil {
@ -1950,20 +2040,24 @@ func TestIssue23496(t *testing.T) {
if err != nil {
t.Fatalf("Failed to reopen persistent database: %v", err)
}
defer db.Close()
chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
chain, err = core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
defer chain.Stop()
if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
}
if head := chain.CurrentFastBlock(); head.NumberU64() != uint64(4) {
t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), uint64(4))
}
if head := chain.CurrentBlock(); head.NumberU64() != uint64(1) {
t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), uint64(1))
}
@ -1972,15 +2066,19 @@ func TestIssue23496(t *testing.T) {
if _, err := chain.InsertChain(blocks[1:]); err != nil {
t.Fatalf("Failed to import canonical chain tail: %v", err)
}
if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
}
if head := chain.CurrentFastBlock(); head.NumberU64() != uint64(4) {
t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), uint64(4))
}
if head := chain.CurrentBlock(); head.NumberU64() != uint64(4) {
t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), uint64(4))
}
if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
t.Error("Failed to regenerate the snapshot of known state")
}

View file

@ -17,7 +17,7 @@
// Tests that setting the chain head backwards doesn't leave the database in some
// strange state with gaps in the chain, nor with block data dangling in the future.
package core
package tests
import (
"fmt"
@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -1959,79 +1960,98 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
if err != nil {
t.Fatalf("Failed to create temporary datadir: %v", err)
}
os.RemoveAll(datadir)
db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false)
if err != nil {
t.Fatalf("Failed to create persistent database: %v", err)
}
defer db.Close()
// Initialize a fresh chain
var (
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
genesis = (&core.Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
engine = ethash.NewFullFaker()
config = &CacheConfig{
config = &core.CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0, // Disable snapshot
}
)
if snapshots {
config.SnapshotLimit = 256
config.SnapshotWait = true
}
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
chain, err := core.NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create chain: %v", err)
}
// If sidechain blocks are needed, make a light chain and import it
var sideblocks types.Blocks
if tt.sidechainBlocks > 0 {
sideblocks, _ = GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.sidechainBlocks, func(i int, b *BlockGen) {
sideblocks, _ = core.GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.sidechainBlocks, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{0x01})
})
if _, err := chain.InsertChain(sideblocks); err != nil {
t.Fatalf("Failed to import side chain: %v", err)
}
}
canonblocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, func(i int, b *BlockGen) {
canonblocks, _ := core.GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{0x02})
b.SetDifficulty(big.NewInt(1000000))
})
if _, err := chain.InsertChain(canonblocks[:tt.commitBlock]); err != nil {
t.Fatalf("Failed to import canonical chain start: %v", err)
}
if tt.commitBlock > 0 {
chain.stateCache.TrieDB().Commit(canonblocks[tt.commitBlock-1].Root(), true, nil)
err = chain.StateCache().TrieDB().Commit(canonblocks[tt.commitBlock-1].Root(), true, nil)
if err != nil {
t.Fatal("on trieDB.Commit", err)
}
if snapshots {
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
if err := chain.Snaps().Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
t.Fatalf("Failed to flatten snapshots: %v", err)
}
}
}
if _, err := chain.InsertChain(canonblocks[tt.commitBlock:]); err != nil {
t.Fatalf("Failed to import canonical chain tail: %v", err)
}
// Manually dereference anything not committed to not have to work with 128+ tries
for _, block := range sideblocks {
chain.stateCache.TrieDB().Dereference(block.Root())
chain.StateCache().TrieDB().Dereference(block.Root())
}
for _, block := range canonblocks {
chain.stateCache.TrieDB().Dereference(block.Root())
chain.StateCache().TrieDB().Dereference(block.Root())
}
// Force run a freeze cycle
type freezer interface {
Freeze(threshold uint64) error
Ancients() (uint64, error)
}
db.(freezer).Freeze(tt.freezeThreshold)
// Set the simulated pivot block
if tt.pivotBlock != nil {
rawdb.WriteLastPivotNumber(db, *tt.pivotBlock)
}
// Set the head of the chain back to the requested number
chain.SetHead(tt.setheadBlock)
@ -2044,12 +2064,15 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
if head := chain.CurrentHeader(); head.Number.Uint64() != tt.expHeadHeader {
t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadHeader)
}
if head := chain.CurrentFastBlock(); head.NumberU64() != tt.expHeadFastBlock {
t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadFastBlock)
}
if head := chain.CurrentBlock(); head.NumberU64() != tt.expHeadBlock {
t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock)
}
if frozen, err := db.(freezer).Ancients(); err != nil {
t.Errorf("Failed to retrieve ancient count: %v\n", err)
} else if int(frozen) != tt.expFrozen {
@ -2059,7 +2082,8 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
// verifyNoGaps checks that there are no gaps after the initial set of blocks in
// the database and errors if found.
func verifyNoGaps(t *testing.T, chain *BlockChain, canonical bool, inserted types.Blocks) {
//nolint:gocognit
func verifyNoGaps(t *testing.T, chain *core.BlockChain, canonical bool, inserted types.Blocks) {
t.Helper()
var end uint64
@ -2111,7 +2135,8 @@ func verifyNoGaps(t *testing.T, chain *BlockChain, canonical bool, inserted type
// verifyCutoff checks that there are no chain data available in the chain after
// the specified limit, but that it is available before.
func verifyCutoff(t *testing.T, chain *BlockChain, canonical bool, inserted types.Blocks, head int) {
//nolint:gocognit
func verifyCutoff(t *testing.T, chain *core.BlockChain, canonical bool, inserted types.Blocks, head int) {
t.Helper()
for i := 1; i <= len(inserted); i++ {

View file

@ -17,7 +17,7 @@
// Tests that abnormal program termination (i.e.crash) and restart can recovery
// the snapshot properly if the snapshot is enabled.
package core
package tests
import (
"bytes"
@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -57,7 +58,9 @@ type snapshotTestBasic struct {
engine consensus.Engine
}
func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Block) {
func (basic *snapshotTestBasic) prepare(t *testing.T) (*core.BlockChain, []*types.Block) {
t.Helper()
// Create a temporary persistent database
datadir, err := ioutil.TempDir("", "")
if err != nil {
@ -71,20 +74,22 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
}
// Initialize a fresh chain
var (
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
genesis = (&core.Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
engine = ethash.NewFullFaker()
gendb = rawdb.NewMemoryDatabase()
// Snapshot is enabled, the first snapshot is created from the Genesis.
// The snapshot memory allowance is 256MB, it means no snapshot flush
// will happen during the block insertion.
cacheConfig = defaultCacheConfig
cacheConfig = core.DefaultCacheConfig
)
chain, err := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
chain, err := core.NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create chain: %v", err)
}
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, gendb, basic.chainBlocks, func(i int, b *BlockGen) {})
blocks, _ := core.GenerateChain(params.TestChainConfig, genesis, engine, gendb, basic.chainBlocks, func(i int, b *core.BlockGen) {})
// Insert the blocks with configured settings.
var breakpoints []uint64
@ -93,27 +98,39 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
} else {
breakpoints = append(breakpoints, basic.commitBlock, basic.snapshotBlock)
}
var startPoint uint64
for _, point := range breakpoints {
if _, err := chain.InsertChain(blocks[startPoint:point]); err != nil {
t.Fatalf("Failed to import canonical chain start: %v", err)
}
startPoint = point
if basic.commitBlock > 0 && basic.commitBlock == point {
chain.stateCache.TrieDB().Commit(blocks[point-1].Root(), true, nil)
err = chain.StateCache().TrieDB().Commit(blocks[point-1].Root(), true, nil)
if err != nil {
t.Fatal("on trieDB.Commit", err)
}
}
if basic.snapshotBlock > 0 && basic.snapshotBlock == point {
// Flushing the entire snap tree into the disk, the
// relevant (a) snapshot root and (b) snapshot generator
// will be persisted atomically.
chain.snaps.Cap(blocks[point-1].Root(), 0)
diskRoot, blockRoot := chain.snaps.DiskRoot(), blocks[point-1].Root()
err = chain.Snaps().Cap(blocks[point-1].Root(), 0)
if err != nil {
t.Fatal("on Snaps.Cap", err)
}
diskRoot, blockRoot := chain.Snaps().DiskRoot(), blocks[point-1].Root()
if !bytes.Equal(diskRoot.Bytes(), blockRoot.Bytes()) {
t.Fatalf("Failed to flush disk layer change, want %x, got %x", blockRoot, diskRoot)
}
}
}
if _, err := chain.InsertChain(blocks[startPoint:]); err != nil {
t.Fatalf("Failed to import canonical chain tail: %v", err)
}
@ -123,10 +140,13 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
basic.db = db
basic.gendb = gendb
basic.engine = engine
return chain, blocks
}
func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks []*types.Block) {
func (basic *snapshotTestBasic) verify(t *testing.T, chain *core.BlockChain, blocks []*types.Block) {
t.Helper()
// Iterate over all the remaining blocks and ensure there are no gaps
verifyNoGaps(t, chain, true, blocks)
verifyCutoff(t, chain, true, blocks, basic.expCanonicalBlocks)
@ -134,9 +154,11 @@ func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks [
if head := chain.CurrentHeader(); head.Number.Uint64() != basic.expHeadHeader {
t.Errorf("Head header mismatch: have %d, want %d", head.Number, basic.expHeadHeader)
}
if head := chain.CurrentFastBlock(); head.NumberU64() != basic.expHeadFastBlock {
t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), basic.expHeadFastBlock)
}
if head := chain.CurrentBlock(); head.NumberU64() != basic.expHeadBlock {
t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), basic.expHeadBlock)
}
@ -145,12 +167,12 @@ func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks [
block := chain.GetBlockByNumber(basic.expSnapshotBottom)
if block == nil {
t.Errorf("The correspnding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom)
} else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
} else if !bytes.Equal(chain.Snaps().DiskRoot().Bytes(), block.Root().Bytes()) {
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.Snaps().DiskRoot())
}
// Check the snapshot, ensure it's integrated
if err := chain.snaps.Verify(block.Root()); err != nil {
if err := chain.Snaps().Verify(block.Root()); err != nil {
t.Errorf("The disk layer is not integrated %v", err)
}
}
@ -223,10 +245,12 @@ func (snaptest *snapshotTest) test(t *testing.T) {
// Restart the chain normally
chain.Stop()
newchain, err := 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)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
defer newchain.Stop()
snaptest.verify(t, newchain, blocks)
@ -245,7 +269,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
chain, blocks := snaptest.prepare(t)
// Pull the plug on the database, simulating a hard crash
db := chain.db
db := chain.DB()
db.Close()
// Start a new blockchain back up and see where the repair leads us
@ -259,13 +283,13 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
// the crash, we do restart twice here: one after the crash and one
// after the normal stop. It's used to ensure the broken snapshot
// can be detected all the time.
newchain, err := 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)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
newchain.Stop()
newchain, err = 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)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
@ -292,27 +316,31 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
// Insert blocks without enabling snapshot if gapping is required.
chain.Stop()
gappedBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.gendb, snaptest.gapped, func(i int, b *BlockGen) {})
gappedBlocks, _ := core.GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.gendb, snaptest.gapped, func(i int, b *core.BlockGen) {})
// Insert a few more blocks without enabling snapshot
var cacheConfig = &CacheConfig{
var cacheConfig = &core.CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0,
}
newchain, err := 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)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
newchain.InsertChain(gappedBlocks)
newchain.Stop()
// Restart the chain with enabling the snapshot
newchain, err = 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)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
defer newchain.Stop()
snaptest.verify(t, newchain, blocks)
@ -337,7 +365,7 @@ func (snaptest *setHeadSnapshotTest) test(t *testing.T) {
chain.SetHead(snaptest.setHead)
chain.Stop()
newchain, err := 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)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
@ -368,11 +396,12 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
// and state committed.
chain.Stop()
newchain, err := 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)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
newBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.gendb, snaptest.newBlocks, func(i int, b *BlockGen) {})
newBlocks, _ := core.GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.gendb, snaptest.newBlocks, func(i int, b *core.BlockGen) {})
newchain.InsertChain(newBlocks)
// Commit the entire snapshot into the disk if requested. Note only
@ -385,7 +414,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
// journal and latest state will be committed
// Restart the chain after the crash
newchain, err = 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)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
@ -414,38 +443,42 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
// and state committed.
chain.Stop()
config := &CacheConfig{
config := &core.CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0,
}
newchain, err := NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
newchain, err := core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
newBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.gendb, snaptest.newBlocks, func(i int, b *BlockGen) {})
newBlocks, _ := core.GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.gendb, snaptest.newBlocks, func(i int, b *core.BlockGen) {})
newchain.InsertChain(newBlocks)
newchain.Stop()
// Restart the chain, the wiper should starts working
config = &CacheConfig{
config = &core.CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256,
SnapshotWait: false, // Don't wait rebuild
}
newchain, err = NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
_, err = core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
// Simulate the blockchain crash.
newchain, err = 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)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
snaptest.verify(t, newchain, blocks)
}

View file

@ -183,7 +183,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
// END: Bor changes
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
var dbVer = "<nil>"
dbVer := "<nil>"
if bcVersion != nil {
dbVer = fmt.Sprintf("%d", *bcVersion)
}
@ -471,8 +471,10 @@ func (s *Ethereum) StartMining(threads int) error {
if threads == 0 {
threads = -1 // Disable the miner from within
}
th.SetThreads(threads)
}
// If the miner was not running, initialize it
if !s.IsMining() {
// Propagate the initial price point to the transaction pool
@ -485,6 +487,7 @@ func (s *Ethereum) StartMining(threads int) error {
eb, err := s.Etherbase()
if err != nil {
log.Error("Cannot start mining without etherbase", "err", err)
return fmt.Errorf("etherbase missing: %v", err)
}
@ -499,20 +502,26 @@ func (s *Ethereum) StartMining(threads int) error {
cli = c
}
}
if cli != nil {
wallet, err := s.accountManager.Find(accounts.Account{Address: eb})
if wallet == nil || err != nil {
log.Error("Etherbase account unavailable locally", "err", err)
return fmt.Errorf("signer missing: %v", err)
}
cli.Authorize(eb, wallet.SignData)
}
if bor, ok := s.engine.(*bor.Bor); ok {
wallet, err := s.accountManager.Find(accounts.Account{Address: eb})
if wallet == nil || err != nil {
log.Error("Etherbase account unavailable locally", "err", err)
return fmt.Errorf("signer missing: %v", err)
}
bor.Authorize(eb, wallet.SignData)
}
}
@ -522,6 +531,7 @@ func (s *Ethereum) StartMining(threads int) error {
go s.miner.Start(eb)
}
return nil
}
@ -575,6 +585,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
if s.config.SnapshotCache > 0 {
protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...)
}
return protos
}
@ -599,6 +610,7 @@ func (s *Ethereum) Start() error {
}
// Start the networking layer and the light server if requested
s.handler.Start(maxPeers)
return nil
}

View file

@ -29,6 +29,9 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/bor/contract"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
@ -237,7 +240,10 @@ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, et
// In order to pass the ethereum transaction tests, we need to set the burn contract which is in the bor config
// Then, bor != nil will also be enabled for ethash and clique. Only enable Bor for real if there is a validator contract present.
if chainConfig.Bor != nil && chainConfig.Bor.ValidatorContract != "" {
return bor.New(chainConfig, db, blockchainAPI, ethConfig.HeimdallURL, ethConfig.WithoutHeimdall)
genesisContractsClient := contract.NewGenesisContractsClient(chainConfig, chainConfig.Bor.ValidatorContract, chainConfig.Bor.StateReceiverContract, blockchainAPI)
spanner := span.NewChainSpanner(blockchainAPI, contract.ValidatorSet(), chainConfig, common.HexToAddress(chainConfig.Bor.ValidatorContract))
return bor.New(chainConfig, db, blockchainAPI, spanner, heimdall.NewHeimdallClient(ethConfig.HeimdallURL), genesisContractsClient)
} else {
switch config.PowMode {
case ethash.ModeFake:

View file

@ -47,16 +47,19 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
if err != nil {
return nil, err
}
res.Block = blockFields
parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
if err != nil {
return nil, err
}
reexec := defaultTraceReexec
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
// TODO: discuss consequences of setting preferDisk false.
statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
if err != nil {
@ -96,6 +99,7 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
if len(execRes.Revert()) > 0 {
returnVal = fmt.Sprintf("%x", execRes.Revert())
}
result := &ethapi.ExecutionResult{
Gas: execRes.UsedGas,
Failed: execRes.Failed(),
@ -106,12 +110,14 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
Result: result,
IntermediateHash: statedb.IntermediateRoot(deleteEmptyObjects),
}
return res
}
for indx, tx := range txs {
res.Transactions = append(res.Transactions, traceTxn(indx, tx))
}
return res, nil
}
@ -139,5 +145,6 @@ func (api *API) TraceBorBlock(req *TraceBlockRequest) (*BlockTraceResult, error)
if err != nil {
return nil, err
}
return api.traceBorBlock(ctx, block, req.Config)
}

79
go.mod
View file

@ -1,10 +1,9 @@
module github.com/ethereum/go-ethereum
go 1.16
go 1.18
require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/VictoriaMetrics/fastcache v1.6.0
github.com/aws/aws-sdk-go-v2 v1.2.0
github.com/aws/aws-sdk-go-v2/config v1.1.1
@ -16,20 +15,15 @@ require (
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set v1.8.0
github.com/deepmap/oapi-codegen v1.8.2 // indirect
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48
github.com/edsrzf/mmap-go v1.0.0
github.com/fatih/color v1.7.0
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
github.com/go-critic/go-critic v0.6.3 // indirect
github.com/go-kit/kit v0.9.0 // indirect
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/go-ole/go-ole v1.2.1 // indirect
github.com/go-stack/stack v1.8.0
github.com/go-toolsmith/astcopy v1.0.1 // indirect
github.com/golang-jwt/jwt/v4 v4.3.0
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.2
github.com/golang/snappy v0.0.4
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa
@ -45,25 +39,19 @@ require (
github.com/imdario/mergo v0.3.11
github.com/influxdata/influxdb v1.8.3
github.com/influxdata/influxdb-client-go/v2 v2.4.0
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect
github.com/jackpal/go-nat-pmp v1.0.2
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e
github.com/julienschmidt/httprouter v1.3.0
github.com/karalabe/usb v0.0.2
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-isatty v0.0.12
github.com/mitchellh/cli v1.1.2
github.com/mitchellh/go-grpc-net-conn v0.0.0-20200427190222-eb030e4876f0
github.com/mitchellh/go-homedir v1.1.0
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
github.com/olekukonko/tablewriter v0.0.5
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
github.com/polyfloyd/go-errorlint v0.0.0-20220510153142-36539f2bdac3 // indirect
github.com/prometheus/tsdb v0.7.1
github.com/quasilyte/gogrep v0.0.0-20220429205452-5e2753ee08f9 // indirect
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
github.com/rjeczalik/notify v0.9.1
github.com/rs/cors v1.7.0
github.com/ryanuber/columnize v2.1.2+incompatible
@ -71,24 +59,81 @@ require (
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
github.com/stretchr/testify v1.7.0
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/tklauser/go-sysconf v0.3.5 // indirect
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef
github.com/xsleonard/go-merkle v1.1.0
go.opentelemetry.io/otel v1.2.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0
go.opentelemetry.io/otel/sdk v1.2.0
go.uber.org/goleak v1.1.12 // indirect
go.uber.org/goleak v1.1.12
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6
golang.org/x/text v0.3.7
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
golang.org/x/tools v0.1.10
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
google.golang.org/grpc v1.42.0
google.golang.org/protobuf v1.27.1
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6
gopkg.in/urfave/cli.v1 v1.20.0
pgregory.net/rapid v0.4.7
)
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 // indirect
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.1.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.1.1 // indirect
github.com/aws/smithy-go v1.1.0 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/deepmap/oapi-codegen v1.8.2 // indirect
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect
github.com/go-kit/kit v0.9.0 // indirect
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/go-ole/go-ole v1.2.1 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.0.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/opentracing/opentracing-go v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/posener/complete v1.1.1 // indirect
github.com/tklauser/go-sysconf v0.3.5 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/zclconf/go-cty v1.8.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 // indirect
go.opentelemetry.io/otel/trace v1.2.0 // indirect
go.opentelemetry.io/proto/otlp v0.10.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gotest.tools v2.2.0+incompatible // indirect
)

75
go.sum
View file

@ -49,7 +49,6 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
@ -82,14 +81,12 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2
github.com/btcsuite/btcd/btcec/v2 v2.1.2 h1:YoYoC9J0jwfukodSBMzZYUVQ8PTiYg4BnOWiJVzTmLs=
github.com/btcsuite/btcd/btcec/v2 v2.1.2/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0 h1:MSskdM4/xJYcFzy0altH/C/xHopifpWzHUi1JeVI34Q=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@ -162,8 +159,6 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
github.com/go-critic/go-critic v0.6.3 h1:abibh5XYBTASawfTQ0rA7dVtQT+6KzpGqb/J+DxRDaw=
github.com/go-critic/go-critic v0.6.3/go.mod h1:c6b3ZP1MQ7o6lPR7Rv3lEf7pYQUmAcx8ABHgdZCQt/k=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -184,25 +179,6 @@ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astcopy v1.0.1 h1:l09oBhAPyV74kLJ3ZO31iBU8htZGTwr9LTjuMCyL8go=
github.com/go-toolsmith/astcopy v1.0.1/go.mod h1:4TcEdbElGc9twQEYpVo/aieIXfHhiuLh4aLAck6dO7Y=
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astequal v1.0.1/go.mod h1:4oGA3EZXTVItV/ipGiOx7NWkY5veFfcsOJVS2YxltLw=
github.com/go-toolsmith/astequal v1.0.2 h1:+XvaV8zNxua+9+Oa4AHmgmpo4RYAbwr/qjNppLfX2yM=
github.com/go-toolsmith/astequal v1.0.2/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4=
github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
github.com/go-toolsmith/pkgload v1.0.2-0.20220101231613-e814995d17c5 h1:eD9POs68PHkwrx7hAB78z1cb6PfGq/jyWn3wJywsH1o=
github.com/go-toolsmith/pkgload v1.0.2-0.20220101231613-e814995d17c5/go.mod h1:3NAwwmD4uY/yggRxoEjk/S00MIV3A+H7rrE3i87eYxM=
github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYwvlk=
github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
@ -216,6 +192,8 @@ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4er
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -247,10 +225,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@ -429,8 +405,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polyfloyd/go-errorlint v0.0.0-20220510153142-36539f2bdac3 h1:GhmKVnwiethXkJVYqq/kdcw3+u2TIuhPwmHhB+ehpIQ=
github.com/polyfloyd/go-errorlint v0.0.0-20220510153142-36539f2bdac3/go.mod h1:KZy4xxPJyy88/gldCe5OdW6OQRtNO3EZE7hXzmnebgA=
github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@ -445,21 +419,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30=
github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a h1:sWFavxtIctGrVs5SYZ5Ml1CvrDAs8Kf5kx2PI3C41dA=
github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a/go.mod h1:VMX+OnnSw4LicdiEGtRSD/1X8kW7GuEscjYNr4cOIT4=
github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/go-ruleguard/dsl v0.3.16/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc=
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50=
github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5/go.mod h1:wSEyW6O61xRV6zb6My3HxrQ5/8ke7NE2OayqCHa3xRM=
github.com/quasilyte/gogrep v0.0.0-20220429205452-5e2753ee08f9 h1:mEQjyqtrQ6eB7oW9Qm+5po33faQIbhWp3qfhxGYuHKc=
github.com/quasilyte/gogrep v0.0.0-20220429205452-5e2753ee08f9/go.mod h1:MsVMK2P94jAne/7vEaLCRmjmOL0aTGAkvVOVuT+UYww=
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU=
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs=
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE=
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
@ -488,7 +447,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -518,10 +476,8 @@ github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPyS
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xsleonard/go-merkle v1.1.0 h1:fHe1fuhJjGH22ZzVTAH0jqHLhTGhOq3wQjJN+8P0jQg=
github.com/xsleonard/go-merkle v1.1.0/go.mod h1:cW4z+UZ/4f2n9IJgIiyDCdYguchoDyDAPmpuOWGxdGg=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
@ -557,9 +513,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8=
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -571,10 +525,7 @@ golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxT
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299 h1:zQpM52jfKHG6II1ISZY1ZcpygvuSFZpLwfluuF89XOg=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171 h1:DZhP7zSquENyG3Yb6ZpGqNEtgE8dfXhcLcheIF9RQHY=
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@ -585,6 +536,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
@ -592,9 +544,7 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -615,7 +565,6 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@ -625,10 +574,7 @@ golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -642,7 +588,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -686,12 +631,6 @@ golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 h1:uCLL3g5wH2xjxVREVuAbP9JM5PPKjRbXKRa6IBjkzmU=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
@ -737,18 +676,14 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -847,5 +782,7 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g=
pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View file

@ -19,6 +19,7 @@ func NewFlagSet(name string) *Flagset {
flags: []*FlagVar{},
set: flag.NewFlagSet(name, flag.ContinueOnError),
}
return f
}
@ -34,10 +35,12 @@ func (f *Flagset) addFlag(fl *FlagVar) {
func (f *Flagset) Help() string {
str := "Options:\n\n"
items := []string{}
for _, item := range f.flags {
items = append(items, fmt.Sprintf(" -%s\n %s", item.Name, item.Usage))
}
return str + strings.Join(items, "\n\n")
}
@ -53,25 +56,30 @@ func (f *Flagset) MarkDown() string {
groups[item.Group] = append(groups[item.Group], item)
}
keys := make([]string, len(groups))
i := 0
keys := make([]string, len(groups))
for k := range groups {
keys[i] = k
i++
}
sort.Strings(keys)
items := []string{}
for _, k := range keys {
if k == "" {
items = append(items, fmt.Sprintf("## Options"))
items = append(items, "## Options")
} else {
items = append(items, fmt.Sprintf("### %s Options", k))
}
for _, item := range groups[k] {
items = append(items, fmt.Sprintf("- ```%s```: %s", item.Name, item.Usage))
}
}
return strings.Join(items, "\n\n")
}
@ -162,6 +170,7 @@ func (b *BigIntFlag) String() string {
if b.Value == nil {
return ""
}
return b.Value.String()
}
@ -174,10 +183,12 @@ func (b *BigIntFlag) Set(value string) error {
} else {
num, ok = num.SetString(value, 10)
}
if !ok {
return fmt.Errorf("failed to set big int")
}
b.Value = num
return nil
}
@ -201,11 +212,13 @@ func (i *SliceStringFlag) String() string {
if i.Value == nil {
return ""
}
return strings.Join(*i.Value, ",")
}
func (i *SliceStringFlag) Set(value string) error {
*i.Value = append(*i.Value, strings.Split(value, ",")...)
return nil
}
@ -246,10 +259,12 @@ func (m *MapStringFlag) String() string {
if m.Value == nil {
return ""
}
ls := []string{}
for k, v := range *m.Value {
ls = append(ls, k+"="+v)
}
return strings.Join(ls, ",")
}
@ -257,6 +272,7 @@ func (m *MapStringFlag) Set(value string) error {
if m.Value == nil {
m.Value = &map[string]string{}
}
for _, t := range strings.Split(value, ",") {
if t != "" {
kv := strings.Split(t, "=")
@ -266,6 +282,7 @@ func (m *MapStringFlag) Set(value string) error {
}
}
}
return nil
}

View file

@ -26,18 +26,23 @@ var chains = map[string]*Chain{
}
func GetChain(name string) (*Chain, error) {
var chain *Chain
var err error
var (
chain *Chain
err error
)
if _, fileErr := os.Stat(name); fileErr == nil {
if chain, err = ImportFromFile(name); err != nil {
return nil, fmt.Errorf("error importing chain from file: %v", err)
}
return chain, nil
} else if errors.Is(fileErr, os.ErrNotExist) {
var ok bool
if chain, ok = chains[name]; !ok {
return nil, fmt.Errorf("chain %s not found", name)
}
return chain, nil
} else {
return nil, fileErr
@ -62,10 +67,12 @@ func importChain(content []byte) (*Chain, error) {
if chain.Genesis == nil {
log.Info("Try reading as legacy genesis")
var genesis core.Genesis
if err := json.Unmarshal(content, &genesis); err != nil {
return nil, err
}
if genesis.Config != nil {
chain.Genesis = &genesis
chain.NetworkId = genesis.Config.ChainID.Uint64()

View file

@ -5,9 +5,12 @@ import (
)
func TestChain_ImportFromFile(t *testing.T) {
t.Parallel()
type args struct {
filename string
}
tests := []struct {
name string
args args
@ -34,8 +37,13 @@ func TestChain_ImportFromFile(t *testing.T) {
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
_, err := ImportFromFile(tt.args.filename)
if (err != nil) != tt.wantErr {
t.Errorf("ImportFromFile() error = %v, wantErr %v", err, tt.wantErr)

View file

@ -33,6 +33,7 @@ func (c *Command) MarkDown() string {
"The ```bor server``` command runs the Bor client.",
c.Flags().MarkDown(),
}
return strings.Join(items, "\n\n")
}

View file

@ -58,7 +58,7 @@ type Config struct {
// GcMode selects the garbage collection mode for the trie
GcMode string `hcl:"gc-mode,optional"`
// NoSnapshot disbales the snapshot database mode
// NoSnapshot disables the snapshot database mode
NoSnapshot bool `hcl:"no-snapshot,optional"`
// Ethstats is the address of the ethstats server to send telemetry
@ -527,18 +527,22 @@ func (c *Config) fillBigInt() error {
b := new(big.Int)
var ok bool
if strings.HasPrefix(*x.str, "0x") {
b, ok = b.SetString((*x.str)[2:], 16)
} else {
b, ok = b.SetString(*x.str, 10)
}
if !ok {
return fmt.Errorf("%s can't parse big int %s", x.path, *x.str)
}
*x.str = ""
*x.td = b
}
}
return nil
}
@ -559,10 +563,12 @@ func (c *Config) fillTimeDurations() error {
if err != nil {
return fmt.Errorf("%s can't parse time duration %s", x.path, *x.str)
}
*x.str = ""
*x.td = d
}
}
return nil
}
@ -574,6 +580,7 @@ func readConfigFile(path string) (*Config, error) {
if err != nil {
return nil, err
}
return readLegacyConfig(data)
}
@ -582,15 +589,19 @@ func readConfigFile(path string) (*Config, error) {
Cache: &CacheConfig{},
Sealer: &SealerConfig{},
}
if err := hclsimple.DecodeFile(path, nil, config); err != nil {
return nil, fmt.Errorf("failed to decode config file '%s': %v", path, err)
}
if err := config.fillBigInt(); err != nil {
return nil, err
}
if err := config.fillTimeDurations(); err != nil {
return nil, err
}
return config, nil
}
@ -599,6 +610,7 @@ func (c *Config) loadChain() error {
if err != nil {
return err
}
c.chain = chain
// preload some default values that depend on the chain file
@ -612,14 +624,17 @@ func (c *Config) loadChain() error {
} else {
c.Cache.Cache = 1024
}
return nil
}
//nolint:gocognit
func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*ethconfig.Config, error) {
dbHandles, err := makeDatabaseHandles()
if err != nil {
return nil, err
}
n := ethconfig.Defaults
// only update for non-developer mode as we don't yet
@ -663,6 +678,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
if !common.IsHexAddress(etherbase) {
return nil, fmt.Errorf("etherbase is not an address: %s", etherbase)
}
n.Miner.Etherbase = common.HexToAddress(etherbase)
}
}
@ -672,17 +688,24 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() {
return nil, fmt.Errorf("account unlock with HTTP access is forbidden")
}
ks := accountManager.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
passwords, err := MakePasswordListFromFile(c.Accounts.PasswordFile)
if err != nil {
return nil, err
}
if len(passwords) < len(c.Accounts.Unlock) {
return nil, fmt.Errorf("number of passwords provided (%v) is less than number of accounts (%v) to unlock",
len(passwords), len(c.Accounts.Unlock))
}
for i, account := range c.Accounts.Unlock {
ks.Unlock(accounts.Account{Address: common.HexToAddress(account)}, passwords[i])
err = ks.Unlock(accounts.Account{Address: common.HexToAddress(account)}, passwords[i])
if err != nil {
return nil, fmt.Errorf("could not unlock an account %q", account)
}
}
}
@ -700,6 +723,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
passphrase string
err error
)
// etherbase has been set above, configuring the miner address from command line flags.
if n.Miner.Etherbase != (common.Address{}) {
developer = accounts.Account{Address: n.Miner.Etherbase}
@ -714,6 +738,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
if err := ks.Unlock(developer, passphrase); err != nil {
return nil, fmt.Errorf("failed to unlock developer account: %v", err)
}
log.Info("Using developer account", "address", developer.Address)
// Set the Etherbase
@ -753,10 +778,12 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
if err != nil {
return nil, fmt.Errorf("invalid required block number %s: %v", k, err)
}
var hash common.Hash
if err = hash.UnmarshalText([]byte(v)); err != nil {
return nil, fmt.Errorf("invalid required block hash %s: %v", v, err)
}
n.PeerRequiredBlocks[number] = hash
}
}
@ -775,6 +802,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
log.Warn("Lowering memory allowance on 32bit arch", "available", mem.Total/1024/1024, "addressable", 2*1024)
mem.Total = 2 * 1024 * 1024 * 1024
}
allowance := uint64(mem.Total / 1024 / 1024 / 3)
if cache > allowance {
log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
@ -804,6 +832,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
} else {
log.Info("Global gas cap disabled")
}
n.RPCTxFeeCap = c.JsonRPC.TxFeeCap
// sync mode. It can either be "fast", "full" or "snap". We disable
@ -843,6 +872,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
}
n.DatabaseHandles = dbHandles
return &n, nil
}
@ -906,6 +936,7 @@ func (c *Config) buildNode() (*node.Config, error) {
cfg.HTTPHost = c.JsonRPC.Http.Host
cfg.HTTPPort = int(c.JsonRPC.Http.Port)
}
if c.JsonRPC.Ws.Enabled {
cfg.WSHost = c.JsonRPC.Ws.Host
cfg.WSPort = int(c.JsonRPC.Ws.Port)
@ -916,6 +947,7 @@ func (c *Config) buildNode() (*node.Config, error) {
if err != nil {
return nil, fmt.Errorf("wrong 'nat' flag: %v", err)
}
cfg.P2P.NAT = natif
// only check for non-developer modes
@ -926,21 +958,27 @@ func (c *Config) buildNode() (*node.Config, error) {
if len(bootnodes) == 0 {
bootnodes = c.chain.Bootnodes
}
if cfg.P2P.BootstrapNodes, err = parseBootnodes(bootnodes); err != nil {
return nil, err
}
if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil {
return nil, err
}
if cfg.P2P.StaticNodes, err = parseBootnodes(c.P2P.Discovery.StaticNodes); err != nil {
return nil, err
}
if len(cfg.P2P.StaticNodes) == 0 {
cfg.P2P.StaticNodes = cfg.StaticNodes()
}
if cfg.P2P.TrustedNodes, err = parseBootnodes(c.P2P.Discovery.TrustedNodes); err != nil {
return nil, err
}
if len(cfg.P2P.TrustedNodes) == 0 {
cfg.P2P.TrustedNodes = cfg.TrustedNodes()
}
@ -951,6 +989,7 @@ func (c *Config) buildNode() (*node.Config, error) {
cfg.P2P.MaxPeers = 0
cfg.P2P.NoDiscovery = true
}
return cfg, nil
}
@ -960,6 +999,7 @@ func (c *Config) Merge(cc ...*Config) error {
return fmt.Errorf("failed to merge configurations: %v", err)
}
}
return nil
}
@ -968,10 +1008,12 @@ func makeDatabaseHandles() (int, error) {
if err != nil {
return -1, err
}
raised, err := fdlimit.Raise(uint64(limit))
if err != nil {
return -1, err
}
return int(raised / 2), nil
}
@ -986,6 +1028,7 @@ func parseBootnodes(urls []string) ([]*enode.Node, error) {
dst = append(dst, node)
}
}
return dst, nil
}
@ -996,6 +1039,7 @@ func defaultDataDir() string {
// we cannot guess a stable location
return ""
}
switch runtime.GOOS {
case "darwin":
return filepath.Join(home, "Library", "Bor")
@ -1005,6 +1049,7 @@ func defaultDataDir() string {
// Windows XP and below don't have LocalAppData.
panic("environment variable LocalAppData is undefined")
}
return filepath.Join(appdata, "Bor")
default:
return filepath.Join(home, ".bor")
@ -1016,6 +1061,7 @@ func Hostname() string {
if err != nil {
return "bor"
}
return hostname
}
@ -1024,10 +1070,13 @@ func MakePasswordListFromFile(path string) ([]string, error) {
if err != nil {
return nil, fmt.Errorf("Failed to read password file: %v", err)
}
lines := strings.Split(string(text), "\n")
// Sanitise DOS line endings.
for i := range lines {
lines[i] = strings.TrimRight(lines[i], "\r")
}
return lines, nil
}

View file

@ -52,6 +52,7 @@ func TestConfigMerge(t *testing.T) {
},
},
}
expected := &Config{
Chain: "1",
NoSnapshot: false,
@ -69,11 +70,14 @@ func TestConfigMerge(t *testing.T) {
},
},
}
assert.NoError(t, c0.Merge(c1))
assert.Equal(t, c0, expected)
}
func TestDefaultDatatypeOverride(t *testing.T) {
t.Parallel()
// This test is specific to `maxpeers` flag (for now) to check
// if default datatype value (0 in case of uint64) is overridden.
c0 := &Config{
@ -81,16 +85,19 @@ func TestDefaultDatatypeOverride(t *testing.T) {
MaxPeers: 30,
},
}
c1 := &Config{
P2P: &P2PConfig{
MaxPeers: 0,
},
}
expected := &Config{
P2P: &P2PConfig{
MaxPeers: 0,
},
}
assert.NoError(t, c0.Merge(c1))
assert.Equal(t, c0, expected)
}
@ -99,6 +106,7 @@ func TestConfigLoadFile(t *testing.T) {
readFile := func(path string) {
config, err := readConfigFile(path)
assert.NoError(t, err)
assert.Equal(t, config, &Config{
DataDir: "./data",
RequiredBlocks: map[string]string{
@ -122,6 +130,7 @@ func TestConfigLoadFile(t *testing.T) {
t.Run("hcl", func(t *testing.T) {
readFile("./testdata/simple.hcl")
})
// read file in json format
t.Run("json", func(t *testing.T) {
readFile("./testdata/simple.json")
@ -152,7 +161,11 @@ func TestConfigBootnodesDefault(t *testing.T) {
}
func TestMakePasswordListFromFile(t *testing.T) {
t.Parallel()
t.Run("ReadPasswordFile", func(t *testing.T) {
t.Parallel()
result, _ := MakePasswordListFromFile("./testdata/password.txt")
assert.Equal(t, []string{"test1", "test2"}, result)
})

View file

@ -17,10 +17,13 @@ func findAvailablePort(from int32, count int32) (int32, error) {
if count == maxPortCheck {
return 0, fmt.Errorf("no available port found")
}
port := atomic.AddInt32(&from, 1)
addr := fmt.Sprintf("localhost:%d", port)
lis, err := net.Listen("tcp", addr)
count++
lis, err := net.Listen("tcp", addr)
if err == nil {
lis.Close()
return port, nil
@ -36,8 +39,13 @@ func CreateMockServer(config *Config) (*Server, error) {
// find available port for grpc server
rand.Seed(time.Now().UnixNano())
var from int32 = 60000 // the min port to start checking from
var to int32 = 61000 // the max port to start checking from
var (
from int32 = 60000 // the min port to start checking from
to int32 = 61000 // the max port to start checking from
)
//nolint: gosec
port, err := findAvailablePort(rand.Int31n(to-from+1)+from, 0)
if err != nil {
return nil, err
@ -53,10 +61,13 @@ func CreateMockServer(config *Config) (*Server, error) {
// find available port for http server
from = 8545
to = 9545
//nolint: gosec
port, err = findAvailablePort(rand.Int31n(to-from+1)+from, 0)
if err != nil {
return nil, err
}
config.JsonRPC.Http.Port = uint64(port)
// start the server

View file

@ -11,6 +11,16 @@ import (
"strings"
"time"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"google.golang.org/grpc"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/consensus/beacon"
@ -26,15 +36,6 @@ import (
"github.com/ethereum/go-ethereum/metrics/influxdb"
"github.com/ethereum/go-ethereum/metrics/prometheus"
"github.com/ethereum/go-ethereum/node"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"google.golang.org/grpc"
)
type Server struct {
@ -71,6 +72,7 @@ func NewServer(config *Config) (*Server, error) {
if err != nil {
return nil, err
}
stack, err := node.New(nodeCfg)
if err != nil {
return nil, err
@ -82,6 +84,7 @@ func NewServer(config *Config) (*Server, error) {
// register backend to account manager with keystore for signing
keydir := stack.KeyStoreDir()
n, p := keystore.StandardScryptN, keystore.StandardScryptP
if config.Accounts.UseLightweightKDF {
n, p = keystore.LightScryptN, keystore.LightScryptP
@ -94,6 +97,7 @@ func NewServer(config *Config) (*Server, error) {
authorized := false
// check if personal wallet endpoints are disabled or not
// nolint:nestif
if !config.Accounts.DisableBorWallet {
// add keystore globally to the node's account manager if personal wallet is enabled
stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p))
@ -103,10 +107,12 @@ func NewServer(config *Config) (*Server, error) {
if err != nil {
return nil, err
}
backend, err := eth.New(stack, ethCfg)
if err != nil {
return nil, err
}
srv.backend = backend
} else {
// register the ethereum backend (with temporary created account manager)
@ -114,10 +120,12 @@ func NewServer(config *Config) (*Server, error) {
if err != nil {
return nil, err
}
backend, err := eth.New(stack, ethCfg)
if err != nil {
return nil, err
}
srv.backend = backend
// authorize only if mining or in developer mode
@ -126,6 +134,7 @@ func NewServer(config *Config) (*Server, error) {
eb, err := srv.backend.Etherbase()
if err != nil {
log.Error("Cannot start mining without etherbase", "err", err)
return nil, fmt.Errorf("etherbase missing: %v", err)
}
@ -142,8 +151,10 @@ func NewServer(config *Config) (*Server, error) {
wallet, err := accountManager.Find(accounts.Account{Address: eb})
if wallet == nil || err != nil {
log.Error("Etherbase account unavailable locally", "err", err)
return nil, fmt.Errorf("signer missing: %v", err)
}
cli.Authorize(eb, wallet.SignData)
authorized = true
}
@ -155,6 +166,7 @@ func NewServer(config *Config) (*Server, error) {
log.Error("Etherbase account unavailable locally", "err", err)
return nil, fmt.Errorf("signer missing: %v", err)
}
bor.Authorize(eb, wallet.SignData)
authorized = true
}
@ -200,6 +212,7 @@ func NewServer(config *Config) (*Server, error) {
if err := srv.node.Start(); err != nil {
return nil, err
}
return srv, nil
}
@ -238,10 +251,12 @@ func (s *Server) setupMetrics(config *TelemetryConfig, serviceName string) error
if v1Enabled {
log.Info("Enabling metrics export to InfluxDB (v1)")
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Database, cfg.Username, cfg.Password, "geth.", tags)
}
if v2Enabled {
log.Info("Enabling metrics export to InfluxDB (v2)")
go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Token, cfg.Bucket, cfg.Organization, "geth.", tags)
}
}
@ -328,6 +343,7 @@ func (s *Server) setupGRPCServer(addr string) error {
}()
log.Info("GRPC Server started", "addr", addr)
return nil
}
@ -338,16 +354,20 @@ func (s *Server) withLoggingUnaryInterceptor() grpc.ServerOption {
func (s *Server) loggingServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
start := time.Now()
h, err := handler(ctx, req)
log.Trace("Request", "method", info.FullMethod, "duration", time.Since(start), "error", err)
return h, err
}
func setupLogger(logLevel string) {
output := io.Writer(os.Stderr)
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
if usecolor {
output = colorable.NewColorableStderr()
}
ostream := log.StreamHandler(output, log.TerminalFormat(usecolor))
glogger := log.NewGlogHandler(ostream)
@ -358,6 +378,7 @@ func setupLogger(logLevel string) {
} else {
glogger.Verbosity(log.LvlInfo)
}
log.Root().SetHandler(glogger)
}

View file

@ -8,6 +8,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/assert"
)
@ -18,9 +19,11 @@ func nextPort() uint64 {
log.Info("Checking for new port", "current", initialPort)
port := atomic.AddUint64(&initialPort, 1)
addr := fmt.Sprintf("localhost:%d", port)
lis, err := net.Listen("tcp", addr)
if err == nil {
lis.Close()
return port
} else {
return nextPort()
@ -28,6 +31,8 @@ func nextPort() uint64 {
}
func TestServer_DeveloperMode(t *testing.T) {
t.Parallel()
// get the default config
config := DefaultConfig()
@ -38,6 +43,7 @@ func TestServer_DeveloperMode(t *testing.T) {
// start the mock server
server, err := CreateMockServer(config)
assert.NoError(t, err)
defer CloseMockServer(server)
// record the initial block number

View file

@ -8,6 +8,10 @@ import (
"reflect"
"strings"
gproto "github.com/golang/protobuf/proto" //nolint:staticcheck,typecheck
"github.com/golang/protobuf/ptypes/empty"
grpc_net_conn "github.com/mitchellh/go-grpc-net-conn"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/tracers"
@ -16,9 +20,6 @@ import (
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
gproto "github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/empty"
grpc_net_conn "github.com/mitchellh/go-grpc-net-conn"
)
const chunkSize = 1024 * 1024 * 1024
@ -45,6 +46,7 @@ func sendStreamDebugFile(stream proto.Bor_DebugPprofServer, headers map[string]s
Request: &proto.DebugFileResponse_Input{},
Encode: grpc_net_conn.ChunkedEncoder(encoder, chunkSize),
}
if _, err := conn.Write(data); err != nil {
return err
}
@ -61,11 +63,14 @@ func sendStreamDebugFile(stream proto.Bor_DebugPprofServer, headers map[string]s
}
func (s *Server) DebugPprof(req *proto.DebugPprofRequest, stream proto.Bor_DebugPprofServer) error {
var payload []byte
var headers map[string]string
var err error
var (
payload []byte
headers map[string]string
err error
)
ctx := context.Background()
switch req.Type {
case proto.DebugPprofRequest_CPU:
payload, headers, err = pprof.CPUProfile(ctx, int(req.Seconds))
@ -74,6 +79,7 @@ func (s *Server) DebugPprof(req *proto.DebugPprofRequest, stream proto.Bor_Debug
case proto.DebugPprofRequest_LOOKUP:
payload, headers, err = pprof.Profile(req.Profile, 0, 0)
}
if err != nil {
return err
}
@ -82,6 +88,7 @@ func (s *Server) DebugPprof(req *proto.DebugPprofRequest, stream proto.Bor_Debug
if err := sendStreamDebugFile(stream, headers, payload); err != nil {
return err
}
return nil
}
@ -192,6 +199,7 @@ func (s *Server) DebugBlock(req *proto.DebugBlockRequest, stream proto.Bor_Debug
},
},
}
res, err := s.tracerAPI.TraceBorBlock(traceReq)
if err != nil {
return err
@ -202,6 +210,7 @@ func (s *Server) DebugBlock(req *proto.DebugBlockRequest, stream proto.Bor_Debug
if err != nil {
return err
}
if err := sendStreamDebugFile(stream, map[string]string{}, data); err != nil {
return err
}

233
miner/fake_miner.go Normal file
View file

@ -0,0 +1,233 @@
package miner
import (
"errors"
"math/big"
"testing"
"time"
"github.com/golang/mock/gomock"
"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/api"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"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/ethdb"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/tests/bor/mocks"
"github.com/ethereum/go-ethereum/trie"
)
type DefaultBorMiner struct {
Miner *Miner
Mux *event.TypeMux //nolint:staticcheck
Cleanup func(skipMiner bool)
Ctrl *gomock.Controller
EthAPIMock api.Caller
HeimdallClientMock bor.IHeimdallClient
ContractMock bor.GenesisContract
}
func NewBorDefaultMiner(t *testing.T) *DefaultBorMiner {
t.Helper()
ctrl := gomock.NewController(t)
ethAPI := api.NewMockCaller(ctrl)
ethAPI.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
spanner := bor.NewMockSpanner(ctrl)
spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any()).Return([]*valset.Validator{
{
ID: 0,
Address: common.Address{0x1},
VotingPower: 100,
ProposerPriority: 0,
},
}, nil).AnyTimes()
heimdallClient := mocks.NewMockIHeimdallClient(ctrl)
heimdallClient.EXPECT().Close().Times(1)
genesisContracts := bor.NewMockGenesisContract(ctrl)
miner, mux, cleanup := createBorMiner(t, ethAPI, spanner, heimdallClient, genesisContracts)
return &DefaultBorMiner{
Miner: miner,
Mux: mux,
Cleanup: cleanup,
Ctrl: ctrl,
EthAPIMock: ethAPI,
HeimdallClientMock: heimdallClient,
ContractMock: genesisContracts,
}
}
//nolint:staticcheck
func createBorMiner(t *testing.T, ethAPIMock api.Caller, spanner bor.Spanner, heimdallClientMock bor.IHeimdallClient, contractMock bor.GenesisContract) (*Miner, *event.TypeMux, func(skipMiner bool)) {
t.Helper()
// Create Ethash config
chainDB, _, chainConfig := NewDBForFakes(t)
engine := NewFakeBor(t, chainDB, chainConfig, ethAPIMock, spanner, heimdallClientMock, contractMock)
// Create Ethereum backend
bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("can't create new chain %v", err)
}
statedb, _ := state.New(common.Hash{}, state.NewDatabase(chainDB), nil)
blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)}
pool := core.NewTxPool(testTxPoolConfig, chainConfig, blockchain)
backend := NewMockBackend(bc, pool)
// Create event Mux
mux := new(event.TypeMux)
config := Config{
Etherbase: common.HexToAddress("123456789"),
}
// Create Miner
miner := New(backend, &config, chainConfig, mux, engine, nil)
cleanup := func(skipMiner bool) {
bc.Stop()
engine.Close()
pool.Stop()
if !skipMiner {
miner.Close()
}
}
return miner, mux, cleanup
}
func NewDBForFakes(t *testing.T) (ethdb.Database, *core.Genesis, *params.ChainConfig) {
t.Helper()
memdb := memorydb.New()
chainDB := rawdb.NewDatabase(memdb)
genesis := core.DeveloperGenesisBlock(2, 11_500_000, common.HexToAddress("12345"))
chainConfig, _, err := core.SetupGenesisBlock(chainDB, genesis)
if err != nil {
t.Fatalf("can't create new chain config: %v", err)
}
chainConfig.Bor.Period = map[string]uint64{
"0": 1,
}
return chainDB, genesis, chainConfig
}
func NewFakeBor(t *testing.T, chainDB ethdb.Database, chainConfig *params.ChainConfig, ethAPIMock api.Caller, spanner bor.Spanner, heimdallClientMock bor.IHeimdallClient, contractMock bor.GenesisContract) consensus.Engine {
t.Helper()
if chainConfig.Bor == nil {
chainConfig.Bor = params.BorUnittestChainConfig.Bor
}
return bor.New(chainConfig, chainDB, ethAPIMock, spanner, heimdallClientMock, contractMock)
}
type mockBackend struct {
bc *core.BlockChain
txPool *core.TxPool
}
func NewMockBackend(bc *core.BlockChain, txPool *core.TxPool) *mockBackend {
return &mockBackend{
bc: bc,
txPool: txPool,
}
}
func (m *mockBackend) BlockChain() *core.BlockChain {
return m.bc
}
func (m *mockBackend) TxPool() *core.TxPool {
return m.txPool
}
func (m *mockBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
return nil, errors.New("not supported")
}
type testBlockChain struct {
statedb *state.StateDB
gasLimit uint64
chainHeadFeed *event.Feed
}
func (bc *testBlockChain) CurrentBlock() *types.Block {
return types.NewBlock(&types.Header{
GasLimit: bc.gasLimit,
}, nil, nil, nil, trie.NewStackTrie(nil))
}
func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
return bc.CurrentBlock()
}
func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) {
return bc.statedb, nil
}
func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
return bc.chainHeadFeed.Subscribe(ch)
}
var (
// Test chain configurations
testTxPoolConfig core.TxPoolConfig
ethashChainConfig *params.ChainConfig
cliqueChainConfig *params.ChainConfig
// Test accounts
testBankKey, _ = crypto.GenerateKey()
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
testBankFunds = big.NewInt(1000000000000000000)
testUserKey, _ = crypto.GenerateKey()
testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
// Test transactions
pendingTxs []*types.Transaction
newTxs []*types.Transaction
testConfig = &Config{
Recommit: time.Second,
GasCeil: params.GenesisGasLimit,
}
)
func init() {
testTxPoolConfig = core.DefaultTxPoolConfig
testTxPoolConfig.Journal = ""
ethashChainConfig = new(params.ChainConfig)
*ethashChainConfig = *params.TestChainConfig
cliqueChainConfig = new(params.ChainConfig)
*cliqueChainConfig = *params.TestChainConfig
cliqueChainConfig.Clique = &params.CliqueConfig{
Period: 10,
Epoch: 30000,
}
}

View file

@ -18,80 +18,32 @@
package miner
import (
"errors"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/trie"
)
type mockBackend struct {
bc *core.BlockChain
txPool *core.TxPool
}
func NewMockBackend(bc *core.BlockChain, txPool *core.TxPool) *mockBackend {
return &mockBackend{
bc: bc,
txPool: txPool,
}
}
func (m *mockBackend) BlockChain() *core.BlockChain {
return m.bc
}
func (m *mockBackend) TxPool() *core.TxPool {
return m.txPool
}
func (m *mockBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
return nil, errors.New("not supported")
}
type testBlockChain struct {
statedb *state.StateDB
gasLimit uint64
chainHeadFeed *event.Feed
}
func (bc *testBlockChain) CurrentBlock() *types.Block {
return types.NewBlock(&types.Header{
GasLimit: bc.gasLimit,
}, nil, nil, nil, trie.NewStackTrie(nil))
}
func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
return bc.CurrentBlock()
}
func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) {
return bc.statedb, nil
}
func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
return bc.chainHeadFeed.Subscribe(ch)
}
func TestMiner(t *testing.T) {
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
t.Parallel()
minerBor := NewBorDefaultMiner(t)
defer func() {
minerBor.Cleanup(false)
minerBor.Ctrl.Finish()
}()
miner := minerBor.Miner
mux := minerBor.Mux
miner.Start(common.HexToAddress("0x12345"))
waitForMiningState(t, miner, true)
// Start the downloader
mux.Post(downloader.StartEvent{})
waitForMiningState(t, miner, false)
// Stop the downloader and wait for the update loop to run
mux.Post(downloader.DoneEvent{})
waitForMiningState(t, miner, true)
@ -113,10 +65,20 @@ func TestMiner(t *testing.T) {
// An initial FailedEvent should allow mining to stop on a subsequent
// downloader StartEvent.
func TestMinerDownloaderFirstFails(t *testing.T) {
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
t.Parallel()
minerBor := NewBorDefaultMiner(t)
defer func() {
minerBor.Cleanup(false)
minerBor.Ctrl.Finish()
}()
miner := minerBor.Miner
mux := minerBor.Mux
miner.Start(common.HexToAddress("0x12345"))
waitForMiningState(t, miner, true)
// Start the downloader
mux.Post(downloader.StartEvent{})
waitForMiningState(t, miner, false)
@ -145,10 +107,20 @@ func TestMinerDownloaderFirstFails(t *testing.T) {
}
func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
t.Parallel()
minerBor := NewBorDefaultMiner(t)
defer func() {
minerBor.Cleanup(false)
minerBor.Ctrl.Finish()
}()
miner := minerBor.Miner
mux := minerBor.Mux
miner.Start(common.HexToAddress("0x12345"))
waitForMiningState(t, miner, true)
// Start the downloader
mux.Post(downloader.StartEvent{})
waitForMiningState(t, miner, false)
@ -168,58 +140,104 @@ func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
}
func TestStartWhileDownload(t *testing.T) {
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
t.Parallel()
minerBor := NewBorDefaultMiner(t)
defer func() {
minerBor.Cleanup(false)
minerBor.Ctrl.Finish()
}()
miner := minerBor.Miner
mux := minerBor.Mux
waitForMiningState(t, miner, false)
miner.Start(common.HexToAddress("0x12345"))
waitForMiningState(t, miner, true)
// Stop the downloader and wait for the update loop to run
mux.Post(downloader.StartEvent{})
waitForMiningState(t, miner, false)
// Starting the miner after the downloader should not work
miner.Start(common.HexToAddress("0x12345"))
waitForMiningState(t, miner, false)
}
func TestStartStopMiner(t *testing.T) {
miner, _, cleanup := createMiner(t)
defer cleanup(false)
t.Parallel()
minerBor := NewBorDefaultMiner(t)
defer func() {
minerBor.Cleanup(false)
minerBor.Ctrl.Finish()
}()
miner := minerBor.Miner
waitForMiningState(t, miner, false)
miner.Start(common.HexToAddress("0x12345"))
waitForMiningState(t, miner, true)
miner.Stop()
waitForMiningState(t, miner, false)
waitForMiningState(t, miner, true)
miner.Stop()
waitForMiningState(t, miner, false)
}
func TestCloseMiner(t *testing.T) {
miner, _, cleanup := createMiner(t)
defer cleanup(true)
t.Parallel()
minerBor := NewBorDefaultMiner(t)
defer func() {
minerBor.Cleanup(true)
minerBor.Ctrl.Finish()
}()
miner := minerBor.Miner
waitForMiningState(t, miner, false)
miner.Start(common.HexToAddress("0x12345"))
waitForMiningState(t, miner, true)
// Terminate the miner and wait for the update loop to run
miner.Close()
waitForMiningState(t, miner, false)
}
// TestMinerSetEtherbase checks that etherbase becomes set even if mining isn't
// possible at the moment
func TestMinerSetEtherbase(t *testing.T) {
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
t.Parallel()
minerBor := NewBorDefaultMiner(t)
defer func() {
minerBor.Cleanup(false)
minerBor.Ctrl.Finish()
}()
miner := minerBor.Miner
mux := minerBor.Mux
// Start with a 'bad' mining address
miner.Start(common.HexToAddress("0xdead"))
waitForMiningState(t, miner, true)
// Start the downloader
mux.Post(downloader.StartEvent{})
waitForMiningState(t, miner, false)
// Now user tries to configure proper mining address
miner.Start(common.HexToAddress("0x1337"))
// Stop the downloader and wait for the update loop to run
mux.Post(downloader.DoneEvent{})
waitForMiningState(t, miner, true)
// The miner should now be using the good address
if got, exp := miner.coinbase, common.HexToAddress("0x1337"); got != exp {
t.Fatalf("Wrong coinbase, got %x expected %x", got, exp)
@ -239,46 +257,6 @@ func waitForMiningState(t *testing.T, m *Miner, mining bool) {
return
}
}
t.Fatalf("Mining() == %t, want %t", state, mining)
}
func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) {
// Create Ethash config
config := Config{
Etherbase: common.HexToAddress("123456789"),
}
// Create chainConfig
memdb := memorydb.New()
chainDB := rawdb.NewDatabase(memdb)
genesis := core.DeveloperGenesisBlock(15, 11_500_000, common.HexToAddress("12345"))
chainConfig, _, err := core.SetupGenesisBlock(chainDB, genesis)
if err != nil {
t.Fatalf("can't create new chain config: %v", err)
}
// Create consensus engine
engine := clique.New(chainConfig.Clique, chainDB)
// Create Ethereum backend
consensus.NewMerger(rawdb.NewMemoryDatabase())
bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("can't create new chain %v", err)
}
statedb, _ := state.New(common.Hash{}, state.NewDatabase(chainDB), nil)
blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)}
pool := core.NewTxPool(testTxPoolConfig, chainConfig, blockchain)
backend := NewMockBackend(bc, pool)
// Create event Mux
mux := new(event.TypeMux)
// Create Miner
miner := New(backend, &config, chainConfig, mux, engine, nil)
cleanup := func(skipMiner bool) {
bc.Stop()
engine.Close()
pool.Stop()
if !skipMiner {
miner.Close()
}
}
return miner, mux, cleanup
}

View file

@ -44,6 +44,7 @@ func TestUnconfirmedInsertBounds(t *testing.T) {
for i := 0; i < int(depth); i++ {
pool.Insert(depth, [32]byte{byte(depth), byte(i)})
}
// Validate that no blocks below the depth allowance are left in
pool.blocks.Do(func(block interface{}) {
if block := block.(*unconfirmedBlock); block.index+uint64(limit) <= depth {
@ -64,23 +65,29 @@ func TestUnconfirmedShifts(t *testing.T) {
for depth := start; depth < start+uint64(limit); depth++ {
pool.Insert(depth, [32]byte{byte(depth)})
}
// Try to shift below the limit and ensure no blocks are dropped
pool.Shift(start + uint64(limit) - 1)
if n := pool.blocks.Len(); n != int(limit) {
t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit)
}
// Try to shift half the blocks out and verify remainder
pool.Shift(start + uint64(limit) - 1 + uint64(limit/2))
if n := pool.blocks.Len(); n != int(limit)/2 {
t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit/2)
}
// Try to shift all the remaining blocks out and verify emptyness
pool.Shift(start + 2*uint64(limit))
if n := pool.blocks.Len(); n != 0 {
t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0)
}
// Try to shift out from the empty set and make sure it doesn't break
pool.Shift(start + 3*uint64(limit))
if n := pool.blocks.Len(); n != 0 {
t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0)
}

View file

@ -25,6 +25,7 @@ import (
"time"
mapset "github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
@ -393,8 +394,10 @@ func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) t
prevF = float64(prev.Nanoseconds())
next float64
)
if inc {
next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target+intervalAdjustBias)
max := float64(maxRecommitInterval.Nanoseconds())
if next > max {
next = max
@ -406,6 +409,7 @@ func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) t
next = min
}
}
return time.Duration(int64(next))
}

View file

@ -24,9 +24,14 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/ethereum/go-ethereum/accounts"
"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/api"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
@ -38,6 +43,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/tests/bor/mocks"
)
const (
@ -49,43 +55,9 @@ const (
testGas = 144109
)
var (
// Test chain configurations
testTxPoolConfig core.TxPoolConfig
ethashChainConfig *params.ChainConfig
cliqueChainConfig *params.ChainConfig
// Test accounts
testBankKey, _ = crypto.GenerateKey()
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
testBankFunds = big.NewInt(1000000000000000000)
testUserKey, _ = crypto.GenerateKey()
testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
// Test transactions
pendingTxs []*types.Transaction
newTxs []*types.Transaction
testConfig = &Config{
Recommit: time.Second,
GasCeil: params.GenesisGasLimit,
}
)
func init() {
testTxPoolConfig = core.DefaultTxPoolConfig
testTxPoolConfig.Journal = ""
ethashChainConfig = new(params.ChainConfig)
*ethashChainConfig = *params.TestChainConfig
cliqueChainConfig = new(params.ChainConfig)
*cliqueChainConfig = *params.TestChainConfig
cliqueChainConfig.Clique = &params.CliqueConfig{
Period: 10,
Epoch: 30000,
}
signer := types.LatestSigner(params.TestChainConfig)
tx1 := types.MustSignNewTx(testBankKey, signer, &types.AccessListTx{
ChainID: params.TestChainConfig.ChainID,
Nonce: 0,
@ -94,6 +66,7 @@ func init() {
Gas: params.TxGas,
GasPrice: big.NewInt(params.InitialBaseFee),
})
pendingTxs = append(pendingTxs, tx1)
tx2 := types.MustSignNewTx(testBankKey, signer, &types.LegacyTx{
@ -103,6 +76,7 @@ func init() {
Gas: params.TxGas,
GasPrice: big.NewInt(params.InitialBaseFee),
})
newTxs = append(newTxs, tx2)
rand.Seed(time.Now().UnixNano())
@ -125,6 +99,12 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
}
switch e := engine.(type) {
case *bor.Bor:
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes())
e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
return crypto.Sign(crypto.Keccak256(data), testBankKey)
})
case *clique.Clique:
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes())
@ -135,6 +115,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
default:
t.Fatalf("unexpected consensus engine type: %T", engine)
}
genesis := gspec.MustCommit(db)
chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil)
@ -149,10 +130,12 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
t.Fatalf("failed to insert origin chain: %v", err)
}
}
parent := genesis
if n > 0 {
parent = chain.GetBlockByHash(chain.CurrentBlock().ParentHash())
}
blocks, _ := core.GenerateChain(chainConfig, parent, engine, db, 1, func(i int, gen *core.BlockGen) {
gen.SetCoinbase(testUserAddress)
})
@ -174,28 +157,36 @@ func (b *testWorkerBackend) StateAtBlock(block *types.Block, reexec uint64, base
func (b *testWorkerBackend) newRandomUncle() *types.Block {
var parent *types.Block
cur := b.chain.CurrentBlock()
if cur.NumberU64() == 0 {
parent = b.chain.Genesis()
} else {
parent = b.chain.GetBlockByHash(b.chain.CurrentBlock().ParentHash())
}
blocks, _ := core.GenerateChain(b.chain.Config(), parent, b.chain.Engine(), b.db, 1, func(i int, gen *core.BlockGen) {
var addr = make([]byte, common.AddressLength)
rand.Read(addr)
gen.SetCoinbase(common.BytesToAddress(addr))
})
return blocks[0]
}
func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
var tx *types.Transaction
gasPrice := big.NewInt(10 * params.InitialBaseFee)
if creation {
tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(testBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(testCode)), types.HomesteadSigner{}, testBankKey)
} else {
tx, _ = types.SignTx(types.NewTransaction(b.txPool.Nonce(testBankAddress), testUserAddress, big.NewInt(1000), params.TxGas, gasPrice, nil), types.HomesteadSigner{}, testBankKey)
}
return tx
}
@ -204,39 +195,85 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens
backend.txPool.AddLocals(pendingTxs)
w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false)
w.setEtherbase(testBankAddress)
return w, backend
}
func TestGenerateBlockAndImportEthash(t *testing.T) {
testGenerateBlockAndImport(t, false)
t.Parallel()
testGenerateBlockAndImport(t, false, false)
}
func TestGenerateBlockAndImportClique(t *testing.T) {
testGenerateBlockAndImport(t, true)
t.Parallel()
testGenerateBlockAndImport(t, true, false)
}
func testGenerateBlockAndImport(t *testing.T, isClique bool) {
func TestGenerateBlockAndImportBor(t *testing.T) {
t.Parallel()
testGenerateBlockAndImport(t, false, true)
}
//nolint:thelper
func testGenerateBlockAndImport(t *testing.T, isClique bool, isBor bool) {
var (
engine consensus.Engine
chainConfig *params.ChainConfig
db = rawdb.NewMemoryDatabase()
)
if isClique {
chainConfig = params.AllCliqueProtocolChanges
chainConfig.Clique = &params.CliqueConfig{Period: 1, Epoch: 30000}
engine = clique.New(chainConfig.Clique, db)
if isBor {
chainConfig = params.BorUnittestChainConfig
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ethAPIMock := api.NewMockCaller(ctrl)
ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
spanner := bor.NewMockSpanner(ctrl)
spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any()).Return([]*valset.Validator{
{
ID: 0,
Address: testBankAddress,
VotingPower: 100,
ProposerPriority: 0,
},
}, nil).AnyTimes()
heimdallClientMock := mocks.NewMockIHeimdallClient(ctrl)
heimdallClientMock.EXPECT().Close().Times(1)
contractMock := bor.NewMockGenesisContract(ctrl)
db, _, _ = NewDBForFakes(t)
engine = NewFakeBor(t, db, chainConfig, ethAPIMock, spanner, heimdallClientMock, contractMock)
} else {
chainConfig = params.AllEthashProtocolChanges
engine = ethash.NewFaker()
if isClique {
chainConfig = params.AllCliqueProtocolChanges
chainConfig.Clique = &params.CliqueConfig{Period: 1, Epoch: 30000}
engine = clique.New(chainConfig.Clique, db)
} else {
chainConfig = params.AllEthashProtocolChanges
engine = ethash.NewFaker()
}
}
defer engine.Close()
chainConfig.LondonBlock = big.NewInt(0)
w, b := newTestWorker(t, chainConfig, engine, db, 0)
defer w.close()
// This test chain imports the mined blocks.
db2 := rawdb.NewMemoryDatabase()
b.genesis.MustCommit(db2)
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil)
defer chain.Stop()
@ -287,20 +324,25 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens
taskIndex int
taskCh = make(chan struct{}, 2)
)
checkEqual := func(t *testing.T, task *task, index int) {
// The first empty work without any txs included
receiptLen, balance := 0, big.NewInt(0)
if index == 1 {
// The second full work with 1 tx included
receiptLen, balance = 1, big.NewInt(1000)
}
if len(task.receipts) != receiptLen {
t.Fatalf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen)
}
if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 {
t.Fatalf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance)
}
}
w.newTaskHook = func(task *task) {
if task.block.NumberU64() == 1 {
checkEqual(t, task, taskIndex)
@ -308,11 +350,14 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens
taskCh <- struct{}{}
}
}
w.skipSealHook = func(task *task) bool { return true }
w.fullTaskHook = func() {
time.Sleep(100 * time.Millisecond)
}
w.start() // Start mining!
for i := 0; i < 2; i += 1 {
select {
case <-taskCh:
@ -344,16 +389,20 @@ func TestStreamUncleBlock(t *testing.T) {
t.Errorf("uncle hash mismatch: have %s, want %s", have.Hex(), want.Hex())
}
}
taskCh <- struct{}{}
taskIndex += 1
}
}
w.skipSealHook = func(task *task) bool {
return true
}
w.fullTaskHook = func() {
time.Sleep(100 * time.Millisecond)
}
w.start()
for i := 0; i < 2; i += 1 {
@ -396,20 +445,25 @@ func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, en
// one has 1 pending tx, the third one has 2 txs
if taskIndex == 2 {
receiptLen, balance := 2, big.NewInt(2000)
if len(task.receipts) != receiptLen {
t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen)
}
if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 {
t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance)
}
}
taskCh <- struct{}{}
taskIndex += 1
}
}
w.skipSealHook = func(task *task) bool {
return true
}
w.fullTaskHook = func() {
time.Sleep(100 * time.Millisecond)
}
@ -423,6 +477,7 @@ func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, en
t.Error("new task timeout")
}
}
b.txPool.AddLocals(newTxs)
time.Sleep(time.Second)
@ -459,6 +514,7 @@ func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine co
index = 0
start uint32
)
w.resubmitHook = func(minInterval time.Duration, recommitInterval time.Duration) {
// Short circuit if interval checking hasn't started.
if atomic.LoadUint32(&start) == 0 {
@ -489,16 +545,19 @@ func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine co
if recommitInterval != wantRecommitInterval {
t.Errorf("resubmit interval mismatch: have %v, want %v", recommitInterval, wantRecommitInterval)
}
result = append(result, float64(recommitInterval.Nanoseconds()))
index += 1
progress <- struct{}{}
}
w.start()
time.Sleep(time.Second) // Ensure two tasks have been summitted due to start opt
atomic.StoreUint32(&start, 1)
w.setRecommitInterval(3 * time.Second)
select {
case <-progress:
case <-time.NewTimer(time.Second).C:
@ -506,6 +565,7 @@ func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine co
}
w.resubmitAdjustCh <- &intervalAdjust{inc: true, ratio: 0.8}
select {
case <-progress:
case <-time.NewTimer(time.Second).C:
@ -513,6 +573,7 @@ func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine co
}
w.resubmitAdjustCh <- &intervalAdjust{inc: false}
select {
case <-progress:
case <-time.NewTimer(time.Second).C:
@ -520,6 +581,7 @@ func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine co
}
w.setRecommitInterval(500 * time.Millisecond)
select {
case <-progress:
case <-time.NewTimer(time.Second).C:
@ -554,9 +616,11 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
w.skipSealHook = func(task *task) bool {
return true
}
w.fullTaskHook = func() {
time.Sleep(100 * time.Millisecond)
}
timestamp := uint64(time.Now().Unix())
assertBlock := func(block *types.Block, number uint64, coinbase common.Address, random common.Hash) {
if block.Time() != timestamp {
@ -564,14 +628,17 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
// is even smaller than parent block's. It's OK.
t.Logf("Invalid timestamp, want %d, get %d", timestamp, block.Time())
}
if len(block.Uncles()) != 0 {
t.Error("Unexpected uncle block")
}
_, isClique := engine.(*clique.Clique)
if !isClique {
if len(block.Extra()) != 0 {
t.Error("Unexpected extra field")
}
if block.Coinbase() != coinbase {
t.Errorf("Unexpected coinbase got %x want %x", block.Coinbase(), coinbase)
}
@ -580,18 +647,22 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
t.Error("Unexpected coinbase")
}
}
if !isClique {
if block.MixDigest() != random {
t.Error("Unexpected mix digest")
}
}
if block.Nonce() != 0 {
t.Error("Unexpected block nonce")
}
if block.NumberU64() != number {
t.Errorf("Mismatched block number, want %d got %d", number, block.NumberU64())
}
}
var cases = []struct {
parent common.Hash
coinbase common.Address
@ -639,6 +710,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
// This API should work even when the automatic sealing is not enabled
for _, c := range cases {
block, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random)
if c.expectErr {
if err == nil {
t.Error("Expect error but get nil")
@ -647,12 +719,14 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
if err != nil {
t.Errorf("Unexpected error %v", err)
}
assertBlock(block, c.expectNumber, c.coinbase, c.random)
}
}
// This API should work even when the automatic sealing is enabled
w.start()
for _, c := range cases {
block, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random)
if c.expectErr {
@ -663,6 +737,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
if err != nil {
t.Errorf("Unexpected error %v", err)
}
assertBlock(block, c.expectNumber, c.coinbase, c.random)
}
}

View file

@ -23,8 +23,9 @@ import (
"sort"
"strconv"
"github.com/ethereum/go-ethereum/common"
"golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum/common"
)
// Genesis hashes to enforce below configs on.
@ -289,6 +290,38 @@ var (
},
},
}
BorUnittestChainConfig = &ChainConfig{
ChainID: big.NewInt(80001),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: true,
EIP150Hash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
Bor: &BorConfig{
Period: map[string]uint64{
"0": 1,
},
ProducerDelay: 3,
Sprint: 32,
BackupMultiplier: map[string]uint64{
"0": 2,
},
ValidatorContract: "0x0000000000000000000000000000000000001000",
StateReceiverContract: "0x0000000000000000000000000000000000001001",
BurntContract: map[string]string{
"0": "0x00000000000000000000000000000000000000000",
},
},
}
// MumbaiChainConfig contains the chain parameters to run a node on the Mumbai test network.
MumbaiChainConfig = &ChainConfig{
@ -550,7 +583,9 @@ func (c *BorConfig) calculateBorConfigHelper(field map[string]uint64, number uin
for k := range field {
keys = append(keys, k)
}
sort.Strings(keys)
for i := 0; i < len(keys)-1; i++ {
valUint, _ := strconv.ParseUint(keys[i], 10, 64)
valUintNext, _ := strconv.ParseUint(keys[i+1], 10, 64)
@ -558,6 +593,7 @@ func (c *BorConfig) calculateBorConfigHelper(field map[string]uint64, number uin
return field[keys[i]]
}
}
return field[keys[len(keys)-1]]
}

View file

@ -5,14 +5,17 @@ package bor
import (
"encoding/hex"
"encoding/json"
"io"
"math/big"
"testing"
"time"
"github.com/golang/mock/gomock"
"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/span"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
@ -22,12 +25,12 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests/bor/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"golang.org/x/crypto/sha3"
)
var (
const (
spanPath = "bor/span/1"
clerkPath = "clerk/event-record/list"
clerkQueryParams = "from-time=%d&to-time=%d&page=%d&limit=50"
@ -38,7 +41,10 @@ func TestInsertingSpanSizeBlocks(t *testing.T) {
chain := init.ethereum.BlockChain()
engine := init.ethereum.Engine()
_bor := engine.(*bor.Bor)
h, heimdallSpan := getMockedHeimdallClient(t)
h, heimdallSpan, ctrl := getMockedHeimdallClient(t)
defer ctrl.Finish()
_bor.SetHeimdallClient(h)
db := init.ethereum.ChainDb()
@ -51,7 +57,6 @@ func TestInsertingSpanSizeBlocks(t *testing.T) {
insertNewBlock(t, chain, block)
}
assert.True(t, h.AssertCalled(t, "FetchWithRetry", spanPath, ""))
validators, err := _bor.GetCurrentValidators(block.Hash(), spanSize) // check validator set at the first block of new span
if err != nil {
t.Fatalf("%s", err)
@ -82,8 +87,12 @@ func TestFetchStateSyncEvents(t *testing.T) {
// B. Before inserting 1st block of the next sprint, mock heimdall deps
// B.1 Mock /bor/span/1
res, _ := loadSpanFromFile(t)
h := &mocks.IHeimdallClient{}
h.On("FetchWithRetry", spanPath, "").Return(res, nil)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
h := mocks.NewMockIHeimdallClient(ctrl)
h.EXPECT().Span(uint64(1)).Return(&res.Result, nil).AnyTimes()
// B.2 Mock State Sync events
fromID := uint64(1)
@ -94,14 +103,12 @@ func TestFetchStateSyncEvents(t *testing.T) {
sample := getSampleEventRecord(t)
sample.Time = time.Unix(to-int64(eventCount+1), 0) // last event.Time will be just < to
eventRecords := generateFakeStateSyncEvents(sample, eventCount)
h.On("FetchStateSyncEvents", fromID, to).Return(eventRecords, nil)
h.EXPECT().StateSyncEvents(fromID, to).Return(eventRecords, nil).AnyTimes()
_bor.SetHeimdallClient(h)
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor)
insertNewBlock(t, chain, block)
assert.True(t, h.AssertCalled(t, "FetchWithRetry", spanPath, ""))
assert.True(t, h.AssertCalled(t, "FetchStateSyncEvents", fromID, to))
}
func TestFetchStateSyncEvents_2(t *testing.T) {
@ -112,8 +119,12 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
// Mock /bor/span/1
res, _ := loadSpanFromFile(t)
h := &mocks.IHeimdallClient{}
h.On("FetchWithRetry", spanPath, "").Return(res, nil)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
h := mocks.NewMockIHeimdallClient(ctrl)
h.EXPECT().Span(uint64(1)).Return(&res.Result, nil).AnyTimes()
// Mock State Sync events
// at # sprintSize, events are fetched for [fromID, (block-sprint).Time)
@ -123,7 +134,7 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
// First query will be from [id=1, (block-sprint).Time]
// Insert 5 events in this time range
eventRecords := []*bor.EventRecordWithTime{
eventRecords := []*clerk.EventRecordWithTime{
buildStateEvent(sample, 1, 3), // id = 1, time = 1
buildStateEvent(sample, 2, 1), // id = 2, time = 3
buildStateEvent(sample, 3, 2), // id = 3, time = 2
@ -131,7 +142,8 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
buildStateEvent(sample, 4, 5), // id = 4, time = 5
buildStateEvent(sample, 6, 4), // id = 6, time = 4
}
h.On("FetchStateSyncEvents", fromID, to).Return(eventRecords, nil)
h.EXPECT().StateSyncEvents(fromID, to).Return(eventRecords, nil).AnyTimes()
_bor.SetHeimdallClient(h)
// Insert blocks for 0th sprint
@ -141,25 +153,27 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor)
insertNewBlock(t, chain, block)
}
assert.True(t, h.AssertCalled(t, "FetchWithRetry", spanPath, ""))
assert.True(t, h.AssertCalled(t, "FetchStateSyncEvents", fromID, to))
lastStateID, _ := _bor.GenesisContractsClient.LastStateId(sprintSize)
// state 6 was not written
assert.Equal(t, uint64(4), lastStateID.Uint64())
//
fromID = uint64(5)
to = int64(chain.GetHeaderByNumber(sprintSize).Time)
eventRecords = []*bor.EventRecordWithTime{
eventRecords = []*clerk.EventRecordWithTime{
buildStateEvent(sample, 5, 7),
buildStateEvent(sample, 6, 4),
}
h.On("FetchStateSyncEvents", fromID, to).Return(eventRecords, nil)
h.EXPECT().StateSyncEvents(fromID, to).Return(eventRecords, nil).AnyTimes()
for i := sprintSize + 1; i <= spanSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor)
insertNewBlock(t, chain, block)
}
assert.True(t, h.AssertCalled(t, "FetchStateSyncEvents", fromID, to))
lastStateID, _ = _bor.GenesisContractsClient.LastStateId(spanSize)
assert.Equal(t, uint64(6), lastStateID.Uint64())
}
@ -169,7 +183,10 @@ func TestOutOfTurnSigning(t *testing.T) {
chain := init.ethereum.BlockChain()
engine := init.ethereum.Engine()
_bor := engine.(*bor.Bor)
h, _ := getMockedHeimdallClient(t)
h, _, ctrl := getMockedHeimdallClient(t)
defer ctrl.Finish()
_bor.SetHeimdallClient(h)
db := init.ethereum.ChainDb()
@ -217,7 +234,10 @@ func TestSignerNotFound(t *testing.T) {
chain := init.ethereum.BlockChain()
engine := init.ethereum.Engine()
_bor := engine.(*bor.Bor)
h, _ := getMockedHeimdallClient(t)
h, _, ctrl := getMockedHeimdallClient(t)
defer ctrl.Finish()
_bor.SetHeimdallClient(h)
db := init.ethereum.ChainDb()
@ -236,47 +256,46 @@ func TestSignerNotFound(t *testing.T) {
bor.UnauthorizedSignerError{Number: 0, Signer: addr.Bytes()})
}
func getMockedHeimdallClient(t *testing.T) (*mocks.IHeimdallClient, *bor.HeimdallSpan) {
res, heimdallSpan := loadSpanFromFile(t)
h := &mocks.IHeimdallClient{}
h.On("FetchWithRetry", "bor/span/1", "").Return(res, nil)
h.On(
"FetchStateSyncEvents",
mock.AnythingOfType("uint64"),
mock.AnythingOfType("int64")).Return([]*bor.EventRecordWithTime{getSampleEventRecord(t)}, nil)
return h, heimdallSpan
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(uint64(1)).Return(heimdallSpan, nil).AnyTimes()
h.EXPECT().StateSyncEvents(gomock.Any(), gomock.Any()).
Return([]*clerk.EventRecordWithTime{getSampleEventRecord(t)}, nil).AnyTimes()
return h, heimdallSpan, ctrl
}
func generateFakeStateSyncEvents(sample *bor.EventRecordWithTime, count int) []*bor.EventRecordWithTime {
events := make([]*bor.EventRecordWithTime, count)
func generateFakeStateSyncEvents(sample *clerk.EventRecordWithTime, count int) []*clerk.EventRecordWithTime {
events := make([]*clerk.EventRecordWithTime, count)
event := *sample
event.ID = 1
events[0] = &bor.EventRecordWithTime{}
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] = &bor.EventRecordWithTime{}
events[i] = &clerk.EventRecordWithTime{}
*events[i] = event
}
return events
}
func buildStateEvent(sample *bor.EventRecordWithTime, id uint64, timeStamp int64) *bor.EventRecordWithTime {
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) *bor.EventRecordWithTime {
res := stateSyncEventsPayload(t)
var _eventRecords []*bor.EventRecordWithTime
if err := json.Unmarshal(res.Result, &_eventRecords); err != nil {
t.Fatalf("%s", err)
}
_eventRecords[0].Time = time.Unix(1, 0)
return _eventRecords[0]
func getSampleEventRecord(t *testing.T) *clerk.EventRecordWithTime {
eventRecords := stateSyncEventsPayload(t)
eventRecords.Result[0].Time = time.Unix(1, 0)
return eventRecords.Result[0]
}
// TestEIP1559Transition tests the following:

View file

@ -11,6 +11,9 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
@ -46,17 +49,23 @@ type initializeData struct {
}
func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData {
t.Helper()
genesisData, err := ioutil.ReadFile("./testdata/genesis.json")
if err != nil {
t.Fatalf("%s", err)
}
gen := &core.Genesis{}
if err := json.Unmarshal(genesisData, gen); err != nil {
t.Fatalf("%s", err)
}
ethConf := &eth.Config{
Genesis: gen,
}
ethConf.Genesis.MustCommit(db)
ethereum := utils.CreateBorEthereum(ethConf)
@ -65,6 +74,7 @@ func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData {
}
ethConf.Genesis.MustCommit(ethereum.ChainDb())
return &initializeData{
genesis: gen,
ethereum: ethereum,
@ -72,12 +82,16 @@ func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData {
}
func insertNewBlock(t *testing.T, chain *core.BlockChain, block *types.Block) {
t.Helper()
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
t.Fatalf("%s", err)
}
}
func buildNextBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, block *types.Block, signer []byte, borConfig *params.BorConfig) *types.Block {
t.Helper()
header := block.Header()
header.Number.Add(header.Number, big.NewInt(1))
number := header.Number.Uint64()
@ -90,11 +104,12 @@ func buildNextBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, block *
header.Time += bor.CalcProducerDelay(header.Number.Uint64(), 0, borConfig)
header.Extra = make([]byte, 32+65) // vanity + extraSeal
currentValidators := []*bor.Validator{bor.NewValidator(addr, 10)}
currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)}
isSpanEnd := (number+1)%spanSize == 0
isSpanStart := number%spanSize == 0
isSprintEnd := (header.Number.Uint64()+1)%sprintSize == 0
if isSpanEnd {
_, heimdallSpan := loadSpanFromFile(t)
// this is to stash the validator bytes in the header
@ -102,18 +117,23 @@ func buildNextBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, block *
} else if isSpanStart {
header.Difficulty = new(big.Int).SetInt64(3)
}
if isSprintEnd {
sort.Sort(bor.ValidatorsByAddress(currentValidators))
sort.Sort(valset.ValidatorsByAddress(currentValidators))
validatorBytes := make([]byte, len(currentValidators)*validatorHeaderBytesLength)
header.Extra = make([]byte, 32+len(validatorBytes)+65) // vanity + validatorBytes + extraSeal
for i, val := range currentValidators {
copy(validatorBytes[i*validatorHeaderBytesLength:], val.HeaderBytes())
}
copy(header.Extra[32:], validatorBytes)
}
if chain.Config().IsLondon(header.Number) {
header.BaseFee = misc.CalcBaseFee(chain.Config(), block.Header())
if !chain.Config().IsLondon(block.Number()) {
parentGasLimit := block.GasLimit() * params.ElasticityMultiplier
header.GasLimit = core.CalcGasLimit(parentGasLimit, parentGasLimit)
@ -124,58 +144,73 @@ func buildNextBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, block *
if err != nil {
t.Fatalf("%s", err)
}
_, err = _bor.FinalizeAndAssemble(chain, header, state, nil, nil, nil)
if err != nil {
t.Fatalf("%s", err)
}
sign(t, header, signer, borConfig)
return types.NewBlockWithHeader(header)
}
func sign(t *testing.T, header *types.Header, signer []byte, c *params.BorConfig) {
t.Helper()
sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header, c)), signer)
if err != nil {
t.Fatalf("%s", err)
}
copy(header.Extra[len(header.Extra)-extraSeal:], sig)
}
func stateSyncEventsPayload(t *testing.T) *bor.ResponseWithHeight {
//nolint:unused,deadcode
func stateSyncEventsPayload(t *testing.T) *heimdall.StateSyncEventsResponse {
t.Helper()
stateData, err := ioutil.ReadFile("./testdata/states.json")
if err != nil {
t.Fatalf("%s", err)
}
res := &bor.ResponseWithHeight{}
res := &heimdall.StateSyncEventsResponse{}
if err := json.Unmarshal(stateData, res); err != nil {
t.Fatalf("%s", err)
}
return res
}
func loadSpanFromFile(t *testing.T) (*bor.ResponseWithHeight, *bor.HeimdallSpan) {
//nolint:unused,deadcode
func loadSpanFromFile(t *testing.T) (*heimdall.SpanResponse, *span.HeimdallSpan) {
t.Helper()
spanData, err := ioutil.ReadFile("./testdata/span.json")
if err != nil {
t.Fatalf("%s", err)
}
res := &bor.ResponseWithHeight{}
res := &heimdall.SpanResponse{}
if err := json.Unmarshal(spanData, res); err != nil {
t.Fatalf("%s", err)
}
heimdallSpan := &bor.HeimdallSpan{}
if err := json.Unmarshal(res.Result, heimdallSpan); err != nil {
t.Fatalf("%s", err)
}
return res, heimdallSpan
return res, &res.Result
}
func getSignerKey(number uint64) []byte {
signerKey := privKey
isSpanStart := number%spanSize == 0
if isSpanStart {
// validator set in the new span has changed
signerKey = privKey2
}
_key, _ := hex.DecodeString(signerKey)
return _key
}

View file

@ -1,87 +1,78 @@
// Code generated by mockery v2.10.0. DO NOT EDIT.
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/ethereum/go-ethereum/consensus/bor (interfaces: IHeimdallClient)
// Package mocks is a generated GoMock package.
package mocks
import (
bor "github.com/ethereum/go-ethereum/consensus/bor"
mock "github.com/stretchr/testify/mock"
reflect "reflect"
clerk "github.com/ethereum/go-ethereum/consensus/bor/clerk"
span "github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
gomock "github.com/golang/mock/gomock"
)
// IHeimdallClient is an autogenerated mock type for the IHeimdallClient type
type IHeimdallClient struct {
mock.Mock
// MockIHeimdallClient is a mock of IHeimdallClient interface.
type MockIHeimdallClient struct {
ctrl *gomock.Controller
recorder *MockIHeimdallClientMockRecorder
}
// Close provides a mock function with given fields:
func (_m *IHeimdallClient) Close() {
_m.Called()
// MockIHeimdallClientMockRecorder is the mock recorder for MockIHeimdallClient.
type MockIHeimdallClientMockRecorder struct {
mock *MockIHeimdallClient
}
// Fetch provides a mock function with given fields: path, query
func (_m *IHeimdallClient) Fetch(path string, query string) (*bor.ResponseWithHeight, error) {
ret := _m.Called(path, query)
var r0 *bor.ResponseWithHeight
if rf, ok := ret.Get(0).(func(string, string) *bor.ResponseWithHeight); ok {
r0 = rf(path, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*bor.ResponseWithHeight)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string) error); ok {
r1 = rf(path, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
// NewMockIHeimdallClient creates a new mock instance.
func NewMockIHeimdallClient(ctrl *gomock.Controller) *MockIHeimdallClient {
mock := &MockIHeimdallClient{ctrl: ctrl}
mock.recorder = &MockIHeimdallClientMockRecorder{mock}
return mock
}
// FetchStateSyncEvents provides a mock function with given fields: fromID, to
func (_m *IHeimdallClient) FetchStateSyncEvents(fromID uint64, to int64) ([]*bor.EventRecordWithTime, error) {
ret := _m.Called(fromID, to)
var r0 []*bor.EventRecordWithTime
if rf, ok := ret.Get(0).(func(uint64, int64) []*bor.EventRecordWithTime); ok {
r0 = rf(fromID, to)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*bor.EventRecordWithTime)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(uint64, int64) error); ok {
r1 = rf(fromID, to)
} else {
r1 = ret.Error(1)
}
return r0, r1
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockIHeimdallClient) EXPECT() *MockIHeimdallClientMockRecorder {
return m.recorder
}
// FetchWithRetry provides a mock function with given fields: path, query
func (_m *IHeimdallClient) FetchWithRetry(path string, query string) (*bor.ResponseWithHeight, error) {
ret := _m.Called(path, query)
var r0 *bor.ResponseWithHeight
if rf, ok := ret.Get(0).(func(string, string) *bor.ResponseWithHeight); ok {
r0 = rf(path, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*bor.ResponseWithHeight)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string) error); ok {
r1 = rf(path, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
// Close mocks base method.
func (m *MockIHeimdallClient) Close() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Close")
}
// Close indicates an expected call of Close.
func (mr *MockIHeimdallClientMockRecorder) Close() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockIHeimdallClient)(nil).Close))
}
// Span mocks base method.
func (m *MockIHeimdallClient) Span(arg0 uint64) (*span.HeimdallSpan, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Span", arg0)
ret0, _ := ret[0].(*span.HeimdallSpan)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Span indicates an expected call of Span.
func (mr *MockIHeimdallClientMockRecorder) Span(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Span", reflect.TypeOf((*MockIHeimdallClient)(nil).Span), arg0)
}
// StateSyncEvents mocks base method.
func (m *MockIHeimdallClient) StateSyncEvents(arg0 uint64, arg1 int64) ([]*clerk.EventRecordWithTime, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "StateSyncEvents", arg0, arg1)
ret0, _ := ret[0].([]*clerk.EventRecordWithTime)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// StateSyncEvents indicates an expected call of StateSyncEvents.
func (mr *MockIHeimdallClientMockRecorder) StateSyncEvents(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateSyncEvents", reflect.TypeOf((*MockIHeimdallClient)(nil).StateSyncEvents), arg0, arg1)
}

7
tests/deps/fake.go Normal file
View file

@ -0,0 +1,7 @@
package deps
// it is a fake file to lock deps
//nolint:typecheck
import (
_ "github.com/golang/mock/mockgen/model"
)

View file

@ -19,9 +19,10 @@ package trie
import (
"sync"
"golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
)
// hasher is a type used for the trie Hash operation. A hasher has some