mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
resolve merge conflict
This commit is contained in:
commit
638d82bdf7
427 changed files with 12594 additions and 18374 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
|
@ -10,6 +10,7 @@ core/ @karalabe @holiman @rjl493456442
|
|||
eth/ @karalabe @holiman @rjl493456442
|
||||
eth/catalyst/ @gballet
|
||||
eth/tracers/ @s1na
|
||||
core/tracing/ @s1na
|
||||
graphql/ @s1na
|
||||
les/ @zsfelfoldi @rjl493456442
|
||||
light/ @zsfelfoldi @rjl493456442
|
||||
|
|
|
|||
4
.github/workflows/go.yml
vendored
4
.github/workflows/go.yml
vendored
|
|
@ -11,9 +11,9 @@ jobs:
|
|||
build:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21.4
|
||||
- name: Run tests
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ run:
|
|||
# default is true. Enables skipping of directories:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
skip-dirs-use-default: true
|
||||
skip-files:
|
||||
- core/genesis_alloc.go
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
|
|
@ -26,6 +24,8 @@ linters:
|
|||
- exportloopref
|
||||
- whitespace
|
||||
|
||||
### linters we tried and will not be using:
|
||||
###
|
||||
# - structcheck # lots of false positives
|
||||
# - errcheck #lot of false positives
|
||||
# - contextcheck
|
||||
|
|
@ -40,6 +40,8 @@ linters-settings:
|
|||
simplify: true
|
||||
|
||||
issues:
|
||||
exclude-files:
|
||||
- core/genesis_alloc.go
|
||||
exclude-rules:
|
||||
- path: crypto/bn256/cloudflare/optate.go
|
||||
linters:
|
||||
|
|
|
|||
56
.travis.yml
56
.travis.yml
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
if: type = push
|
||||
os: linux
|
||||
arch: amd64
|
||||
dist: bionic
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
env:
|
||||
- docker
|
||||
|
|
@ -32,7 +32,7 @@ jobs:
|
|||
if: type = push
|
||||
os: linux
|
||||
arch: arm64
|
||||
dist: bionic
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
env:
|
||||
- docker
|
||||
|
|
@ -49,21 +49,20 @@ jobs:
|
|||
- stage: build
|
||||
if: type = push
|
||||
os: linux
|
||||
dist: bionic
|
||||
dist: noble
|
||||
sudo: required
|
||||
go: 1.22.x
|
||||
env:
|
||||
- azure-linux
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
script:
|
||||
# Build for the primary platforms that Trusty can manage
|
||||
# build amd64
|
||||
- go run build/ci.go install -dlgo
|
||||
- go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
||||
|
||||
# build 386
|
||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib
|
||||
- go run build/ci.go install -dlgo -arch 386
|
||||
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
||||
|
||||
|
|
@ -98,48 +97,34 @@ jobs:
|
|||
|
||||
# These builders run the tests
|
||||
- stage: build
|
||||
if: type = push
|
||||
os: linux
|
||||
arch: amd64
|
||||
dist: bionic
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
script:
|
||||
- travis_wait 30 go run build/ci.go test $TEST_PACKAGES
|
||||
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
||||
|
||||
- stage: build
|
||||
if: type = pull_request
|
||||
if: type = push
|
||||
os: linux
|
||||
arch: arm64
|
||||
dist: bionic
|
||||
dist: noble
|
||||
go: 1.21.x
|
||||
script:
|
||||
- travis_wait 30 go run build/ci.go test $TEST_PACKAGES
|
||||
|
||||
- stage: build
|
||||
os: linux
|
||||
dist: bionic
|
||||
go: 1.21.x
|
||||
script:
|
||||
- travis_wait 30 go run build/ci.go test $TEST_PACKAGES
|
||||
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
||||
|
||||
# This builder does the Ubuntu PPA nightly uploads
|
||||
- stage: build
|
||||
if: type = cron || (type = push && tag ~= /^v[0-9]/)
|
||||
os: linux
|
||||
dist: bionic
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
env:
|
||||
- ubuntu-ppa
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- devscripts
|
||||
- debhelper
|
||||
- dput
|
||||
- fakeroot
|
||||
- python-bzrlib
|
||||
- python-paramiko
|
||||
before_install:
|
||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot
|
||||
script:
|
||||
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
||||
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
||||
|
|
@ -148,7 +133,7 @@ jobs:
|
|||
- stage: build
|
||||
if: type = cron
|
||||
os: linux
|
||||
dist: bionic
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
env:
|
||||
- azure-purge
|
||||
|
|
@ -161,8 +146,9 @@ jobs:
|
|||
- stage: build
|
||||
if: type = cron
|
||||
os: linux
|
||||
dist: bionic
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
env:
|
||||
- racetests
|
||||
script:
|
||||
- travis_wait 30 go run build/ci.go test -race $TEST_PACKAGES
|
||||
|
||||
- travis_wait 60 go run build/ci.go test -race $TEST_PACKAGES
|
||||
|
|
|
|||
24
Makefile
24
Makefile
|
|
@ -2,31 +2,35 @@
|
|||
# with Go source code. If you know what GOPATH is then you probably
|
||||
# don't need to bother with make.
|
||||
|
||||
.PHONY: geth all test lint clean devtools help
|
||||
.PHONY: geth all test lint fmt clean devtools help
|
||||
|
||||
GOBIN = ./build/bin
|
||||
GO ?= latest
|
||||
GORUN = go run
|
||||
|
||||
#? geth: Build geth
|
||||
#? geth: Build geth.
|
||||
geth:
|
||||
$(GORUN) build/ci.go install ./cmd/geth
|
||||
@echo "Done building."
|
||||
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
||||
|
||||
#? all: Build all packages and executables
|
||||
#? all: Build all packages and executables.
|
||||
all:
|
||||
$(GORUN) build/ci.go install
|
||||
|
||||
#? test: Run the tests
|
||||
#? test: Run the tests.
|
||||
test: all
|
||||
$(GORUN) build/ci.go test
|
||||
|
||||
#? lint: Run certain pre-selected linters
|
||||
#? lint: Run certain pre-selected linters.
|
||||
lint: ## Run linters.
|
||||
$(GORUN) build/ci.go lint
|
||||
|
||||
#? clean: Clean go cache, built executables, and the auto generated folder
|
||||
#? fmt: Ensure consistent code formatting.
|
||||
fmt:
|
||||
gofmt -s -w $(shell find . -name "*.go")
|
||||
|
||||
#? clean: Clean go cache, built executables, and the auto generated folder.
|
||||
clean:
|
||||
go clean -cache
|
||||
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
||||
|
|
@ -34,7 +38,7 @@ clean:
|
|||
# The devtools target installs tools required for 'go generate'.
|
||||
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
||||
|
||||
#? devtools: Install recommended developer tools
|
||||
#? devtools: Install recommended developer tools.
|
||||
devtools:
|
||||
env GOBIN= go install golang.org/x/tools/cmd/stringer@latest
|
||||
env GOBIN= go install github.com/fjl/gencodec@latest
|
||||
|
|
@ -45,5 +49,9 @@ devtools:
|
|||
|
||||
#? help: Get more info on make commands.
|
||||
help: Makefile
|
||||
@echo " Choose a command run in go-ethereum:"
|
||||
@echo ''
|
||||
@echo 'Usage:'
|
||||
@echo ' make [target]'
|
||||
@echo ''
|
||||
@echo 'Targets:'
|
||||
@sed -n 's/^#?//p' $< | column -t -s ':' | sort | sed -e 's/^/ /'
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// ConvertType converts an interface of a runtime type into a interface of the
|
||||
// ConvertType converts an interface of a runtime type into an interface of the
|
||||
// given type, e.g. turn this code:
|
||||
//
|
||||
// var fields []reflect.StructField
|
||||
|
|
@ -33,7 +33,7 @@ import (
|
|||
// Name: "X",
|
||||
// Type: reflect.TypeOf(new(big.Int)),
|
||||
// Tag: reflect.StructTag("json:\"" + "x" + "\""),
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// into:
|
||||
//
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// typeWithoutStringer is a alias for the Type type which simply doesn't implement
|
||||
// typeWithoutStringer is an alias for the Type type which simply doesn't implement
|
||||
// the stringer interface to allow printing type details in the tests below.
|
||||
type typeWithoutStringer Type
|
||||
|
||||
|
|
|
|||
15
accounts/external/backend.go
vendored
15
accounts/external/backend.go
vendored
|
|
@ -205,7 +205,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
|||
to = &t
|
||||
}
|
||||
args := &apitypes.SendTxArgs{
|
||||
Data: &data,
|
||||
Input: &data,
|
||||
Nonce: hexutil.Uint64(tx.Nonce()),
|
||||
Value: hexutil.Big(*tx.Value()),
|
||||
Gas: hexutil.Uint64(tx.Gas()),
|
||||
|
|
@ -215,7 +215,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
|||
switch tx.Type() {
|
||||
case types.LegacyTxType, types.AccessListTxType:
|
||||
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
|
||||
case types.DynamicFeeTxType:
|
||||
case types.DynamicFeeTxType, types.BlobTxType:
|
||||
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
|
||||
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
|
||||
default:
|
||||
|
|
@ -235,6 +235,17 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
|||
accessList := tx.AccessList()
|
||||
args.AccessList = &accessList
|
||||
}
|
||||
if tx.Type() == types.BlobTxType {
|
||||
args.BlobHashes = tx.BlobHashes()
|
||||
sidecar := tx.BlobTxSidecar()
|
||||
if sidecar == nil {
|
||||
return nil, errors.New("blobs must be present for signing")
|
||||
}
|
||||
args.Blobs = sidecar.Blobs
|
||||
args.Commitments = sidecar.Commitments
|
||||
args.Proofs = sidecar.Proofs
|
||||
}
|
||||
|
||||
var res signTransactionResult
|
||||
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -326,6 +326,11 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
|||
|
||||
// Create a temporary keystore to test with
|
||||
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int()))
|
||||
|
||||
// Create the directory
|
||||
os.MkdirAll(dir, 0700)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||
|
||||
list := ks.Accounts()
|
||||
|
|
@ -335,9 +340,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
|||
if !waitWatcherStart(ks) {
|
||||
t.Fatal("keystore watcher didn't start in time")
|
||||
}
|
||||
// Create the directory and copy a key file into it.
|
||||
os.MkdirAll(dir, 0700)
|
||||
defer os.RemoveAll(dir)
|
||||
// Copy a key file into it
|
||||
file := filepath.Join(dir, "aaa")
|
||||
|
||||
// Place one of our testfiles in there
|
||||
|
|
|
|||
|
|
@ -312,11 +312,10 @@ func (ks *KeyStore) Unlock(a accounts.Account, passphrase string) error {
|
|||
// Lock removes the private key with the given address from memory.
|
||||
func (ks *KeyStore) Lock(addr common.Address) error {
|
||||
ks.mu.Lock()
|
||||
if unl, found := ks.unlocked[addr]; found {
|
||||
unl, found := ks.unlocked[addr]
|
||||
ks.mu.Unlock()
|
||||
if found {
|
||||
ks.expire(addr, unl, time.Duration(0)*time.Nanosecond)
|
||||
} else {
|
||||
ks.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ func (hub *Hub) readPairings() error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
defer pairingFile.Close()
|
||||
|
||||
pairingData, err := io.ReadAll(pairingFile)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package blsync
|
|||
import (
|
||||
"github.com/ethereum/go-ethereum/beacon/light/request"
|
||||
"github.com/ethereum/go-ethereum/beacon/light/sync"
|
||||
"github.com/ethereum/go-ethereum/beacon/params"
|
||||
"github.com/ethereum/go-ethereum/beacon/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
|
|
@ -40,7 +41,7 @@ type beaconBlockSync struct {
|
|||
|
||||
type headTracker interface {
|
||||
PrefetchHead() types.HeadInfo
|
||||
ValidatedHead() (types.SignedHeader, bool)
|
||||
ValidatedOptimistic() (types.OptimisticUpdate, bool)
|
||||
ValidatedFinality() (types.FinalityUpdate, bool)
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ func (s *beaconBlockSync) Process(requester request.Requester, events []request.
|
|||
case request.EvResponse, request.EvFail, request.EvTimeout:
|
||||
sid, req, resp := event.RequestInfo()
|
||||
blockRoot := common.Hash(req.(sync.ReqBeaconBlock))
|
||||
log.Debug("Beacon block event", "type", event.Type.Name, "hash", blockRoot)
|
||||
if resp != nil {
|
||||
s.recentBlocks.Add(blockRoot, resp.(*types.BeaconBlock))
|
||||
}
|
||||
|
|
@ -79,8 +81,8 @@ func (s *beaconBlockSync) Process(requester request.Requester, events []request.
|
|||
}
|
||||
s.updateEventFeed()
|
||||
// request validated head block if unavailable and not yet requested
|
||||
if vh, ok := s.headTracker.ValidatedHead(); ok {
|
||||
s.tryRequestBlock(requester, vh.Header.Hash(), false)
|
||||
if vh, ok := s.headTracker.ValidatedOptimistic(); ok {
|
||||
s.tryRequestBlock(requester, vh.Attested.Hash(), false)
|
||||
}
|
||||
// request prefetch head if the given server has announced it
|
||||
if prefetchHead := s.headTracker.PrefetchHead().BlockRoot; prefetchHead != (common.Hash{}) {
|
||||
|
|
@ -113,19 +115,34 @@ func blockHeadInfo(block *types.BeaconBlock) types.HeadInfo {
|
|||
}
|
||||
|
||||
func (s *beaconBlockSync) updateEventFeed() {
|
||||
head, ok := s.headTracker.ValidatedHead()
|
||||
optimistic, ok := s.headTracker.ValidatedOptimistic()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
finality, ok := s.headTracker.ValidatedFinality() //TODO fetch directly if subscription does not deliver
|
||||
if !ok || head.Header.Epoch() != finality.Attested.Header.Epoch() {
|
||||
return
|
||||
}
|
||||
validatedHead := head.Header.Hash()
|
||||
|
||||
validatedHead := optimistic.Attested.Hash()
|
||||
headBlock, ok := s.recentBlocks.Get(validatedHead)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var finalizedHash common.Hash
|
||||
if finality, ok := s.headTracker.ValidatedFinality(); ok {
|
||||
he := optimistic.Attested.Epoch()
|
||||
fe := finality.Attested.Header.Epoch()
|
||||
switch {
|
||||
case he == fe:
|
||||
finalizedHash = finality.Finalized.PayloadHeader.BlockHash()
|
||||
case he < fe:
|
||||
return
|
||||
case he == fe+1:
|
||||
parent, ok := s.recentBlocks.Get(optimistic.Attested.ParentRoot)
|
||||
if !ok || parent.Slot()/params.EpochLength == fe {
|
||||
return // head is at first slot of next epoch, wait for finality update
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headInfo := blockHeadInfo(headBlock)
|
||||
if headInfo == s.lastHeadInfo {
|
||||
return
|
||||
|
|
@ -139,8 +156,8 @@ func (s *beaconBlockSync) updateEventFeed() {
|
|||
return
|
||||
}
|
||||
s.chainHeadFeed.Send(types.ChainHeadEvent{
|
||||
BeaconHead: head.Header,
|
||||
BeaconHead: optimistic.Attested.Header,
|
||||
Block: execBlock,
|
||||
Finalized: finality.Finalized.PayloadHeader.BlockHash(),
|
||||
Finalized: finalizedHash,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
testServer1 = "testServer1"
|
||||
testServer2 = "testServer2"
|
||||
testServer1 = testServer("testServer1")
|
||||
testServer2 = testServer("testServer2")
|
||||
|
||||
testBlock1 = types.NewBeaconBlock(&deneb.BeaconBlock{
|
||||
Slot: 123,
|
||||
|
|
@ -51,6 +51,12 @@ var (
|
|||
})
|
||||
)
|
||||
|
||||
type testServer string
|
||||
|
||||
func (t testServer) Name() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
func TestBlockSync(t *testing.T) {
|
||||
ht := &testHeadTracker{}
|
||||
blockSync := newBeaconBlockSync(ht)
|
||||
|
|
@ -134,8 +140,12 @@ func (h *testHeadTracker) PrefetchHead() types.HeadInfo {
|
|||
return h.prefetch
|
||||
}
|
||||
|
||||
func (h *testHeadTracker) ValidatedHead() (types.SignedHeader, bool) {
|
||||
return h.validated, h.validated.Header != (types.Header{})
|
||||
func (h *testHeadTracker) ValidatedOptimistic() (types.OptimisticUpdate, bool) {
|
||||
return types.OptimisticUpdate{
|
||||
Attested: types.HeaderWithExecProof{Header: h.validated.Header},
|
||||
Signature: h.validated.Signature,
|
||||
SignatureSlot: h.validated.SignatureSlot,
|
||||
}, h.validated.Header != (types.Header{})
|
||||
}
|
||||
|
||||
// TODO add test case for finality
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ func (ec *engineClient) updateLoop(headCh <-chan types.ChainHeadEvent) {
|
|||
for {
|
||||
select {
|
||||
case <-ec.rootCtx.Done():
|
||||
log.Debug("Stopping engine API update loop")
|
||||
return
|
||||
|
||||
case event := <-headCh:
|
||||
|
|
@ -73,12 +74,14 @@ func (ec *engineClient) updateLoop(headCh <-chan types.ChainHeadEvent) {
|
|||
fork := ec.config.ForkAtEpoch(event.BeaconHead.Epoch())
|
||||
forkName := strings.ToLower(fork.Name)
|
||||
|
||||
log.Debug("Calling NewPayload", "number", event.Block.NumberU64(), "hash", event.Block.Hash())
|
||||
if status, err := ec.callNewPayload(forkName, event); err == nil {
|
||||
log.Info("Successful NewPayload", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "status", status)
|
||||
} else {
|
||||
log.Error("Failed NewPayload", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "error", err)
|
||||
}
|
||||
|
||||
log.Debug("Calling ForkchoiceUpdated", "head", event.Block.Hash())
|
||||
if status, err := ec.callForkchoiceUpdated(forkName, event); err == nil {
|
||||
log.Info("Successful ForkchoiceUpdated", "head", event.Block.Hash(), "status", status)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package engine
|
|||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
|
@ -132,12 +133,7 @@ func (b PayloadID) Version() PayloadVersion {
|
|||
|
||||
// Is returns whether the identifier matches any of provided payload versions.
|
||||
func (b PayloadID) Is(versions ...PayloadVersion) bool {
|
||||
for _, v := range versions {
|
||||
if v == b.Version() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(versions, b.Version())
|
||||
}
|
||||
|
||||
func (b PayloadID) String() string {
|
||||
|
|
@ -213,7 +209,7 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash,
|
|||
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
||||
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
||||
}
|
||||
var blobHashes []common.Hash
|
||||
var blobHashes = make([]common.Hash, 0, len(txs))
|
||||
for _, tx := range txs {
|
||||
blobHashes = append(blobHashes, tx.BlobHashes()...)
|
||||
}
|
||||
|
|
@ -254,7 +250,7 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash,
|
|||
BlobGasUsed: params.BlobGasUsed,
|
||||
ParentBeaconRoot: beaconRoot,
|
||||
}
|
||||
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
|
||||
block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: params.Withdrawals})
|
||||
if block.Hash() != params.BlockHash {
|
||||
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,13 +46,13 @@ func (s *ApiServer) Subscribe(eventCallback func(event request.Event)) {
|
|||
log.Debug("New head received", "slot", slot, "blockRoot", blockRoot)
|
||||
eventCallback(request.Event{Type: sync.EvNewHead, Data: types.HeadInfo{Slot: slot, BlockRoot: blockRoot}})
|
||||
},
|
||||
OnSignedHead: func(head types.SignedHeader) {
|
||||
log.Debug("New signed head received", "slot", head.Header.Slot, "blockRoot", head.Header.Hash(), "signerCount", head.Signature.SignerCount())
|
||||
eventCallback(request.Event{Type: sync.EvNewSignedHead, Data: head})
|
||||
OnOptimistic: func(update types.OptimisticUpdate) {
|
||||
log.Debug("New optimistic update received", "slot", update.Attested.Slot, "blockRoot", update.Attested.Hash(), "signerCount", update.Signature.SignerCount())
|
||||
eventCallback(request.Event{Type: sync.EvNewOptimisticUpdate, Data: update})
|
||||
},
|
||||
OnFinality: func(head types.FinalityUpdate) {
|
||||
log.Debug("New finality update received", "slot", head.Attested.Slot, "blockRoot", head.Attested.Hash(), "signerCount", head.Signature.SignerCount())
|
||||
eventCallback(request.Event{Type: sync.EvNewFinalityUpdate, Data: head})
|
||||
OnFinality: func(update types.FinalityUpdate) {
|
||||
log.Debug("New finality update received", "slot", update.Attested.Slot, "blockRoot", update.Attested.Hash(), "signerCount", update.Signature.SignerCount())
|
||||
eventCallback(request.Event{Type: sync.EvNewFinalityUpdate, Data: update})
|
||||
},
|
||||
OnError: func(err error) {
|
||||
log.Warn("Head event stream error", "err", err)
|
||||
|
|
@ -73,14 +73,19 @@ func (s *ApiServer) SendRequest(id request.ID, req request.Request) {
|
|||
r.Updates, r.Committees, err = s.api.GetBestUpdatesAndCommittees(data.FirstPeriod, data.Count)
|
||||
resp = r
|
||||
case sync.ReqHeader:
|
||||
var r sync.RespHeader
|
||||
log.Debug("Beacon API: requesting header", "reqid", id, "hash", common.Hash(data))
|
||||
resp, err = s.api.GetHeader(common.Hash(data))
|
||||
r.Header, r.Canonical, r.Finalized, err = s.api.GetHeader(common.Hash(data))
|
||||
resp = r
|
||||
case sync.ReqCheckpointData:
|
||||
log.Debug("Beacon API: requesting checkpoint data", "reqid", id, "hash", common.Hash(data))
|
||||
resp, err = s.api.GetCheckpointData(common.Hash(data))
|
||||
case sync.ReqBeaconBlock:
|
||||
log.Debug("Beacon API: requesting block", "reqid", id, "hash", common.Hash(data))
|
||||
resp, err = s.api.GetBeaconBlock(common.Hash(data))
|
||||
case sync.ReqFinality:
|
||||
log.Debug("Beacon API: requesting finality update")
|
||||
resp, err = s.api.GetFinalityUpdate()
|
||||
default:
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +93,7 @@ func (s *ApiServer) SendRequest(id request.ID, req request.Request) {
|
|||
log.Warn("Beacon API request failed", "type", reflect.TypeOf(req), "reqid", id, "err", err)
|
||||
s.eventCallback(request.Event{Type: request.EvFail, Data: request.RequestResponse{ID: id, Request: req}})
|
||||
} else {
|
||||
log.Debug("Beacon API request answered", "type", reflect.TypeOf(req), "reqid", id)
|
||||
s.eventCallback(request.Event{Type: request.EvResponse, Data: request.RequestResponse{ID: id, Request: req, Response: resp}})
|
||||
}
|
||||
}()
|
||||
|
|
@ -101,3 +107,8 @@ func (s *ApiServer) Unsubscribe() {
|
|||
s.unsubscribe = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Name implements request.Server
|
||||
func (s *ApiServer) Name() string {
|
||||
return s.api.url
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/beacon/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -184,46 +185,56 @@ func (api *BeaconLightApi) GetBestUpdatesAndCommittees(firstPeriod, count uint64
|
|||
return updates, committees, nil
|
||||
}
|
||||
|
||||
// GetOptimisticHeadUpdate fetches a signed header based on the latest available
|
||||
// optimistic update. Note that the signature should be verified by the caller
|
||||
// as its validity depends on the update chain.
|
||||
// GetOptimisticUpdate fetches the latest available optimistic update.
|
||||
// Note that the signature should be verified by the caller as its validity
|
||||
// depends on the update chain.
|
||||
//
|
||||
// See data structure definition here:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientoptimisticupdate
|
||||
func (api *BeaconLightApi) GetOptimisticHeadUpdate() (types.SignedHeader, error) {
|
||||
func (api *BeaconLightApi) GetOptimisticUpdate() (types.OptimisticUpdate, error) {
|
||||
resp, err := api.httpGet("/eth/v1/beacon/light_client/optimistic_update")
|
||||
if err != nil {
|
||||
return types.SignedHeader{}, err
|
||||
return types.OptimisticUpdate{}, err
|
||||
}
|
||||
return decodeOptimisticHeadUpdate(resp)
|
||||
return decodeOptimisticUpdate(resp)
|
||||
}
|
||||
|
||||
func decodeOptimisticHeadUpdate(enc []byte) (types.SignedHeader, error) {
|
||||
func decodeOptimisticUpdate(enc []byte) (types.OptimisticUpdate, error) {
|
||||
var data struct {
|
||||
Version string
|
||||
Data struct {
|
||||
Header jsonBeaconHeader `json:"attested_header"`
|
||||
Attested jsonHeaderWithExecProof `json:"attested_header"`
|
||||
Aggregate types.SyncAggregate `json:"sync_aggregate"`
|
||||
SignatureSlot common.Decimal `json:"signature_slot"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(enc, &data); err != nil {
|
||||
return types.SignedHeader{}, err
|
||||
return types.OptimisticUpdate{}, err
|
||||
}
|
||||
if data.Data.Header.Beacon.StateRoot == (common.Hash{}) {
|
||||
// Decode the execution payload headers.
|
||||
attestedExecHeader, err := types.ExecutionHeaderFromJSON(data.Version, data.Data.Attested.Execution)
|
||||
if err != nil {
|
||||
return types.OptimisticUpdate{}, fmt.Errorf("invalid attested header: %v", err)
|
||||
}
|
||||
if data.Data.Attested.Beacon.StateRoot == (common.Hash{}) {
|
||||
// workaround for different event encoding format in Lodestar
|
||||
if err := json.Unmarshal(enc, &data.Data); err != nil {
|
||||
return types.SignedHeader{}, err
|
||||
return types.OptimisticUpdate{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(data.Data.Aggregate.Signers) != params.SyncCommitteeBitmaskSize {
|
||||
return types.SignedHeader{}, errors.New("invalid sync_committee_bits length")
|
||||
return types.OptimisticUpdate{}, errors.New("invalid sync_committee_bits length")
|
||||
}
|
||||
if len(data.Data.Aggregate.Signature) != params.BLSSignatureSize {
|
||||
return types.SignedHeader{}, errors.New("invalid sync_committee_signature length")
|
||||
return types.OptimisticUpdate{}, errors.New("invalid sync_committee_signature length")
|
||||
}
|
||||
return types.SignedHeader{
|
||||
Header: data.Data.Header.Beacon,
|
||||
return types.OptimisticUpdate{
|
||||
Attested: types.HeaderWithExecProof{
|
||||
Header: data.Data.Attested.Beacon,
|
||||
PayloadHeader: attestedExecHeader,
|
||||
PayloadBranch: data.Data.Attested.ExecutionBranch,
|
||||
},
|
||||
Signature: data.Data.Aggregate,
|
||||
SignatureSlot: uint64(data.Data.SignatureSlot),
|
||||
}, nil
|
||||
|
|
@ -291,7 +302,9 @@ func decodeFinalityUpdate(enc []byte) (types.FinalityUpdate, error) {
|
|||
|
||||
// GetHeader fetches and validates the beacon header with the given blockRoot.
|
||||
// If blockRoot is null hash then the latest head header is fetched.
|
||||
func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, error) {
|
||||
// The values of the canonical and finalized flags are also returned. Note that
|
||||
// these flags are not validated.
|
||||
func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, bool, bool, error) {
|
||||
var blockId string
|
||||
if blockRoot == (common.Hash{}) {
|
||||
blockId = "head"
|
||||
|
|
@ -300,10 +313,11 @@ func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, error
|
|||
}
|
||||
resp, err := api.httpGetf("/eth/v1/beacon/headers/%s", blockId)
|
||||
if err != nil {
|
||||
return types.Header{}, err
|
||||
return types.Header{}, false, false, err
|
||||
}
|
||||
|
||||
var data struct {
|
||||
Finalized bool `json:"finalized"`
|
||||
Data struct {
|
||||
Root common.Hash `json:"root"`
|
||||
Canonical bool `json:"canonical"`
|
||||
|
|
@ -314,16 +328,16 @@ func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, error
|
|||
} `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(resp, &data); err != nil {
|
||||
return types.Header{}, err
|
||||
return types.Header{}, false, false, err
|
||||
}
|
||||
header := data.Data.Header.Message
|
||||
if blockRoot == (common.Hash{}) {
|
||||
blockRoot = data.Data.Root
|
||||
}
|
||||
if header.Hash() != blockRoot {
|
||||
return types.Header{}, errors.New("retrieved beacon header root does not match")
|
||||
return types.Header{}, false, false, errors.New("retrieved beacon header root does not match")
|
||||
}
|
||||
return header, nil
|
||||
return header, data.Data.Canonical, data.Finalized, nil
|
||||
}
|
||||
|
||||
// GetCheckpointData fetches and validates bootstrap data belonging to the given checkpoint.
|
||||
|
|
@ -408,7 +422,7 @@ func decodeHeadEvent(enc []byte) (uint64, common.Hash, error) {
|
|||
|
||||
type HeadEventListener struct {
|
||||
OnNewHead func(slot uint64, blockRoot common.Hash)
|
||||
OnSignedHead func(head types.SignedHeader)
|
||||
OnOptimistic func(head types.OptimisticUpdate)
|
||||
OnFinality func(head types.FinalityUpdate)
|
||||
OnError func(err error)
|
||||
}
|
||||
|
|
@ -446,21 +460,35 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
|||
defer wg.Done()
|
||||
|
||||
// Request initial data.
|
||||
if head, err := api.GetHeader(common.Hash{}); err == nil {
|
||||
log.Trace("Requesting initial head header")
|
||||
if head, _, _, err := api.GetHeader(common.Hash{}); err == nil {
|
||||
log.Trace("Retrieved initial head header", "slot", head.Slot, "hash", head.Hash())
|
||||
listener.OnNewHead(head.Slot, head.Hash())
|
||||
} else {
|
||||
log.Debug("Failed to retrieve initial head header", "error", err)
|
||||
}
|
||||
if signedHead, err := api.GetOptimisticHeadUpdate(); err == nil {
|
||||
listener.OnSignedHead(signedHead)
|
||||
log.Trace("Requesting initial optimistic update")
|
||||
if optimisticUpdate, err := api.GetOptimisticUpdate(); err == nil {
|
||||
log.Trace("Retrieved initial optimistic update", "slot", optimisticUpdate.Attested.Slot, "hash", optimisticUpdate.Attested.Hash())
|
||||
listener.OnOptimistic(optimisticUpdate)
|
||||
} else {
|
||||
log.Debug("Failed to retrieve initial optimistic update", "error", err)
|
||||
}
|
||||
log.Trace("Requesting initial finality update")
|
||||
if finalityUpdate, err := api.GetFinalityUpdate(); err == nil {
|
||||
log.Trace("Retrieved initial finality update", "slot", finalityUpdate.Finalized.Slot, "hash", finalityUpdate.Finalized.Hash())
|
||||
listener.OnFinality(finalityUpdate)
|
||||
} else {
|
||||
log.Debug("Failed to retrieve initial finality update", "error", err)
|
||||
}
|
||||
|
||||
log.Trace("Starting event stream processing loop")
|
||||
// Receive the stream.
|
||||
var stream *eventsource.Stream
|
||||
select {
|
||||
case stream = <-streamCh:
|
||||
case <-ctx.Done():
|
||||
log.Trace("Stopping event stream processing loop")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -471,8 +499,10 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
|||
|
||||
case event, ok := <-stream.Events:
|
||||
if !ok {
|
||||
log.Trace("Event stream closed")
|
||||
return
|
||||
}
|
||||
log.Trace("New event received from event stream", "type", event.Event())
|
||||
switch event.Event() {
|
||||
case "head":
|
||||
slot, blockRoot, err := decodeHeadEvent([]byte(event.Data()))
|
||||
|
|
@ -482,9 +512,9 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
|||
listener.OnError(fmt.Errorf("error decoding head event: %v", err))
|
||||
}
|
||||
case "light_client_optimistic_update":
|
||||
signedHead, err := decodeOptimisticHeadUpdate([]byte(event.Data()))
|
||||
optimisticUpdate, err := decodeOptimisticUpdate([]byte(event.Data()))
|
||||
if err == nil {
|
||||
listener.OnSignedHead(signedHead)
|
||||
listener.OnOptimistic(optimisticUpdate)
|
||||
} else {
|
||||
listener.OnError(fmt.Errorf("error decoding optimistic update event: %v", err))
|
||||
}
|
||||
|
|
@ -518,7 +548,8 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
|||
// established. It can only return nil when the context is canceled.
|
||||
func (api *BeaconLightApi) startEventStream(ctx context.Context, listener *HeadEventListener) *eventsource.Stream {
|
||||
for retry := true; retry; retry = ctxSleep(ctx, 5*time.Second) {
|
||||
path := "/eth/v1/events?topics=head&topics=light_client_optimistic_update&topics=light_client_finality_update"
|
||||
path := "/eth/v1/events?topics=head&topics=light_client_finality_update&topics=light_client_optimistic_update"
|
||||
log.Trace("Sending event subscription request")
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", api.url+path, nil)
|
||||
if err != nil {
|
||||
listener.OnError(fmt.Errorf("error creating event subscription request: %v", err))
|
||||
|
|
@ -532,6 +563,7 @@ func (api *BeaconLightApi) startEventStream(ctx context.Context, listener *HeadE
|
|||
listener.OnError(fmt.Errorf("error creating event subscription: %v", err))
|
||||
continue
|
||||
}
|
||||
log.Trace("Successfully created event stream")
|
||||
return stream
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ type HeadTracker struct {
|
|||
lock sync.RWMutex
|
||||
committeeChain *CommitteeChain
|
||||
minSignerCount int
|
||||
signedHead types.SignedHeader
|
||||
hasSignedHead bool
|
||||
optimisticUpdate types.OptimisticUpdate
|
||||
hasOptimisticUpdate bool
|
||||
finalityUpdate types.FinalityUpdate
|
||||
hasFinalityUpdate bool
|
||||
prefetchHead types.HeadInfo
|
||||
|
|
@ -48,15 +48,15 @@ func NewHeadTracker(committeeChain *CommitteeChain, minSignerCount int) *HeadTra
|
|||
}
|
||||
}
|
||||
|
||||
// ValidatedHead returns the latest validated head.
|
||||
func (h *HeadTracker) ValidatedHead() (types.SignedHeader, bool) {
|
||||
// ValidatedOptimistic returns the latest validated optimistic update.
|
||||
func (h *HeadTracker) ValidatedOptimistic() (types.OptimisticUpdate, bool) {
|
||||
h.lock.RLock()
|
||||
defer h.lock.RUnlock()
|
||||
|
||||
return h.signedHead, h.hasSignedHead
|
||||
return h.optimisticUpdate, h.hasOptimisticUpdate
|
||||
}
|
||||
|
||||
// ValidatedFinality returns the latest validated finality.
|
||||
// ValidatedFinality returns the latest validated finality update.
|
||||
func (h *HeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
||||
h.lock.RLock()
|
||||
defer h.lock.RUnlock()
|
||||
|
|
@ -64,26 +64,36 @@ func (h *HeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
|||
return h.finalityUpdate, h.hasFinalityUpdate
|
||||
}
|
||||
|
||||
// ValidateHead validates the given signed head. If the head is successfully validated
|
||||
// and it is better than the old validated head (higher slot or same slot and more
|
||||
// signers) then ValidatedHead is updated. The boolean return flag signals if
|
||||
// ValidatedHead has been changed.
|
||||
func (h *HeadTracker) ValidateHead(head types.SignedHeader) (bool, error) {
|
||||
// ValidateOptimistic validates the given optimistic update. If the update is
|
||||
// successfully validated and it is better than the old validated update (higher
|
||||
// slot or same slot and more signers) then ValidatedOptimistic is updated.
|
||||
// The boolean return flag signals if ValidatedOptimistic has been changed.
|
||||
func (h *HeadTracker) ValidateOptimistic(update types.OptimisticUpdate) (bool, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
replace, err := h.validate(head, h.signedHead)
|
||||
if err := update.Validate(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
replace, err := h.validate(update.SignedHeader(), h.optimisticUpdate.SignedHeader())
|
||||
if replace {
|
||||
h.signedHead, h.hasSignedHead = head, true
|
||||
h.optimisticUpdate, h.hasOptimisticUpdate = update, true
|
||||
h.changeCounter++
|
||||
}
|
||||
return replace, err
|
||||
}
|
||||
|
||||
// ValidateFinality validates the given finality update. If the update is
|
||||
// successfully validated and it is better than the old validated update (higher
|
||||
// slot or same slot and more signers) then ValidatedFinality is updated.
|
||||
// The boolean return flag signals if ValidatedFinality has been changed.
|
||||
func (h *HeadTracker) ValidateFinality(update types.FinalityUpdate) (bool, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
if err := update.Validate(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
replace, err := h.validate(update.SignedHeader(), h.finalityUpdate.SignedHeader())
|
||||
if replace {
|
||||
h.finalityUpdate, h.hasFinalityUpdate = update, true
|
||||
|
|
@ -142,6 +152,7 @@ func (h *HeadTracker) SetPrefetchHead(head types.HeadInfo) {
|
|||
h.changeCounter++
|
||||
}
|
||||
|
||||
// ChangeCounter implements request.targetData
|
||||
func (h *HeadTracker) ChangeCounter() uint64 {
|
||||
h.lock.RLock()
|
||||
defer h.lock.RUnlock()
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ type Requester interface {
|
|||
// allow new operations.
|
||||
type Scheduler struct {
|
||||
lock sync.Mutex
|
||||
modules []Module // first has highest priority
|
||||
modules []Module // first has the highest priority
|
||||
names map[Module]string
|
||||
servers map[server]struct{}
|
||||
targets map[targetData]uint64
|
||||
|
|
@ -93,7 +93,9 @@ type (
|
|||
// the modules that do not interact with them directly.
|
||||
// In order to make module testing easier, Server interface is used in
|
||||
// events and modules.
|
||||
Server any
|
||||
Server interface {
|
||||
Name() string
|
||||
}
|
||||
Request any
|
||||
Response any
|
||||
ID uint64
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ type testServer struct {
|
|||
canRequest int
|
||||
}
|
||||
|
||||
func (s *testServer) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *testServer) subscribe(eventCb func(Event)) {
|
||||
s.eventCb = eventCb
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ const (
|
|||
// EvResponse or EvFail. Additionally, it may also send application-defined
|
||||
// events that the Modules can interpret.
|
||||
type requestServer interface {
|
||||
Name() string
|
||||
Subscribe(eventCallback func(Event))
|
||||
SendRequest(ID, Request)
|
||||
Unsubscribe()
|
||||
|
|
@ -69,6 +70,7 @@ type requestServer interface {
|
|||
// limit the number of parallel in-flight requests and temporarily disable
|
||||
// new requests based on timeouts and response failures.
|
||||
type server interface {
|
||||
Server
|
||||
subscribe(eventCallback func(Event))
|
||||
canRequestNow() bool
|
||||
sendRequest(Request) ID
|
||||
|
|
@ -138,6 +140,11 @@ type serverWithTimeout struct {
|
|||
lastID ID
|
||||
}
|
||||
|
||||
// Name implements request.Server
|
||||
func (s *serverWithTimeout) Name() string {
|
||||
return s.parent.Name()
|
||||
}
|
||||
|
||||
// init initializes serverWithTimeout
|
||||
func (s *serverWithTimeout) init(clock mclock.Clock) {
|
||||
s.clock = clock
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ type testRequestServer struct {
|
|||
eventCb func(Event)
|
||||
}
|
||||
|
||||
func (rs *testRequestServer) Name() string { return "" }
|
||||
func (rs *testRequestServer) Subscribe(eventCb func(Event)) { rs.eventCb = eventCb }
|
||||
func (rs *testRequestServer) SendRequest(ID, Request) {}
|
||||
func (rs *testRequestServer) Unsubscribe() {}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ package sync
|
|||
import (
|
||||
"github.com/ethereum/go-ethereum/beacon/light/request"
|
||||
"github.com/ethereum/go-ethereum/beacon/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
type headTracker interface {
|
||||
ValidateHead(head types.SignedHeader) (bool, error)
|
||||
ValidateOptimistic(update types.OptimisticUpdate) (bool, error)
|
||||
ValidateFinality(head types.FinalityUpdate) (bool, error)
|
||||
ValidatedFinality() (types.FinalityUpdate, bool)
|
||||
SetPrefetchHead(head types.HeadInfo)
|
||||
}
|
||||
|
||||
|
|
@ -37,9 +39,10 @@ type HeadSync struct {
|
|||
chain committeeChain
|
||||
nextSyncPeriod uint64
|
||||
chainInit bool
|
||||
unvalidatedHeads map[request.Server]types.SignedHeader
|
||||
unvalidatedOptimistic map[request.Server]types.OptimisticUpdate
|
||||
unvalidatedFinality map[request.Server]types.FinalityUpdate
|
||||
serverHeads map[request.Server]types.HeadInfo
|
||||
reqFinalityEpoch map[request.Server]uint64 // next epoch to request finality update
|
||||
headServerCount map[types.HeadInfo]headServerCount
|
||||
headCounter uint64
|
||||
prefetchHead types.HeadInfo
|
||||
|
|
@ -59,73 +62,96 @@ func NewHeadSync(headTracker headTracker, chain committeeChain) *HeadSync {
|
|||
s := &HeadSync{
|
||||
headTracker: headTracker,
|
||||
chain: chain,
|
||||
unvalidatedHeads: make(map[request.Server]types.SignedHeader),
|
||||
unvalidatedOptimistic: make(map[request.Server]types.OptimisticUpdate),
|
||||
unvalidatedFinality: make(map[request.Server]types.FinalityUpdate),
|
||||
serverHeads: make(map[request.Server]types.HeadInfo),
|
||||
headServerCount: make(map[types.HeadInfo]headServerCount),
|
||||
reqFinalityEpoch: make(map[request.Server]uint64),
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Process implements request.Module.
|
||||
func (s *HeadSync) Process(requester request.Requester, events []request.Event) {
|
||||
nextPeriod, chainInit := s.chain.NextSyncPeriod()
|
||||
if nextPeriod != s.nextSyncPeriod || chainInit != s.chainInit {
|
||||
s.nextSyncPeriod, s.chainInit = nextPeriod, chainInit
|
||||
s.processUnvalidatedUpdates()
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
switch event.Type {
|
||||
case EvNewHead:
|
||||
s.setServerHead(event.Server, event.Data.(types.HeadInfo))
|
||||
case EvNewSignedHead:
|
||||
s.newSignedHead(event.Server, event.Data.(types.SignedHeader))
|
||||
case EvNewOptimisticUpdate:
|
||||
update := event.Data.(types.OptimisticUpdate)
|
||||
s.newOptimisticUpdate(event.Server, update)
|
||||
epoch := update.Attested.Epoch()
|
||||
if epoch < s.reqFinalityEpoch[event.Server] {
|
||||
continue
|
||||
}
|
||||
if finality, ok := s.headTracker.ValidatedFinality(); ok && finality.Attested.Header.Epoch() >= epoch {
|
||||
continue
|
||||
}
|
||||
requester.Send(event.Server, ReqFinality{})
|
||||
s.reqFinalityEpoch[event.Server] = epoch + 1
|
||||
case EvNewFinalityUpdate:
|
||||
s.newFinalityUpdate(event.Server, event.Data.(types.FinalityUpdate))
|
||||
case request.EvResponse:
|
||||
_, _, resp := event.RequestInfo()
|
||||
s.newFinalityUpdate(event.Server, resp.(types.FinalityUpdate))
|
||||
case request.EvUnregistered:
|
||||
s.setServerHead(event.Server, types.HeadInfo{})
|
||||
delete(s.serverHeads, event.Server)
|
||||
delete(s.unvalidatedHeads, event.Server)
|
||||
delete(s.unvalidatedOptimistic, event.Server)
|
||||
delete(s.unvalidatedFinality, event.Server)
|
||||
}
|
||||
}
|
||||
|
||||
nextPeriod, chainInit := s.chain.NextSyncPeriod()
|
||||
if nextPeriod != s.nextSyncPeriod || chainInit != s.chainInit {
|
||||
s.nextSyncPeriod, s.chainInit = nextPeriod, chainInit
|
||||
s.processUnvalidated()
|
||||
}
|
||||
}
|
||||
|
||||
// newSignedHead handles received signed head; either validates it if the chain
|
||||
// is properly synced or stores it for further validation.
|
||||
func (s *HeadSync) newSignedHead(server request.Server, signedHead types.SignedHeader) {
|
||||
if !s.chainInit || types.SyncPeriod(signedHead.SignatureSlot) > s.nextSyncPeriod {
|
||||
s.unvalidatedHeads[server] = signedHead
|
||||
// newOptimisticUpdate handles received optimistic update; either validates it if
|
||||
// the chain is properly synced or stores it for further validation.
|
||||
func (s *HeadSync) newOptimisticUpdate(server request.Server, optimisticUpdate types.OptimisticUpdate) {
|
||||
if !s.chainInit || types.SyncPeriod(optimisticUpdate.SignatureSlot) > s.nextSyncPeriod {
|
||||
s.unvalidatedOptimistic[server] = optimisticUpdate
|
||||
return
|
||||
}
|
||||
s.headTracker.ValidateHead(signedHead)
|
||||
if _, err := s.headTracker.ValidateOptimistic(optimisticUpdate); err != nil {
|
||||
log.Debug("Error validating optimistic update", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// newFinalityUpdate handles received finality update; either validates it if the chain
|
||||
// is properly synced or stores it for further validation.
|
||||
// newFinalityUpdate handles received finality update; either validates it if
|
||||
// the chain is properly synced or stores it for further validation.
|
||||
func (s *HeadSync) newFinalityUpdate(server request.Server, finalityUpdate types.FinalityUpdate) {
|
||||
if !s.chainInit || types.SyncPeriod(finalityUpdate.SignatureSlot) > s.nextSyncPeriod {
|
||||
s.unvalidatedFinality[server] = finalityUpdate
|
||||
return
|
||||
}
|
||||
s.headTracker.ValidateFinality(finalityUpdate)
|
||||
if _, err := s.headTracker.ValidateFinality(finalityUpdate); err != nil {
|
||||
log.Debug("Error validating finality update", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// processUnvalidated iterates the list of unvalidated heads and validates
|
||||
// processUnvalidatedUpdates iterates the list of unvalidated updates and validates
|
||||
// those which can be validated.
|
||||
func (s *HeadSync) processUnvalidated() {
|
||||
func (s *HeadSync) processUnvalidatedUpdates() {
|
||||
if !s.chainInit {
|
||||
return
|
||||
}
|
||||
for server, signedHead := range s.unvalidatedHeads {
|
||||
if types.SyncPeriod(signedHead.SignatureSlot) <= s.nextSyncPeriod {
|
||||
s.headTracker.ValidateHead(signedHead)
|
||||
delete(s.unvalidatedHeads, server)
|
||||
for server, optimisticUpdate := range s.unvalidatedOptimistic {
|
||||
if types.SyncPeriod(optimisticUpdate.SignatureSlot) <= s.nextSyncPeriod {
|
||||
if _, err := s.headTracker.ValidateOptimistic(optimisticUpdate); err != nil {
|
||||
log.Debug("Error validating deferred optimistic update", "error", err)
|
||||
}
|
||||
delete(s.unvalidatedOptimistic, server)
|
||||
}
|
||||
}
|
||||
for server, finalityUpdate := range s.unvalidatedFinality {
|
||||
if types.SyncPeriod(finalityUpdate.SignatureSlot) <= s.nextSyncPeriod {
|
||||
s.headTracker.ValidateFinality(finalityUpdate)
|
||||
if _, err := s.headTracker.ValidateFinality(finalityUpdate); err != nil {
|
||||
log.Debug("Error validating deferred finality update", "error", err)
|
||||
}
|
||||
delete(s.unvalidatedFinality, server)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,15 +19,17 @@ package sync
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/beacon/light/request"
|
||||
"github.com/ethereum/go-ethereum/beacon/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var (
|
||||
testServer1 = "testServer1"
|
||||
testServer2 = "testServer2"
|
||||
testServer3 = "testServer3"
|
||||
testServer4 = "testServer4"
|
||||
testServer1 = testServer("testServer1")
|
||||
testServer2 = testServer("testServer2")
|
||||
testServer3 = testServer("testServer3")
|
||||
testServer4 = testServer("testServer4")
|
||||
testServer5 = testServer("testServer5")
|
||||
|
||||
testHead0 = types.HeadInfo{}
|
||||
testHead1 = types.HeadInfo{Slot: 123, BlockRoot: common.Hash{1}}
|
||||
|
|
@ -35,13 +37,27 @@ var (
|
|||
testHead3 = types.HeadInfo{Slot: 124, BlockRoot: common.Hash{3}}
|
||||
testHead4 = types.HeadInfo{Slot: 125, BlockRoot: common.Hash{4}}
|
||||
|
||||
testSHead1 = types.SignedHeader{SignatureSlot: 0x0124, Header: types.Header{Slot: 0x0123, StateRoot: common.Hash{1}}}
|
||||
testSHead2 = types.SignedHeader{SignatureSlot: 0x2010, Header: types.Header{Slot: 0x200e, StateRoot: common.Hash{2}}}
|
||||
// testSHead3 is at the end of period 1 but signed in period 2
|
||||
testSHead3 = types.SignedHeader{SignatureSlot: 0x4000, Header: types.Header{Slot: 0x3fff, StateRoot: common.Hash{3}}}
|
||||
testSHead4 = types.SignedHeader{SignatureSlot: 0x6444, Header: types.Header{Slot: 0x6443, StateRoot: common.Hash{4}}}
|
||||
testOptUpdate1 = types.OptimisticUpdate{SignatureSlot: 0x0124, Attested: types.HeaderWithExecProof{Header: types.Header{Slot: 0x0123, StateRoot: common.Hash{1}}}}
|
||||
testOptUpdate2 = types.OptimisticUpdate{SignatureSlot: 0x2010, Attested: types.HeaderWithExecProof{Header: types.Header{Slot: 0x200e, StateRoot: common.Hash{2}}}}
|
||||
// testOptUpdate3 is at the end of period 1 but signed in period 2
|
||||
testOptUpdate3 = types.OptimisticUpdate{SignatureSlot: 0x4000, Attested: types.HeaderWithExecProof{Header: types.Header{Slot: 0x3fff, StateRoot: common.Hash{3}}}}
|
||||
testOptUpdate4 = types.OptimisticUpdate{SignatureSlot: 0x6444, Attested: types.HeaderWithExecProof{Header: types.Header{Slot: 0x6443, StateRoot: common.Hash{4}}}}
|
||||
)
|
||||
|
||||
func finality(opt types.OptimisticUpdate) types.FinalityUpdate {
|
||||
return types.FinalityUpdate{
|
||||
SignatureSlot: opt.SignatureSlot,
|
||||
Attested: opt.Attested,
|
||||
Finalized: types.HeaderWithExecProof{Header: types.Header{Slot: (opt.Attested.Header.Slot - 64) & uint64(0xffffffffffffffe0)}},
|
||||
}
|
||||
}
|
||||
|
||||
type testServer string
|
||||
|
||||
func (t testServer) Name() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
func TestValidatedHead(t *testing.T) {
|
||||
chain := &TestCommitteeChain{}
|
||||
ht := &TestHeadTracker{}
|
||||
|
|
@ -51,50 +67,66 @@ func TestValidatedHead(t *testing.T) {
|
|||
ht.ExpValidated(t, 0, nil)
|
||||
|
||||
ts.AddServer(testServer1, 1)
|
||||
ts.ServerEvent(EvNewSignedHead, testServer1, testSHead1)
|
||||
ts.Run(1)
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer1, testOptUpdate1)
|
||||
ts.Run(1, testServer1, ReqFinality{})
|
||||
// announced head should be queued because of uninitialized chain
|
||||
ht.ExpValidated(t, 1, nil)
|
||||
|
||||
chain.SetNextSyncPeriod(0) // initialize chain
|
||||
ts.Run(2)
|
||||
// expect previously queued head to be validated
|
||||
ht.ExpValidated(t, 2, []types.SignedHeader{testSHead1})
|
||||
ht.ExpValidated(t, 2, []types.OptimisticUpdate{testOptUpdate1})
|
||||
|
||||
chain.SetNextSyncPeriod(1)
|
||||
ts.ServerEvent(EvNewSignedHead, testServer1, testSHead2)
|
||||
ts.ServerEvent(EvNewFinalityUpdate, testServer1, finality(testOptUpdate2))
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer1, testOptUpdate2)
|
||||
ts.AddServer(testServer2, 1)
|
||||
ts.ServerEvent(EvNewSignedHead, testServer2, testSHead2)
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer2, testOptUpdate2)
|
||||
ts.Run(3)
|
||||
// expect both head announcements to be validated instantly
|
||||
ht.ExpValidated(t, 3, []types.SignedHeader{testSHead2, testSHead2})
|
||||
ht.ExpValidated(t, 3, []types.OptimisticUpdate{testOptUpdate2, testOptUpdate2})
|
||||
|
||||
ts.ServerEvent(EvNewSignedHead, testServer1, testSHead3)
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer1, testOptUpdate3)
|
||||
ts.AddServer(testServer3, 1)
|
||||
ts.ServerEvent(EvNewSignedHead, testServer3, testSHead4)
|
||||
ts.Run(4)
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer3, testOptUpdate4)
|
||||
// finality should be requested from both servers
|
||||
ts.Run(4, testServer1, ReqFinality{}, testServer3, ReqFinality{})
|
||||
// future period announced heads should be queued
|
||||
ht.ExpValidated(t, 4, nil)
|
||||
|
||||
chain.SetNextSyncPeriod(2)
|
||||
ts.Run(5)
|
||||
// testSHead3 can be validated now but not testSHead4
|
||||
ht.ExpValidated(t, 5, []types.SignedHeader{testSHead3})
|
||||
// testOptUpdate3 can be validated now but not testOptUpdate4
|
||||
ht.ExpValidated(t, 5, []types.OptimisticUpdate{testOptUpdate3})
|
||||
|
||||
ts.AddServer(testServer4, 1)
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer4, testOptUpdate3)
|
||||
// new server joined with recent optimistic update but still no finality; should be requested
|
||||
ts.Run(6, testServer4, ReqFinality{})
|
||||
ht.ExpValidated(t, 6, []types.OptimisticUpdate{testOptUpdate3})
|
||||
|
||||
ts.AddServer(testServer5, 1)
|
||||
ts.RequestEvent(request.EvResponse, ts.Request(6, 1), finality(testOptUpdate3))
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer5, testOptUpdate3)
|
||||
// finality update request answered; new server should not be requested
|
||||
ts.Run(7)
|
||||
ht.ExpValidated(t, 7, []types.OptimisticUpdate{testOptUpdate3})
|
||||
|
||||
// server 3 disconnected without proving period 3, its announced head should be dropped
|
||||
ts.RemoveServer(testServer3)
|
||||
ts.Run(6)
|
||||
ht.ExpValidated(t, 6, nil)
|
||||
ts.Run(8)
|
||||
ht.ExpValidated(t, 8, nil)
|
||||
|
||||
chain.SetNextSyncPeriod(3)
|
||||
ts.Run(7)
|
||||
// testSHead4 could be validated now but it's not queued by any registered server
|
||||
ht.ExpValidated(t, 7, nil)
|
||||
ts.Run(9)
|
||||
// testOptUpdate4 could be validated now but it's not queued by any registered server
|
||||
ht.ExpValidated(t, 9, nil)
|
||||
|
||||
ts.ServerEvent(EvNewSignedHead, testServer2, testSHead4)
|
||||
ts.Run(8)
|
||||
// now testSHead4 should be validated
|
||||
ht.ExpValidated(t, 8, []types.SignedHeader{testSHead4})
|
||||
ts.ServerEvent(EvNewFinalityUpdate, testServer2, finality(testOptUpdate4))
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer2, testOptUpdate4)
|
||||
ts.Run(10)
|
||||
// now testOptUpdate4 should be validated
|
||||
ht.ExpValidated(t, 10, []types.OptimisticUpdate{testOptUpdate4})
|
||||
}
|
||||
|
||||
func TestPrefetchHead(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ func (ts *TestScheduler) Run(testIndex int, exp ...any) {
|
|||
if count == 0 {
|
||||
continue
|
||||
}
|
||||
ts.t.Errorf("Missing %d Server.Fail(s) from server %s in test case #%d", count, server.(string), testIndex)
|
||||
ts.t.Errorf("Missing %d Server.Fail(s) from server %s in test case #%d", count, server.Name(), testIndex)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ts.sent[testIndex], expReqs) {
|
||||
|
|
@ -104,7 +104,7 @@ func (ts *TestScheduler) Send(server request.Server, req request.Request) reques
|
|||
|
||||
func (ts *TestScheduler) Fail(server request.Server, desc string) {
|
||||
if ts.expFail[server] == 0 {
|
||||
ts.t.Errorf("Unexpected Fail from server %s in test case #%d: %s", server.(string), ts.testIndex, desc)
|
||||
ts.t.Errorf("Unexpected Fail from server %s in test case #%d: %s", server.Name(), ts.testIndex, desc)
|
||||
return
|
||||
}
|
||||
ts.expFail[server]--
|
||||
|
|
@ -212,32 +212,37 @@ func (tc *TestCommitteeChain) ExpNextSyncPeriod(t *testing.T, expNsp uint64) {
|
|||
|
||||
type TestHeadTracker struct {
|
||||
phead types.HeadInfo
|
||||
validated []types.SignedHeader
|
||||
validated []types.OptimisticUpdate
|
||||
finality types.FinalityUpdate
|
||||
}
|
||||
|
||||
func (ht *TestHeadTracker) ValidateHead(head types.SignedHeader) (bool, error) {
|
||||
ht.validated = append(ht.validated, head)
|
||||
func (ht *TestHeadTracker) ValidateOptimistic(update types.OptimisticUpdate) (bool, error) {
|
||||
ht.validated = append(ht.validated, update)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// TODO add test case for finality
|
||||
func (ht *TestHeadTracker) ValidateFinality(head types.FinalityUpdate) (bool, error) {
|
||||
func (ht *TestHeadTracker) ValidateFinality(update types.FinalityUpdate) (bool, error) {
|
||||
ht.finality = update
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (ht *TestHeadTracker) ExpValidated(t *testing.T, tci int, expHeads []types.SignedHeader) {
|
||||
func (ht *TestHeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
||||
return ht.finality, ht.finality.Attested.Header != (types.Header{})
|
||||
}
|
||||
|
||||
func (ht *TestHeadTracker) ExpValidated(t *testing.T, tci int, expHeads []types.OptimisticUpdate) {
|
||||
for i, expHead := range expHeads {
|
||||
if i >= len(ht.validated) {
|
||||
t.Errorf("Missing validated head in test case #%d index #%d (expected {slot %d blockRoot %x}, got none)", tci, i, expHead.Header.Slot, expHead.Header.Hash())
|
||||
t.Errorf("Missing validated head in test case #%d index #%d (expected {slot %d blockRoot %x}, got none)", tci, i, expHead.Attested.Header.Slot, expHead.Attested.Header.Hash())
|
||||
continue
|
||||
}
|
||||
if ht.validated[i] != expHead {
|
||||
vhead := ht.validated[i].Header
|
||||
t.Errorf("Wrong validated head in test case #%d index #%d (expected {slot %d blockRoot %x}, got {slot %d blockRoot %x})", tci, i, expHead.Header.Slot, expHead.Header.Hash(), vhead.Slot, vhead.Hash())
|
||||
if !reflect.DeepEqual(ht.validated[i], expHead) {
|
||||
vhead := ht.validated[i].Attested.Header
|
||||
t.Errorf("Wrong validated head in test case #%d index #%d (expected {slot %d blockRoot %x}, got {slot %d blockRoot %x})", tci, i, expHead.Attested.Header.Slot, expHead.Attested.Header.Hash(), vhead.Slot, vhead.Hash())
|
||||
}
|
||||
}
|
||||
for i := len(expHeads); i < len(ht.validated); i++ {
|
||||
vhead := ht.validated[i].Header
|
||||
vhead := ht.validated[i].Attested.Header
|
||||
t.Errorf("Unexpected validated head in test case #%d index #%d (expected none, got {slot %d blockRoot %x})", tci, i, vhead.Slot, vhead.Hash())
|
||||
}
|
||||
ht.validated = nil
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
var (
|
||||
EvNewHead = &request.EventType{Name: "newHead"} // data: types.HeadInfo
|
||||
EvNewSignedHead = &request.EventType{Name: "newSignedHead"} // data: types.SignedHeader
|
||||
EvNewOptimisticUpdate = &request.EventType{Name: "newOptimisticUpdate"} // data: types.OptimisticUpdate
|
||||
EvNewFinalityUpdate = &request.EventType{Name: "newFinalityUpdate"} // data: types.FinalityUpdate
|
||||
)
|
||||
|
||||
|
|
@ -37,6 +37,11 @@ type (
|
|||
Committees []*types.SerializedSyncCommittee
|
||||
}
|
||||
ReqHeader common.Hash
|
||||
RespHeader struct {
|
||||
Header types.Header
|
||||
Canonical, Finalized bool
|
||||
}
|
||||
ReqCheckpointData common.Hash
|
||||
ReqBeaconBlock common.Hash
|
||||
ReqFinality struct{}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/beacon/light"
|
||||
"github.com/ethereum/go-ethereum/beacon/light/request"
|
||||
"github.com/ethereum/go-ethereum/beacon/params"
|
||||
"github.com/ethereum/go-ethereum/beacon/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
|
@ -42,6 +43,31 @@ type CheckpointInit struct {
|
|||
checkpointHash common.Hash
|
||||
locked request.ServerAndID
|
||||
initialized bool
|
||||
// per-server state is used to track the state of requesting checkpoint header
|
||||
// info. Part of this info (canonical and finalized state) is not validated
|
||||
// and therefore it is requested from each server separately after it has
|
||||
// reported a missing checkpoint (which is also not validated info).
|
||||
serverState map[request.Server]serverState
|
||||
// the following fields are used to determine whether the checkpoint is on
|
||||
// epoch boundary. This information is validated and therefore stored globally.
|
||||
parentHash common.Hash
|
||||
hasEpochInfo, epochBoundary bool
|
||||
cpSlot, parentSlot uint64
|
||||
}
|
||||
|
||||
const (
|
||||
ssDefault = iota // no action yet or checkpoint requested
|
||||
ssNeedHeader // checkpoint req failed, need cp header
|
||||
ssHeaderRequested // cp header requested
|
||||
ssNeedParent // cp header slot %32 != 0, need parent to check epoch boundary
|
||||
ssParentRequested // cp parent header requested
|
||||
ssPrintStatus // has all necessary info, print log message if init still not successful
|
||||
ssDone // log message printed, no more action required
|
||||
)
|
||||
|
||||
type serverState struct {
|
||||
state int
|
||||
hasHeader, canonical, finalized bool // stored per server because not validated
|
||||
}
|
||||
|
||||
// NewCheckpointInit creates a new CheckpointInit.
|
||||
|
|
@ -49,40 +75,113 @@ func NewCheckpointInit(chain committeeChain, checkpointHash common.Hash) *Checkp
|
|||
return &CheckpointInit{
|
||||
chain: chain,
|
||||
checkpointHash: checkpointHash,
|
||||
serverState: make(map[request.Server]serverState),
|
||||
}
|
||||
}
|
||||
|
||||
// Process implements request.Module.
|
||||
func (s *CheckpointInit) Process(requester request.Requester, events []request.Event) {
|
||||
for _, event := range events {
|
||||
if !event.IsRequestEvent() {
|
||||
continue
|
||||
if s.initialized {
|
||||
return
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
switch event.Type {
|
||||
case request.EvResponse, request.EvFail, request.EvTimeout:
|
||||
sid, req, resp := event.RequestInfo()
|
||||
if s.locked == sid {
|
||||
s.locked = request.ServerAndID{}
|
||||
}
|
||||
if event.Type == request.EvTimeout {
|
||||
continue
|
||||
}
|
||||
switch s.serverState[sid.Server].state {
|
||||
case ssDefault:
|
||||
if resp != nil {
|
||||
if checkpoint := resp.(*types.BootstrapData); checkpoint.Header.Hash() == common.Hash(req.(ReqCheckpointData)) {
|
||||
s.chain.CheckpointInit(*checkpoint)
|
||||
s.initialized = true
|
||||
return
|
||||
}
|
||||
|
||||
requester.Fail(event.Server, "invalid checkpoint data")
|
||||
}
|
||||
s.serverState[sid.Server] = serverState{state: ssNeedHeader}
|
||||
case ssHeaderRequested:
|
||||
if resp == nil {
|
||||
s.serverState[sid.Server] = serverState{state: ssPrintStatus}
|
||||
continue
|
||||
}
|
||||
newState := serverState{
|
||||
hasHeader: true,
|
||||
canonical: resp.(RespHeader).Canonical,
|
||||
finalized: resp.(RespHeader).Finalized,
|
||||
}
|
||||
s.cpSlot, s.parentHash = resp.(RespHeader).Header.Slot, resp.(RespHeader).Header.ParentRoot
|
||||
if s.cpSlot%params.EpochLength == 0 {
|
||||
s.hasEpochInfo, s.epochBoundary = true, true
|
||||
}
|
||||
if s.hasEpochInfo {
|
||||
newState.state = ssPrintStatus
|
||||
} else {
|
||||
newState.state = ssNeedParent
|
||||
}
|
||||
s.serverState[sid.Server] = newState
|
||||
case ssParentRequested:
|
||||
s.parentSlot = resp.(RespHeader).Header.Slot
|
||||
s.hasEpochInfo, s.epochBoundary = true, s.cpSlot/params.EpochLength > s.parentSlot/params.EpochLength
|
||||
newState := s.serverState[sid.Server]
|
||||
newState.state = ssPrintStatus
|
||||
s.serverState[sid.Server] = newState
|
||||
}
|
||||
|
||||
case request.EvUnregistered:
|
||||
delete(s.serverState, event.Server)
|
||||
}
|
||||
}
|
||||
|
||||
// start a request if possible
|
||||
if s.initialized || s.locked != (request.ServerAndID{}) {
|
||||
return
|
||||
}
|
||||
cs := requester.CanSendTo()
|
||||
if len(cs) == 0 {
|
||||
return
|
||||
}
|
||||
server := cs[0]
|
||||
for _, server := range requester.CanSendTo() {
|
||||
switch s.serverState[server].state {
|
||||
case ssDefault:
|
||||
if s.locked == (request.ServerAndID{}) {
|
||||
id := requester.Send(server, ReqCheckpointData(s.checkpointHash))
|
||||
s.locked = request.ServerAndID{Server: server, ID: id}
|
||||
}
|
||||
case ssNeedHeader:
|
||||
requester.Send(server, ReqHeader(s.checkpointHash))
|
||||
newState := s.serverState[server]
|
||||
newState.state = ssHeaderRequested
|
||||
s.serverState[server] = newState
|
||||
case ssNeedParent:
|
||||
requester.Send(server, ReqHeader(s.parentHash))
|
||||
newState := s.serverState[server]
|
||||
newState.state = ssParentRequested
|
||||
s.serverState[server] = newState
|
||||
}
|
||||
}
|
||||
|
||||
// print log message if necessary
|
||||
for server, state := range s.serverState {
|
||||
if state.state != ssPrintStatus {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case !state.hasHeader:
|
||||
log.Error("blsync: checkpoint block is not available, reported as unknown", "server", server.Name())
|
||||
case !state.canonical:
|
||||
log.Error("blsync: checkpoint block is not available, reported as non-canonical", "server", server.Name())
|
||||
case !s.hasEpochInfo:
|
||||
// should be available if hasHeader is true and state is ssPrintStatus
|
||||
panic("checkpoint epoch info not available when printing retrieval status")
|
||||
case !s.epochBoundary:
|
||||
log.Error("blsync: checkpoint block is not first of epoch", "slot", s.cpSlot, "parent", s.parentSlot, "server", server.Name())
|
||||
case !state.finalized:
|
||||
log.Error("blsync: checkpoint block is reported as non-finalized", "server", server.Name())
|
||||
default:
|
||||
log.Error("blsync: checkpoint not available, but reported as finalized; specified checkpoint hash might be too old", "server", server.Name())
|
||||
}
|
||||
s.serverState[server] = serverState{state: ssDone}
|
||||
}
|
||||
}
|
||||
|
||||
// ForwardUpdateSync implements request.Module; it fetches updates between the
|
||||
|
|
@ -221,9 +320,9 @@ func (s *ForwardUpdateSync) Process(requester request.Requester, events []reques
|
|||
if !queued {
|
||||
s.unlockRange(sid, req)
|
||||
}
|
||||
case EvNewSignedHead:
|
||||
signedHead := event.Data.(types.SignedHeader)
|
||||
s.nextSyncPeriod[event.Server] = types.SyncPeriod(signedHead.SignatureSlot + 256)
|
||||
case EvNewOptimisticUpdate:
|
||||
update := event.Data.(types.OptimisticUpdate)
|
||||
s.nextSyncPeriod[event.Server] = types.SyncPeriod(update.SignatureSlot + 256)
|
||||
case request.EvUnregistered:
|
||||
delete(s.nextSyncPeriod, event.Server)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,9 +68,9 @@ func TestUpdateSyncParallel(t *testing.T) {
|
|||
ts := NewTestScheduler(t, updateSync)
|
||||
// add 2 servers, head at period 100; allow 3-3 parallel requests for each
|
||||
ts.AddServer(testServer1, 3)
|
||||
ts.ServerEvent(EvNewSignedHead, testServer1, types.SignedHeader{SignatureSlot: 0x2000*100 + 0x1000})
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer1, types.OptimisticUpdate{SignatureSlot: 0x2000*100 + 0x1000})
|
||||
ts.AddServer(testServer2, 3)
|
||||
ts.ServerEvent(EvNewSignedHead, testServer2, types.SignedHeader{SignatureSlot: 0x2000*100 + 0x1000})
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer2, types.OptimisticUpdate{SignatureSlot: 0x2000*100 + 0x1000})
|
||||
|
||||
// expect 6 requests to be sent
|
||||
ts.Run(1,
|
||||
|
|
@ -150,11 +150,11 @@ func TestUpdateSyncDifferentHeads(t *testing.T) {
|
|||
ts := NewTestScheduler(t, updateSync)
|
||||
// add 3 servers with different announced head periods
|
||||
ts.AddServer(testServer1, 1)
|
||||
ts.ServerEvent(EvNewSignedHead, testServer1, types.SignedHeader{SignatureSlot: 0x2000*15 + 0x1000})
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer1, types.OptimisticUpdate{SignatureSlot: 0x2000*15 + 0x1000})
|
||||
ts.AddServer(testServer2, 1)
|
||||
ts.ServerEvent(EvNewSignedHead, testServer2, types.SignedHeader{SignatureSlot: 0x2000*16 + 0x1000})
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer2, types.OptimisticUpdate{SignatureSlot: 0x2000*16 + 0x1000})
|
||||
ts.AddServer(testServer3, 1)
|
||||
ts.ServerEvent(EvNewSignedHead, testServer3, types.SignedHeader{SignatureSlot: 0x2000*17 + 0x1000})
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer3, types.OptimisticUpdate{SignatureSlot: 0x2000*17 + 0x1000})
|
||||
|
||||
// expect request to the best announced head
|
||||
ts.Run(1, testServer3, ReqUpdates{FirstPeriod: 10, Count: 7})
|
||||
|
|
@ -190,7 +190,7 @@ func TestUpdateSyncDifferentHeads(t *testing.T) {
|
|||
|
||||
// a new server is registered with announced head period 17
|
||||
ts.AddServer(testServer4, 1)
|
||||
ts.ServerEvent(EvNewSignedHead, testServer4, types.SignedHeader{SignatureSlot: 0x2000*17 + 0x1000})
|
||||
ts.ServerEvent(EvNewOptimisticUpdate, testServer4, types.OptimisticUpdate{SignatureSlot: 0x2000*17 + 0x1000})
|
||||
// expect request to sync one more period
|
||||
ts.Run(7, testServer4, ReqUpdates{FirstPeriod: 16, Count: 1})
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ package types
|
|||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -27,6 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/beacon/merkle"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
|
@ -34,6 +37,8 @@ import (
|
|||
// across signing different data structures.
|
||||
const syncCommitteeDomain = 7
|
||||
|
||||
var knownForks = []string{"GENESIS", "ALTAIR", "BELLATRIX", "CAPELLA", "DENEB"}
|
||||
|
||||
// Fork describes a single beacon chain fork and also stores the calculated
|
||||
// signature domain used after this fork.
|
||||
type Fork struct {
|
||||
|
|
@ -46,6 +51,9 @@ type Fork struct {
|
|||
// Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types
|
||||
Version []byte
|
||||
|
||||
// index in list of known forks or MaxInt if unknown
|
||||
knownIndex int
|
||||
|
||||
// calculated by computeDomain, based on fork version and genesis validators root
|
||||
domain merkle.Value
|
||||
}
|
||||
|
|
@ -101,7 +109,12 @@ func (f Forks) SigningRoot(header Header) (common.Hash, error) {
|
|||
|
||||
func (f Forks) Len() int { return len(f) }
|
||||
func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
||||
func (f Forks) Less(i, j int) bool { return f[i].Epoch < f[j].Epoch }
|
||||
func (f Forks) Less(i, j int) bool {
|
||||
if f[i].Epoch != f[j].Epoch {
|
||||
return f[i].Epoch < f[j].Epoch
|
||||
}
|
||||
return f[i].knownIndex < f[j].knownIndex
|
||||
}
|
||||
|
||||
// ChainConfig contains the beacon chain configuration.
|
||||
type ChainConfig struct {
|
||||
|
|
@ -122,16 +135,22 @@ func (c *ChainConfig) ForkAtEpoch(epoch uint64) Fork {
|
|||
|
||||
// AddFork adds a new item to the list of forks.
|
||||
func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainConfig {
|
||||
knownIndex := slices.Index(knownForks, name)
|
||||
if knownIndex == -1 {
|
||||
knownIndex = math.MaxInt // assume that the unknown fork happens after the known ones
|
||||
if epoch != math.MaxUint64 {
|
||||
log.Warn("Unknown fork in config.yaml", "fork name", name, "known forks", knownForks)
|
||||
}
|
||||
}
|
||||
fork := &Fork{
|
||||
Name: name,
|
||||
Epoch: epoch,
|
||||
Version: version,
|
||||
knownIndex: knownIndex,
|
||||
}
|
||||
fork.computeDomain(c.GenesisValidatorsRoot)
|
||||
|
||||
c.Forks = append(c.Forks, fork)
|
||||
sort.Sort(c.Forks)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
|
|
@ -181,6 +200,5 @@ func (c *ChainConfig) LoadForks(path string) error {
|
|||
for name := range versions {
|
||||
return fmt.Errorf("epoch number missing for fork %q in beacon chain config file", name)
|
||||
}
|
||||
sort.Sort(c.Forks)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,12 +63,9 @@ func convertPayload[T payloadType](payload T, parentRoot *zrntcommon.Root) (*typ
|
|||
panic("unsupported block type")
|
||||
}
|
||||
|
||||
block := types.NewBlockWithHeader(&header)
|
||||
block = block.WithBody(transactions, nil)
|
||||
block = block.WithWithdrawals(withdrawals)
|
||||
hash := block.Hash()
|
||||
if hash != expectedHash {
|
||||
return block, fmt.Errorf("Sanity check failed, payload hash does not match (expected %x, got %x)", expectedHash, hash)
|
||||
block := types.NewBlockWithHeader(&header).WithBody(types.Body{Transactions: transactions, Withdrawals: withdrawals})
|
||||
if hash := block.Hash(); hash != expectedHash {
|
||||
return nil, fmt.Errorf("sanity check failed, payload hash does not match (expected %x, got %x)", expectedHash, hash)
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/beacon/merkle"
|
||||
"github.com/ethereum/go-ethereum/beacon/params"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
ctypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// HeadInfo represents an unvalidated new head announcement.
|
||||
|
|
@ -142,17 +142,57 @@ func (u UpdateScore) BetterThan(w UpdateScore) bool {
|
|||
return u.SignerCount > w.SignerCount
|
||||
}
|
||||
|
||||
// HeaderWithExecProof contains a beacon header and proves the belonging execution
|
||||
// payload header with a Merkle proof.
|
||||
type HeaderWithExecProof struct {
|
||||
Header
|
||||
PayloadHeader *ExecutionHeader
|
||||
PayloadBranch merkle.Values
|
||||
}
|
||||
|
||||
// Validate verifies the Merkle proof of the execution payload header.
|
||||
func (h *HeaderWithExecProof) Validate() error {
|
||||
payloadRoot := h.PayloadHeader.PayloadRoot()
|
||||
return merkle.VerifyProof(h.BodyRoot, params.BodyIndexExecPayload, h.PayloadBranch, payloadRoot)
|
||||
return merkle.VerifyProof(h.BodyRoot, params.BodyIndexExecPayload, h.PayloadBranch, h.PayloadHeader.PayloadRoot())
|
||||
}
|
||||
|
||||
// OptimisticUpdate proves sync committee commitment on the attested beacon header.
|
||||
// It also proves the belonging execution payload header with a Merkle proof.
|
||||
//
|
||||
// See data structure definition here:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientoptimisticupdate
|
||||
type OptimisticUpdate struct {
|
||||
Attested HeaderWithExecProof
|
||||
// Sync committee BLS signature aggregate
|
||||
Signature SyncAggregate
|
||||
// Slot in which the signature has been created (newer than Header.Slot,
|
||||
// determines the signing sync committee)
|
||||
SignatureSlot uint64
|
||||
}
|
||||
|
||||
// SignedHeader returns the signed attested header of the update.
|
||||
func (u *OptimisticUpdate) SignedHeader() SignedHeader {
|
||||
return SignedHeader{
|
||||
Header: u.Attested.Header,
|
||||
Signature: u.Signature,
|
||||
SignatureSlot: u.SignatureSlot,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate verifies the Merkle proof proving the execution payload header.
|
||||
// Note that the sync committee signature of the attested header should be
|
||||
// verified separately by a synced committee chain.
|
||||
func (u *OptimisticUpdate) Validate() error {
|
||||
return u.Attested.Validate()
|
||||
}
|
||||
|
||||
// FinalityUpdate proves a finalized beacon header by a sync committee commitment
|
||||
// on an attested beacon header, referring to the latest finalized header with a
|
||||
// Merkle proof.
|
||||
// It also proves the execution payload header belonging to both the attested and
|
||||
// the finalized beacon header with Merkle proofs.
|
||||
//
|
||||
// See data structure definition here:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientfinalityupdate
|
||||
type FinalityUpdate struct {
|
||||
Attested, Finalized HeaderWithExecProof
|
||||
FinalityBranch merkle.Values
|
||||
|
|
@ -163,6 +203,7 @@ type FinalityUpdate struct {
|
|||
SignatureSlot uint64
|
||||
}
|
||||
|
||||
// SignedHeader returns the signed attested header of the update.
|
||||
func (u *FinalityUpdate) SignedHeader() SignedHeader {
|
||||
return SignedHeader{
|
||||
Header: u.Attested.Header,
|
||||
|
|
@ -171,6 +212,10 @@ func (u *FinalityUpdate) SignedHeader() SignedHeader {
|
|||
}
|
||||
}
|
||||
|
||||
// Validate verifies the Merkle proofs proving the finalized beacon header and
|
||||
// the execution payload headers belonging to the attested and finalized headers.
|
||||
// Note that the sync committee signature of the attested header should be
|
||||
// verified separately by a synced committee chain.
|
||||
func (u *FinalityUpdate) Validate() error {
|
||||
if err := u.Attested.Validate(); err != nil {
|
||||
return err
|
||||
|
|
@ -186,6 +231,6 @@ func (u *FinalityUpdate) Validate() error {
|
|||
// finalized execution block.
|
||||
type ChainHeadEvent struct {
|
||||
BeaconHead Header
|
||||
Block *types.Block
|
||||
Block *ctypes.Block
|
||||
Finalized common.Hash
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,61 +5,97 @@
|
|||
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
||||
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
||||
|
||||
# version:golang 1.22.1
|
||||
# version:golang 1.22.4
|
||||
# https://go.dev/dl/
|
||||
79c9b91d7f109515a25fc3ecdaad125d67e6bdb54f6d4d98580f46799caea321 go1.22.1.src.tar.gz
|
||||
3bc971772f4712fec0364f4bc3de06af22a00a12daab10b6f717fdcd13156cc0 go1.22.1.darwin-amd64.tar.gz
|
||||
f6a9cec6b8a002fcc9c0ee24ec04d67f430a52abc3cfd613836986bcc00d8383 go1.22.1.darwin-arm64.tar.gz
|
||||
99f81c10d5a3f8a886faf8fa86aaa2aaf929fbed54a972ae5eec3c5e0bdb961a go1.22.1.freebsd-386.tar.gz
|
||||
51c614ddd92ee4a9913a14c39bf80508d9cfba08561f24d2f075fd00f3cfb067 go1.22.1.freebsd-amd64.tar.gz
|
||||
8484df36d3d40139eaf0fe5e647b006435d826cc12f9ae72973bf7ec265e0ae4 go1.22.1.linux-386.tar.gz
|
||||
aab8e15785c997ae20f9c88422ee35d962c4562212bb0f879d052a35c8307c7f go1.22.1.linux-amd64.tar.gz
|
||||
e56685a245b6a0c592fc4a55f0b7803af5b3f827aaa29feab1f40e491acf35b8 go1.22.1.linux-arm64.tar.gz
|
||||
8cb7a90e48c20daed39a6ac8b8a40760030ba5e93c12274c42191d868687c281 go1.22.1.linux-armv6l.tar.gz
|
||||
ac775e19d93cc1668999b77cfe8c8964abfbc658718feccfe6e0eb87663cd668 go1.22.1.linux-ppc64le.tar.gz
|
||||
7bb7dd8e10f95c9a4cc4f6bef44c816a6e7c9e03f56ac6af6efbb082b19b379f go1.22.1.linux-s390x.tar.gz
|
||||
0c5ebb7eb39b7884ec99f92b425d4c03a96a72443562aafbf6e7d15c42a3108a go1.22.1.windows-386.zip
|
||||
cf9c66a208a106402a527f5b956269ca506cfe535fc388e828d249ea88ed28ba go1.22.1.windows-amd64.zip
|
||||
85b8511b298c9f4199ecae26afafcc3d46155bac934d43f2357b9224bcaa310f go1.22.1.windows-arm64.zip
|
||||
fed720678e728a7ca30ba8d1ded1caafe27d16028fab0232b8ba8e22008fb784 go1.22.4.src.tar.gz
|
||||
b9647fa9fc83a0cc5d4f092a19eaeaecf45f063a5aa7d4962fde65aeb7ae6ce1 go1.22.4.aix-ppc64.tar.gz
|
||||
7788f40f3a46f201df1dc46ca640403eb535d5513fc33449164a90dbd229b761 go1.22.4.darwin-amd64.pkg
|
||||
c95967f50aa4ace34af0c236cbdb49a9a3e80ee2ad09d85775cb4462a5c19ed3 go1.22.4.darwin-amd64.tar.gz
|
||||
4036c88faf57a6b096916f1827edcdbf5290a47cc5f59956e88cdd9b1b71088c go1.22.4.darwin-arm64.pkg
|
||||
242b78dc4c8f3d5435d28a0d2cec9b4c1aa999b601fb8aa59fb4e5a1364bf827 go1.22.4.darwin-arm64.tar.gz
|
||||
f2fbb51af4719d3616efb482d6ed2b96579b474156f85a7ddc6f126764feec4b go1.22.4.dragonfly-amd64.tar.gz
|
||||
7c54884bb9f274884651d41e61d1bc12738863ad1497e97ea19ad0e9aa6bf7b5 go1.22.4.freebsd-386.tar.gz
|
||||
88d44500e1701dd35797619774d6dd51bf60f45a8338b0a82ddc018e4e63fb78 go1.22.4.freebsd-amd64.tar.gz
|
||||
3d9efe47db142a22679aba46b1772e3900b0d87ae13bd2b3bc80dbf2ac0b2cd6 go1.22.4.freebsd-arm.tar.gz
|
||||
726dc093cf020277be45debf03c3b02b43c2efb3e2a5d4fba8f52579d65327dc go1.22.4.freebsd-arm64.tar.gz
|
||||
5f6b67e5e32f1d6ccb2d4dcb44934a5e2e870a877ba7443d86ec43cfc28afa71 go1.22.4.freebsd-riscv64.tar.gz
|
||||
d56ecc2f85b6418a21ef83879594d0c42ab4f65391a676bb12254870e6690d63 go1.22.4.illumos-amd64.tar.gz
|
||||
47a2a8d249a91eb8605c33bceec63aedda0441a43eac47b4721e3975ff916cec go1.22.4.linux-386.tar.gz
|
||||
ba79d4526102575196273416239cca418a651e049c2b099f3159db85e7bade7d go1.22.4.linux-amd64.tar.gz
|
||||
a8e177c354d2e4a1b61020aca3562e27ea3e8f8247eca3170e3fa1e0c2f9e771 go1.22.4.linux-arm64.tar.gz
|
||||
e2b143fbacbc9cbd448e9ef41ac3981f0488ce849af1cf37e2341d09670661de go1.22.4.linux-armv6l.tar.gz
|
||||
e2ff9436e4b34bf6926b06d97916e26d67a909a2effec17967245900f0816f1d go1.22.4.linux-loong64.tar.gz
|
||||
73f0dcc60458c4770593b05a7bc01cc0d31fc98f948c0c2334812c7a1f2fc3f1 go1.22.4.linux-mips.tar.gz
|
||||
417af97fc2630a647052375768be4c38adcc5af946352ea5b28613ea81ca5d45 go1.22.4.linux-mips64.tar.gz
|
||||
7486e2d7dd8c98eb44df815ace35a7fe7f30b7c02326e3741bd934077508139b go1.22.4.linux-mips64le.tar.gz
|
||||
69479c8aad301e459a8365b40cad1074a0dbba5defb9291669f94809c4c4be6e go1.22.4.linux-mipsle.tar.gz
|
||||
dd238847e65bc3e2745caca475a5db6522a2fcf85cf6c38fc36a06642b19efd7 go1.22.4.linux-ppc64.tar.gz
|
||||
a3e5834657ef92523f570f798fed42f1f87bc18222a16815ec76b84169649ec4 go1.22.4.linux-ppc64le.tar.gz
|
||||
56a827ff7dc6245bcd7a1e9288dffaa1d8b0fd7468562264c1523daf3b4f1b4a go1.22.4.linux-riscv64.tar.gz
|
||||
7590c3e278e2dc6040aae0a39da3ca1eb2e3921673a7304cc34d588c45889eec go1.22.4.linux-s390x.tar.gz
|
||||
ddd2eebe34471a2502de6c5dad04ab27c9fc80cbde7a9ad5b3c66ecec4504e1d go1.22.4.netbsd-386.tar.gz
|
||||
33af79f6f935f6fbacc5d23876450b3567b79348fc065beef8e64081127dd234 go1.22.4.netbsd-amd64.tar.gz
|
||||
fa3550ebd5375a70b3bcd342b5a71f4bd271dcbbfaf4eabefa2144ab5d8924b6 go1.22.4.netbsd-arm.tar.gz
|
||||
c9a2971dec9f6d320c6f2b049b2353c6d0a2d35e87b8a4b2d78a2f0d62545f8e go1.22.4.netbsd-arm64.tar.gz
|
||||
d21af022331bfdc2b5b161d616c3a1a4573d33cf7a30416ee509a8f3641deb47 go1.22.4.openbsd-386.tar.gz
|
||||
72c0094c43f7e5722ec49c2a3e9dfa7a1123ac43a5f3a63eecf3e3795d3ff0ae go1.22.4.openbsd-amd64.tar.gz
|
||||
1096831ea3c5ea3ca57d14251d9eda3786889531eb40d7d6775dcaa324d4b065 go1.22.4.openbsd-arm.tar.gz
|
||||
a7ab8d4e0b02bf06ed144ba42c61c0e93ee00f2b433415dfd4ad4b6e79f31650 go1.22.4.openbsd-arm64.tar.gz
|
||||
9716327c8a628358798898dc5148c49dbbeb5196bf2cbf088e550721a6e4f60b go1.22.4.openbsd-ppc64.tar.gz
|
||||
a8dd4503c95c32a502a616ab78870a19889c9325fe9bd31eb16dd69346e4bfa8 go1.22.4.plan9-386.tar.gz
|
||||
5423a25808d76fe5aca8607a2e5ac5673abf45446b168cb5e9d8519ee9fe39a1 go1.22.4.plan9-amd64.tar.gz
|
||||
6af939ad583f5c85c09c53728ab7d38c3cc2b39167562d6c18a07c5c6608b370 go1.22.4.plan9-arm.tar.gz
|
||||
e8cabe69c03085725afdb32a6f9998191a3e55a747b270d835fd05000d56abba go1.22.4.solaris-amd64.tar.gz
|
||||
5c6446e2ea80bc6a971d2b34446f16e6517e638b0ff8d3ea229228d1931790b0 go1.22.4.windows-386.msi
|
||||
aca4e2c37278a10f1c70dd0df142f7d66b50334fcee48978d409202d308d6d25 go1.22.4.windows-386.zip
|
||||
3c21105d7b584759b6e266383b777caf6e87142d304a10b539dbc66ab482bb5f go1.22.4.windows-amd64.msi
|
||||
26321c4d945a0035d8a5bc4a1965b0df401ff8ceac66ce2daadabf9030419a98 go1.22.4.windows-amd64.zip
|
||||
c4303f02b864304eb83dd1db0b4ebf9d2ec9d216e7ef44a7657b166a52889c7f go1.22.4.windows-arm.msi
|
||||
5fcd0671a49cecf39b41021621ee1b6e7aa1370f37122b72e80d4fd4185833b6 go1.22.4.windows-arm.zip
|
||||
553cc6c460f4e3eb4fad5b897c0bb22cd8bbeb20929f0e3eeb939420320292ce go1.22.4.windows-arm64.msi
|
||||
8a2daa9ea28cbdafddc6171aefed384f4e5b6e714fb52116fe9ed25a132f37ed go1.22.4.windows-arm64.zip
|
||||
|
||||
# version:golangci 1.55.2
|
||||
# version:golangci 1.59.0
|
||||
# https://github.com/golangci/golangci-lint/releases/
|
||||
# https://github.com/golangci/golangci-lint/releases/download/v1.55.2/
|
||||
632e96e6d5294fbbe7b2c410a49c8fa01c60712a0af85a567de85bcc1623ea21 golangci-lint-1.55.2-darwin-amd64.tar.gz
|
||||
234463f059249f82045824afdcdd5db5682d0593052f58f6a3039a0a1c3899f6 golangci-lint-1.55.2-darwin-arm64.tar.gz
|
||||
2bdd105e2d4e003a9058c33a22bb191a1e0f30fa0790acca0d8fbffac1d6247c golangci-lint-1.55.2-freebsd-386.tar.gz
|
||||
e75056e8b082386676ce23eba455cf893931a792c0d87e1e3743c0aec33c7fb5 golangci-lint-1.55.2-freebsd-amd64.tar.gz
|
||||
5789b933facaf6136bd23f1d50add67b79bbcf8dfdfc9069a37f729395940a66 golangci-lint-1.55.2-freebsd-armv6.tar.gz
|
||||
7f21ab1008d05f32c954f99470fc86a83a059e530fe2add1d0b7d8ed4d8992a7 golangci-lint-1.55.2-freebsd-armv7.tar.gz
|
||||
33ab06139b9219a28251f10821da94423db30285cc2af97494cbb2a281927de9 golangci-lint-1.55.2-illumos-amd64.tar.gz
|
||||
57ce6f8ce3ad6ee45d7cc3d9a047545a851c2547637834a3fcb086c7b40b1e6b golangci-lint-1.55.2-linux-386.tar.gz
|
||||
ca21c961a33be3bc15e4292dc40c98c8dcc5463a7b6768a3afc123761630c09c golangci-lint-1.55.2-linux-amd64.tar.gz
|
||||
8eb0cee9b1dbf0eaa49871798c7f8a5b35f2960c52d776a5f31eb7d886b92746 golangci-lint-1.55.2-linux-arm64.tar.gz
|
||||
3195f3e0f37d353fd5bd415cabcd4e263f5c29d3d0ffb176c26ff3d2c75eb3bb golangci-lint-1.55.2-linux-armv6.tar.gz
|
||||
c823ee36eb1a719e171de1f2f5ca3068033dce8d9817232fd10ed71fd6650406 golangci-lint-1.55.2-linux-armv7.tar.gz
|
||||
758a5d2a356dc494bd13ed4c0d4bf5a54a4dc91267ea5ecdd87b86c7ca0624e7 golangci-lint-1.55.2-linux-loong64.tar.gz
|
||||
2c7b9abdce7cae802a67d583cd7c6dca520bff6d0e17c8535a918e2f2b437aa0 golangci-lint-1.55.2-linux-mips64.tar.gz
|
||||
024e0a15b85352cc27271285526e16a4ab66d3e67afbbe446c9808c06cb8dbed golangci-lint-1.55.2-linux-mips64le.tar.gz
|
||||
6b00f89ba5506c1de1efdd9fa17c54093013a294fefd8b9b31534db626a672ee golangci-lint-1.55.2-linux-ppc64le.tar.gz
|
||||
0faa0d047d9bf7b703ed3ea65b6117043c93504f9ca1de25ae929d3901c73d4a golangci-lint-1.55.2-linux-riscv64.tar.gz
|
||||
30dec9b22e7d5bb4e9d5ccea96da20f71cd7db3c8cf30b8ddc7cb9174c4d742a golangci-lint-1.55.2-linux-s390x.tar.gz
|
||||
5a0ede48f79ad707902fdb29be8cd2abd8302dc122b65ebae3fdfc86751c7698 golangci-lint-1.55.2-netbsd-386.tar.gz
|
||||
95af20a2e617126dd5b08122ece7819101070e1582a961067ce8c41172f901ad golangci-lint-1.55.2-netbsd-amd64.tar.gz
|
||||
94fb7dacb7527847cc95d7120904e19a2a0a81a0d50d61766c9e0251da72ab9d golangci-lint-1.55.2-netbsd-armv6.tar.gz
|
||||
ca906bce5fee9619400e4a321c56476fe4a4efb6ac4fc989d340eb5563348873 golangci-lint-1.55.2-netbsd-armv7.tar.gz
|
||||
45b442f69fc8915c4500201c0247b7f3f69544dbc9165403a61f9095f2c57355 golangci-lint-1.55.2-windows-386.zip
|
||||
f57d434d231d43417dfa631587522f8c1991220b43c8ffadb9c7bd279508bf81 golangci-lint-1.55.2-windows-amd64.zip
|
||||
fd7dc8f4c6829ee6fafb252a4d81d2155cd35da7833665cbb25d53ce7cecd990 golangci-lint-1.55.2-windows-arm64.zip
|
||||
1892c3c24f9e7ef44b02f6750c703864b6dc350129f3ec39510300007b2376f1 golangci-lint-1.55.2-windows-armv6.zip
|
||||
a5e68ae73d38748b5269fad36ac7575e3c162a5dc63ef58abdea03cc5da4522a golangci-lint-1.55.2-windows-armv7.zip
|
||||
# https://github.com/golangci/golangci-lint/releases/download/v1.59.0/
|
||||
418acf7e255ddc0783e97129c9b03d9311b77826a5311d425a01c708a86417e7 golangci-lint-1.59.0-darwin-amd64.tar.gz
|
||||
5f6a1d95a6dd69f6e328eb56dd311a38e04cfab79a1305fbf4957f4e203f47b6 golangci-lint-1.59.0-darwin-arm64.tar.gz
|
||||
8899bf589185d49f747f3e5db9f0bde8a47245a100c64a3dd4d65e8e92cfc4f2 golangci-lint-1.59.0-freebsd-386.tar.gz
|
||||
658212f138d9df2ac89427e22115af34bf387c0871d70f2a25101718946a014f golangci-lint-1.59.0-freebsd-amd64.tar.gz
|
||||
4c6395ea40f314d3b6fa17d8997baab93464d5d1deeaab513155e625473bd03a golangci-lint-1.59.0-freebsd-armv6.tar.gz
|
||||
ff37da4fbaacdb6bbae70fdbdbb1ba932a859956f788c82822fa06bef5b7c6b3 golangci-lint-1.59.0-freebsd-armv7.tar.gz
|
||||
439739469ed2bda182b1ec276d40c40e02f195537f78e3672996741ad223d6b6 golangci-lint-1.59.0-illumos-amd64.tar.gz
|
||||
940801d46790e40d0a097d8fee34e2606f0ef148cd039654029b0b8750a15ed6 golangci-lint-1.59.0-linux-386.tar.gz
|
||||
3b14a439f33c4fff83dbe0349950d984042b9a1feb6c62f82787b598fc3ab5f4 golangci-lint-1.59.0-linux-amd64.tar.gz
|
||||
c57e6c0b0fa03089a2611dceddd5bc5d206716cccdff8b149da8baac598719a1 golangci-lint-1.59.0-linux-arm64.tar.gz
|
||||
93149e2d3b25ac754df9a23172403d8aa6d021a7e0d9c090a12f51897f68c9a0 golangci-lint-1.59.0-linux-armv6.tar.gz
|
||||
d10ac38239d9efee3ee87b55c96cdf3fa09e1a525babe3ffdaaf65ccc48cf3dc golangci-lint-1.59.0-linux-armv7.tar.gz
|
||||
047338114b4f0d5f08f0fb9a397b03cc171916ed0960be7dfb355c2320cd5e9c golangci-lint-1.59.0-linux-loong64.tar.gz
|
||||
5632df0f7f8fc03a80a266130faef0b5902d280cf60621f1b2bdc1aef6d97ee9 golangci-lint-1.59.0-linux-mips64.tar.gz
|
||||
71dd638c82fa4439171e7126d2c7a32b5d103bfdef282cea40c83632cb3d1f4b golangci-lint-1.59.0-linux-mips64le.tar.gz
|
||||
6cf9ea0d34e91669948483f9ae7f07da319a879344373a1981099fbd890cde00 golangci-lint-1.59.0-linux-ppc64le.tar.gz
|
||||
af0205fa6fbab197cee613c359947711231739095d21b5c837086233b36ad971 golangci-lint-1.59.0-linux-riscv64.tar.gz
|
||||
a9d2fb93f3c688ebccef94f5dc96c0b07c4d20bf6556cddebd8442159b0c80f6 golangci-lint-1.59.0-linux-s390x.tar.gz
|
||||
68ab4c57a847b8ace9679887f2f8b2b6760e57ee29dcde8c3f40dd8bb2654fa2 golangci-lint-1.59.0-netbsd-386.tar.gz
|
||||
d277b8b435c19406d00de4d509eadf5a024a5782878332e9a1b7c02bb76e87a7 golangci-lint-1.59.0-netbsd-amd64.tar.gz
|
||||
83211656be8dcfa1545af4f92894409f412d1f37566798cb9460a526593ad62c golangci-lint-1.59.0-netbsd-arm64.tar.gz
|
||||
6c6866d28bf79fa9817a0f7d2b050890ed109cae80bdb4dfa39536a7226da237 golangci-lint-1.59.0-netbsd-armv6.tar.gz
|
||||
11587566363bd03ca586b7df9776ccaed569fcd1f3489930ac02f9375b307503 golangci-lint-1.59.0-netbsd-armv7.tar.gz
|
||||
466181a8967bafa495e41494f93a0bec829c2cf715de874583b0460b3b8ae2b8 golangci-lint-1.59.0-windows-386.zip
|
||||
3317d8a87a99a49a0a1321d295c010790e6dbf43ee96b318f4b8bb23eae7a565 golangci-lint-1.59.0-windows-amd64.zip
|
||||
b3af955c7fceac8220a36fc799e1b3f19d3b247d32f422caac5f9845df8f7316 golangci-lint-1.59.0-windows-arm64.zip
|
||||
6f083c7d0c764e5a0e5bde46ee3e91ae357d80c194190fe1d9754392e9064c7e golangci-lint-1.59.0-windows-armv6.zip
|
||||
3709b4dd425deadab27748778d08e03c0f804d7748f7dd5b6bb488d98aa031c7 golangci-lint-1.59.0-windows-armv7.zip
|
||||
|
||||
# This is the builder on PPA that will build Go itself (inception-y), don't modify!
|
||||
#
|
||||
# This version is fine to be old and full of security holes, we just use it
|
||||
# to build the latest Go. Don't change it. If it ever becomes insufficient,
|
||||
# we need to switch over to a recursive builder to jump across supported
|
||||
# versions.
|
||||
# to build the latest Go. Don't change it.
|
||||
#
|
||||
# version:ppa-builder 1.19.6
|
||||
# version:ppa-builder-1 1.19.6
|
||||
# https://go.dev/dl/
|
||||
d7f0013f82e6d7f862cc6cb5c8cdb48eef5f2e239b35baa97e2f1a7466043767 go1.19.6.src.tar.gz
|
||||
|
||||
# version:ppa-builder-2 1.21.9
|
||||
# https://go.dev/dl/
|
||||
58f0c5ced45a0012bce2ff7a9df03e128abcc8818ebabe5027bb92bafe20e421 go1.21.9.src.tar.gz
|
||||
|
|
|
|||
54
build/ci.go
54
build/ci.go
|
|
@ -117,23 +117,15 @@ var (
|
|||
debEthereum,
|
||||
}
|
||||
|
||||
// Distros for which packages are created.
|
||||
// Note: vivid is unsupported because there is no golang-1.6 package for it.
|
||||
// Note: the following Ubuntu releases have been officially deprecated on Launchpad:
|
||||
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish,
|
||||
// kinetic, lunar
|
||||
debDistroGoBoots = map[string]string{
|
||||
"trusty": "golang-1.11", // 14.04, EOL: 04/2024
|
||||
"xenial": "golang-go", // 16.04, EOL: 04/2026
|
||||
"bionic": "golang-go", // 18.04, EOL: 04/2028
|
||||
"focal": "golang-go", // 20.04, EOL: 04/2030
|
||||
"jammy": "golang-go", // 22.04, EOL: 04/2032
|
||||
"mantic": "golang-go", // 23.10, EOL: 07/2024
|
||||
}
|
||||
// Distros for which packages are created
|
||||
debDistros = []string{
|
||||
"xenial", // 16.04, EOL: 04/2026
|
||||
"bionic", // 18.04, EOL: 04/2028
|
||||
"focal", // 20.04, EOL: 04/2030
|
||||
"jammy", // 22.04, EOL: 04/2032
|
||||
"noble", // 24.04, EOL: 04/2034
|
||||
|
||||
debGoBootPaths = map[string]string{
|
||||
"golang-1.11": "/usr/lib/go-1.11",
|
||||
"golang-go": "/usr/lib/go",
|
||||
"mantic", // 23.10, EOL: 07/2024
|
||||
}
|
||||
|
||||
// This is where the tests should be unpacked.
|
||||
|
|
@ -694,7 +686,7 @@ func doDebianSource(cmdline []string) {
|
|||
}
|
||||
// Download and verify the Go source packages.
|
||||
var (
|
||||
gobootbundle = downloadGoBootstrapSources(*cachedir)
|
||||
gobootbundles = downloadGoBootstrapSources(*cachedir)
|
||||
gobundle = downloadGoSources(*cachedir)
|
||||
)
|
||||
// Download all the dependencies needed to build the sources and run the ci script
|
||||
|
|
@ -708,18 +700,20 @@ func doDebianSource(cmdline []string) {
|
|||
|
||||
// Create Debian packages and upload them.
|
||||
for _, pkg := range debPackages {
|
||||
for distro, goboot := range debDistroGoBoots {
|
||||
for _, distro := range debDistros {
|
||||
// Prepare the debian package with the go-ethereum sources.
|
||||
meta := newDebMetadata(distro, goboot, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables)
|
||||
meta := newDebMetadata(distro, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables)
|
||||
pkgdir := stageDebianSource(*workdir, meta)
|
||||
|
||||
// Add bootstrapper Go source code
|
||||
for i, gobootbundle := range gobootbundles {
|
||||
if err := build.ExtractArchive(gobootbundle, pkgdir); err != nil {
|
||||
log.Fatalf("Failed to extract bootstrapper Go sources: %v", err)
|
||||
}
|
||||
if err := os.Rename(filepath.Join(pkgdir, "go"), filepath.Join(pkgdir, ".goboot")); err != nil {
|
||||
if err := os.Rename(filepath.Join(pkgdir, "go"), filepath.Join(pkgdir, fmt.Sprintf(".goboot-%d", i+1))); err != nil {
|
||||
log.Fatalf("Failed to rename bootstrapper Go source folder: %v", err)
|
||||
}
|
||||
}
|
||||
// Add builder Go source code
|
||||
if err := build.ExtractArchive(gobundle, pkgdir); err != nil {
|
||||
log.Fatalf("Failed to extract builder Go sources: %v", err)
|
||||
|
|
@ -754,11 +748,14 @@ func doDebianSource(cmdline []string) {
|
|||
}
|
||||
}
|
||||
|
||||
// downloadGoBootstrapSources downloads the Go source tarball that will be used
|
||||
// downloadGoBootstrapSources downloads the Go source tarball(s) that will be used
|
||||
// to bootstrap the builder Go.
|
||||
func downloadGoBootstrapSources(cachedir string) string {
|
||||
func downloadGoBootstrapSources(cachedir string) []string {
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
gobootVersion, err := build.Version(csdb, "ppa-builder")
|
||||
|
||||
var bundles []string
|
||||
for _, booter := range []string{"ppa-builder-1", "ppa-builder-2"} {
|
||||
gobootVersion, err := build.Version(csdb, booter)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
@ -768,7 +765,9 @@ func downloadGoBootstrapSources(cachedir string) string {
|
|||
if err := csdb.DownloadFile(url, dst); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return dst
|
||||
bundles = append(bundles, dst)
|
||||
}
|
||||
return bundles
|
||||
}
|
||||
|
||||
// downloadGoSources downloads the Go source tarball.
|
||||
|
|
@ -847,9 +846,6 @@ type debPackage struct {
|
|||
|
||||
type debMetadata struct {
|
||||
Env build.Environment
|
||||
GoBootPackage string
|
||||
GoBootPath string
|
||||
|
||||
PackageName string
|
||||
|
||||
// go-ethereum version being built. Note that this
|
||||
|
|
@ -877,14 +873,12 @@ func (d debExecutable) Package() string {
|
|||
return d.BinaryName
|
||||
}
|
||||
|
||||
func newDebMetadata(distro, goboot, author string, env build.Environment, t time.Time, name string, version string, exes []debExecutable) debMetadata {
|
||||
func newDebMetadata(distro, author string, env build.Environment, t time.Time, name string, version string, exes []debExecutable) debMetadata {
|
||||
if author == "" {
|
||||
// No signing key, use default author.
|
||||
author = "Ethereum Builds <fjl@ethereum.org>"
|
||||
}
|
||||
return debMetadata{
|
||||
GoBootPackage: goboot,
|
||||
GoBootPath: debGoBootPaths[goboot],
|
||||
PackageName: name,
|
||||
Env: env,
|
||||
Author: author,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ Source: {{.Name}}
|
|||
Section: science
|
||||
Priority: extra
|
||||
Maintainer: {{.Author}}
|
||||
Build-Depends: debhelper (>= 8.0.0), {{.GoBootPackage}}
|
||||
Build-Depends: debhelper (>= 8.0.0), golang-go
|
||||
Standards-Version: 3.9.5
|
||||
Homepage: https://ethereum.org
|
||||
Vcs-Git: https://github.com/ethereum/go-ethereum.git
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
# Launchpad rejects Go's access to $HOME, use custom folders
|
||||
export GOCACHE=/tmp/go-build
|
||||
export GOPATH=/tmp/gopath
|
||||
export GOROOT_BOOTSTRAP={{.GoBootPath}}
|
||||
export GOROOT_BOOTSTRAP=/usr/lib/go
|
||||
|
||||
override_dh_auto_clean:
|
||||
# Don't try to be smart Launchpad, we know our build rules better than you
|
||||
|
|
@ -19,8 +19,9 @@ override_dh_auto_build:
|
|||
#
|
||||
# We're also shipping the bootstrapper as of Go 1.20 as it had minimum version
|
||||
# requirements opposed to older versions of Go.
|
||||
(mv .goboot ../ && cd ../.goboot/src && ./make.bash)
|
||||
(mv .go ../ && cd ../.go/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot ./make.bash)
|
||||
(mv .goboot-1 ../ && cd ../.goboot-1/src && ./make.bash)
|
||||
(mv .goboot-2 ../ && cd ../.goboot-2/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot-1 ./make.bash)
|
||||
(mv .go ../ && cd ../.go/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot-2 ./make.bash)
|
||||
|
||||
# We can't download external go modules within Launchpad, so we're shipping the
|
||||
# entire dependency source cache with go-ethereum.
|
||||
|
|
|
|||
|
|
@ -291,8 +291,8 @@ func writeAuthors(files []string) {
|
|||
}
|
||||
}
|
||||
// Write sorted list of authors back to the file.
|
||||
slices.SortFunc(list, func(a, b string) bool {
|
||||
return strings.ToLower(a) < strings.ToLower(b)
|
||||
slices.SortFunc(list, func(a, b string) int {
|
||||
return strings.Compare(strings.ToLower(a), strings.ToLower(b))
|
||||
})
|
||||
content := new(bytes.Buffer)
|
||||
content.WriteString(authorsFileHeader)
|
||||
|
|
|
|||
|
|
@ -552,7 +552,7 @@ func listWallets(c *cli.Context) error {
|
|||
// accountImport imports a raw hexadecimal private key via CLI.
|
||||
func accountImport(c *cli.Context) error {
|
||||
if c.Args().Len() != 1 {
|
||||
return errors.New("<keyfile> must be given as first argument.")
|
||||
return errors.New("<keyfile> must be given as first argument")
|
||||
}
|
||||
internalApi, ui, err := initInternalApi(c)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ It enables usecases like the following:
|
|||
|
||||
The two main features that are required for this to work well are;
|
||||
|
||||
1. Rule Implementation: how to create, manage and interpret rules in a flexible but secure manner
|
||||
2. Credential managements and credentials; how to provide auto-unlock without exposing keys unnecessarily.
|
||||
1. Rule Implementation: how to create, manage, and interpret rules in a flexible but secure manner
|
||||
2. Credential management and credentials; how to provide auto-unlock without exposing keys unnecessarily.
|
||||
|
||||
The section below deals with both of them
|
||||
|
||||
## Rule Implementation
|
||||
|
||||
A ruleset file is implemented as a `js` file. Under the hood, the ruleset-engine is a `SignerUI`, implementing the same methods as the `json-rpc` methods
|
||||
A ruleset file is implemented as a `js` file. Under the hood, the ruleset engine is a `SignerUI`, implementing the same methods as the `json-rpc` methods
|
||||
defined in the UI protocol. Example:
|
||||
|
||||
```js
|
||||
|
|
@ -27,7 +27,7 @@ function asBig(str) {
|
|||
return new BigNumber(str)
|
||||
}
|
||||
|
||||
// Approve transactions to a certain contract if value is below a certain limit
|
||||
// Approve transactions to a certain contract if the value is below a certain limit
|
||||
function ApproveTx(req) {
|
||||
var limit = big.Newint("0xb1a2bc2ec50000")
|
||||
var value = asBig(req.transaction.value);
|
||||
|
|
@ -70,7 +70,7 @@ The Otto vm has a few [caveats](https://github.com/robertkrimen/otto):
|
|||
Additionally, a few more have been added
|
||||
|
||||
* The rule execution cannot load external javascript files.
|
||||
* The only preloaded library is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the github repository.
|
||||
* The only preloaded library is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the GitHub repository.
|
||||
* Each invocation is made in a fresh virtual machine. This means that you cannot store data in global variables between invocations. This is a deliberate choice -- if you want to store data, use the disk-backed `storage`, since rules should not rely on ephemeral data.
|
||||
* Javascript API parameters are _always_ an object. This is also a design choice, to ensure that parameters are accessed by _key_ and not by order. This is to prevent mistakes due to missing parameters or parameter changes.
|
||||
* The JS engine has access to `storage` and `console`.
|
||||
|
|
@ -88,8 +88,8 @@ Some security precautions can be made, such as:
|
|||
|
||||
##### Security of implementation
|
||||
|
||||
The drawbacks of this very flexible solution is that the `signer` needs to contain a javascript engine. This is pretty simple to implement, since it's already
|
||||
implemented for `geth`. There are no known security vulnerabilities in, nor have we had any security-problems with it so far.
|
||||
The drawback of this very flexible solution is that the `signer` needs to contain a javascript engine. This is pretty simple to implement since it's already
|
||||
implemented for `geth`. There are no known security vulnerabilities in it, nor have we had any security problems with it so far.
|
||||
|
||||
The javascript engine would be an added attack surface; but if the validation of `rulesets` is made good (with hash-based attestation), the actual javascript cannot be considered
|
||||
an attack surface -- if an attacker can control the ruleset, a much simpler attack would be to implement an "always-approve" rule instead of exploiting the js vm. The only benefit
|
||||
|
|
@ -105,7 +105,7 @@ It's unclear whether any other DSL could be more secure; since there's always th
|
|||
|
||||
## Credential management
|
||||
|
||||
The ability to auto-approve transaction means that the signer needs to have necessary credentials to decrypt keyfiles. These passwords are hereafter called `ksp` (keystore pass).
|
||||
The ability to auto-approve transactions means that the signer needs to have the necessary credentials to decrypt keyfiles. These passwords are hereafter called `ksp` (keystore pass).
|
||||
|
||||
### Example implementation
|
||||
|
||||
|
|
@ -127,8 +127,8 @@ The `vault.dat` would be an encrypted container storing the following informatio
|
|||
|
||||
### Security considerations
|
||||
|
||||
This would leave it up to the user to ensure that the `path/to/masterseed` is handled in a secure way. It's difficult to get around this, although one could
|
||||
imagine leveraging OS-level keychains where supported. The setup is however in general similar to how ssh-keys are stored in `.ssh/`.
|
||||
This would leave it up to the user to ensure that the `path/to/masterseed` is handled securely. It's difficult to get around this, although one could
|
||||
imagine leveraging OS-level keychains where supported. The setup is however, in general, similar to how ssh-keys are stored in `.ssh/`.
|
||||
|
||||
|
||||
# Implementation status
|
||||
|
|
@ -149,7 +149,7 @@ function big(str) {
|
|||
// Time window: 1 week
|
||||
var window = 1000* 3600*24*7;
|
||||
|
||||
// Limit : 1 ether
|
||||
// Limit: 1 ether
|
||||
var limit = new BigNumber("1e18");
|
||||
|
||||
function isLimitOk(transaction) {
|
||||
|
|
@ -163,7 +163,7 @@ function isLimitOk(transaction) {
|
|||
if (stored != "") {
|
||||
txs = JSON.parse(stored)
|
||||
}
|
||||
// First, remove all that have passed out of the time-window
|
||||
// First, remove all that has passed out of the time window
|
||||
var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart});
|
||||
console.log(txs, newtxs.length);
|
||||
|
||||
|
|
@ -174,7 +174,7 @@ function isLimitOk(transaction) {
|
|||
console.log("ApproveTx > Sum so far", sum);
|
||||
console.log("ApproveTx > Requested", value.toNumber());
|
||||
|
||||
// Would we exceed weekly limit ?
|
||||
// Would we exceed the weekly limit ?
|
||||
return sum.plus(value).lt(limit)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -28,9 +29,11 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
@ -45,6 +48,7 @@ var (
|
|||
discv4ResolveJSONCommand,
|
||||
discv4CrawlCommand,
|
||||
discv4TestCommand,
|
||||
discv4ListenCommand,
|
||||
},
|
||||
}
|
||||
discv4PingCommand = &cli.Command{
|
||||
|
|
@ -75,6 +79,14 @@ var (
|
|||
Flags: discoveryNodeFlags,
|
||||
ArgsUsage: "<nodes.json file>",
|
||||
}
|
||||
discv4ListenCommand = &cli.Command{
|
||||
Name: "listen",
|
||||
Usage: "Runs a discovery node",
|
||||
Action: discv4Listen,
|
||||
Flags: flags.Merge(discoveryNodeFlags, []cli.Flag{
|
||||
httpAddrFlag,
|
||||
}),
|
||||
}
|
||||
discv4CrawlCommand = &cli.Command{
|
||||
Name: "crawl",
|
||||
Usage: "Updates a nodes.json file with random nodes found in the DHT",
|
||||
|
|
@ -131,6 +143,10 @@ var (
|
|||
Usage: "Enode of the remote node under test",
|
||||
EnvVars: []string{"REMOTE_ENODE"},
|
||||
}
|
||||
httpAddrFlag = &cli.StringFlag{
|
||||
Name: "rpc",
|
||||
Usage: "HTTP server listening address",
|
||||
}
|
||||
)
|
||||
|
||||
var discoveryNodeFlags = []cli.Flag{
|
||||
|
|
@ -154,6 +170,27 @@ func discv4Ping(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func discv4Listen(ctx *cli.Context) error {
|
||||
disc, _ := startV4(ctx)
|
||||
defer disc.Close()
|
||||
|
||||
fmt.Println(disc.Self())
|
||||
|
||||
httpAddr := ctx.String(httpAddrFlag.Name)
|
||||
if httpAddr == "" {
|
||||
// Non-HTTP mode.
|
||||
select {}
|
||||
}
|
||||
|
||||
api := &discv4API{disc}
|
||||
log.Info("Starting RPC API server", "addr", httpAddr)
|
||||
srv := rpc.NewServer()
|
||||
srv.RegisterName("discv4", api)
|
||||
http.DefaultServeMux.Handle("/", srv)
|
||||
httpsrv := http.Server{Addr: httpAddr, Handler: http.DefaultServeMux}
|
||||
return httpsrv.ListenAndServe()
|
||||
}
|
||||
|
||||
func discv4RequestRecord(ctx *cli.Context) error {
|
||||
n := getNodeArg(ctx)
|
||||
disc, _ := startV4(ctx)
|
||||
|
|
@ -362,3 +399,23 @@ func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
|
|||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
type discv4API struct {
|
||||
host *discover.UDPv4
|
||||
}
|
||||
|
||||
func (api *discv4API) LookupRandom(n int) (ns []*enode.Node) {
|
||||
it := api.host.RandomNodes()
|
||||
for len(ns) < n && it.Next() {
|
||||
ns = append(ns, it.Node())
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
func (api *discv4API) Buckets() [][]discover.BucketNode {
|
||||
return api.host.TableBuckets()
|
||||
}
|
||||
|
||||
func (api *discv4API) Self() *enode.Node {
|
||||
return api.host.Self()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ func (s *Suite) dial() (*Conn, error) {
|
|||
// dialAs attempts to dial a given node and perform a handshake using the given
|
||||
// private key.
|
||||
func (s *Suite) dialAs(key *ecdsa.PrivateKey) (*Conn, error) {
|
||||
fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP()))
|
||||
tcpEndpoint, _ := s.Dest.TCPEndpoint()
|
||||
fd, err := net.Dial("tcp", tcpEndpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
func (c *Conn) snapRequest(code uint64, msg any) (any, error) {
|
||||
|
|
@ -905,7 +904,7 @@ func (s *Suite) snapGetByteCodes(t *utesting.T, tc *byteCodesTest) error {
|
|||
// that the serving node is missing
|
||||
var (
|
||||
bytecodes = res.Codes
|
||||
hasher = sha3.NewLegacyKeccak256().(crypto.KeccakState)
|
||||
hasher = crypto.NewKeccakState()
|
||||
hash = make([]byte, 32)
|
||||
codes = make([][]byte, len(req.Hashes))
|
||||
)
|
||||
|
|
@ -964,7 +963,7 @@ func (s *Suite) snapGetTrieNodes(t *utesting.T, tc *trieNodesTest) error {
|
|||
|
||||
// Cross reference the requested trienodes with the response to find gaps
|
||||
// that the serving node is missing
|
||||
hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState)
|
||||
hasher := crypto.NewKeccakState()
|
||||
hash := make([]byte, 32)
|
||||
trienodes := res.Nodes
|
||||
if got, want := len(trienodes), len(tc.expHashes); got != want {
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ func (s *Suite) sendTxs(t *utesting.T, txs []*types.Transaction) error {
|
|||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("timed out waiting for txs")
|
||||
return errors.New("timed out waiting for txs")
|
||||
}
|
||||
|
||||
func (s *Suite) sendInvalidTxs(t *utesting.T, txs []*types.Transaction) error {
|
||||
|
|
|
|||
|
|
@ -53,16 +53,18 @@ func newTestEnv(remote string, listen1, listen2 string) *testenv {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if node.IP() == nil || node.UDP() == 0 {
|
||||
if !node.IPAddr().IsValid() || node.UDP() == 0 {
|
||||
var ip net.IP
|
||||
var tcpPort, udpPort int
|
||||
if ip = node.IP(); ip == nil {
|
||||
if node.IPAddr().IsValid() {
|
||||
ip = node.IPAddr().AsSlice()
|
||||
} else {
|
||||
ip = net.ParseIP("127.0.0.1")
|
||||
}
|
||||
if tcpPort = node.TCP(); tcpPort == 0 {
|
||||
tcpPort = 30303
|
||||
}
|
||||
if udpPort = node.TCP(); udpPort == 0 {
|
||||
if udpPort = node.UDP(); udpPort == 0 {
|
||||
udpPort = 30303
|
||||
}
|
||||
node = enode.NewV4(node.Pubkey(), ip, tcpPort, udpPort)
|
||||
|
|
@ -110,7 +112,7 @@ func (te *testenv) localEndpoint(c net.PacketConn) v4wire.Endpoint {
|
|||
}
|
||||
|
||||
func (te *testenv) remoteEndpoint() v4wire.Endpoint {
|
||||
return v4wire.NewEndpoint(te.remoteAddr, 0)
|
||||
return v4wire.NewEndpoint(te.remoteAddr.AddrPort(), 0)
|
||||
}
|
||||
|
||||
func contains(ns []v4wire.Node, key v4wire.Pubkey) bool {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package v5test
|
|||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -266,7 +267,7 @@ func (s *Suite) TestFindnodeResults(t *utesting.T) {
|
|||
n := bn.conn.localNode.Node()
|
||||
expect[n.ID()] = n
|
||||
d := uint(enode.LogDist(n.ID(), s.Dest.ID()))
|
||||
if !containsUint(dists, d) {
|
||||
if !slices.Contains(dists, d) {
|
||||
dists = append(dists, d)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,12 +252,3 @@ func checkRecords(records []*enr.Record) ([]*enode.Node, error) {
|
|||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func containsUint(ints []uint, x uint) bool {
|
||||
for i := range ints {
|
||||
if ints[i] == x {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ package main
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -205,11 +205,11 @@ func trueFilter(args []string) (nodeFilter, error) {
|
|||
}
|
||||
|
||||
func ipFilter(args []string) (nodeFilter, error) {
|
||||
_, cidr, err := net.ParseCIDR(args[0])
|
||||
prefix, err := netip.ParsePrefix(args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f := func(n nodeJSON) bool { return cidr.Contains(n.N.IP()) }
|
||||
f := func(n nodeJSON) bool { return prefix.Contains(n.N.IPAddr()) }
|
||||
return f, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,11 @@ var (
|
|||
|
||||
func rlpxPing(ctx *cli.Context) error {
|
||||
n := getNodeArg(ctx)
|
||||
fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", n.IP(), n.TCP()))
|
||||
tcpEndpoint, ok := n.TCPEndpoint()
|
||||
if !ok {
|
||||
return fmt.Errorf("node has no TCP endpoint")
|
||||
}
|
||||
fd, err := net.Dial("tcp", tcpEndpoint.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,4 +50,4 @@ contains the password.
|
|||
|
||||
## JSON
|
||||
|
||||
In case you need to output the result in a JSON format, you shall by using the `--json` flag.
|
||||
In case you need to output the result in a JSON format, you shall use the `--json` flag.
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ func (i *bbInput) ToBlock() *types.Block {
|
|||
if i.Header.Difficulty != nil {
|
||||
header.Difficulty = i.Header.Difficulty
|
||||
}
|
||||
return types.NewBlockWithHeader(header).WithBody(i.Txs, i.Ommers).WithWithdrawals(i.Withdrawals)
|
||||
return types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: i.Txs, Uncles: i.Ommers, Withdrawals: i.Withdrawals})
|
||||
}
|
||||
|
||||
// SealBlock seals the given block using the configured engine.
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
|||
return nil
|
||||
}
|
||||
if env.ParentBaseFee == nil || env.Number == 0 {
|
||||
return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
|
||||
return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'parentBaseFee' in env section"))
|
||||
}
|
||||
env.BaseFee = eip1559.CalcBaseFee(chainConfig, &types.Header{
|
||||
Number: new(big.Int).SetUint64(env.Number - 1),
|
||||
|
|
@ -296,7 +296,7 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
|||
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
|
||||
var storage map[common.Hash]common.Hash
|
||||
if dumpAccount.Storage != nil {
|
||||
storage = make(map[common.Hash]common.Hash)
|
||||
storage = make(map[common.Hash]common.Hash, len(dumpAccount.Storage))
|
||||
for k, v := range dumpAccount.Storage {
|
||||
storage[k] = common.HexToHash(v)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,8 +272,17 @@ func runCmd(ctx *cli.Context) error {
|
|||
output, leftOverGas, stats, err := timedExec(bench, execFunc)
|
||||
|
||||
if ctx.Bool(DumpFlag.Name) {
|
||||
statedb.Commit(genesisConfig.Number, true)
|
||||
fmt.Println(string(statedb.Dump(nil)))
|
||||
root, err := statedb.Commit(genesisConfig.Number, true)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to commit changes %v\n", err)
|
||||
return err
|
||||
}
|
||||
dumpdb, err := state.New(root, sdb, nil)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to open statedb %v\n", err)
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(dumpdb.Dump(nil)))
|
||||
}
|
||||
|
||||
if ctx.Bool(DebugFlag.Name) {
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ func TestT8n(t *testing.T) {
|
|||
{ // Test post-merge transition
|
||||
base: "./testdata/24",
|
||||
input: t8nInput{
|
||||
"alloc.json", "txs.json", "env.json", "Merge", "",
|
||||
"alloc.json", "txs.json", "env.json", "Paris", "",
|
||||
},
|
||||
output: t8nOutput{alloc: true, result: true},
|
||||
expOut: "exp.json",
|
||||
|
|
@ -242,7 +242,7 @@ func TestT8n(t *testing.T) {
|
|||
{ // Test post-merge transition where input is missing random
|
||||
base: "./testdata/24",
|
||||
input: t8nInput{
|
||||
"alloc.json", "txs.json", "env-missingrandom.json", "Merge", "",
|
||||
"alloc.json", "txs.json", "env-missingrandom.json", "Paris", "",
|
||||
},
|
||||
output: t8nOutput{alloc: false, result: false},
|
||||
expExitCode: 3,
|
||||
|
|
@ -250,7 +250,7 @@ func TestT8n(t *testing.T) {
|
|||
{ // Test base fee calculation
|
||||
base: "./testdata/25",
|
||||
input: t8nInput{
|
||||
"alloc.json", "txs.json", "env.json", "Merge", "",
|
||||
"alloc.json", "txs.json", "env.json", "Paris", "",
|
||||
},
|
||||
output: t8nOutput{alloc: true, result: true},
|
||||
expOut: "exp.json",
|
||||
|
|
@ -378,7 +378,7 @@ func TestT8nTracing(t *testing.T) {
|
|||
{
|
||||
base: "./testdata/32",
|
||||
input: t8nInput{
|
||||
"alloc.json", "txs.json", "env.json", "Merge", "",
|
||||
"alloc.json", "txs.json", "env.json", "Paris", "",
|
||||
},
|
||||
extraArgs: []string{"--trace", "--trace.callframes"},
|
||||
expectedTraces: []string{"trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl"},
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
|
|||
utils.MetricsInfluxDBOrganizationFlag,
|
||||
utils.TxLookupLimitFlag,
|
||||
utils.VMTraceFlag,
|
||||
utils.VMTraceConfigFlag,
|
||||
utils.VMTraceJsonConfigFlag,
|
||||
utils.TransactionHistoryFlag,
|
||||
utils.StateHistoryFlag,
|
||||
}, utils.DatabaseFlags),
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/eth/catalyst"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/internal/version"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
|
@ -169,7 +168,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
|||
}
|
||||
|
||||
// makeFullNode loads geth configuration and creates the Ethereum backend.
|
||||
func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
stack, cfg := makeConfigNode(ctx)
|
||||
if ctx.IsSet(utils.OverrideCancun.Name) {
|
||||
v := ctx.Uint64(utils.OverrideCancun.Name)
|
||||
|
|
@ -238,7 +237,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
|||
utils.Fatalf("failed to register catalyst service: %v", err)
|
||||
}
|
||||
}
|
||||
return stack, backend
|
||||
return stack
|
||||
}
|
||||
|
||||
// dumpConfig is the dumpconfig command.
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ JavaScript API. See https://geth.ethereum.org/docs/interacting-with-geth/javascr
|
|||
func localConsole(ctx *cli.Context) error {
|
||||
// Create and start the node based on the CLI flags
|
||||
prepare(ctx)
|
||||
stack, backend := makeFullNode(ctx)
|
||||
startNode(ctx, stack, backend, true)
|
||||
stack := makeFullNode(ctx)
|
||||
startNode(ctx, stack, true)
|
||||
defer stack.Close()
|
||||
|
||||
// Attach to the newly started node and create the JavaScript console.
|
||||
|
|
|
|||
|
|
@ -103,17 +103,17 @@ func TestAttachWelcome(t *testing.T) {
|
|||
"--http", "--http.port", httpPort,
|
||||
"--ws", "--ws.port", wsPort)
|
||||
t.Run("ipc", func(t *testing.T) {
|
||||
waitForEndpoint(t, ipc, 3*time.Second)
|
||||
waitForEndpoint(t, ipc, 4*time.Second)
|
||||
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
||||
})
|
||||
t.Run("http", func(t *testing.T) {
|
||||
endpoint := "http://127.0.0.1:" + httpPort
|
||||
waitForEndpoint(t, endpoint, 3*time.Second)
|
||||
waitForEndpoint(t, endpoint, 4*time.Second)
|
||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||
})
|
||||
t.Run("ws", func(t *testing.T) {
|
||||
endpoint := "ws://127.0.0.1:" + wsPort
|
||||
waitForEndpoint(t, endpoint, 3*time.Second)
|
||||
waitForEndpoint(t, endpoint, 4*time.Second)
|
||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||
})
|
||||
geth.Kill()
|
||||
|
|
|
|||
|
|
@ -246,11 +246,17 @@ func removeDB(ctx *cli.Context) error {
|
|||
ancientDir = config.Node.ResolvePath(ancientDir)
|
||||
}
|
||||
// Delete state data
|
||||
statePaths := []string{rootDir, filepath.Join(ancientDir, rawdb.StateFreezerName)}
|
||||
statePaths := []string{
|
||||
rootDir,
|
||||
filepath.Join(ancientDir, rawdb.StateFreezerName),
|
||||
}
|
||||
confirmAndRemoveDB(statePaths, "state data", ctx, removeStateDataFlag.Name)
|
||||
|
||||
// Delete ancient chain
|
||||
chainPaths := []string{filepath.Join(ancientDir, rawdb.ChainFreezerName)}
|
||||
chainPaths := []string{filepath.Join(
|
||||
ancientDir,
|
||||
rawdb.ChainFreezerName,
|
||||
)}
|
||||
confirmAndRemoveDB(chainPaths, "ancient chain", ctx, removeChainDataFlag.Name)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ func testConsoleLogging(t *testing.T, format string, tStart, tEnd int) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer readFile.Close()
|
||||
wantLines := split(readFile)
|
||||
haveLines := split(bytes.NewBuffer(haveB))
|
||||
for i, want := range wantLines {
|
||||
|
|
@ -109,6 +110,7 @@ func TestJsonLogging(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer readFile.Close()
|
||||
wantLines := split(readFile)
|
||||
haveLines := split(bytes.NewBuffer(haveB))
|
||||
for i, wantLine := range wantLines {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/internal/debug"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
|
|
@ -138,7 +137,7 @@ var (
|
|||
utils.DeveloperPeriodFlag,
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.VMTraceFlag,
|
||||
utils.VMTraceConfigFlag,
|
||||
utils.VMTraceJsonConfigFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
utils.NoCompactionFlag,
|
||||
|
|
@ -348,10 +347,10 @@ func geth(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
prepare(ctx)
|
||||
stack, backend := makeFullNode(ctx)
|
||||
stack := makeFullNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
startNode(ctx, stack, backend, false)
|
||||
startNode(ctx, stack, false)
|
||||
stack.Wait()
|
||||
return nil
|
||||
}
|
||||
|
|
@ -359,7 +358,7 @@ func geth(ctx *cli.Context) error {
|
|||
// startNode boots up the system node and all registered protocols, after which
|
||||
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
||||
// miner.
|
||||
func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isConsole bool) {
|
||||
func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
|
||||
debug.Memsize.Add("node", stack)
|
||||
|
||||
// Start up the node itself
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ data, and verifies that all snapshot storage data has a corresponding account.
|
|||
},
|
||||
{
|
||||
Name: "inspect-account",
|
||||
Usage: "Check all snapshot layers for the a specific account",
|
||||
Usage: "Check all snapshot layers for the specific account",
|
||||
ArgsUsage: "<address | hash>",
|
||||
Action: checkAccount,
|
||||
Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags),
|
||||
|
|
|
|||
32
cmd/geth/testdata/vcheck/vulnerabilities.json
vendored
32
cmd/geth/testdata/vcheck/vulnerabilities.json
vendored
|
|
@ -166,5 +166,37 @@
|
|||
"severity": "Low",
|
||||
"CVE": "CVE-2022-29177",
|
||||
"check": "(Geth\\/v1\\.10\\.(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16)-.*)$"
|
||||
},
|
||||
{
|
||||
"name": "DoS via malicious p2p message",
|
||||
"uid": "GETH-2023-01",
|
||||
"summary": "A vulnerable node can be made to consume unbounded amounts of memory when handling specially crafted p2p messages sent from an attacker node.",
|
||||
"description": "The p2p handler spawned a new goroutine to respond to ping requests. By flooding a node with ping requests, an unbounded number of goroutines can be created, leading to resource exhaustion and potentially crash due to OOM.",
|
||||
"links": [
|
||||
"https://github.com/ethereum/go-ethereum/security/advisories/GHSA-ppjg-v974-84cm",
|
||||
"https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities"
|
||||
],
|
||||
"introduced": "v1.10.0",
|
||||
"fixed": "v1.12.1",
|
||||
"published": "2023-09-06",
|
||||
"severity": "High",
|
||||
"CVE": "CVE-2023-40591",
|
||||
"check": "(Geth\\/v1\\.(10|11)\\..*)|(Geth\\/v1\\.12\\.0-.*)$"
|
||||
},
|
||||
{
|
||||
"name": "DoS via malicious p2p message",
|
||||
"uid": "GETH-2024-01",
|
||||
"summary": "A vulnerable node can be made to consume very large amounts of memory when handling specially crafted p2p messages sent from an attacker node.",
|
||||
"description": "A vulnerable node can be made to consume very large amounts of memory when handling specially crafted p2p messages sent from an attacker node. Full details will be available at the Github security [advisory](https://github.com/ethereum/go-ethereum/security/advisories/GHSA-4xc9-8hmq-j652)",
|
||||
"links": [
|
||||
"https://github.com/ethereum/go-ethereum/security/advisories/GHSA-4xc9-8hmq-j652",
|
||||
"https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities"
|
||||
],
|
||||
"introduced": "v1.10.0",
|
||||
"fixed": "v1.13.15",
|
||||
"published": "2024-05-06",
|
||||
"severity": "High",
|
||||
"CVE": "CVE-2024-32972",
|
||||
"check": "(Geth\\/v1\\.(10|11|12)\\..*)|(Geth\\/v1\\.13\\.\\d-.*)|(Geth\\/v1\\.13\\.1(0|1|2|3|4)-.*)$"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/gballet/go-verkle"
|
||||
"github.com/ethereum/go-verkle"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -544,8 +544,8 @@ var (
|
|||
Usage: "Name of tracer which should record internal VM operations (costly)",
|
||||
Category: flags.VMCategory,
|
||||
}
|
||||
VMTraceConfigFlag = &cli.StringFlag{
|
||||
Name: "vmtrace.config",
|
||||
VMTraceJsonConfigFlag = &cli.StringFlag{
|
||||
Name: "vmtrace.jsonconfig",
|
||||
Usage: "Tracer configuration (JSON)",
|
||||
Category: flags.VMCategory,
|
||||
}
|
||||
|
|
@ -661,7 +661,7 @@ var (
|
|||
}
|
||||
HTTPPathPrefixFlag = &cli.StringFlag{
|
||||
Name: "http.rpcprefix",
|
||||
Usage: "HTTP path path prefix on which JSON-RPC is served. Use '/' to serve on all paths.",
|
||||
Usage: "HTTP path prefix on which JSON-RPC is served. Use '/' to serve on all paths.",
|
||||
Value: "",
|
||||
Category: flags.APICategory,
|
||||
}
|
||||
|
|
@ -1872,13 +1872,15 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
Fatalf("Could not read genesis from database: %v", err)
|
||||
}
|
||||
if !genesis.Config.TerminalTotalDifficultyPassed {
|
||||
Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficultyPassed must be true in developer mode")
|
||||
Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficultyPassed must be true")
|
||||
}
|
||||
if genesis.Config.TerminalTotalDifficulty == nil {
|
||||
Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficulty must be specified.")
|
||||
Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficulty must be specified")
|
||||
} else if genesis.Config.TerminalTotalDifficulty.Cmp(big.NewInt(0)) != 0 {
|
||||
Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficulty must be 0")
|
||||
}
|
||||
if genesis.Difficulty.Cmp(genesis.Config.TerminalTotalDifficulty) != 1 {
|
||||
Fatalf("Bad developer-mode genesis configuration: genesis block difficulty must be > terminalTotalDifficulty")
|
||||
if genesis.Difficulty.Cmp(big.NewInt(0)) != 0 {
|
||||
Fatalf("Bad developer-mode genesis configuration: difficulty must be 0")
|
||||
}
|
||||
}
|
||||
chaindb.Close()
|
||||
|
|
@ -1903,12 +1905,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
if ctx.IsSet(VMTraceFlag.Name) {
|
||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||
var config string
|
||||
if ctx.IsSet(VMTraceConfigFlag.Name) {
|
||||
config = ctx.String(VMTraceConfigFlag.Name)
|
||||
if ctx.IsSet(VMTraceJsonConfigFlag.Name) {
|
||||
config = ctx.String(VMTraceJsonConfigFlag.Name)
|
||||
}
|
||||
|
||||
cfg.VMTrace = name
|
||||
cfg.VMTraceConfig = config
|
||||
cfg.VMTraceJsonConfig = config
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1959,7 +1961,7 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf
|
|||
})
|
||||
stack.RegisterAPIs([]rpc.API{{
|
||||
Namespace: "eth",
|
||||
Service: filters.NewFilterAPI(filterSystem, false),
|
||||
Service: filters.NewFilterAPI(filterSystem),
|
||||
}})
|
||||
return filterSystem
|
||||
}
|
||||
|
|
@ -2192,8 +2194,8 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
|||
if ctx.IsSet(VMTraceFlag.Name) {
|
||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||
var config json.RawMessage
|
||||
if ctx.IsSet(VMTraceConfigFlag.Name) {
|
||||
config = json.RawMessage(ctx.String(VMTraceConfigFlag.Name))
|
||||
if ctx.IsSet(VMTraceJsonConfigFlag.Name) {
|
||||
config = json.RawMessage(ctx.String(VMTraceJsonConfigFlag.Name))
|
||||
}
|
||||
t, err := tracers.LiveDirectory.New(name, config)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ func TestHistoryImportAndExport(t *testing.T) {
|
|||
t.Fatalf("unable to initialize chain: %v", err)
|
||||
}
|
||||
if _, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("error insterting chain: %v", err)
|
||||
t.Fatalf("error inserting chain: %v", err)
|
||||
}
|
||||
|
||||
// Make temp directory for era files.
|
||||
|
|
@ -162,8 +162,7 @@ func TestHistoryImportAndExport(t *testing.T) {
|
|||
}
|
||||
|
||||
// Now import Era.
|
||||
freezer := t.TempDir()
|
||||
db2, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), freezer, "", false)
|
||||
db2, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,9 +115,7 @@ func (c *BasicLRU[K, V]) Peek(key K) (value V, ok bool) {
|
|||
// Purge empties the cache.
|
||||
func (c *BasicLRU[K, V]) Purge() {
|
||||
c.list.init()
|
||||
for k := range c.items {
|
||||
delete(c.items, k)
|
||||
}
|
||||
clear(c.items)
|
||||
}
|
||||
|
||||
// Remove drops an item from the cache. Returns true if the key was present in cache.
|
||||
|
|
|
|||
|
|
@ -180,9 +180,9 @@ func BenchmarkByteAtOld(b *testing.B) {
|
|||
func TestReadBits(t *testing.T) {
|
||||
check := func(input string) {
|
||||
want, _ := hex.DecodeString(input)
|
||||
int, _ := new(big.Int).SetString(input, 16)
|
||||
n, _ := new(big.Int).SetString(input, 16)
|
||||
buf := make([]byte, len(want))
|
||||
ReadBits(int, buf)
|
||||
ReadBits(n, buf)
|
||||
if !bytes.Equal(buf, want) {
|
||||
t.Errorf("have: %x\nwant: %x", buf, want)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@ func (i *HexOrDecimal64) UnmarshalJSON(input []byte) error {
|
|||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (i *HexOrDecimal64) UnmarshalText(input []byte) error {
|
||||
int, ok := ParseUint64(string(input))
|
||||
n, ok := ParseUint64(string(input))
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid hex or decimal integer %q", input)
|
||||
}
|
||||
*i = HexOrDecimal64(int)
|
||||
*i = HexOrDecimal64(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"container/heap"
|
||||
)
|
||||
|
||||
// Priority queue data structure.
|
||||
// Prque is a priority queue data structure.
|
||||
type Prque[P cmp.Ordered, V any] struct {
|
||||
cont *sstack[P, V]
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ func New[P cmp.Ordered, V any](setIndex SetIndexCallback[V]) *Prque[P, V] {
|
|||
return &Prque[P, V]{newSstack[P, V](setIndex)}
|
||||
}
|
||||
|
||||
// Pushes a value with a given priority into the queue, expanding if necessary.
|
||||
// Push a value with a given priority into the queue, expanding if necessary.
|
||||
func (p *Prque[P, V]) Push(data V, priority P) {
|
||||
heap.Push(p.cont, &item[P, V]{data, priority})
|
||||
}
|
||||
|
|
@ -43,14 +43,14 @@ func (p *Prque[P, V]) Peek() (V, P) {
|
|||
return item.value, item.priority
|
||||
}
|
||||
|
||||
// Pops the value with the greatest priority off the stack and returns it.
|
||||
// Pop the value with the greatest priority off the stack and returns it.
|
||||
// Currently no shrinking is done.
|
||||
func (p *Prque[P, V]) Pop() (V, P) {
|
||||
item := heap.Pop(p.cont).(*item[P, V])
|
||||
return item.value, item.priority
|
||||
}
|
||||
|
||||
// Pops only the item from the queue, dropping the associated priority value.
|
||||
// PopItem pops only the item from the queue, dropping the associated priority value.
|
||||
func (p *Prque[P, V]) PopItem() V {
|
||||
return heap.Pop(p.cont).(*item[P, V]).value
|
||||
}
|
||||
|
|
@ -60,17 +60,17 @@ func (p *Prque[P, V]) Remove(i int) V {
|
|||
return heap.Remove(p.cont, i).(*item[P, V]).value
|
||||
}
|
||||
|
||||
// Checks whether the priority queue is empty.
|
||||
// Empty checks whether the priority queue is empty.
|
||||
func (p *Prque[P, V]) Empty() bool {
|
||||
return p.cont.Len() == 0
|
||||
}
|
||||
|
||||
// Returns the number of element in the priority queue.
|
||||
// Size returns the number of element in the priority queue.
|
||||
func (p *Prque[P, V]) Size() int {
|
||||
return p.cont.Len()
|
||||
}
|
||||
|
||||
// Clears the contents of the priority queue.
|
||||
// Reset clears the contents of the priority queue.
|
||||
func (p *Prque[P, V]) Reset() {
|
||||
*p = *New[P, V](p.cont.setIndex)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func newSstack[P cmp.Ordered, V any](setIndex SetIndexCallback[V]) *sstack[P, V]
|
|||
return result
|
||||
}
|
||||
|
||||
// Pushes a value onto the stack, expanding it if necessary. Required by
|
||||
// Push a value onto the stack, expanding it if necessary. Required by
|
||||
// heap.Interface.
|
||||
func (s *sstack[P, V]) Push(data any) {
|
||||
if s.size == s.capacity {
|
||||
|
|
@ -69,7 +69,7 @@ func (s *sstack[P, V]) Push(data any) {
|
|||
s.size++
|
||||
}
|
||||
|
||||
// Pops a value off the stack and returns it. Currently no shrinking is done.
|
||||
// Pop a value off the stack and returns it. Currently no shrinking is done.
|
||||
// Required by heap.Interface.
|
||||
func (s *sstack[P, V]) Pop() (res any) {
|
||||
s.size--
|
||||
|
|
@ -85,18 +85,18 @@ func (s *sstack[P, V]) Pop() (res any) {
|
|||
return
|
||||
}
|
||||
|
||||
// Returns the length of the stack. Required by sort.Interface.
|
||||
// Len returns the length of the stack. Required by sort.Interface.
|
||||
func (s *sstack[P, V]) Len() int {
|
||||
return s.size
|
||||
}
|
||||
|
||||
// Compares the priority of two elements of the stack (higher is first).
|
||||
// Less compares the priority of two elements of the stack (higher is first).
|
||||
// Required by sort.Interface.
|
||||
func (s *sstack[P, V]) Less(i, j int) bool {
|
||||
return s.blocks[i/blockSize][i%blockSize].priority > s.blocks[j/blockSize][j%blockSize].priority
|
||||
}
|
||||
|
||||
// Swaps two elements in the stack. Required by sort.Interface.
|
||||
// Swap two elements in the stack. Required by sort.Interface.
|
||||
func (s *sstack[P, V]) Swap(i, j int) {
|
||||
ib, io, jb, jo := i/blockSize, i%blockSize, j/blockSize, j%blockSize
|
||||
a, b := s.blocks[jb][jo], s.blocks[ib][io]
|
||||
|
|
@ -107,7 +107,7 @@ func (s *sstack[P, V]) Swap(i, j int) {
|
|||
s.blocks[ib][io], s.blocks[jb][jo] = a, b
|
||||
}
|
||||
|
||||
// Resets the stack, effectively clearing its contents.
|
||||
// Reset the stack, effectively clearing its contents.
|
||||
func (s *sstack[P, V]) Reset() {
|
||||
*s = *newSstack[P, V](s.setIndex)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -388,7 +388,7 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
|||
header.Root = state.IntermediateRoot(true)
|
||||
|
||||
// Assemble and return the final block.
|
||||
return types.NewBlockWithWithdrawals(header, body.Transactions, body.Uncles, receipts, body.Withdrawals, trie.NewStackTrie(nil)), nil
|
||||
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), nil
|
||||
}
|
||||
|
||||
// Seal generates a new sealing request for the given input block and pushes
|
||||
|
|
|
|||
|
|
@ -597,7 +597,7 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
|
|||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||
|
||||
// Assemble and return the final block for sealing.
|
||||
return types.NewBlock(header, body.Transactions, nil, receipts, trie.NewStackTrie(nil)), nil
|
||||
return types.NewBlock(header, &types.Body{Transactions: body.Transactions}, receipts, trie.NewStackTrie(nil)), nil
|
||||
}
|
||||
|
||||
// Authorize injects a private key into the consensus engine to mint new blocks
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package clique
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
|
|
@ -108,28 +109,16 @@ func (s *Snapshot) store(db ethdb.Database) error {
|
|||
|
||||
// copy creates a deep copy of the snapshot, though not the individual votes.
|
||||
func (s *Snapshot) copy() *Snapshot {
|
||||
cpy := &Snapshot{
|
||||
return &Snapshot{
|
||||
config: s.config,
|
||||
sigcache: s.sigcache,
|
||||
Number: s.Number,
|
||||
Hash: s.Hash,
|
||||
Signers: make(map[common.Address]struct{}),
|
||||
Recents: make(map[uint64]common.Address),
|
||||
Votes: make([]*Vote, len(s.Votes)),
|
||||
Tally: make(map[common.Address]Tally),
|
||||
Signers: maps.Clone(s.Signers),
|
||||
Recents: maps.Clone(s.Recents),
|
||||
Votes: slices.Clone(s.Votes),
|
||||
Tally: maps.Clone(s.Tally),
|
||||
}
|
||||
for signer := range s.Signers {
|
||||
cpy.Signers[signer] = struct{}{}
|
||||
}
|
||||
for block, signer := range s.Recents {
|
||||
cpy.Recents[block] = signer
|
||||
}
|
||||
for address, tally := range s.Tally {
|
||||
cpy.Tally[address] = tally
|
||||
}
|
||||
copy(cpy.Votes, s.Votes)
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
// validVote returns whether it makes sense to cast the specified vote in the
|
||||
|
|
|
|||
|
|
@ -520,7 +520,7 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
|||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||
|
||||
// Header seems complete, assemble into a block and return
|
||||
return types.NewBlock(header, body.Transactions, body.Uncles, receipts, trie.NewStackTrie(nil)), nil
|
||||
return types.NewBlock(header, &types.Body{Transactions: body.Transactions, Uncles: body.Uncles}, receipts, trie.NewStackTrie(nil)), nil
|
||||
}
|
||||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
|
|||
if rbloom != header.Bloom {
|
||||
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
|
||||
}
|
||||
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
|
||||
// The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
|
||||
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
|
||||
if receiptSha != header.ReceiptHash {
|
||||
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
|
||||
|
|
|
|||
|
|
@ -154,12 +154,10 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
|||
preHeaders := make([]*types.Header, len(preBlocks))
|
||||
for i, block := range preBlocks {
|
||||
preHeaders[i] = block.Header()
|
||||
t.Logf("Pre-merge header: %d", block.NumberU64())
|
||||
}
|
||||
postHeaders := make([]*types.Header, len(postBlocks))
|
||||
for i, block := range postBlocks {
|
||||
postHeaders[i] = block.Header()
|
||||
t.Logf("Post-merge header: %d", block.NumberU64())
|
||||
}
|
||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ var (
|
|||
accountCommitTimer = metrics.NewRegisteredResettingTimer("chain/account/commits", nil)
|
||||
|
||||
storageReadTimer = metrics.NewRegisteredResettingTimer("chain/storage/reads", nil)
|
||||
storageHashTimer = metrics.NewRegisteredResettingTimer("chain/storage/hashes", nil)
|
||||
storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil)
|
||||
storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil)
|
||||
|
||||
|
|
@ -101,7 +100,6 @@ const (
|
|||
blockCacheLimit = 256
|
||||
receiptsCacheLimit = 32
|
||||
txLookupCacheLimit = 1024
|
||||
TriesInMemory = 128
|
||||
|
||||
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
|
||||
//
|
||||
|
|
@ -244,6 +242,8 @@ type BlockChain struct {
|
|||
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
|
||||
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
|
||||
blockCache *lru.Cache[common.Hash, *types.Block]
|
||||
|
||||
txLookupLock sync.RWMutex
|
||||
txLookupCache *lru.Cache[common.Hash, txLookup]
|
||||
|
||||
wg sync.WaitGroup
|
||||
|
|
@ -411,37 +411,18 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
// it in advance.
|
||||
bc.engine.VerifyHeader(bc, bc.CurrentHeader())
|
||||
|
||||
// Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
|
||||
for hash := range BadHashes {
|
||||
if header := bc.GetHeaderByHash(hash); header != nil {
|
||||
// get the canonical block corresponding to the offending header's number
|
||||
headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64())
|
||||
// make sure the headerByNumber (if present) is in our current canonical chain
|
||||
if headerByNumber != nil && headerByNumber.Hash() == header.Hash() {
|
||||
log.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash)
|
||||
if err := bc.SetHead(header.Number.Uint64() - 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Error("Chain rewind was successful, resuming normal operation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bc.logger != nil && bc.logger.OnBlockchainInit != nil {
|
||||
bc.logger.OnBlockchainInit(chainConfig)
|
||||
}
|
||||
|
||||
if bc.logger != nil && bc.logger.OnGenesisBlock != nil {
|
||||
if block := bc.CurrentBlock(); block.Number.Uint64() == 0 {
|
||||
alloc, err := getGenesisState(bc.db, block.Hash())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get genesis state: %w", err)
|
||||
}
|
||||
|
||||
if alloc == nil {
|
||||
return nil, fmt.Errorf("live blockchain tracer requires genesis alloc to be set")
|
||||
return nil, errors.New("live blockchain tracer requires genesis alloc to be set")
|
||||
}
|
||||
|
||||
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
|
||||
}
|
||||
}
|
||||
|
|
@ -1146,7 +1127,7 @@ func (bc *BlockChain) Stop() {
|
|||
if !bc.cacheConfig.TrieDirtyDisabled {
|
||||
triedb := bc.triedb
|
||||
|
||||
for _, offset := range []uint64{0, 1, TriesInMemory - 1} {
|
||||
for _, offset := range []uint64{0, 1, state.TriesInMemory - 1} {
|
||||
if number := bc.CurrentBlock().Number.Uint64(); number > offset {
|
||||
recent := bc.GetBlockByNumber(number - offset)
|
||||
|
||||
|
|
@ -1170,6 +1151,10 @@ func (bc *BlockChain) Stop() {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Allow tracers to clean-up and release resources.
|
||||
if bc.logger != nil && bc.logger.OnClose != nil {
|
||||
bc.logger.OnClose()
|
||||
}
|
||||
// Close the trie database, release all the held resources as the last step.
|
||||
if err := bc.triedb.Close(); err != nil {
|
||||
log.Error("Failed to close trie database", "err", err)
|
||||
|
|
@ -1323,7 +1308,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
// Delete block data from the main database.
|
||||
var (
|
||||
batch = bc.db.NewBatch()
|
||||
canonHashes = make(map[common.Hash]struct{})
|
||||
canonHashes = make(map[common.Hash]struct{}, len(blockChain))
|
||||
)
|
||||
for _, block := range blockChain {
|
||||
canonHashes[block.Hash()] = struct{}{}
|
||||
|
|
@ -1466,7 +1451,7 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
|
|||
|
||||
// writeBlockWithState writes block, metadata and corresponding state data to the
|
||||
// database.
|
||||
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) error {
|
||||
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, statedb *state.StateDB) error {
|
||||
// Calculate the total difficulty of the block
|
||||
ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||
if ptd == nil {
|
||||
|
|
@ -1483,12 +1468,12 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), externTd)
|
||||
rawdb.WriteBlock(blockBatch, block)
|
||||
rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts)
|
||||
rawdb.WritePreimages(blockBatch, state.Preimages())
|
||||
rawdb.WritePreimages(blockBatch, statedb.Preimages())
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to write block into disk", "err", err)
|
||||
}
|
||||
// Commit all cached state changes into underlying memory database.
|
||||
root, err := state.Commit(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()))
|
||||
root, err := statedb.Commit(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -1507,7 +1492,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
|
||||
// Flush limits are not considered for the first TriesInMemory blocks.
|
||||
current := block.NumberU64()
|
||||
if current <= TriesInMemory {
|
||||
if current <= state.TriesInMemory {
|
||||
return nil
|
||||
}
|
||||
// If we exceeded our memory allowance, flush matured singleton nodes to disk
|
||||
|
|
@ -1519,7 +1504,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
bc.triedb.Cap(limit - ethdb.IdealBatchSize)
|
||||
}
|
||||
// Find the next state trie we need to commit
|
||||
chosen := current - TriesInMemory
|
||||
chosen := current - state.TriesInMemory
|
||||
flushInterval := time.Duration(bc.flushInterval.Load())
|
||||
// If we exceeded time allowance, flush an entire trie to disk
|
||||
if bc.gcproc > flushInterval {
|
||||
|
|
@ -1531,8 +1516,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
} else {
|
||||
// If we're exceeding limits but haven't reached a large enough memory gap,
|
||||
// warn the user that the system is becoming unstable.
|
||||
if chosen < bc.lastWrite+TriesInMemory && bc.gcproc >= 2*flushInterval {
|
||||
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", flushInterval, "optimum", float64(chosen-bc.lastWrite)/TriesInMemory)
|
||||
if chosen < bc.lastWrite+state.TriesInMemory && bc.gcproc >= 2*flushInterval {
|
||||
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", flushInterval, "optimum", float64(chosen-bc.lastWrite)/state.TriesInMemory)
|
||||
}
|
||||
// Flush an entire trie and restart the counters
|
||||
bc.triedb.Commit(header.Root, true)
|
||||
|
|
@ -1760,11 +1745,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
|||
log.Debug("Abort during block processing")
|
||||
break
|
||||
}
|
||||
// If the header is a banned one, straight out abort
|
||||
if BadHashes[block.Hash()] {
|
||||
bc.reportBlock(block, nil, ErrBannedHash)
|
||||
return it.index, ErrBannedHash
|
||||
}
|
||||
// If the block is known (in the middle of the chain), it's a special case for
|
||||
// Clique blocks where they can share state among each other, so importing an
|
||||
// older block might complete the state of the subsequent one. In this case,
|
||||
|
|
@ -1825,8 +1805,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
|||
}
|
||||
statedb.SetLogger(bc.logger)
|
||||
|
||||
// Enable prefetching to pull in trie node paths while processing transactions
|
||||
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||
// useless due to the intermediate root hashing after each transaction.
|
||||
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||
statedb.StartPrefetcher("chain")
|
||||
}
|
||||
activeState = statedb
|
||||
|
||||
// If we have a followup block, run that against the current state to pre-cache
|
||||
|
|
@ -1955,8 +1939,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
|||
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
|
||||
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
|
||||
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
|
||||
storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation)
|
||||
triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing
|
||||
triehash := statedb.AccountHashes // The time spent on tries hashing
|
||||
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
|
||||
trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read
|
||||
trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read
|
||||
|
|
@ -1983,7 +1966,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
|||
snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them
|
||||
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
|
||||
|
||||
blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
||||
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
||||
blockInsertTimer.UpdateSince(start)
|
||||
|
||||
return &blockProcessingResult{usedGas: usedGas, procTime: proctime, status: status}, nil
|
||||
|
|
@ -2290,14 +2273,14 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error {
|
|||
// rewind the canonical chain to a lower point.
|
||||
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain))
|
||||
}
|
||||
// Reset the tx lookup cache in case to clear stale txlookups.
|
||||
// This is done before writing any new chain data to avoid the
|
||||
// weird scenario that canonical chain is changed while the
|
||||
// stale lookups are still cached.
|
||||
bc.txLookupCache.Purge()
|
||||
// Acquire the tx-lookup lock before mutation. This step is essential
|
||||
// as the txlookups should be changed atomically, and all subsequent
|
||||
// reads should be blocked until the mutation is complete.
|
||||
bc.txLookupLock.Lock()
|
||||
|
||||
// Insert the new chain(except the head block(reverse order)),
|
||||
// taking care of the proper incremental order.
|
||||
// Insert the new chain segment in incremental order, from the old
|
||||
// to the new. The new chain head (newChain[0]) is not inserted here,
|
||||
// as it will be handled separately outside of this function
|
||||
for i := len(newChain) - 1; i >= 1; i-- {
|
||||
// Insert the block in the canonical way, re-writing history
|
||||
bc.writeHeadBlock(newChain[i])
|
||||
|
|
@ -2334,6 +2317,11 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error {
|
|||
if err := indexesBatch.Write(); err != nil {
|
||||
log.Crit("Failed to delete useless indexes", "err", err)
|
||||
}
|
||||
// Reset the tx lookup cache to clear stale txlookup cache.
|
||||
bc.txLookupCache.Purge()
|
||||
|
||||
// Release the tx-lookup lock after mutation.
|
||||
bc.txLookupLock.Unlock()
|
||||
|
||||
// Send out events for logs from the old canon chain, and 'reborn'
|
||||
// logs from the new canon chain. The number of logs can be very
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ func (it *insertIterator) current() *types.Header {
|
|||
return it.chain[it.index].Header()
|
||||
}
|
||||
|
||||
// first returns the first block in the it.
|
||||
// first returns the first block in it.
|
||||
func (it *insertIterator) first() *types.Block {
|
||||
return it.chain[0]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,6 +266,9 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max
|
|||
// transaction indexing is already finished. The transaction is not existent
|
||||
// from the node's perspective.
|
||||
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
|
||||
bc.txLookupLock.RLock()
|
||||
defer bc.txLookupLock.RUnlock()
|
||||
|
||||
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
||||
if item, exist := bc.txLookupCache.Get(hash); exist {
|
||||
return item.lookup, item.transaction, nil
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ package core
|
|||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -1966,7 +1966,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
|||
|
||||
// Create a temporary persistent database
|
||||
datadir := t.TempDir()
|
||||
ancient := path.Join(datadir, "ancient")
|
||||
ancient := filepath.Join(datadir, "ancient")
|
||||
|
||||
db, err := rawdb.Open(rawdb.OpenOptions{
|
||||
Directory: datadir,
|
||||
|
|
|
|||
|
|
@ -658,107 +658,6 @@ func testReorg(t *testing.T, first, second []int64, td int64, full bool, scheme
|
|||
}
|
||||
}
|
||||
|
||||
// Tests that the insertion functions detect banned hashes.
|
||||
func TestBadHeaderHashes(t *testing.T) {
|
||||
testBadHashes(t, false, rawdb.HashScheme)
|
||||
testBadHashes(t, false, rawdb.PathScheme)
|
||||
}
|
||||
func TestBadBlockHashes(t *testing.T) {
|
||||
testBadHashes(t, true, rawdb.HashScheme)
|
||||
testBadHashes(t, true, rawdb.PathScheme)
|
||||
}
|
||||
|
||||
func testBadHashes(t *testing.T, full bool, scheme string) {
|
||||
// Create a pristine chain and database
|
||||
genDb, _, blockchain, err := newCanonical(ethash.NewFaker(), 0, full, scheme)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create pristine chain: %v", err)
|
||||
}
|
||||
defer blockchain.Stop()
|
||||
|
||||
// Create a chain, ban a hash and try to import
|
||||
if full {
|
||||
blocks := makeBlockChain(blockchain.chainConfig, blockchain.GetBlockByHash(blockchain.CurrentBlock().Hash()), 3, ethash.NewFaker(), genDb, 10)
|
||||
|
||||
BadHashes[blocks[2].Header().Hash()] = true
|
||||
defer func() { delete(BadHashes, blocks[2].Header().Hash()) }()
|
||||
|
||||
_, err = blockchain.InsertChain(blocks)
|
||||
} else {
|
||||
headers := makeHeaderChain(blockchain.chainConfig, blockchain.CurrentHeader(), 3, ethash.NewFaker(), genDb, 10)
|
||||
|
||||
BadHashes[headers[2].Hash()] = true
|
||||
defer func() { delete(BadHashes, headers[2].Hash()) }()
|
||||
|
||||
_, err = blockchain.InsertHeaderChain(headers)
|
||||
}
|
||||
if !errors.Is(err, ErrBannedHash) {
|
||||
t.Errorf("error mismatch: have: %v, want: %v", err, ErrBannedHash)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that bad hashes are detected on boot, and the chain rolled back to a
|
||||
// good state prior to the bad hash.
|
||||
func TestReorgBadHeaderHashes(t *testing.T) {
|
||||
testReorgBadHashes(t, false, rawdb.HashScheme)
|
||||
testReorgBadHashes(t, false, rawdb.PathScheme)
|
||||
}
|
||||
func TestReorgBadBlockHashes(t *testing.T) {
|
||||
testReorgBadHashes(t, true, rawdb.HashScheme)
|
||||
testReorgBadHashes(t, true, rawdb.PathScheme)
|
||||
}
|
||||
|
||||
func testReorgBadHashes(t *testing.T, full bool, scheme string) {
|
||||
// Create a pristine chain and database
|
||||
genDb, gspec, blockchain, err := newCanonical(ethash.NewFaker(), 0, full, scheme)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create pristine chain: %v", err)
|
||||
}
|
||||
// Create a chain, import and ban afterwards
|
||||
headers := makeHeaderChain(blockchain.chainConfig, blockchain.CurrentHeader(), 4, ethash.NewFaker(), genDb, 10)
|
||||
blocks := makeBlockChain(blockchain.chainConfig, blockchain.GetBlockByHash(blockchain.CurrentBlock().Hash()), 4, ethash.NewFaker(), genDb, 10)
|
||||
|
||||
if full {
|
||||
if _, err = blockchain.InsertChain(blocks); err != nil {
|
||||
t.Errorf("failed to import blocks: %v", err)
|
||||
}
|
||||
if blockchain.CurrentBlock().Hash() != blocks[3].Hash() {
|
||||
t.Errorf("last block hash mismatch: have: %x, want %x", blockchain.CurrentBlock().Hash(), blocks[3].Header().Hash())
|
||||
}
|
||||
BadHashes[blocks[3].Header().Hash()] = true
|
||||
defer func() { delete(BadHashes, blocks[3].Header().Hash()) }()
|
||||
} else {
|
||||
if _, err = blockchain.InsertHeaderChain(headers); err != nil {
|
||||
t.Errorf("failed to import headers: %v", err)
|
||||
}
|
||||
if blockchain.CurrentHeader().Hash() != headers[3].Hash() {
|
||||
t.Errorf("last header hash mismatch: have: %x, want %x", blockchain.CurrentHeader().Hash(), headers[3].Hash())
|
||||
}
|
||||
BadHashes[headers[3].Hash()] = true
|
||||
defer func() { delete(BadHashes, headers[3].Hash()) }()
|
||||
}
|
||||
blockchain.Stop()
|
||||
|
||||
// Create a new BlockChain and check that it rolled back the state.
|
||||
ncm, err := NewBlockChain(blockchain.db, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new chain manager: %v", err)
|
||||
}
|
||||
if full {
|
||||
if ncm.CurrentBlock().Hash() != blocks[2].Header().Hash() {
|
||||
t.Errorf("last block hash mismatch: have: %x, want %x", ncm.CurrentBlock().Hash(), blocks[2].Header().Hash())
|
||||
}
|
||||
if blocks[2].Header().GasLimit != ncm.GasLimit() {
|
||||
t.Errorf("last block gasLimit mismatch: have: %d, want %d", ncm.GasLimit(), blocks[2].Header().GasLimit)
|
||||
}
|
||||
} else {
|
||||
if ncm.CurrentHeader().Hash() != headers[2].Hash() {
|
||||
t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash())
|
||||
}
|
||||
}
|
||||
ncm.Stop()
|
||||
}
|
||||
|
||||
// Tests chain insertions in the face of one entity containing an invalid nonce.
|
||||
func TestHeadersInsertNonceError(t *testing.T) {
|
||||
testInsertNonceError(t, false, rawdb.HashScheme)
|
||||
|
|
@ -886,7 +785,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
|||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
// Freezer style fast import the chain.
|
||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
|
||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
|
|
@ -976,12 +875,12 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
|||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
)
|
||||
height := uint64(1024)
|
||||
height := uint64(64)
|
||||
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil)
|
||||
|
||||
// makeDb creates a db instance for testing.
|
||||
makeDb := func() ethdb.Database {
|
||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
|
||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
|
|
@ -1813,7 +1712,7 @@ func TestTrieForkGC(t *testing.T) {
|
|||
Config: params.TestChainConfig,
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*state.TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
// Generate a bunch of fork blocks, each side forking from the canonical chain
|
||||
forks := make([]*types.Block, len(blocks))
|
||||
|
|
@ -1841,7 +1740,7 @@ func TestTrieForkGC(t *testing.T) {
|
|||
}
|
||||
}
|
||||
// Dereference all the recent tries and ensure no past trie is left in
|
||||
for i := 0; i < TriesInMemory; i++ {
|
||||
for i := 0; i < state.TriesInMemory; i++ {
|
||||
chain.TrieDB().Dereference(blocks[len(blocks)-1-i].Root())
|
||||
chain.TrieDB().Dereference(forks[len(blocks)-1-i].Root())
|
||||
}
|
||||
|
|
@ -1865,11 +1764,11 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) {
|
|||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
genDb, shared, _ := GenerateChainWithGenesis(genesis, engine, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
original, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
||||
competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
||||
original, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*state.TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
||||
competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*state.TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
||||
|
||||
// Import the shared chain and the original canonical one
|
||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
|
||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
defer db.Close()
|
||||
|
||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
|
|
@ -1905,7 +1804,7 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) {
|
|||
}
|
||||
// In path-based trie database implementation, it will keep 128 diff + 1 disk
|
||||
// layers, totally 129 latest states available. In hash-based it's 128.
|
||||
states := TriesInMemory
|
||||
states := state.TriesInMemory
|
||||
if scheme == rawdb.PathScheme {
|
||||
states = states + 1
|
||||
}
|
||||
|
|
@ -1934,7 +1833,7 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
|||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{address: {Balance: funds}}}
|
||||
)
|
||||
height := uint64(1024)
|
||||
height := uint64(64)
|
||||
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil)
|
||||
|
||||
// Import the chain as a ancient-first node and ensure all pointers are updated
|
||||
|
|
@ -2009,7 +1908,7 @@ func testInsertReceiptChainRollback(t *testing.T, scheme string) {
|
|||
}
|
||||
|
||||
// Set up a BlockChain that uses the ancient store.
|
||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
|
||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
|
|
@ -2073,13 +1972,13 @@ func testLowDiffLongChain(t *testing.T, scheme string) {
|
|||
}
|
||||
// We must use a pretty long chain to ensure that the fork doesn't overtake us
|
||||
// until after at least 128 blocks post tip
|
||||
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 6*TriesInMemory, func(i int, b *BlockGen) {
|
||||
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 6*state.TriesInMemory, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{1})
|
||||
b.OffsetTime(-9)
|
||||
})
|
||||
|
||||
// Import the canonical chain
|
||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
|
||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
defer diskdb.Close()
|
||||
|
||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
|
|
@ -2093,7 +1992,7 @@ func testLowDiffLongChain(t *testing.T, scheme string) {
|
|||
}
|
||||
// Generate fork chain, starting from an early block
|
||||
parent := blocks[10]
|
||||
fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 8*TriesInMemory, func(i int, b *BlockGen) {
|
||||
fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 8*state.TriesInMemory, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{2})
|
||||
})
|
||||
|
||||
|
|
@ -2156,7 +2055,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
|||
// Set the terminal total difficulty in the config
|
||||
gspec.Config.TerminalTotalDifficulty = big.NewInt(0)
|
||||
}
|
||||
genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2*TriesInMemory, func(i int, gen *BlockGen) {
|
||||
genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2*state.TriesInMemory, func(i int, gen *BlockGen) {
|
||||
tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tx: %v", err)
|
||||
|
|
@ -2171,9 +2070,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
|||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
lastPrunedIndex := len(blocks) - TriesInMemory - 1
|
||||
lastPrunedIndex := len(blocks) - state.TriesInMemory - 1
|
||||
lastPrunedBlock := blocks[lastPrunedIndex]
|
||||
firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory]
|
||||
firstNonPrunedBlock := blocks[len(blocks)-state.TriesInMemory]
|
||||
|
||||
// Verify pruning of lastPrunedBlock
|
||||
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
|
||||
|
|
@ -2200,7 +2099,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
|||
// Generate fork chain, make it longer than canon
|
||||
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
|
||||
parent := blocks[parentIndex]
|
||||
fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*TriesInMemory, func(i int, b *BlockGen) {
|
||||
fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*state.TriesInMemory, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{2})
|
||||
if int(b.header.Number.Uint64()) >= mergeBlock {
|
||||
b.SetPoS()
|
||||
|
|
@ -2291,7 +2190,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
|||
b.OffsetTime(-9) // A higher difficulty
|
||||
})
|
||||
// Import the shared chain and the original canonical one
|
||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
|
||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
|
|
@ -2462,7 +2361,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
|||
}
|
||||
})
|
||||
// Import the shared chain and the original canonical one
|
||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
|
||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
|
|
@ -2843,7 +2742,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
|
|||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
// Generate and import the canonical chain
|
||||
_, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*TriesInMemory, nil)
|
||||
_, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*state.TriesInMemory, nil)
|
||||
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
|
|
@ -2856,9 +2755,9 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
|
|||
}
|
||||
// In path-based trie database implementation, it will keep 128 diff + 1 disk
|
||||
// layers, totally 129 latest states available. In hash-based it's 128.
|
||||
states := TriesInMemory
|
||||
states := state.TriesInMemory
|
||||
if scheme == rawdb.PathScheme {
|
||||
states = TriesInMemory + 1
|
||||
states = state.TriesInMemory + 1
|
||||
}
|
||||
lastPrunedIndex := len(blocks) - states - 1
|
||||
lastPrunedBlock := blocks[lastPrunedIndex]
|
||||
|
|
@ -3737,16 +3636,17 @@ func testSetCanonical(t *testing.T, scheme string) {
|
|||
}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
engine = ethash.NewFaker()
|
||||
chainLength = 10
|
||||
)
|
||||
// Generate and import the canonical chain
|
||||
_, canon, _ := GenerateChainWithGenesis(gspec, engine, 2*TriesInMemory, func(i int, gen *BlockGen) {
|
||||
_, canon, _ := GenerateChainWithGenesis(gspec, engine, chainLength, func(i int, gen *BlockGen) {
|
||||
tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gen.AddTx(tx)
|
||||
})
|
||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
|
||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
defer diskdb.Close()
|
||||
|
||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
|
|
@ -3760,7 +3660,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
|||
}
|
||||
|
||||
// Generate the side chain and import them
|
||||
_, side, _ := GenerateChainWithGenesis(gspec, engine, 2*TriesInMemory, func(i int, gen *BlockGen) {
|
||||
_, side, _ := GenerateChainWithGenesis(gspec, engine, chainLength, func(i int, gen *BlockGen) {
|
||||
tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1), params.TxGas, gen.header.BaseFee, nil), signer, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -3799,8 +3699,8 @@ func testSetCanonical(t *testing.T, scheme string) {
|
|||
verify(side[len(side)-1])
|
||||
|
||||
// Reset the chain head to original chain
|
||||
chain.SetCanonical(canon[TriesInMemory-1])
|
||||
verify(canon[TriesInMemory-1])
|
||||
chain.SetCanonical(canon[chainLength-1])
|
||||
verify(canon[chainLength-1])
|
||||
}
|
||||
|
||||
// TestCanonicalHashMarker tests all the canonical hash markers are updated/deleted
|
||||
|
|
|
|||
|
|
@ -596,6 +596,9 @@ func (s *MatcherSession) deliverSections(bit uint, sections []uint64, bitsets []
|
|||
// of the session, any request in-flight need to be responded to! Empty responses
|
||||
// are fine though in that case.
|
||||
func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan *Retrieval) {
|
||||
waitTimer := time.NewTimer(wait)
|
||||
defer waitTimer.Stop()
|
||||
|
||||
for {
|
||||
// Allocate a new bloom bit index to retrieve data for, stopping when done
|
||||
bit, ok := s.allocateRetrieval()
|
||||
|
|
@ -604,6 +607,7 @@ func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan
|
|||
}
|
||||
// Bit allocated, throttle a bit if we're below our batch limit
|
||||
if s.pendingSections(bit) < batch {
|
||||
waitTimer.Reset(wait)
|
||||
select {
|
||||
case <-s.quit:
|
||||
// Session terminating, we can't meaningfully service, abort
|
||||
|
|
@ -611,7 +615,7 @@ func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan
|
|||
s.deliverSections(bit, []uint64{}, [][]byte{})
|
||||
return
|
||||
|
||||
case <-time.After(wait):
|
||||
case <-waitTimer.C:
|
||||
// Throttling up, fetch whatever is available
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import (
|
|||
// request represents a bloom retrieval task to prioritize and pull from the local
|
||||
// database or remotely from the network.
|
||||
type request struct {
|
||||
section uint64 // Section index to retrieve the a bit-vector from
|
||||
section uint64 // Section index to retrieve the bit-vector from
|
||||
bit uint // Bit index within the section to retrieve the vector of
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ func (b *testChainIndexBackend) Process(ctx context.Context, header *types.Heade
|
|||
b.t.Error("Unexpected call to Process")
|
||||
// Can't use Fatal since this is not the test's goroutine.
|
||||
// Returning error stops the chainIndexer's updateLoop
|
||||
return errors.New("Unexpected call to Process")
|
||||
return errors.New("unexpected call to Process")
|
||||
case b.processCh <- header.Number.Uint64():
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/gballet/go-verkle"
|
||||
"github.com/ethereum/go-verkle"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -43,12 +43,11 @@ func TestGeneratePOSChain(t *testing.T) {
|
|||
bb = common.Address{0xbb}
|
||||
funds = big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(params.Ether))
|
||||
config = *params.AllEthashProtocolChanges
|
||||
asm4788 = common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500")
|
||||
gspec = &Genesis{
|
||||
Config: &config,
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
params.BeaconRootsAddress: {Balance: common.Big0, Code: asm4788},
|
||||
params.BeaconRootsAddress: {Code: params.BeaconRootsCode},
|
||||
},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Difficulty: common.Big1,
|
||||
|
|
|
|||
|
|
@ -26,9 +26,6 @@ var (
|
|||
// ErrKnownBlock is returned when a block to import is already known locally.
|
||||
ErrKnownBlock = errors.New("block already known")
|
||||
|
||||
// ErrBannedHash is returned if a block to import is on the banned list.
|
||||
ErrBannedHash = errors.New("banned hash")
|
||||
|
||||
// ErrNoGenesis is returned when there is no Genesis Block.
|
||||
ErrNoGenesis = errors.New("genesis not found in chain")
|
||||
|
||||
|
|
@ -67,6 +64,11 @@ var (
|
|||
// than init code size limit.
|
||||
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||
|
||||
// ErrInsufficientBalanceWitness is returned if the transaction sender has enough
|
||||
// funds to cover the transfer, but not enough to pay for witness access/modification
|
||||
// costs for the transaction
|
||||
ErrInsufficientBalanceWitness = errors.New("insufficient funds to cover witness access costs for transaction")
|
||||
|
||||
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
||||
// is higher than the balance of the user's account.
|
||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
|||
if header.ExcessBlobGas != nil {
|
||||
blobBaseFee = eip4844.CalcBlobFee(*header.ExcessBlobGas)
|
||||
}
|
||||
if header.Difficulty.Cmp(common.Big0) == 0 {
|
||||
if header.Difficulty.Sign() == 0 {
|
||||
random = &header.MixDigest
|
||||
}
|
||||
return vm.BlockContext{
|
||||
|
|
|
|||
|
|
@ -476,7 +476,7 @@ func (g *Genesis) ToBlock() *types.Block {
|
|||
}
|
||||
}
|
||||
}
|
||||
return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)).WithWithdrawals(withdrawals)
|
||||
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil))
|
||||
}
|
||||
|
||||
// Commit writes the block and state of a genesis specification to the database.
|
||||
|
|
@ -582,7 +582,7 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
|||
Config: &config,
|
||||
GasLimit: gasLimit,
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Difficulty: big.NewInt(1),
|
||||
Difficulty: big.NewInt(0),
|
||||
Alloc: map[common.Address]types.Account{
|
||||
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
||||
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
|
||||
|
|
@ -593,6 +593,8 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
|||
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
|
||||
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
|
||||
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
|
||||
// Pre-deploy EIP-4788 system contract
|
||||
params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode},
|
||||
},
|
||||
}
|
||||
if faucet != nil {
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
expected := common.Hex2Bytes("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b")
|
||||
expected := common.FromHex("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b")
|
||||
got := genesis.ToBlock().Root().Bytes()
|
||||
if !bytes.Equal(got, expected) {
|
||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
||||
|
|
@ -314,7 +314,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
|||
triedb := triedb.NewDatabase(db, &triedb.Config{IsVerkle: true, PathDB: pathdb.Defaults})
|
||||
block := genesis.MustCommit(db, triedb)
|
||||
if !bytes.Equal(block.Root().Bytes(), expected) {
|
||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, block.Root())
|
||||
}
|
||||
|
||||
// Test that the trie is verkle
|
||||
|
|
@ -322,7 +322,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
|||
t.Fatalf("expected trie to be verkle")
|
||||
}
|
||||
|
||||
if !rawdb.ExistsAccountTrieNode(db, nil) {
|
||||
if !rawdb.HasAccountTrieNode(db, nil) {
|
||||
t.Fatal("could not find node")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,9 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
mrand "math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
|
|
@ -43,25 +40,28 @@ const (
|
|||
numberCacheLimit = 2048
|
||||
)
|
||||
|
||||
// HeaderChain implements the basic block header chain logic that is shared by
|
||||
// core.BlockChain and light.LightChain. It is not usable in itself, only as
|
||||
// a part of either structure.
|
||||
// HeaderChain implements the basic block header chain logic. It is not usable
|
||||
// in itself, but rather an internal structure of core.Blockchain.
|
||||
//
|
||||
// HeaderChain is responsible for maintaining the header chain including the
|
||||
// header query and updating.
|
||||
//
|
||||
// The components maintained by headerchain includes: (1) total difficulty
|
||||
// (2) header (3) block hash -> number mapping (4) canonical number -> hash mapping
|
||||
// and (5) head header flag.
|
||||
// The data components maintained by HeaderChain include:
|
||||
//
|
||||
// It is not thread safe either, the encapsulating chain structures should do
|
||||
// the necessary mutex locking/unlocking.
|
||||
// - total difficulty
|
||||
// - header
|
||||
// - block hash -> number mapping
|
||||
// - canonical number -> hash mapping
|
||||
// - head header flag.
|
||||
//
|
||||
// It is not thread safe, the encapsulating chain structures should do the
|
||||
// necessary mutex locking/unlocking.
|
||||
type HeaderChain struct {
|
||||
config *params.ChainConfig
|
||||
chainDb ethdb.Database
|
||||
genesisHeader *types.Header
|
||||
|
||||
currentHeader atomic.Value // Current head of the header chain (may be above the block chain!)
|
||||
currentHeader atomic.Pointer[types.Header] // Current head of the header chain (maybe above the block chain!)
|
||||
currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
|
||||
|
||||
headerCache *lru.Cache[common.Hash, *types.Header]
|
||||
|
|
@ -69,19 +69,12 @@ type HeaderChain struct {
|
|||
numberCache *lru.Cache[common.Hash, uint64] // most recent block numbers
|
||||
|
||||
procInterrupt func() bool
|
||||
|
||||
rand *mrand.Rand
|
||||
engine consensus.Engine
|
||||
}
|
||||
|
||||
// NewHeaderChain creates a new HeaderChain structure. ProcInterrupt points
|
||||
// to the parent's interrupt semaphore.
|
||||
func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) {
|
||||
// Seed a fast but crypto originating random generator
|
||||
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc := &HeaderChain{
|
||||
config: config,
|
||||
chainDb: chainDb,
|
||||
|
|
@ -89,7 +82,6 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
|
|||
tdCache: lru.NewCache[common.Hash, *big.Int](tdCacheLimit),
|
||||
numberCache: lru.NewCache[common.Hash, uint64](numberCacheLimit),
|
||||
procInterrupt: procInterrupt,
|
||||
rand: mrand.New(mrand.NewSource(seed.Int64())),
|
||||
engine: engine,
|
||||
}
|
||||
hc.genesisHeader = hc.GetHeaderByNumber(0)
|
||||
|
|
@ -313,14 +305,6 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header) (int, error) {
|
|||
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].Number,
|
||||
parentHash.Bytes()[:4], i, chain[i].Number, hash.Bytes()[:4], chain[i].ParentHash[:4])
|
||||
}
|
||||
// If the header is a banned one, straight out abort
|
||||
if BadHashes[chain[i].ParentHash] {
|
||||
return i - 1, ErrBannedHash
|
||||
}
|
||||
// If it's the last header in the cunk, we need to check it too
|
||||
if i == len(chain)-1 && BadHashes[chain[i].Hash()] {
|
||||
return i, ErrBannedHash
|
||||
}
|
||||
}
|
||||
// Start the parallel verifier
|
||||
abort, results := hc.engine.VerifyHeaders(hc, chain)
|
||||
|
|
@ -525,7 +509,7 @@ func (hc *HeaderChain) GetCanonicalHash(number uint64) common.Hash {
|
|||
// CurrentHeader retrieves the current head header of the canonical chain. The
|
||||
// header is retrieved from the HeaderChain's internal cache.
|
||||
func (hc *HeaderChain) CurrentHeader() *types.Header {
|
||||
return hc.currentHeader.Load().(*types.Header)
|
||||
return hc.currentHeader.Load()
|
||||
}
|
||||
|
||||
// SetCurrentHeader sets the in-memory head header marker of the canonical chan
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ func main() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
if err := json.NewDecoder(file).Decode(g); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package rawdb
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"slices"
|
||||
|
|
@ -316,8 +315,8 @@ func ReadHeaderRange(db ethdb.Reader, number uint64, count uint64) []rlp.RawValu
|
|||
if count == 0 {
|
||||
return rlpHeaders
|
||||
}
|
||||
// read remaining from ancients
|
||||
data, err := db.AncientRange(ChainFreezerHeaderTable, i+1-count, count, 0)
|
||||
// read remaining from ancients, cap at 2M
|
||||
data, err := db.AncientRange(ChainFreezerHeaderTable, i+1-count, count, 2*1024*1024)
|
||||
if err != nil {
|
||||
log.Error("Failed to read headers from freezer", "err", err)
|
||||
return rlpHeaders
|
||||
|
|
@ -695,27 +694,6 @@ func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// deriveLogFields fills the logs in receiptLogs with information such as block number, txhash, etc.
|
||||
func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, txs types.Transactions) error {
|
||||
logIndex := uint(0)
|
||||
if len(txs) != len(receipts) {
|
||||
return errors.New("transaction and receipt count mismatch")
|
||||
}
|
||||
for i := 0; i < len(receipts); i++ {
|
||||
txHash := txs[i].Hash()
|
||||
// The derived log fields can simply be set from the block and transaction
|
||||
for j := 0; j < len(receipts[i].Logs); j++ {
|
||||
receipts[i].Logs[j].BlockNumber = number
|
||||
receipts[i].Logs[j].BlockHash = hash
|
||||
receipts[i].Logs[j].TxHash = txHash
|
||||
receipts[i].Logs[j].TxIndex = uint(i)
|
||||
receipts[i].Logs[j].Index = logIndex
|
||||
logIndex++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadLogs retrieves the logs for all transactions in a block. In case
|
||||
// receipts is not found, a nil is returned.
|
||||
// Note: ReadLogs does not derive unstored log fields.
|
||||
|
|
@ -753,7 +731,7 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
|
|||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles).WithWithdrawals(body.Withdrawals)
|
||||
return types.NewBlockWithHeader(header).WithBody(*body)
|
||||
}
|
||||
|
||||
// WriteBlock serializes a block into the database, header and body separately.
|
||||
|
|
@ -843,7 +821,11 @@ func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block {
|
|||
}
|
||||
for _, bad := range badBlocks {
|
||||
if bad.Header.Hash() == hash {
|
||||
return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals)
|
||||
block := types.NewBlockWithHeader(bad.Header)
|
||||
if bad.Body != nil {
|
||||
block = block.WithBody(*bad.Body)
|
||||
}
|
||||
return block
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
@ -862,7 +844,11 @@ func ReadAllBadBlocks(db ethdb.Reader) []*types.Block {
|
|||
}
|
||||
var blocks []*types.Block
|
||||
for _, bad := range badBlocks {
|
||||
blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals))
|
||||
block := types.NewBlockWithHeader(bad.Header)
|
||||
if bad.Body != nil {
|
||||
block = block.WithBody(*bad.Body)
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
|
|
|||
|
|
@ -640,7 +640,7 @@ func makeTestBlocks(nblock int, txsPerBlock int) []*types.Block {
|
|||
Number: big.NewInt(int64(i)),
|
||||
Extra: []byte("test block"),
|
||||
}
|
||||
blocks[i] = types.NewBlockWithHeader(header).WithBody(txs, nil)
|
||||
blocks[i] = types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs})
|
||||
blocks[i].Hash() // pre-cache the block hash
|
||||
}
|
||||
return blocks
|
||||
|
|
@ -794,7 +794,7 @@ func TestDeriveLogFields(t *testing.T) {
|
|||
}),
|
||||
}
|
||||
// Create the corresponding receipts
|
||||
receipts := []*receiptLogs{
|
||||
receipts := []*types.Receipt{
|
||||
{
|
||||
Logs: []*types.Log{
|
||||
{Address: common.BytesToAddress([]byte{0x11})},
|
||||
|
|
@ -818,9 +818,7 @@ func TestDeriveLogFields(t *testing.T) {
|
|||
// Derive log metadata fields
|
||||
number := big.NewInt(1)
|
||||
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||
if err := deriveLogFields(receipts, hash, number.Uint64(), txs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, number.Uint64(), 0, big.NewInt(0), big.NewInt(0), txs)
|
||||
|
||||
// Iterate over all the computed fields and check that they're correct
|
||||
logIndex := uint(0)
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ func TestLookupStorage(t *testing.T) {
|
|||
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
|
||||
txs := []*types.Transaction{tx1, tx2, tx3}
|
||||
|
||||
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, newTestHasher())
|
||||
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, &types.Body{Transactions: txs}, nil, newTestHasher())
|
||||
|
||||
// Check that no transactions entries are in a pristine database
|
||||
for i, tx := range txs {
|
||||
|
|
|
|||
|
|
@ -92,20 +92,20 @@ func DeleteAccountSnapshot(db ethdb.KeyValueWriter, hash common.Hash) {
|
|||
}
|
||||
}
|
||||
|
||||
// ReadStorageSnapshot retrieves the snapshot entry of an storage trie leaf.
|
||||
// ReadStorageSnapshot retrieves the snapshot entry of a storage trie leaf.
|
||||
func ReadStorageSnapshot(db ethdb.KeyValueReader, accountHash, storageHash common.Hash) []byte {
|
||||
data, _ := db.Get(storageSnapshotKey(accountHash, storageHash))
|
||||
return data
|
||||
}
|
||||
|
||||
// WriteStorageSnapshot stores the snapshot entry of an storage trie leaf.
|
||||
// WriteStorageSnapshot stores the snapshot entry of a storage trie leaf.
|
||||
func WriteStorageSnapshot(db ethdb.KeyValueWriter, accountHash, storageHash common.Hash, entry []byte) {
|
||||
if err := db.Put(storageSnapshotKey(accountHash, storageHash), entry); err != nil {
|
||||
log.Crit("Failed to store storage snapshot", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteStorageSnapshot removes the snapshot entry of an storage trie leaf.
|
||||
// DeleteStorageSnapshot removes the snapshot entry of a storage trie leaf.
|
||||
func DeleteStorageSnapshot(db ethdb.KeyValueWriter, accountHash, storageHash common.Hash) {
|
||||
if err := db.Delete(storageSnapshotKey(accountHash, storageHash)); err != nil {
|
||||
log.Crit("Failed to delete storage snapshot", "err", err)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// HashScheme is the legacy hash-based state scheme with which trie nodes are
|
||||
|
|
@ -50,7 +49,7 @@ const PathScheme = "path"
|
|||
type hasher struct{ sha crypto.KeccakState }
|
||||
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() interface{} { return &hasher{sha: sha3.NewLegacyKeccak256().(crypto.KeccakState)} },
|
||||
New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} },
|
||||
}
|
||||
|
||||
func newHasher() *hasher {
|
||||
|
|
@ -65,33 +64,15 @@ func (h *hasher) release() {
|
|||
hasherPool.Put(h)
|
||||
}
|
||||
|
||||
// ReadAccountTrieNode retrieves the account trie node and the associated node
|
||||
// hash with the specified node path.
|
||||
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) ([]byte, common.Hash) {
|
||||
data, err := db.Get(accountTrieNodeKey(path))
|
||||
if err != nil {
|
||||
return nil, common.Hash{}
|
||||
}
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
return data, h.hash(data)
|
||||
// ReadAccountTrieNode retrieves the account trie node with the specified node path.
|
||||
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte {
|
||||
data, _ := db.Get(accountTrieNodeKey(path))
|
||||
return data
|
||||
}
|
||||
|
||||
// HasAccountTrieNode checks the account trie node presence with the specified
|
||||
// node path and the associated node hash.
|
||||
func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte, hash common.Hash) bool {
|
||||
data, err := db.Get(accountTrieNodeKey(path))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
return h.hash(data) == hash
|
||||
}
|
||||
|
||||
// ExistsAccountTrieNode checks the presence of the account trie node with the
|
||||
// HasAccountTrieNode checks the presence of the account trie node with the
|
||||
// specified node path, regardless of the node hash.
|
||||
func ExistsAccountTrieNode(db ethdb.KeyValueReader, path []byte) bool {
|
||||
func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte) bool {
|
||||
has, err := db.Has(accountTrieNodeKey(path))
|
||||
if err != nil {
|
||||
return false
|
||||
|
|
@ -113,33 +94,15 @@ func DeleteAccountTrieNode(db ethdb.KeyValueWriter, path []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
// ReadStorageTrieNode retrieves the storage trie node and the associated node
|
||||
// hash with the specified node path.
|
||||
func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) ([]byte, common.Hash) {
|
||||
data, err := db.Get(storageTrieNodeKey(accountHash, path))
|
||||
if err != nil {
|
||||
return nil, common.Hash{}
|
||||
}
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
return data, h.hash(data)
|
||||
// ReadStorageTrieNode retrieves the storage trie node with the specified node path.
|
||||
func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) []byte {
|
||||
data, _ := db.Get(storageTrieNodeKey(accountHash, path))
|
||||
return data
|
||||
}
|
||||
|
||||
// HasStorageTrieNode checks the storage trie node presence with the provided
|
||||
// node path and the associated node hash.
|
||||
func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte, hash common.Hash) bool {
|
||||
data, err := db.Get(storageTrieNodeKey(accountHash, path))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
return h.hash(data) == hash
|
||||
}
|
||||
|
||||
// ExistsStorageTrieNode checks the presence of the storage trie node with the
|
||||
// HasStorageTrieNode checks the presence of the storage trie node with the
|
||||
// specified account hash and node path, regardless of the node hash.
|
||||
func ExistsStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) bool {
|
||||
func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) bool {
|
||||
has, err := db.Has(storageTrieNodeKey(accountHash, path))
|
||||
if err != nil {
|
||||
return false
|
||||
|
|
@ -198,10 +161,18 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c
|
|||
case HashScheme:
|
||||
return HasLegacyTrieNode(db, hash)
|
||||
case PathScheme:
|
||||
var blob []byte
|
||||
if owner == (common.Hash{}) {
|
||||
return HasAccountTrieNode(db, path, hash)
|
||||
blob = ReadAccountTrieNode(db, path)
|
||||
} else {
|
||||
blob = ReadStorageTrieNode(db, owner, path)
|
||||
}
|
||||
return HasStorageTrieNode(db, owner, path, hash)
|
||||
if len(blob) == 0 {
|
||||
return false
|
||||
}
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
return h.hash(blob) == hash // exists but not match
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown scheme %v", scheme))
|
||||
}
|
||||
|
|
@ -209,43 +180,35 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c
|
|||
|
||||
// ReadTrieNode retrieves the trie node from database with the provided node info
|
||||
// and associated node hash.
|
||||
// hashScheme-based lookup requires the following:
|
||||
// - hash
|
||||
//
|
||||
// pathScheme-based lookup requires the following:
|
||||
// - owner
|
||||
// - path
|
||||
func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash, scheme string) []byte {
|
||||
switch scheme {
|
||||
case HashScheme:
|
||||
return ReadLegacyTrieNode(db, hash)
|
||||
case PathScheme:
|
||||
var (
|
||||
blob []byte
|
||||
nHash common.Hash
|
||||
)
|
||||
var blob []byte
|
||||
if owner == (common.Hash{}) {
|
||||
blob, nHash = ReadAccountTrieNode(db, path)
|
||||
blob = ReadAccountTrieNode(db, path)
|
||||
} else {
|
||||
blob, nHash = ReadStorageTrieNode(db, owner, path)
|
||||
blob = ReadStorageTrieNode(db, owner, path)
|
||||
}
|
||||
if nHash != hash {
|
||||
if len(blob) == 0 {
|
||||
return nil
|
||||
}
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
if h.hash(blob) != hash {
|
||||
return nil // exists but not match
|
||||
}
|
||||
return blob
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown scheme %v", scheme))
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTrieNode writes the trie node into database with the provided node info
|
||||
// and associated node hash.
|
||||
// hashScheme-based lookup requires the following:
|
||||
// - hash
|
||||
// WriteTrieNode writes the trie node into database with the provided node info.
|
||||
//
|
||||
// pathScheme-based lookup requires the following:
|
||||
// - owner
|
||||
// - path
|
||||
// hash-scheme requires the node hash as the identifier.
|
||||
// path-scheme requires the node owner and path as the identifier.
|
||||
func WriteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, node []byte, scheme string) {
|
||||
switch scheme {
|
||||
case HashScheme:
|
||||
|
|
@ -261,14 +224,10 @@ func WriteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash
|
|||
}
|
||||
}
|
||||
|
||||
// DeleteTrieNode deletes the trie node from database with the provided node info
|
||||
// and associated node hash.
|
||||
// hashScheme-based lookup requires the following:
|
||||
// - hash
|
||||
// DeleteTrieNode deletes the trie node from database with the provided node info.
|
||||
//
|
||||
// pathScheme-based lookup requires the following:
|
||||
// - owner
|
||||
// - path
|
||||
// hash-scheme requires the node hash as the identifier.
|
||||
// path-scheme requires the node owner and path as the identifier.
|
||||
func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, scheme string) {
|
||||
switch scheme {
|
||||
case HashScheme:
|
||||
|
|
@ -287,9 +246,8 @@ func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, has
|
|||
// ReadStateScheme reads the state scheme of persistent state, or none
|
||||
// if the state is not present in database.
|
||||
func ReadStateScheme(db ethdb.Reader) string {
|
||||
// Check if state in path-based scheme is present
|
||||
blob, _ := ReadAccountTrieNode(db, nil)
|
||||
if len(blob) != 0 {
|
||||
// Check if state in path-based scheme is present.
|
||||
if HasAccountTrieNode(db, nil) {
|
||||
return PathScheme
|
||||
}
|
||||
// The root node might be deleted during the initial snap sync, check
|
||||
|
|
@ -304,8 +262,7 @@ func ReadStateScheme(db ethdb.Reader) string {
|
|||
if header == nil {
|
||||
return "" // empty datadir
|
||||
}
|
||||
blob = ReadLegacyTrieNode(db, header.Root)
|
||||
if len(blob) == 0 {
|
||||
if !HasLegacyTrieNode(db, header.Root) {
|
||||
return "" // no state in disk
|
||||
}
|
||||
return HashScheme
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@
|
|||
|
||||
package rawdb
|
||||
|
||||
import "path/filepath"
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
// The list of table names of chain freezer.
|
||||
const (
|
||||
|
|
@ -75,7 +79,15 @@ var (
|
|||
// freezers the collections of all builtin freezers.
|
||||
var freezers = []string{ChainFreezerName, StateFreezerName}
|
||||
|
||||
// NewStateFreezer initializes the freezer for state history.
|
||||
func NewStateFreezer(ancientDir string, readOnly bool) (*ResettableFreezer, error) {
|
||||
return NewResettableFreezer(filepath.Join(ancientDir, StateFreezerName), "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
|
||||
// NewStateFreezer initializes the ancient store for state history.
|
||||
//
|
||||
// - if the empty directory is given, initializes the pure in-memory
|
||||
// state freezer (e.g. dev mode).
|
||||
// - if non-empty directory is given, initializes the regular file-based
|
||||
// state freezer.
|
||||
func NewStateFreezer(ancientDir string, readOnly bool) (ethdb.ResettableAncientStore, error) {
|
||||
if ancientDir == "" {
|
||||
return NewMemoryFreezer(readOnly, stateFreezerNoSnappy), nil
|
||||
}
|
||||
return newResettableFreezer(filepath.Join(ancientDir, StateFreezerName), "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,20 +89,17 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) {
|
|||
infos = append(infos, info)
|
||||
|
||||
case StateFreezerName:
|
||||
if ReadStateScheme(db) != PathScheme {
|
||||
continue
|
||||
}
|
||||
datadir, err := db.AncientDatadir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := NewStateFreezer(datadir, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
continue // might be possible the state freezer is not existent
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
info, err := inspect(StateFreezerName, stateFreezerNoSnappy, f)
|
||||
info, err := inspect(freezer, stateFreezerNoSnappy, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
325
core/rawdb/ancienttest/testsuite.go
Normal file
325
core/rawdb/ancienttest/testsuite.go
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
// Copyright 2024 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package ancienttest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/internal/testrand"
|
||||
)
|
||||
|
||||
// TestAncientSuite runs a suite of tests against an ancient database
|
||||
// implementation.
|
||||
func TestAncientSuite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||
// Test basic read methods
|
||||
t.Run("BasicRead", func(t *testing.T) { basicRead(t, newFn) })
|
||||
|
||||
// Test batch read method
|
||||
t.Run("BatchRead", func(t *testing.T) { batchRead(t, newFn) })
|
||||
|
||||
// Test basic write methods
|
||||
t.Run("BasicWrite", func(t *testing.T) { basicWrite(t, newFn) })
|
||||
|
||||
// Test if data mutation is allowed after db write
|
||||
t.Run("nonMutable", func(t *testing.T) { nonMutable(t, newFn) })
|
||||
}
|
||||
|
||||
func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||
var (
|
||||
db = newFn([]string{"a"})
|
||||
data = makeDataset(100, 32)
|
||||
)
|
||||
defer db.Close()
|
||||
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < len(data); i++ {
|
||||
op.AppendRaw("a", uint64(i), data[i])
|
||||
}
|
||||
return nil
|
||||
})
|
||||
db.TruncateTail(10)
|
||||
db.TruncateHead(90)
|
||||
|
||||
// Test basic tail and head retrievals
|
||||
tail, err := db.Tail()
|
||||
if err != nil || tail != 10 {
|
||||
t.Fatal("Failed to retrieve tail")
|
||||
}
|
||||
ancient, err := db.Ancients()
|
||||
if err != nil || ancient != 90 {
|
||||
t.Fatal("Failed to retrieve ancient")
|
||||
}
|
||||
|
||||
// Test the deleted items shouldn't be reachable
|
||||
var cases = []struct {
|
||||
start int
|
||||
limit int
|
||||
}{
|
||||
{0, 10},
|
||||
{90, 100},
|
||||
}
|
||||
for _, c := range cases {
|
||||
for i := c.start; i < c.limit; i++ {
|
||||
exist, err := db.HasAncient("a", uint64(i))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to check presence, %v", err)
|
||||
}
|
||||
if exist {
|
||||
t.Fatalf("Item %d is already truncated", uint64(i))
|
||||
}
|
||||
_, err = db.Ancient("a", uint64(i))
|
||||
if err == nil {
|
||||
t.Fatal("Error is expected for non-existent item")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test the items in range should be reachable
|
||||
for i := 10; i < 90; i++ {
|
||||
exist, err := db.HasAncient("a", uint64(i))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to check presence, %v", err)
|
||||
}
|
||||
if !exist {
|
||||
t.Fatalf("Item %d is missing", uint64(i))
|
||||
}
|
||||
blob, err := db.Ancient("a", uint64(i))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve item, %v", err)
|
||||
}
|
||||
if !bytes.Equal(blob, data[i]) {
|
||||
t.Fatalf("Unexpected item content, want: %v, got: %v", data[i], blob)
|
||||
}
|
||||
}
|
||||
|
||||
// Test the items in unknown table shouldn't be reachable
|
||||
exist, err := db.HasAncient("b", uint64(0))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to check presence, %v", err)
|
||||
}
|
||||
if exist {
|
||||
t.Fatal("Item in unknown table shouldn't be found")
|
||||
}
|
||||
_, err = db.Ancient("b", uint64(0))
|
||||
if err == nil {
|
||||
t.Fatal("Error is expected for unknown table")
|
||||
}
|
||||
}
|
||||
|
||||
func batchRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||
var (
|
||||
db = newFn([]string{"a"})
|
||||
data = makeDataset(100, 32)
|
||||
)
|
||||
defer db.Close()
|
||||
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), data[i])
|
||||
}
|
||||
return nil
|
||||
})
|
||||
db.TruncateTail(10)
|
||||
db.TruncateHead(90)
|
||||
|
||||
// Test the items in range should be reachable
|
||||
var cases = []struct {
|
||||
start uint64
|
||||
count uint64
|
||||
maxSize uint64
|
||||
expStart int
|
||||
expLimit int
|
||||
}{
|
||||
// Items in range [10, 90) with no size limitation
|
||||
{
|
||||
10, 80, 0, 10, 90,
|
||||
},
|
||||
// Items in range [10, 90) with 32 size cap, single item is expected
|
||||
{
|
||||
10, 80, 32, 10, 11,
|
||||
},
|
||||
// Items in range [10, 90) with 31 size cap, single item is expected
|
||||
{
|
||||
10, 80, 31, 10, 11,
|
||||
},
|
||||
// Items in range [10, 90) with 32*80 size cap, all items are expected
|
||||
{
|
||||
10, 80, 32 * 80, 10, 90,
|
||||
},
|
||||
// Extra items above the last item are not returned
|
||||
{
|
||||
10, 90, 0, 10, 90,
|
||||
},
|
||||
}
|
||||
for i, c := range cases {
|
||||
batch, err := db.AncientRange("a", c.start, c.count, c.maxSize)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve item in range, %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(batch, data[c.expStart:c.expLimit]) {
|
||||
t.Fatalf("Case %d, Batch content is not matched", i)
|
||||
}
|
||||
}
|
||||
|
||||
// Test out-of-range / zero-size retrieval should be rejected
|
||||
_, err := db.AncientRange("a", 0, 1, 0)
|
||||
if err == nil {
|
||||
t.Fatal("Out-of-range retrieval should be rejected")
|
||||
}
|
||||
_, err = db.AncientRange("a", 90, 1, 0)
|
||||
if err == nil {
|
||||
t.Fatal("Out-of-range retrieval should be rejected")
|
||||
}
|
||||
_, err = db.AncientRange("a", 10, 0, 0)
|
||||
if err == nil {
|
||||
t.Fatal("Zero-size retrieval should be rejected")
|
||||
}
|
||||
|
||||
// Test item in unknown table shouldn't be reachable
|
||||
_, err = db.AncientRange("b", 10, 1, 0)
|
||||
if err == nil {
|
||||
t.Fatal("Item in unknown table shouldn't be found")
|
||||
}
|
||||
}
|
||||
|
||||
func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||
var (
|
||||
db = newFn([]string{"a", "b"})
|
||||
dataA = makeDataset(100, 32)
|
||||
dataB = makeDataset(100, 32)
|
||||
)
|
||||
defer db.Close()
|
||||
|
||||
// The ancient write to tables should be aligned
|
||||
_, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), dataA[i])
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Unaligned ancient write should be rejected")
|
||||
}
|
||||
|
||||
// Test normal ancient write
|
||||
size, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), dataA[i])
|
||||
op.AppendRaw("b", uint64(i), dataB[i])
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write ancient data %v", err)
|
||||
}
|
||||
wantSize := int64(6400)
|
||||
if size != wantSize {
|
||||
t.Fatalf("Ancient write size is not expected, want: %d, got: %d", wantSize, size)
|
||||
}
|
||||
|
||||
// Write should work after head truncating
|
||||
db.TruncateHead(90)
|
||||
_, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 90; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), dataA[i])
|
||||
op.AppendRaw("b", uint64(i), dataB[i])
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write ancient data %v", err)
|
||||
}
|
||||
|
||||
// Write should work after truncating everything
|
||||
db.TruncateTail(0)
|
||||
_, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), dataA[i])
|
||||
op.AppendRaw("b", uint64(i), dataB[i])
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write ancient data %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func nonMutable(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||
db := newFn([]string{"a"})
|
||||
defer db.Close()
|
||||
|
||||
// We write 100 zero-bytes to the freezer and immediately mutate the slice
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
data := make([]byte, 100)
|
||||
op.AppendRaw("a", uint64(0), data)
|
||||
for i := range data {
|
||||
data[i] = 0xff
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// Now read it.
|
||||
data, err := db.Ancient("a", uint64(0))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for k, v := range data {
|
||||
if v != 0 {
|
||||
t.Fatalf("byte %d != 0: %x", k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestResettableAncientSuite runs a suite of tests against a resettable ancient
|
||||
// database implementation.
|
||||
func TestResettableAncientSuite(t *testing.T, newFn func(kinds []string) ethdb.ResettableAncientStore) {
|
||||
t.Run("Reset", func(t *testing.T) {
|
||||
var (
|
||||
db = newFn([]string{"a"})
|
||||
data = makeDataset(100, 32)
|
||||
)
|
||||
defer db.Close()
|
||||
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), data[i])
|
||||
}
|
||||
return nil
|
||||
})
|
||||
db.TruncateTail(10)
|
||||
db.TruncateHead(90)
|
||||
|
||||
// Ancient write should work after resetting
|
||||
db.Reset()
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), data[i])
|
||||
}
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func makeDataset(size, value int) [][]byte {
|
||||
var vals [][]byte
|
||||
for i := 0; i < size; i += 1 {
|
||||
vals = append(vals, testrand.Bytes(value))
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
|
@ -39,24 +39,38 @@ const (
|
|||
freezerBatchLimit = 30000
|
||||
)
|
||||
|
||||
// chainFreezer is a wrapper of freezer with additional chain freezing feature.
|
||||
// The background thread will keep moving ancient chain segments from key-value
|
||||
// database to flat files for saving space on live database.
|
||||
// chainFreezer is a wrapper of chain ancient store with additional chain freezing
|
||||
// feature. The background thread will keep moving ancient chain segments from
|
||||
// key-value database to flat files for saving space on live database.
|
||||
type chainFreezer struct {
|
||||
*Freezer
|
||||
ethdb.AncientStore // Ancient store for storing cold chain segment
|
||||
|
||||
quit chan struct{}
|
||||
wg sync.WaitGroup
|
||||
trigger chan chan struct{} // Manual blocking freeze trigger, test determinism
|
||||
}
|
||||
|
||||
// newChainFreezer initializes the freezer for ancient chain data.
|
||||
// newChainFreezer initializes the freezer for ancient chain segment.
|
||||
//
|
||||
// - if the empty directory is given, initializes the pure in-memory
|
||||
// state freezer (e.g. dev mode).
|
||||
// - if non-empty directory is given, initializes the regular file-based
|
||||
// state freezer.
|
||||
func newChainFreezer(datadir string, namespace string, readonly bool) (*chainFreezer, error) {
|
||||
freezer, err := NewChainFreezer(datadir, namespace, readonly)
|
||||
var (
|
||||
err error
|
||||
freezer ethdb.AncientStore
|
||||
)
|
||||
if datadir == "" {
|
||||
freezer = NewMemoryFreezer(readonly, chainFreezerNoSnappy)
|
||||
} else {
|
||||
freezer, err = NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerNoSnappy)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &chainFreezer{
|
||||
Freezer: freezer,
|
||||
AncientStore: freezer,
|
||||
quit: make(chan struct{}),
|
||||
trigger: make(chan chan struct{}),
|
||||
}, nil
|
||||
|
|
@ -70,7 +84,7 @@ func (f *chainFreezer) Close() error {
|
|||
close(f.quit)
|
||||
}
|
||||
f.wg.Wait()
|
||||
return f.Freezer.Close()
|
||||
return f.AncientStore.Close()
|
||||
}
|
||||
|
||||
// readHeadNumber returns the number of chain head block. 0 is returned if the
|
||||
|
|
@ -167,7 +181,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
|||
log.Debug("Current full block not old enough to freeze", "err", err)
|
||||
continue
|
||||
}
|
||||
frozen := f.frozen.Load()
|
||||
frozen, _ := f.Ancients() // no error will occur, safe to ignore
|
||||
|
||||
// Short circuit if the blocks below threshold are already frozen.
|
||||
if frozen != 0 && frozen-1 >= threshold {
|
||||
|
|
@ -190,7 +204,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
|||
backoff = true
|
||||
continue
|
||||
}
|
||||
// Batch of blocks have been frozen, flush them before wiping from leveldb
|
||||
// Batch of blocks have been frozen, flush them before wiping from key-value store
|
||||
if err := f.Sync(); err != nil {
|
||||
log.Crit("Failed to flush frozen tables", "err", err)
|
||||
}
|
||||
|
|
@ -210,7 +224,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
|||
|
||||
// Wipe out side chains also and track dangling side chains
|
||||
var dangling []common.Hash
|
||||
frozen = f.frozen.Load() // Needs reload after during freezeRange
|
||||
frozen, _ = f.Ancients() // Needs reload after during freezeRange
|
||||
for number := first; number < frozen; number++ {
|
||||
// Always keep the genesis block in active database
|
||||
if number != 0 {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue