mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
Merge branch 'ethereum:master' into gethintegration
This commit is contained in:
commit
695fcf6f43
40 changed files with 1011 additions and 736 deletions
|
|
@ -1,31 +1,25 @@
|
||||||
# This file configures github.com/golangci/golangci-lint.
|
# This file configures github.com/golangci/golangci-lint.
|
||||||
|
version: '2'
|
||||||
run:
|
run:
|
||||||
timeout: 20m
|
|
||||||
tests: true
|
tests: true
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
disable-all: true
|
default: none
|
||||||
enable:
|
enable:
|
||||||
- goimports
|
|
||||||
- gosimple
|
|
||||||
- govet
|
|
||||||
- ineffassign
|
|
||||||
- misspell
|
|
||||||
- unconvert
|
|
||||||
- typecheck
|
|
||||||
- unused
|
|
||||||
- staticcheck
|
|
||||||
- bidichk
|
- bidichk
|
||||||
- durationcheck
|
|
||||||
- copyloopvar
|
- copyloopvar
|
||||||
- whitespace
|
|
||||||
- revive # only certain checks enabled
|
|
||||||
- durationcheck
|
- durationcheck
|
||||||
- gocheckcompilerdirectives
|
- gocheckcompilerdirectives
|
||||||
- reassign
|
- govet
|
||||||
|
- ineffassign
|
||||||
- mirror
|
- mirror
|
||||||
|
- misspell
|
||||||
|
- reassign
|
||||||
|
- revive # only certain checks enabled
|
||||||
|
- staticcheck
|
||||||
|
- unconvert
|
||||||
|
- unused
|
||||||
- usetesting
|
- usetesting
|
||||||
|
- whitespace
|
||||||
### linters we tried and will not be using:
|
### linters we tried and will not be using:
|
||||||
###
|
###
|
||||||
# - structcheck # lots of false positives
|
# - structcheck # lots of false positives
|
||||||
|
|
@ -36,10 +30,11 @@ linters:
|
||||||
# - exhaustive # silly check
|
# - exhaustive # silly check
|
||||||
# - makezero # false positives
|
# - makezero # false positives
|
||||||
# - nilerr # several intentional
|
# - nilerr # several intentional
|
||||||
|
settings:
|
||||||
linters-settings:
|
staticcheck:
|
||||||
gofmt:
|
checks:
|
||||||
simplify: true
|
# disable Quickfixes
|
||||||
|
- -QF1*
|
||||||
revive:
|
revive:
|
||||||
enable-all-rules: false
|
enable-all-rules: false
|
||||||
# here we enable specific useful rules
|
# here we enable specific useful rules
|
||||||
|
|
@ -48,22 +43,23 @@ linters-settings:
|
||||||
- name: receiver-naming
|
- name: receiver-naming
|
||||||
severity: warning
|
severity: warning
|
||||||
disabled: false
|
disabled: false
|
||||||
exclude: [""]
|
exclude:
|
||||||
|
- ''
|
||||||
issues:
|
exclusions:
|
||||||
# default is true. Enables skipping of directories:
|
generated: lax
|
||||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
presets:
|
||||||
exclude-dirs-use-default: true
|
- comments
|
||||||
exclude-files:
|
- common-false-positives
|
||||||
- core/genesis_alloc.go
|
- legacy
|
||||||
exclude-rules:
|
- std-error-handling
|
||||||
- path: crypto/bn256/cloudflare/optate.go
|
rules:
|
||||||
linters:
|
- linters:
|
||||||
- deadcode
|
- deadcode
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- path: crypto/bn256/
|
path: crypto/bn256/cloudflare/optate.go
|
||||||
linters:
|
- linters:
|
||||||
- revive
|
- revive
|
||||||
|
path: crypto/bn256/
|
||||||
- path: cmd/utils/flags.go
|
- path: cmd/utils/flags.go
|
||||||
text: "SA1019: cfg.TxLookupLimit is deprecated: use 'TransactionHistory' instead."
|
text: "SA1019: cfg.TxLookupLimit is deprecated: use 'TransactionHistory' instead."
|
||||||
- path: cmd/utils/flags.go
|
- path: cmd/utils/flags.go
|
||||||
|
|
@ -72,8 +68,29 @@ issues:
|
||||||
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
|
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
|
||||||
- path: core/vm/contracts.go
|
- path: core/vm/contracts.go
|
||||||
text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.'
|
text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.'
|
||||||
exclude:
|
- path: (.+)\.go$
|
||||||
- 'SA1019: event.TypeMux is deprecated: use Feed'
|
text: 'SA1019: event.TypeMux is deprecated: use Feed'
|
||||||
- 'SA1019: strings.Title is deprecated'
|
- path: (.+)\.go$
|
||||||
- 'SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.'
|
text: 'SA1019: strings.Title is deprecated'
|
||||||
- 'SA1029: should not use built-in type string as key for value'
|
- path: (.+)\.go$
|
||||||
|
text: 'SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.'
|
||||||
|
- path: (.+)\.go$
|
||||||
|
text: 'SA1029: should not use built-in type string as key for value'
|
||||||
|
paths:
|
||||||
|
- core/genesis_alloc.go
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- goimports
|
||||||
|
settings:
|
||||||
|
gofmt:
|
||||||
|
simplify: true
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- core/genesis_alloc.go
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,11 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/beacon/light/sync"
|
"github.com/ethereum/go-ethereum/beacon/light/sync"
|
||||||
"github.com/ethereum/go-ethereum/beacon/params"
|
"github.com/ethereum/go-ethereum/beacon/params"
|
||||||
"github.com/ethereum/go-ethereum/beacon/types"
|
"github.com/ethereum/go-ethereum/beacon/types"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -46,7 +48,13 @@ func NewClient(config params.ClientConfig) *Client {
|
||||||
var (
|
var (
|
||||||
db = memorydb.New()
|
db = memorydb.New()
|
||||||
committeeChain = light.NewCommitteeChain(db, &config.ChainConfig, config.Threshold, !config.NoFilter)
|
committeeChain = light.NewCommitteeChain(db, &config.ChainConfig, config.Threshold, !config.NoFilter)
|
||||||
headTracker = light.NewHeadTracker(committeeChain, config.Threshold)
|
headTracker = light.NewHeadTracker(committeeChain, config.Threshold, func(checkpoint common.Hash) {
|
||||||
|
if saved, err := config.SaveCheckpointToFile(checkpoint); saved {
|
||||||
|
log.Debug("Saved beacon checkpoint", "file", config.CheckpointFile, "checkpoint", checkpoint)
|
||||||
|
} else if err != nil {
|
||||||
|
log.Error("Failed to save beacon checkpoint", "file", config.CheckpointFile, "checkpoint", checkpoint, "error", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
)
|
)
|
||||||
headSync := sync.NewHeadSync(headTracker, committeeChain)
|
headSync := sync.NewHeadSync(headTracker, committeeChain)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,9 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/beacon/params"
|
||||||
"github.com/ethereum/go-ethereum/beacon/types"
|
"github.com/ethereum/go-ethereum/beacon/types"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,13 +40,15 @@ type HeadTracker struct {
|
||||||
hasFinalityUpdate bool
|
hasFinalityUpdate bool
|
||||||
prefetchHead types.HeadInfo
|
prefetchHead types.HeadInfo
|
||||||
changeCounter uint64
|
changeCounter uint64
|
||||||
|
saveCheckpoint func(common.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHeadTracker creates a new HeadTracker.
|
// NewHeadTracker creates a new HeadTracker.
|
||||||
func NewHeadTracker(committeeChain *CommitteeChain, minSignerCount int) *HeadTracker {
|
func NewHeadTracker(committeeChain *CommitteeChain, minSignerCount int, saveCheckpoint func(common.Hash)) *HeadTracker {
|
||||||
return &HeadTracker{
|
return &HeadTracker{
|
||||||
committeeChain: committeeChain,
|
committeeChain: committeeChain,
|
||||||
minSignerCount: minSignerCount,
|
minSignerCount: minSignerCount,
|
||||||
|
saveCheckpoint: saveCheckpoint,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,6 +104,9 @@ func (h *HeadTracker) ValidateFinality(update types.FinalityUpdate) (bool, error
|
||||||
if replace {
|
if replace {
|
||||||
h.finalityUpdate, h.hasFinalityUpdate = update, true
|
h.finalityUpdate, h.hasFinalityUpdate = update, true
|
||||||
h.changeCounter++
|
h.changeCounter++
|
||||||
|
if h.saveCheckpoint != nil && update.Finalized.Slot%params.EpochLength == 0 {
|
||||||
|
h.saveCheckpoint(update.Finalized.Hash())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return replace, err
|
return replace, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ type ChainConfig struct {
|
||||||
GenesisValidatorsRoot common.Hash // Root hash of the genesis validator set, used for signature domain calculation
|
GenesisValidatorsRoot common.Hash // Root hash of the genesis validator set, used for signature domain calculation
|
||||||
Forks Forks
|
Forks Forks
|
||||||
Checkpoint common.Hash
|
Checkpoint common.Hash
|
||||||
|
CheckpointFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForkAtEpoch returns the latest active fork at the given epoch.
|
// ForkAtEpoch returns the latest active fork at the given epoch.
|
||||||
|
|
@ -211,3 +212,36 @@ func (f Forks) Less(i, j int) bool {
|
||||||
}
|
}
|
||||||
return f[i].knownIndex < f[j].knownIndex
|
return f[i].knownIndex < f[j].knownIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCheckpointFile sets the checkpoint import/export file name and attempts to
|
||||||
|
// read the checkpoint from the file if it already exists. It returns true if
|
||||||
|
// a checkpoint has been loaded.
|
||||||
|
func (c *ChainConfig) SetCheckpointFile(checkpointFile string) (bool, error) {
|
||||||
|
c.CheckpointFile = checkpointFile
|
||||||
|
file, err := os.ReadFile(checkpointFile)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, nil // did not load checkpoint
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("failed to read beacon checkpoint file: %v", err)
|
||||||
|
}
|
||||||
|
cp, err := hexutil.Decode(string(file))
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("failed to decode hex string in beacon checkpoint file: %v", err)
|
||||||
|
}
|
||||||
|
if len(cp) != 32 {
|
||||||
|
return false, fmt.Errorf("invalid hex string length in beacon checkpoint file: %d", len(cp))
|
||||||
|
}
|
||||||
|
copy(c.Checkpoint[:len(cp)], cp)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveCheckpointToFile saves the given checkpoint to file if a checkpoint
|
||||||
|
// import/export file has been specified.
|
||||||
|
func (c *ChainConfig) SaveCheckpointToFile(checkpoint common.Hash) (bool, error) {
|
||||||
|
if c.CheckpointFile == "" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
err := os.WriteFile(c.CheckpointFile, []byte(checkpoint.Hex()), 0600)
|
||||||
|
return err == nil, err
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,60 +54,60 @@ db128981033ac82a64688a123f631e61297b6b8f52ca913145e57caa8ce94cc3 go1.24.1.windo
|
||||||
e28c4e6d0b913955765b46157ab88ae59bb636acaa12d7bec959aa6900f1cebd go1.24.1.windows-arm64.zip
|
e28c4e6d0b913955765b46157ab88ae59bb636acaa12d7bec959aa6900f1cebd go1.24.1.windows-arm64.zip
|
||||||
6d352c1f154a102a5b90c480cc64bab205ccf2681e34e78a3a4d3f1ddfbc81e4 go1.24.1.windows-arm64.msi
|
6d352c1f154a102a5b90c480cc64bab205ccf2681e34e78a3a4d3f1ddfbc81e4 go1.24.1.windows-arm64.msi
|
||||||
|
|
||||||
# version:golangci 1.64.6
|
# version:golangci 2.0.2
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
# https://github.com/golangci/golangci-lint/releases/download/v1.64.6/
|
# https://github.com/golangci/golangci-lint/releases/download/v2.0.2/
|
||||||
08f9459e7125fed2612abd71596e04d172695921aff82120d1c1e5e9b6fff2a3 golangci-lint-1.64.6-darwin-amd64.tar.gz
|
a88cbdc86b483fe44e90bf2dcc3fec2af8c754116e6edf0aa6592cac5baa7a0e golangci-lint-2.0.2-darwin-amd64.tar.gz
|
||||||
8c10d0c7c3935b8c2269d628b6a06a8f48d8fb4cc31af02fe4ce0cd18b0704c1 golangci-lint-1.64.6-darwin-arm64.tar.gz
|
664550e7954f5f4451aae99b4f7382c1a47039c66f39ca605f5d9af1a0d32b49 golangci-lint-2.0.2-darwin-arm64.tar.gz
|
||||||
c07fcabb55deb8d2c4d390025568e76162d7f91b1d14bd2311be45d8d440a624 golangci-lint-1.64.6-freebsd-386.tar.gz
|
bda0f0f27d300502faceda8428834a76ca25986f6d9fc2bd41d201c3ed73f08e golangci-lint-2.0.2-freebsd-386.tar.gz
|
||||||
8ed1ef1102e1a42983ffcaae8e06de6a540334c3a69e205c610b8a7c92d469cd golangci-lint-1.64.6-freebsd-amd64.tar.gz
|
1cbd0c7ade3fb027d61d38a646ec1b51be5846952b4b04a5330e7f4687f2270c golangci-lint-2.0.2-freebsd-amd64.tar.gz
|
||||||
8f8dda66d1b4a85cc8a1daf1b69af6559c3eb0a41dd8033148a9ad85cfc0e1d9 golangci-lint-1.64.6-freebsd-armv6.tar.gz
|
1e828a597726198b2e35acdbcc5f3aad85244d79846d2d2bdb05241c5a535f9e golangci-lint-2.0.2-freebsd-armv6.tar.gz
|
||||||
59e8ca1fa254661b2a55e96515b14a10cd02221d443054deac5b28c3c3e12d6b golangci-lint-1.64.6-freebsd-armv7.tar.gz
|
848b49315dc5cddd0c9ce35e96ab33d584db0ea8fb57bcbf9784f1622bec0269 golangci-lint-2.0.2-freebsd-armv7.tar.gz
|
||||||
e3d323d5f132e9b7593141bfe1d19c8460e65a55cea1008ec96945fed563f981 golangci-lint-1.64.6-illumos-amd64.tar.gz
|
cabf9a6beab574c7f98581eb237919e580024759e3cdc05c4d516b044dce6770 golangci-lint-2.0.2-illumos-amd64.tar.gz
|
||||||
5ce910f2a864c5fbeb32a30cbd506e1b2ef215f7a0f422cd5be6370b13db6f03 golangci-lint-1.64.6-linux-386.deb
|
2fde80d15ed6527791f106d606120620e913c3a663c90a8596861d0a4461169e golangci-lint-2.0.2-linux-386.deb
|
||||||
2caab682a26b9a5fb94ba24e3a7e1404fa9eab2c12e36ae1c5548d80a1be190c golangci-lint-1.64.6-linux-386.rpm
|
804bc6e350a8c613aaa0a33d8d45414a80157b0ba1b2c2335ac859f85ad98ebd golangci-lint-2.0.2-linux-386.rpm
|
||||||
2d82d0a4716e6d9b70c95103054855cb4b5f20f7bbdee42297f0189955bd14b6 golangci-lint-1.64.6-linux-386.tar.gz
|
e64beb72fecf581e57d88ae3adb1c9d4bf022694b6bd92e3c8e460910bbdc37d golangci-lint-2.0.2-linux-386.tar.gz
|
||||||
9cd82503e9841abcaa57663fc899587fe90363c26d86a793a98d3080fd25e907 golangci-lint-1.64.6-linux-amd64.deb
|
9c55aed174d7a52bb1d4006b36e7edee9023631f6b814a80cb39c9860d6f75c3 golangci-lint-2.0.2-linux-amd64.deb
|
||||||
981aaca5e5202d4fbb162ec7080490eb67ef5d32add5fb62fb02210597acc9da golangci-lint-1.64.6-linux-amd64.rpm
|
c55a2ef741a687b4c679696931f7fd4a467babd64c9457cf17bb9632fd1cecd1 golangci-lint-2.0.2-linux-amd64.rpm
|
||||||
71e290acbacb7b3ba4f68f0540fb74dc180c4336ac8a6f3bdcd7fcc48b15148d golangci-lint-1.64.6-linux-amd64.tar.gz
|
89cc8a7810dc63b9a37900da03e37c3601caf46d42265d774e0f1a5d883d53e2 golangci-lint-2.0.2-linux-amd64.tar.gz
|
||||||
718016bb06c1f598a8d23c7964e2643de621dbe5808688cb38857eb0bb773c84 golangci-lint-1.64.6-linux-arm64.deb
|
a3e78583c4e7ea1b63e82559f126bb3a5b12788676f158526752d53e67824b99 golangci-lint-2.0.2-linux-arm64.deb
|
||||||
ddc0e7b4b10b47cf894aea157e89e3674bbb60f8f5c480110c21c49cc2c1634d golangci-lint-1.64.6-linux-arm64.rpm
|
bd5dd52b5c9f18aa7a2904eda9a9f91c628e98623fe70b7afcbb847e2de84422 golangci-lint-2.0.2-linux-arm64.rpm
|
||||||
99a7ff13dec7a8781a68408b6ecb8a1c5e82786cba3189eaa91d5cdcc24362ce golangci-lint-1.64.6-linux-arm64.tar.gz
|
789d5b91219ac68c2336f77d41cd7e33a910420594780f455893f8453d09595b golangci-lint-2.0.2-linux-arm64.tar.gz
|
||||||
90e360f89c244394912b8709fb83a900b6d56cf19141df2afaf9e902ef3057b1 golangci-lint-1.64.6-linux-armv6.deb
|
534cd4c464a66178714ed68152c1ed7aa73e5700bf409e4ed1a8363adf96afca golangci-lint-2.0.2-linux-armv6.deb
|
||||||
46546ff7c98d873ffcdbee06b39dc1024fc08db4fbf1f6309360a44cf976b5c2 golangci-lint-1.64.6-linux-armv6.rpm
|
cf7d02905a5fc80b96c9a64621693b4cc7337b1ce29986c19fd72608dafe66c5 golangci-lint-2.0.2-linux-armv6.rpm
|
||||||
e45c1a42868aca0b0ee54d14fb89da216f3b4409367cd7bce3b5f36788b4fc92 golangci-lint-1.64.6-linux-armv6.tar.gz
|
a0d81cb527d8fe878377f2356b5773e219b0b91832a6b59e7b9bcf9a90fe0b0e golangci-lint-2.0.2-linux-armv6.tar.gz
|
||||||
3cf6ddbbbf358db3de4b64a24f9683bbe2da3f003cfdee86cb610124b57abafb golangci-lint-1.64.6-linux-armv7.deb
|
dedd5be7fff8cba8fe15b658a59347ea90d7d02a9fff87f09c7687e6da05a8b6 golangci-lint-2.0.2-linux-armv7.deb
|
||||||
508b6712710da10f11aab9ea5e63af415c932dfe7fff718e649d3100b838f797 golangci-lint-1.64.6-linux-armv7.rpm
|
85521b6f3ad2f5a2bc9bfe14b9b08623f764964048f75ed6dfcfaf8eb7d57cc1 golangci-lint-2.0.2-linux-armv7.rpm
|
||||||
da9a8bbee86b4dfee73fbc17e0856ec84c5b04919eb09bf3dd5904c39ce41753 golangci-lint-1.64.6-linux-armv7.tar.gz
|
96471046c7780dda4ea680f65e92c2ef56ff58d40bcffaf6cfe9d6d48e3c27aa golangci-lint-2.0.2-linux-armv7.tar.gz
|
||||||
ad496a58284e1e9c8ac6f993fec429dcd96c85a8c4715dbb6530a5af0dae7fbd golangci-lint-1.64.6-linux-loong64.deb
|
815d914a7738e4362466b2d11004e8618b696b49e8ace13df2c2b25f28fb1e17 golangci-lint-2.0.2-linux-loong64.deb
|
||||||
3bd70fa737b224750254dce616d9a188570e49b894b0cdb2fd04155e2c061350 golangci-lint-1.64.6-linux-loong64.rpm
|
f16381e3d8a0f011b95e086d83d620248432b915d01f4beab4d29cfe4dc388b0 golangci-lint-2.0.2-linux-loong64.rpm
|
||||||
a535af973499729f2215a84303eb0de61399057aad6901ddea1b4f73f68d5f2c golangci-lint-1.64.6-linux-loong64.tar.gz
|
1bd8d7714f9c92db6a0f23bae89f39c85ba047bec8eeb42b8748d30ae3228d18 golangci-lint-2.0.2-linux-loong64.tar.gz
|
||||||
6ad2a1cd37ca30303a488abfca2de52aff57519901c6d8d2608656fe9fb05292 golangci-lint-1.64.6-linux-mips64.deb
|
ea6e9d4aabb526aa298e47e8b026d8893d918c5eb919ba0ab403e315def74cc5 golangci-lint-2.0.2-linux-mips64.deb
|
||||||
5f18369f0ca010a02c267352ebe6e3e0514c6debff49899c9e5520906c0da287 golangci-lint-1.64.6-linux-mips64.rpm
|
519d8d53af83fdc9c25cc3fba8b663331ac22ef68131d4b0084cb6f425b6f79a golangci-lint-2.0.2-linux-mips64.rpm
|
||||||
3449d6c13307b91b0e8817f8911c07c3412cdb6931b8d101e42db3e9762e91ad golangci-lint-1.64.6-linux-mips64.tar.gz
|
80d655a0a1ac1b19dcef4b58fa2a7dadb646cc50ad08d460b5c53cdb421165e4 golangci-lint-2.0.2-linux-mips64.tar.gz
|
||||||
d4712a348f65dcaf9f6c58f1cfd6d0984d54a902873dca5e76f0d686f5c59499 golangci-lint-1.64.6-linux-mips64le.deb
|
aa0e75384bb482c865d4dfc95d23ceb25666bf20461b67a832f0eea6670312ec golangci-lint-2.0.2-linux-mips64le.deb
|
||||||
57cea4538894558cb8c956d6b69c5509e4304546abe4a467478fc9ada0c736d6 golangci-lint-1.64.6-linux-mips64le.rpm
|
f2a8b500fb69bdea1b01df6267aaa5218fa4a58aeb781c1a20d0d802fe465a52 golangci-lint-2.0.2-linux-mips64le.rpm
|
||||||
bc030977d44535b2152fddb2732f056d193c043992fe638ddecea21a40ef09fe golangci-lint-1.64.6-linux-mips64le.tar.gz
|
e66a0c0c9a275f02d27a7caa9576112622306f001d73dfc082cf1ae446fc1242 golangci-lint-2.0.2-linux-mips64le.tar.gz
|
||||||
1ceb4e492efa527d246c61798c581f49113756fab8c39bb3eefdb1fa97041f92 golangci-lint-1.64.6-linux-ppc64le.deb
|
e85ad51aac6428be2d8a37000d053697371a538a5bcbc1644caa7c5e77f6d0af golangci-lint-2.0.2-linux-ppc64le.deb
|
||||||
454e1c2b3583a8644e0c33a970fb4ce35b8f11848e1a770d9095d99d159ad0ab golangci-lint-1.64.6-linux-ppc64le.rpm
|
906798365eac1944af2a9b9a303e6fd49ec9043307bc681b7a96277f7f8beea5 golangci-lint-2.0.2-linux-ppc64le.rpm
|
||||||
fddf30d35923eb6c7bb57520d8645768f802bf86c4cbf5a3a13049efb9e71b82 golangci-lint-1.64.6-linux-ppc64le.tar.gz
|
f7f1a271b0af274d6c9ce000f5dc6e1fb194350c67bcc62494f96f791882ba92 golangci-lint-2.0.2-linux-ppc64le.tar.gz
|
||||||
bd75f0dd6f65bee5821c433803b28f3c427ef3582764c3d0e7f7fae1c9d468b6 golangci-lint-1.64.6-linux-riscv64.deb
|
eea8bf643a42bf05de9780530db22923e5ab0d588f0e173594dc6518f2a25d2a golangci-lint-2.0.2-linux-riscv64.deb
|
||||||
58457207c225cbd5340c8dcb75d9a44aa890d604e28464115a3a9762febaed04 golangci-lint-1.64.6-linux-riscv64.rpm
|
4ff40f9fe2954400836e2a011ba4744d00ffab5068a51368552dfce6aba3b81b golangci-lint-2.0.2-linux-riscv64.rpm
|
||||||
82639518a613a6705b7e2de5b28c278e875d782a5c97e9c1b2ac10b4ecd7852f golangci-lint-1.64.6-linux-riscv64.tar.gz
|
531d8f225866674977d630afbf0533eb02f9bec607fb13895f7a2cd7b2e0a648 golangci-lint-2.0.2-linux-riscv64.tar.gz
|
||||||
131d69204f8ced200b1437731e987cc886edac30dc87e6e1dcbd4f833d351713 golangci-lint-1.64.6-linux-s390x.deb
|
6f827647046c603f40d97ea5aadc6f48cd0bb5d19f7a3d56500c3b833d2a0342 golangci-lint-2.0.2-linux-s390x.deb
|
||||||
ca6caf28ca7a1df7cff720f8fd6ac4b8f2eac10c8cbfe7d2eade70876aded570 golangci-lint-1.64.6-linux-s390x.rpm
|
387a090e9576d19ca86aac738172e58e07c19f2784a13bb387f4f0d75fb9c8d3 golangci-lint-2.0.2-linux-s390x.rpm
|
||||||
ed966d28a6512bc2b1120029a9f78ed77f2015e330b589a731d67d59be30b0b1 golangci-lint-1.64.6-linux-s390x.tar.gz
|
57de1fb7722a9feb2d11ed0a007a93959d05b9db5929a392abc222e30012467e golangci-lint-2.0.2-linux-s390x.tar.gz
|
||||||
b181132b41943fc77ace7f9f5523085d12d3613f27774a0e35e17dd5e65ba589 golangci-lint-1.64.6-netbsd-386.tar.gz
|
ed95e0492ea86bf79eb661f0334474b2a4255093685ff587eccd797c5a54db7e golangci-lint-2.0.2-netbsd-386.tar.gz
|
||||||
f7b81c426006e3c699dc8665797a462aecba8c2cd23ac4971472e55184d95bc9 golangci-lint-1.64.6-netbsd-amd64.tar.gz
|
eab81d729778166415d349a80e568b2f2b3a781745a9be3212a92abb1e732daf golangci-lint-2.0.2-netbsd-amd64.tar.gz
|
||||||
663d6fb4c3bef57b8aacdb1b1a4634075e55d294ed170724b443374860897ca6 golangci-lint-1.64.6-netbsd-arm64.tar.gz
|
d20add73f7c2de2c3b01ed4fd7b63ffcf0a6597d5ea228d1699e92339a3cd047 golangci-lint-2.0.2-netbsd-arm64.tar.gz
|
||||||
8c7a76ee822568cc19352bbb9b2b0863dc5e185eb07782adbbaf648afd02ebcd golangci-lint-1.64.6-netbsd-armv6.tar.gz
|
4e4f44e6057879cd62424ff1800a767d25a595c0e91d6d48809eea9186b4c739 golangci-lint-2.0.2-netbsd-armv6.tar.gz
|
||||||
0ce26d56ce78e516529e087118ba3f1bcd71d311b4c5b2bde6ec24a6e8966d85 golangci-lint-1.64.6-netbsd-armv7.tar.gz
|
51ec17b16d8743ae4098a0171f04f0ed4d64561e3051b982778b0e6c306a1b03 golangci-lint-2.0.2-netbsd-armv7.tar.gz
|
||||||
135d5d998168683f46e4fab308cef5aa3c282025b7de6b258f976aadfb820db7 golangci-lint-1.64.6-source.tar.gz
|
5482cf27b93fae1765c70ee2a95d4074d038e9dee61bdd61d017ce8893d3a4a8 golangci-lint-2.0.2-source.tar.gz
|
||||||
ccdb0cc249531a205304a76308cfa7cda830083d083d557884416a2461148263 golangci-lint-1.64.6-windows-386.zip
|
a35d8fdf3e14079a10880dbbb7586b46faec89be96f086b244b3e565aac80313 golangci-lint-2.0.2-windows-386.zip
|
||||||
2d88f282e08e1853c70bc7c914b5f58beaa6509903d56098aeb9bc3df30ea9d5 golangci-lint-1.64.6-windows-amd64.zip
|
fe4b946cc01366b989001215687003a9c4a7098589921f75e6228d6d8cffc15c golangci-lint-2.0.2-windows-amd64.zip
|
||||||
6a3c6e7afc6916392679d7d6b95ac239827644e3e50ec4e8ca6ab1e4e6db65fe golangci-lint-1.64.6-windows-arm64.zip
|
646bd9250ef8c771d85cd22fe8e6f2397ae39599179755e3bbfa9ef97ad44090 golangci-lint-2.0.2-windows-arm64.zip
|
||||||
9c9c1ef9687651566987f93e76252f7526c386901d7d8aeee044ca88115da4b1 golangci-lint-1.64.6-windows-armv6.zip
|
ce1dc0bad6f8a61d64e6b3779eeb773479c175125d6f686b0e67ef9c8432d16e golangci-lint-2.0.2-windows-armv6.zip
|
||||||
4f0df114155791799dfde8cd8cb6307fecfb17fed70b44205486ec925e2f39cf golangci-lint-1.64.6-windows-armv7.zip
|
92684a48faabe792b11ac27ca8b25551eff940b0a1e84ad7244e98b4994962db golangci-lint-2.0.2-windows-armv7.zip
|
||||||
|
|
||||||
# This is the builder on PPA that will build Go itself (inception-y), don't modify!
|
# This is the builder on PPA that will build Go itself (inception-y), don't modify!
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ func main() {
|
||||||
utils.BeaconGenesisRootFlag,
|
utils.BeaconGenesisRootFlag,
|
||||||
utils.BeaconGenesisTimeFlag,
|
utils.BeaconGenesisTimeFlag,
|
||||||
utils.BeaconCheckpointFlag,
|
utils.BeaconCheckpointFlag,
|
||||||
|
utils.BeaconCheckpointFileFlag,
|
||||||
//TODO datadir for optional permanent database
|
//TODO datadir for optional permanent database
|
||||||
utils.MainnetFlag,
|
utils.MainnetFlag,
|
||||||
utils.SepoliaFlag,
|
utils.SepoliaFlag,
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,9 @@ func (s *Suite) AllTests() []utesting.Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestPing sends PING and expects a PONG response.
|
|
||||||
func (s *Suite) TestPing(t *utesting.T) {
|
func (s *Suite) TestPing(t *utesting.T) {
|
||||||
|
t.Log(`This test is just a sanity check. It sends PING and expects a PONG response.`)
|
||||||
|
|
||||||
conn, l1 := s.listen1(t)
|
conn, l1 := s.listen1(t)
|
||||||
defer conn.close()
|
defer conn.close()
|
||||||
|
|
||||||
|
|
@ -85,9 +86,10 @@ func checkPong(t *utesting.T, pong *v5wire.Pong, ping *v5wire.Ping, c net.Packet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestPingLargeRequestID sends PING with a 9-byte request ID, which isn't allowed by the spec.
|
|
||||||
// The remote node should not respond.
|
|
||||||
func (s *Suite) TestPingLargeRequestID(t *utesting.T) {
|
func (s *Suite) TestPingLargeRequestID(t *utesting.T) {
|
||||||
|
t.Log(`This test sends PING with a 9-byte request ID, which isn't allowed by the spec.
|
||||||
|
The remote node should not respond.`)
|
||||||
|
|
||||||
conn, l1 := s.listen1(t)
|
conn, l1 := s.listen1(t)
|
||||||
defer conn.close()
|
defer conn.close()
|
||||||
|
|
||||||
|
|
@ -104,10 +106,11 @@ func (s *Suite) TestPingLargeRequestID(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestPingMultiIP establishes a session from one IP as usual. The session is then reused
|
|
||||||
// on another IP, which shouldn't work. The remote node should respond with WHOAREYOU for
|
|
||||||
// the attempt from a different IP.
|
|
||||||
func (s *Suite) TestPingMultiIP(t *utesting.T) {
|
func (s *Suite) TestPingMultiIP(t *utesting.T) {
|
||||||
|
t.Log(`This test establishes a session from one IP as usual. The session is then reused
|
||||||
|
on another IP, which shouldn't work. The remote node should respond with WHOAREYOU for
|
||||||
|
the attempt from a different IP.`)
|
||||||
|
|
||||||
conn, l1, l2 := s.listen2(t)
|
conn, l1, l2 := s.listen2(t)
|
||||||
defer conn.close()
|
defer conn.close()
|
||||||
|
|
||||||
|
|
@ -120,6 +123,7 @@ func (s *Suite) TestPingMultiIP(t *utesting.T) {
|
||||||
checkPong(t, resp.(*v5wire.Pong), ping, l1)
|
checkPong(t, resp.(*v5wire.Pong), ping, l1)
|
||||||
|
|
||||||
// Send on l2. This reuses the session because there is only one codec.
|
// Send on l2. This reuses the session because there is only one codec.
|
||||||
|
t.Log("sending ping from alternate IP", l2.LocalAddr())
|
||||||
ping2 := &v5wire.Ping{ReqID: conn.nextReqID()}
|
ping2 := &v5wire.Ping{ReqID: conn.nextReqID()}
|
||||||
conn.write(l2, ping2, nil)
|
conn.write(l2, ping2, nil)
|
||||||
switch resp := conn.read(l2).(type) {
|
switch resp := conn.read(l2).(type) {
|
||||||
|
|
@ -158,6 +162,10 @@ func (s *Suite) TestPingMultiIP(t *utesting.T) {
|
||||||
// packet instead of a handshake message packet. The remote node should respond with
|
// packet instead of a handshake message packet. The remote node should respond with
|
||||||
// another WHOAREYOU challenge for the second packet.
|
// another WHOAREYOU challenge for the second packet.
|
||||||
func (s *Suite) TestPingHandshakeInterrupted(t *utesting.T) {
|
func (s *Suite) TestPingHandshakeInterrupted(t *utesting.T) {
|
||||||
|
t.Log(`TestPingHandshakeInterrupted starts a handshake, but doesn't finish it and sends a second ordinary message
|
||||||
|
packet instead of a handshake message packet. The remote node should respond with
|
||||||
|
another WHOAREYOU challenge for the second packet.`)
|
||||||
|
|
||||||
conn, l1 := s.listen1(t)
|
conn, l1 := s.listen1(t)
|
||||||
defer conn.close()
|
defer conn.close()
|
||||||
|
|
||||||
|
|
@ -181,8 +189,10 @@ func (s *Suite) TestPingHandshakeInterrupted(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTalkRequest sends TALKREQ and expects an empty TALKRESP response.
|
|
||||||
func (s *Suite) TestTalkRequest(t *utesting.T) {
|
func (s *Suite) TestTalkRequest(t *utesting.T) {
|
||||||
|
t.Log(`This test sends some examples of TALKREQ with a protocol-id of "test-protocol"
|
||||||
|
and expects an empty TALKRESP response.`)
|
||||||
|
|
||||||
conn, l1 := s.listen1(t)
|
conn, l1 := s.listen1(t)
|
||||||
defer conn.close()
|
defer conn.close()
|
||||||
|
|
||||||
|
|
@ -202,6 +212,7 @@ func (s *Suite) TestTalkRequest(t *utesting.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty request ID.
|
// Empty request ID.
|
||||||
|
t.Log("sending TALKREQ with empty request-id")
|
||||||
resp = conn.reqresp(l1, &v5wire.TalkRequest{Protocol: "test-protocol"})
|
resp = conn.reqresp(l1, &v5wire.TalkRequest{Protocol: "test-protocol"})
|
||||||
switch resp := resp.(type) {
|
switch resp := resp.(type) {
|
||||||
case *v5wire.TalkResponse:
|
case *v5wire.TalkResponse:
|
||||||
|
|
@ -216,8 +227,9 @@ func (s *Suite) TestTalkRequest(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestFindnodeZeroDistance checks that the remote node returns itself for FINDNODE with distance zero.
|
|
||||||
func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) {
|
func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) {
|
||||||
|
t.Log(`This test checks that the remote node returns itself for FINDNODE with distance zero.`)
|
||||||
|
|
||||||
conn, l1 := s.listen1(t)
|
conn, l1 := s.listen1(t)
|
||||||
defer conn.close()
|
defer conn.close()
|
||||||
|
|
||||||
|
|
@ -233,9 +245,11 @@ func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestFindnodeResults pings the node under test from multiple nodes. After waiting for them to be
|
|
||||||
// accepted into the remote table, the test checks that they are returned by FINDNODE.
|
|
||||||
func (s *Suite) TestFindnodeResults(t *utesting.T) {
|
func (s *Suite) TestFindnodeResults(t *utesting.T) {
|
||||||
|
t.Log(`This test pings the node under test from multiple other endpoints and node identities
|
||||||
|
(the 'bystanders'). After waiting for them to be accepted into the remote table, the test checks
|
||||||
|
that they are returned by FINDNODE.`)
|
||||||
|
|
||||||
// Create bystanders.
|
// Create bystanders.
|
||||||
nodes := make([]*bystander, 5)
|
nodes := make([]*bystander, 5)
|
||||||
added := make(chan enode.ID, len(nodes))
|
added := make(chan enode.ID, len(nodes))
|
||||||
|
|
@ -273,6 +287,7 @@ func (s *Suite) TestFindnodeResults(t *utesting.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send FINDNODE for all distances.
|
// Send FINDNODE for all distances.
|
||||||
|
t.Log("requesting nodes")
|
||||||
conn, l1 := s.listen1(t)
|
conn, l1 := s.listen1(t)
|
||||||
defer conn.close()
|
defer conn.close()
|
||||||
foundNodes, err := conn.findnode(l1, dists)
|
foundNodes, err := conn.findnode(l1, dists)
|
||||||
|
|
|
||||||
|
|
@ -79,11 +79,15 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
|
||||||
Usage: "Import a blockchain file",
|
Usage: "Import a blockchain file",
|
||||||
ArgsUsage: "<filename> (<filename 2> ... <filename N>) ",
|
ArgsUsage: "<filename> (<filename 2> ... <filename N>) ",
|
||||||
Flags: slices.Concat([]cli.Flag{
|
Flags: slices.Concat([]cli.Flag{
|
||||||
utils.CacheFlag,
|
|
||||||
utils.GCModeFlag,
|
utils.GCModeFlag,
|
||||||
utils.SnapshotFlag,
|
utils.SnapshotFlag,
|
||||||
|
utils.CacheFlag,
|
||||||
utils.CacheDatabaseFlag,
|
utils.CacheDatabaseFlag,
|
||||||
|
utils.CacheTrieFlag,
|
||||||
utils.CacheGCFlag,
|
utils.CacheGCFlag,
|
||||||
|
utils.CacheSnapshotFlag,
|
||||||
|
utils.CacheNoPrefetchFlag,
|
||||||
|
utils.CachePreimagesFlag,
|
||||||
utils.NoCompactionFlag,
|
utils.NoCompactionFlag,
|
||||||
utils.MetricsEnabledFlag,
|
utils.MetricsEnabledFlag,
|
||||||
utils.MetricsEnabledExpensiveFlag,
|
utils.MetricsEnabledExpensiveFlag,
|
||||||
|
|
@ -457,7 +461,7 @@ func importHistory(ctx *cli.Context) error {
|
||||||
network = networks[0]
|
network = networks[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.ImportHistory(chain, db, dir, network); err != nil {
|
if err := utils.ImportHistory(chain, dir, network); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("Import done in %v\n", time.Since(start))
|
fmt.Printf("Import done in %v\n", time.Since(start))
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,7 @@ var (
|
||||||
utils.BeaconGenesisRootFlag,
|
utils.BeaconGenesisRootFlag,
|
||||||
utils.BeaconGenesisTimeFlag,
|
utils.BeaconGenesisTimeFlag,
|
||||||
utils.BeaconCheckpointFlag,
|
utils.BeaconCheckpointFlag,
|
||||||
|
utils.BeaconCheckpointFileFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabaseFlags)
|
}, utils.NetworkFlags, utils.DatabaseFlags)
|
||||||
|
|
||||||
rpcFlags = []cli.Flag{
|
rpcFlags = []cli.Flag{
|
||||||
|
|
|
||||||
|
|
@ -246,8 +246,9 @@ func readList(filename string) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportHistory imports Era1 files containing historical block information,
|
// ImportHistory imports Era1 files containing historical block information,
|
||||||
// starting from genesis.
|
// starting from genesis. The assumption is held that the provided chain
|
||||||
func ImportHistory(chain *core.BlockChain, db ethdb.Database, dir string, network string) error {
|
// segment in Era1 file should all be canonical and verified.
|
||||||
|
func ImportHistory(chain *core.BlockChain, dir string, network string) error {
|
||||||
if chain.CurrentSnapBlock().Number.BitLen() != 0 {
|
if chain.CurrentSnapBlock().Number.BitLen() != 0 {
|
||||||
return errors.New("history import only supported when starting from genesis")
|
return errors.New("history import only supported when starting from genesis")
|
||||||
}
|
}
|
||||||
|
|
@ -308,11 +309,6 @@ func ImportHistory(chain *core.BlockChain, db ethdb.Database, dir string, networ
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
||||||
}
|
}
|
||||||
if status, err := chain.HeaderChain().InsertHeaderChain([]*types.Header{block.Header()}, start); err != nil {
|
|
||||||
return fmt.Errorf("error inserting header %d: %w", it.Number(), err)
|
|
||||||
} else if status != core.CanonStatTy {
|
|
||||||
return fmt.Errorf("error inserting header %d, not canon: %v", it.Number(), status)
|
|
||||||
}
|
|
||||||
if _, err := chain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{receipts}, 2^64-1); err != nil {
|
if _, err := chain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{receipts}, 2^64-1); err != nil {
|
||||||
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -342,6 +342,11 @@ var (
|
||||||
Usage: "Beacon chain weak subjectivity checkpoint block hash",
|
Usage: "Beacon chain weak subjectivity checkpoint block hash",
|
||||||
Category: flags.BeaconCategory,
|
Category: flags.BeaconCategory,
|
||||||
}
|
}
|
||||||
|
BeaconCheckpointFileFlag = &cli.StringFlag{
|
||||||
|
Name: "beacon.checkpoint.file",
|
||||||
|
Usage: "Beacon chain weak subjectivity checkpoint import/export file",
|
||||||
|
Category: flags.BeaconCategory,
|
||||||
|
}
|
||||||
BlsyncApiFlag = &cli.StringFlag{
|
BlsyncApiFlag = &cli.StringFlag{
|
||||||
Name: "blsync.engine.api",
|
Name: "blsync.engine.api",
|
||||||
Usage: "Target EL engine API URL",
|
Usage: "Target EL engine API URL",
|
||||||
|
|
@ -1890,7 +1895,7 @@ func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig {
|
||||||
if !ctx.IsSet(BeaconGenesisTimeFlag.Name) {
|
if !ctx.IsSet(BeaconGenesisTimeFlag.Name) {
|
||||||
Fatalf("Custom beacon chain config is specified but genesis time is missing")
|
Fatalf("Custom beacon chain config is specified but genesis time is missing")
|
||||||
}
|
}
|
||||||
if !ctx.IsSet(BeaconCheckpointFlag.Name) {
|
if !ctx.IsSet(BeaconCheckpointFlag.Name) && !ctx.IsSet(BeaconCheckpointFileFlag.Name) {
|
||||||
Fatalf("Custom beacon chain config is specified but checkpoint is missing")
|
Fatalf("Custom beacon chain config is specified but checkpoint is missing")
|
||||||
}
|
}
|
||||||
config.ChainConfig = bparams.ChainConfig{
|
config.ChainConfig = bparams.ChainConfig{
|
||||||
|
|
@ -1915,13 +1920,28 @@ func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Checkpoint is required with custom chain config and is optional with pre-defined config
|
// Checkpoint is required with custom chain config and is optional with pre-defined config
|
||||||
if ctx.IsSet(BeaconCheckpointFlag.Name) {
|
// If both checkpoint block hash and checkpoint file are specified then the
|
||||||
if c, err := hexutil.Decode(ctx.String(BeaconCheckpointFlag.Name)); err == nil && len(c) <= 32 {
|
// client is initialized with the specified block hash and new checkpoints
|
||||||
copy(config.Checkpoint[:len(c)], c)
|
// are saved to the specified file.
|
||||||
} else {
|
if ctx.IsSet(BeaconCheckpointFileFlag.Name) {
|
||||||
Fatalf("Invalid hex string", "beacon.checkpoint", ctx.String(BeaconCheckpointFlag.Name), "error", err)
|
if _, err := config.SetCheckpointFile(ctx.String(BeaconCheckpointFileFlag.Name)); err != nil {
|
||||||
|
Fatalf("Could not load beacon checkpoint file", "beacon.checkpoint.file", ctx.String(BeaconCheckpointFileFlag.Name), "error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ctx.IsSet(BeaconCheckpointFlag.Name) {
|
||||||
|
hex := ctx.String(BeaconCheckpointFlag.Name)
|
||||||
|
c, err := hexutil.Decode(hex)
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("Invalid hex string", "beacon.checkpoint", hex, "error", err)
|
||||||
|
}
|
||||||
|
if len(c) != 32 {
|
||||||
|
Fatalf("Invalid hex string length", "beacon.checkpoint", hex, "length", len(c))
|
||||||
|
}
|
||||||
|
copy(config.Checkpoint[:len(c)], c)
|
||||||
|
}
|
||||||
|
if config.Checkpoint == (common.Hash{}) {
|
||||||
|
Fatalf("Beacon checkpoint not specified")
|
||||||
|
}
|
||||||
config.Apis = ctx.StringSlice(BeaconApiFlag.Name)
|
config.Apis = ctx.StringSlice(BeaconApiFlag.Name)
|
||||||
if config.Apis == nil {
|
if config.Apis == nil {
|
||||||
Fatalf("Beacon node light client API URL not specified")
|
Fatalf("Beacon node light client API URL not specified")
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ func TestHistoryImportAndExport(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to initialize chain: %v", err)
|
t.Fatalf("unable to initialize chain: %v", err)
|
||||||
}
|
}
|
||||||
if err := ImportHistory(imported, db2, dir, "mainnet"); err != nil {
|
if err := ImportHistory(imported, dir, "mainnet"); err != nil {
|
||||||
t.Fatalf("failed to import chain: %v", err)
|
t.Fatalf("failed to import chain: %v", err)
|
||||||
}
|
}
|
||||||
if have, want := imported.CurrentHeader(), chain.CurrentHeader(); have.Hash() != want.Hash() {
|
if have, want := imported.CurrentHeader(), chain.CurrentHeader(); have.Hash() != want.Hash() {
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||||
if !disk {
|
if !disk {
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
} else {
|
} else {
|
||||||
pdb, err := pebble.New(b.TempDir(), 128, 128, "", false)
|
pdb, err := pebble.New(b.TempDir(), 128, 128, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("cannot create temporary database: %v", err)
|
b.Fatalf("cannot create temporary database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -303,7 +303,7 @@ func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uin
|
||||||
func benchWriteChain(b *testing.B, full bool, count uint64) {
|
func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||||
genesis := &Genesis{Config: params.AllEthashProtocolChanges}
|
genesis := &Genesis{Config: params.AllEthashProtocolChanges}
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false)
|
pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database: %v", err)
|
b.Fatalf("error opening database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +316,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||||
func benchReadChain(b *testing.B, full bool, count uint64) {
|
func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
dir := b.TempDir()
|
dir := b.TempDir()
|
||||||
|
|
||||||
pdb, err := pebble.New(dir, 1024, 128, "", false)
|
pdb, err := pebble.New(dir, 1024, 128, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database: %v", err)
|
b.Fatalf("error opening database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -332,7 +332,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
pdb, err = pebble.New(dir, 1024, 128, "", false)
|
pdb, err = pebble.New(dir, 1024, 128, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database: %v", err)
|
b.Fatalf("error opening database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -157,7 +158,8 @@ type CacheConfig struct {
|
||||||
|
|
||||||
// This defines the cutoff block for history expiry.
|
// This defines the cutoff block for history expiry.
|
||||||
// Blocks before this number may be unavailable in the chain database.
|
// Blocks before this number may be unavailable in the chain database.
|
||||||
HistoryPruningCutoff uint64
|
HistoryPruningCutoffNumber uint64
|
||||||
|
HistoryPruningCutoffHash common.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// triedbConfig derives the configures for trie database.
|
// triedbConfig derives the configures for trie database.
|
||||||
|
|
@ -262,7 +264,6 @@ type BlockChain struct {
|
||||||
txLookupLock sync.RWMutex
|
txLookupLock sync.RWMutex
|
||||||
txLookupCache *lru.Cache[common.Hash, txLookup]
|
txLookupCache *lru.Cache[common.Hash, txLookup]
|
||||||
|
|
||||||
wg sync.WaitGroup
|
|
||||||
quit chan struct{} // shutdown signal, closed in Stop.
|
quit chan struct{} // shutdown signal, closed in Stop.
|
||||||
stopping atomic.Bool // false if chain is running, true when stopped
|
stopping atomic.Bool // false if chain is running, true when stopped
|
||||||
procInterrupt atomic.Bool // interrupt signaler for block processing
|
procInterrupt atomic.Bool // interrupt signaler for block processing
|
||||||
|
|
@ -333,10 +334,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
bc.processor = NewStateProcessor(chainConfig, bc.hc)
|
bc.processor = NewStateProcessor(chainConfig, bc.hc)
|
||||||
|
|
||||||
genesisHeader := bc.GetHeaderByNumber(0)
|
genesisHeader := bc.GetHeaderByNumber(0)
|
||||||
bc.genesisBlock = types.NewBlockWithHeader(genesisHeader)
|
if genesisHeader == nil {
|
||||||
if bc.genesisBlock == nil {
|
|
||||||
return nil, ErrNoGenesis
|
return nil, ErrNoGenesis
|
||||||
}
|
}
|
||||||
|
bc.genesisBlock = types.NewBlockWithHeader(genesisHeader)
|
||||||
|
|
||||||
bc.currentBlock.Store(nil)
|
bc.currentBlock.Store(nil)
|
||||||
bc.currentSnapBlock.Store(nil)
|
bc.currentSnapBlock.Store(nil)
|
||||||
|
|
@ -1110,7 +1111,6 @@ func (bc *BlockChain) stopWithoutSaving() {
|
||||||
// the mutex should become available quickly. It cannot be taken again after Close has
|
// the mutex should become available quickly. It cannot be taken again after Close has
|
||||||
// returned.
|
// returned.
|
||||||
bc.chainmu.Close()
|
bc.chainmu.Close()
|
||||||
bc.wg.Wait()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the blockchain service. If any imports are currently in progress
|
// Stop stops the blockchain service. If any imports are currently in progress
|
||||||
|
|
@ -1197,79 +1197,64 @@ const (
|
||||||
SideStatTy
|
SideStatTy
|
||||||
)
|
)
|
||||||
|
|
||||||
// InsertReceiptChain attempts to complete an already existing header chain with
|
// InsertReceiptChain inserts a batch of blocks along with their receipts into
|
||||||
// transaction and receipt data.
|
// the database. Unlike InsertChain, this function does not verify the state root
|
||||||
|
// in the blocks. It is used exclusively for snap sync. All the inserted blocks
|
||||||
|
// will be regarded as canonical, chain reorg is not supported.
|
||||||
|
//
|
||||||
|
// The optional ancientLimit can also be specified and chain segment before that
|
||||||
|
// will be directly stored in the ancient, getting rid of the chain migration.
|
||||||
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts, ancientLimit uint64) (int, error) {
|
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts, ancientLimit uint64) (int, error) {
|
||||||
// We don't require the chainMu here since we want to maximize the
|
// Verify the supplied headers before insertion without lock
|
||||||
// concurrency of header insertion and receipt insertion.
|
var headers []*types.Header
|
||||||
bc.wg.Add(1)
|
for _, block := range blockChain {
|
||||||
defer bc.wg.Done()
|
headers = append(headers, block.Header())
|
||||||
|
|
||||||
var (
|
// Here we also validate that blob transactions in the block do not
|
||||||
ancientBlocks, liveBlocks types.Blocks
|
// contain a sidecar. While the sidecar does not affect the block hash
|
||||||
ancientReceipts, liveReceipts []types.Receipts
|
// or tx hash, sending blobs within a block is not allowed.
|
||||||
)
|
|
||||||
// Do a sanity check that the provided chain is actually ordered and linked
|
|
||||||
for i, block := range blockChain {
|
|
||||||
if i != 0 {
|
|
||||||
prev := blockChain[i-1]
|
|
||||||
if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() {
|
|
||||||
log.Error("Non contiguous receipt insert",
|
|
||||||
"number", block.Number(), "hash", block.Hash(), "parent", block.ParentHash(),
|
|
||||||
"prevnumber", prev.Number(), "prevhash", prev.Hash())
|
|
||||||
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])",
|
|
||||||
i-1, prev.NumberU64(), prev.Hash().Bytes()[:4],
|
|
||||||
i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if block.NumberU64() <= ancientLimit {
|
|
||||||
ancientBlocks, ancientReceipts = append(ancientBlocks, block), append(ancientReceipts, receiptChain[i])
|
|
||||||
} else {
|
|
||||||
liveBlocks, liveReceipts = append(liveBlocks, block), append(liveReceipts, receiptChain[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we also validate that blob transactions in the block do not contain a sidecar.
|
|
||||||
// While the sidecar does not affect the block hash / tx hash, sending blobs within a block is not allowed.
|
|
||||||
for txIndex, tx := range block.Transactions() {
|
for txIndex, tx := range block.Transactions() {
|
||||||
if tx.Type() == types.BlobTxType && tx.BlobTxSidecar() != nil {
|
if tx.Type() == types.BlobTxType && tx.BlobTxSidecar() != nil {
|
||||||
return 0, fmt.Errorf("block #%d contains unexpected blob sidecar in tx at index %d", block.NumberU64(), txIndex)
|
return 0, fmt.Errorf("block #%d contains unexpected blob sidecar in tx at index %d", block.NumberU64(), txIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if n, err := bc.hc.ValidateHeaderChain(headers); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
// Hold the mutation lock
|
||||||
|
if !bc.chainmu.TryLock() {
|
||||||
|
return 0, errChainStopped
|
||||||
|
}
|
||||||
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
stats = struct{ processed, ignored int32 }{}
|
stats = struct{ processed, ignored int32 }{}
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
size = int64(0)
|
size = int64(0)
|
||||||
)
|
)
|
||||||
|
// updateHead updates the head header and head snap block flags.
|
||||||
// updateHead updates the head snap sync block if the inserted blocks are better
|
updateHead := func(header *types.Header) error {
|
||||||
// and returns an indicator whether the inserted blocks are canonical.
|
batch := bc.db.NewBatch()
|
||||||
updateHead := func(head *types.Block) bool {
|
hash := header.Hash()
|
||||||
if !bc.chainmu.TryLock() {
|
rawdb.WriteHeadHeaderHash(batch, hash)
|
||||||
return false
|
rawdb.WriteHeadFastBlockHash(batch, hash)
|
||||||
|
if err := batch.Write(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
bc.hc.currentHeader.Store(header)
|
||||||
|
bc.currentSnapBlock.Store(header)
|
||||||
// Rewind may have occurred, skip in that case.
|
headHeaderGauge.Update(header.Number.Int64())
|
||||||
if bc.CurrentHeader().Number.Cmp(head.Number()) >= 0 {
|
headFastBlockGauge.Update(header.Number.Int64())
|
||||||
rawdb.WriteHeadFastBlockHash(bc.db, head.Hash())
|
return nil
|
||||||
bc.currentSnapBlock.Store(head.Header())
|
|
||||||
headFastBlockGauge.Update(int64(head.NumberU64()))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
// writeAncient writes blockchain and corresponding receipt chain into ancient store.
|
// writeAncient writes blockchain and corresponding receipt chain into ancient store.
|
||||||
//
|
//
|
||||||
// this function only accepts canonical chain data. All side chain will be reverted
|
// this function only accepts canonical chain data. All side chain will be reverted
|
||||||
// eventually.
|
// eventually.
|
||||||
writeAncient := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
writeAncient := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
||||||
first := blockChain[0]
|
// Ensure genesis is in the ancient store
|
||||||
last := blockChain[len(blockChain)-1]
|
if blockChain[0].NumberU64() == 1 {
|
||||||
|
|
||||||
// Ensure genesis is in ancients.
|
|
||||||
if first.NumberU64() == 1 {
|
|
||||||
if frozen, _ := bc.db.Ancients(); frozen == 0 {
|
if frozen, _ := bc.db.Ancients(); frozen == 0 {
|
||||||
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil})
|
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1280,12 +1265,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
log.Info("Wrote genesis to ancients")
|
log.Info("Wrote genesis to ancients")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Before writing the blocks to the ancients, we need to ensure that
|
|
||||||
// they correspond to the what the headerchain 'expects'.
|
|
||||||
// We only check the last block/header, since it's a contiguous chain.
|
|
||||||
if !bc.HasHeader(last.Hash(), last.NumberU64()) {
|
|
||||||
return 0, fmt.Errorf("containing header #%d [%x..] unknown", last.Number(), last.Hash().Bytes()[:4])
|
|
||||||
}
|
|
||||||
// Write all chain data to ancients.
|
// Write all chain data to ancients.
|
||||||
writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain)
|
writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1298,44 +1277,28 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
if err := bc.db.Sync(); err != nil {
|
if err := bc.db.Sync(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
// Update the current snap block because all block data is now present in DB.
|
// Write hash to number mappings
|
||||||
previousSnapBlock := bc.CurrentSnapBlock().Number.Uint64()
|
batch := bc.db.NewBatch()
|
||||||
if !updateHead(blockChain[len(blockChain)-1]) {
|
|
||||||
// We end up here if the header chain has reorg'ed, and the blocks/receipts
|
|
||||||
// don't match the canonical chain.
|
|
||||||
if _, err := bc.db.TruncateHead(previousSnapBlock + 1); err != nil {
|
|
||||||
log.Error("Can't truncate ancient store after failed insert", "err", err)
|
|
||||||
}
|
|
||||||
return 0, errSideChainReceipts
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete block data from the main database.
|
|
||||||
var (
|
|
||||||
batch = bc.db.NewBatch()
|
|
||||||
canonHashes = make(map[common.Hash]struct{}, len(blockChain))
|
|
||||||
)
|
|
||||||
for _, block := range blockChain {
|
for _, block := range blockChain {
|
||||||
canonHashes[block.Hash()] = struct{}{}
|
rawdb.WriteHeaderNumber(batch, block.Hash(), block.NumberU64())
|
||||||
if block.NumberU64() == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rawdb.DeleteCanonicalHash(batch, block.NumberU64())
|
|
||||||
rawdb.DeleteBlockWithoutNumber(batch, block.Hash(), block.NumberU64())
|
|
||||||
}
|
|
||||||
// Delete side chain hash-to-number mappings.
|
|
||||||
for _, nh := range rawdb.ReadAllHashesInRange(bc.db, first.NumberU64(), last.NumberU64()) {
|
|
||||||
if _, canon := canonHashes[nh.Hash]; !canon {
|
|
||||||
rawdb.DeleteHeader(batch, nh.Hash, nh.Number)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if err := batch.Write(); err != nil {
|
if err := batch.Write(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
// Update the current snap block because all block data is now present in DB.
|
||||||
|
if err := updateHead(blockChain[len(blockChain)-1].Header()); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
stats.processed += int32(len(blockChain))
|
stats.processed += int32(len(blockChain))
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeLive writes blockchain and corresponding receipt chain into active store.
|
// writeLive writes the blockchain and corresponding receipt chain to the active store.
|
||||||
|
//
|
||||||
|
// Notably, in different snap sync cycles, the supplied chain may partially reorganize
|
||||||
|
// existing local chain segments (reorg around the chain tip). The reorganized part
|
||||||
|
// will be included in the provided chain segment, and stale canonical markers will be
|
||||||
|
// silently rewritten. Therefore, no explicit reorg logic is needed.
|
||||||
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
||||||
var (
|
var (
|
||||||
skipPresenceCheck = false
|
skipPresenceCheck = false
|
||||||
|
|
@ -1346,10 +1309,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
if bc.insertStopped() {
|
if bc.insertStopped() {
|
||||||
return 0, errInsertionInterrupted
|
return 0, errInsertionInterrupted
|
||||||
}
|
}
|
||||||
// Short circuit if the owner header is unknown
|
|
||||||
if !bc.HasHeader(block.Hash(), block.NumberU64()) {
|
|
||||||
return i, fmt.Errorf("containing header #%d [%x..] unknown", block.Number(), block.Hash().Bytes()[:4])
|
|
||||||
}
|
|
||||||
if !skipPresenceCheck {
|
if !skipPresenceCheck {
|
||||||
// Ignore if the entire data is already known
|
// Ignore if the entire data is already known
|
||||||
if bc.HasBlock(block.Hash(), block.NumberU64()) {
|
if bc.HasBlock(block.Hash(), block.NumberU64()) {
|
||||||
|
|
@ -1363,7 +1322,8 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Write all the data out into the database
|
// Write all the data out into the database
|
||||||
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
|
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
|
||||||
|
rawdb.WriteBlock(batch, block)
|
||||||
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
||||||
|
|
||||||
// Write everything belongs to the blocks into the database. So that
|
// Write everything belongs to the blocks into the database. So that
|
||||||
|
|
@ -1387,21 +1347,27 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateHead(blockChain[len(blockChain)-1])
|
if err := updateHead(blockChain[len(blockChain)-1].Header()); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write downloaded chain data and corresponding receipt chain data
|
// Split the supplied blocks into two groups, according to the
|
||||||
if len(ancientBlocks) > 0 {
|
// given ancient limit.
|
||||||
if n, err := writeAncient(ancientBlocks, ancientReceipts); err != nil {
|
index := sort.Search(len(blockChain), func(i int) bool {
|
||||||
|
return blockChain[i].NumberU64() >= ancientLimit
|
||||||
|
})
|
||||||
|
if index > 0 {
|
||||||
|
if n, err := writeAncient(blockChain[:index], receiptChain[:index]); err != nil {
|
||||||
if err == errInsertionInterrupted {
|
if err == errInsertionInterrupted {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(liveBlocks) > 0 {
|
if index != len(blockChain) {
|
||||||
if n, err := writeLive(liveBlocks, liveReceipts); err != nil {
|
if n, err := writeLive(blockChain[index:], receiptChain[index:]); err != nil {
|
||||||
if err == errInsertionInterrupted {
|
if err == errInsertionInterrupted {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1420,7 +1386,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
context = append(context, []interface{}{"ignored", stats.ignored}...)
|
context = append(context, []interface{}{"ignored", stats.ignored}...)
|
||||||
}
|
}
|
||||||
log.Debug("Imported new block receipts", context...)
|
log.Debug("Imported new block receipts", context...)
|
||||||
|
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2505,15 +2470,83 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header) (int, error) {
|
||||||
if i, err := bc.hc.ValidateHeaderChain(chain); err != nil {
|
if i, err := bc.hc.ValidateHeaderChain(chain); err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bc.chainmu.TryLock() {
|
if !bc.chainmu.TryLock() {
|
||||||
return 0, errChainStopped
|
return 0, errChainStopped
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
_, err := bc.hc.InsertHeaderChain(chain, start)
|
_, err := bc.hc.InsertHeaderChain(chain, start)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InsertHeadersBeforeCutoff inserts the given headers into the ancient store
|
||||||
|
// as they are claimed older than the configured chain cutoff point. All the
|
||||||
|
// inserted headers are regarded as canonical and chain reorg is not supported.
|
||||||
|
func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, error) {
|
||||||
|
if len(headers) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
// TODO(rjl493456442): Headers before the configured cutoff have already
|
||||||
|
// been verified by the hash of cutoff header. Theoretically, header validation
|
||||||
|
// could be skipped here.
|
||||||
|
if n, err := bc.hc.ValidateHeaderChain(headers); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if !bc.chainmu.TryLock() {
|
||||||
|
return 0, errChainStopped
|
||||||
|
}
|
||||||
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
|
// Initialize the ancient store with genesis block if it's empty.
|
||||||
|
var (
|
||||||
|
frozen, _ = bc.db.Ancients()
|
||||||
|
first = headers[0].Number.Uint64()
|
||||||
|
)
|
||||||
|
if first == 1 && frozen == 0 {
|
||||||
|
_, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil})
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error writing genesis to ancients", "err", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
log.Info("Wrote genesis to ancient store")
|
||||||
|
} else if frozen != first {
|
||||||
|
return 0, fmt.Errorf("headers are gapped with the ancient store, first: %d, ancient: %d", first, frozen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write headers to the ancient store, with block bodies and receipts set to nil
|
||||||
|
// to ensure consistency across tables in the freezer.
|
||||||
|
_, err := rawdb.WriteAncientHeaderChain(bc.db, headers)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := bc.db.Sync(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// Write hash to number mappings
|
||||||
|
batch := bc.db.NewBatch()
|
||||||
|
for _, header := range headers {
|
||||||
|
rawdb.WriteHeaderNumber(batch, header.Hash(), header.Number.Uint64())
|
||||||
|
}
|
||||||
|
// Write head header and head snap block flags
|
||||||
|
last := headers[len(headers)-1]
|
||||||
|
rawdb.WriteHeadHeaderHash(batch, last.Hash())
|
||||||
|
rawdb.WriteHeadFastBlockHash(batch, last.Hash())
|
||||||
|
if err := batch.Write(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// Truncate the useless chain segment (zero bodies and receipts) in the
|
||||||
|
// ancient store.
|
||||||
|
if _, err := bc.db.TruncateTail(last.Number.Uint64() + 1); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// Last step update all in-memory markers
|
||||||
|
bc.hc.currentHeader.Store(last)
|
||||||
|
bc.currentSnapBlock.Store(last)
|
||||||
|
headHeaderGauge.Update(last.Number.Int64())
|
||||||
|
headFastBlockGauge.Update(last.Number.Int64())
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetBlockValidatorAndProcessorForTesting sets the current validator and processor.
|
// SetBlockValidatorAndProcessorForTesting sets the current validator and processor.
|
||||||
// This method can be used to force an invalid blockchain to be verified for tests.
|
// This method can be used to force an invalid blockchain to be verified for tests.
|
||||||
// This method is unsafe and should only be used before block import starts.
|
// This method is unsafe and should only be used before block import starts.
|
||||||
|
|
@ -2533,9 +2566,3 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
|
||||||
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
|
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
|
||||||
return time.Duration(bc.flushInterval.Load())
|
return time.Duration(bc.flushInterval.Load())
|
||||||
}
|
}
|
||||||
|
|
||||||
// HistoryPruningCutoff returns the configured history pruning point.
|
|
||||||
// Blocks before this might not be available in the database.
|
|
||||||
func (bc *BlockChain) HistoryPruningCutoff() uint64 {
|
|
||||||
return bc.cacheConfig.HistoryPruningCutoff
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -407,6 +407,12 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
|
||||||
return bc.txIndexer.txIndexProgress()
|
return bc.txIndexer.txIndexProgress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HistoryPruningCutoff returns the configured history pruning point.
|
||||||
|
// Blocks before this might not be available in the database.
|
||||||
|
func (bc *BlockChain) HistoryPruningCutoff() (uint64, common.Hash) {
|
||||||
|
return bc.cacheConfig.HistoryPruningCutoffNumber, bc.cacheConfig.HistoryPruningCutoffHash
|
||||||
|
}
|
||||||
|
|
||||||
// TrieDB retrieves the low level trie database used for data storage.
|
// TrieDB retrieves the low level trie database used for data storage.
|
||||||
func (bc *BlockChain) TrieDB() *triedb.Database {
|
func (bc *BlockChain) TrieDB() *triedb.Database {
|
||||||
return bc.triedb
|
return bc.triedb
|
||||||
|
|
|
||||||
|
|
@ -1765,7 +1765,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := filepath.Join(datadir, "ancient")
|
ancient := filepath.Join(datadir, "ancient")
|
||||||
|
|
||||||
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1850,7 +1850,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
chain.stopWithoutSaving()
|
chain.stopWithoutSaving()
|
||||||
|
|
||||||
// Start a new blockchain back up and see where the repair leads us
|
// Start a new blockchain back up and see where the repair leads us
|
||||||
pdb, err = pebble.New(datadir, 0, 0, "", false)
|
pdb, err = pebble.New(datadir, 0, 0, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1915,7 +1915,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := filepath.Join(datadir, "ancient")
|
ancient := filepath.Join(datadir, "ancient")
|
||||||
|
|
||||||
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1973,7 +1973,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
chain.stopWithoutSaving()
|
chain.stopWithoutSaving()
|
||||||
|
|
||||||
// Start a new blockchain back up and see where the repair leads us
|
// Start a new blockchain back up and see where the repair leads us
|
||||||
pdb, err = pebble.New(datadir, 0, 0, "", false)
|
pdb, err = pebble.New(datadir, 0, 0, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1969,7 +1969,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := filepath.Join(datadir, "ancient")
|
ancient := filepath.Join(datadir, "ancient")
|
||||||
|
|
||||||
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := filepath.Join(datadir, "ancient")
|
ancient := filepath.Join(datadir, "ancient")
|
||||||
|
|
||||||
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -257,7 +257,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
||||||
chain.triedb.Close()
|
chain.triedb.Close()
|
||||||
|
|
||||||
// Start a new blockchain back up and see where the repair leads us
|
// Start a new blockchain back up and see where the repair leads us
|
||||||
pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false)
|
pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -733,13 +733,6 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer fast.Stop()
|
defer fast.Stop()
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
|
||||||
for i, block := range blocks {
|
|
||||||
headers[i] = block.Header()
|
|
||||||
}
|
|
||||||
if n, err := fast.InsertHeaderChain(headers); err != nil {
|
|
||||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
||||||
}
|
|
||||||
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
@ -753,9 +746,6 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer ancient.Stop()
|
defer ancient.Stop()
|
||||||
|
|
||||||
if n, err := ancient.InsertHeaderChain(headers); err != nil {
|
|
||||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
||||||
}
|
|
||||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil {
|
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
@ -880,13 +870,6 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer fast.Stop()
|
defer fast.Stop()
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
|
||||||
for i, block := range blocks {
|
|
||||||
headers[i] = block.Header()
|
|
||||||
}
|
|
||||||
if n, err := fast.InsertHeaderChain(headers); err != nil {
|
|
||||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
||||||
}
|
|
||||||
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
@ -900,9 +883,6 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer ancient.Stop()
|
defer ancient.Stop()
|
||||||
|
|
||||||
if n, err := ancient.InsertHeaderChain(headers); err != nil {
|
|
||||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
||||||
}
|
|
||||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
@ -916,6 +896,11 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||||
// Import the chain as a light node and ensure all pointers are updated
|
// Import the chain as a light node and ensure all pointers are updated
|
||||||
lightDb := makeDb()
|
lightDb := makeDb()
|
||||||
defer lightDb.Close()
|
defer lightDb.Close()
|
||||||
|
|
||||||
|
headers := make([]*types.Header, len(blocks))
|
||||||
|
for i, block := range blocks {
|
||||||
|
headers[i] = block.Header()
|
||||||
|
}
|
||||||
light, _ := NewBlockChain(lightDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
light, _ := NewBlockChain(lightDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
if n, err := light.InsertHeaderChain(headers); err != nil {
|
if n, err := light.InsertHeaderChain(headers); err != nil {
|
||||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
t.Fatalf("failed to insert header %d: %v", n, err)
|
||||||
|
|
@ -1710,13 +1695,6 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
||||||
defer ancientDb.Close()
|
defer ancientDb.Close()
|
||||||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
|
||||||
for i, block := range blocks {
|
|
||||||
headers[i] = block.Header()
|
|
||||||
}
|
|
||||||
if n, err := ancient.InsertHeaderChain(headers); err != nil {
|
|
||||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
||||||
}
|
|
||||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
@ -1741,82 +1719,6 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test checks that InsertReceiptChain will roll back correctly when attempting to insert a side chain.
|
|
||||||
func TestInsertReceiptChainRollback(t *testing.T) {
|
|
||||||
testInsertReceiptChainRollback(t, rawdb.HashScheme)
|
|
||||||
testInsertReceiptChainRollback(t, rawdb.PathScheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testInsertReceiptChainRollback(t *testing.T, scheme string) {
|
|
||||||
// Generate forked chain. The returned BlockChain object is used to process the side chain blocks.
|
|
||||||
tmpChain, sideblocks, canonblocks, gspec, err := getLongAndShortChains(scheme)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer tmpChain.Stop()
|
|
||||||
// Get the side chain receipts.
|
|
||||||
if _, err := tmpChain.InsertChain(sideblocks); err != nil {
|
|
||||||
t.Fatal("processing side chain failed:", err)
|
|
||||||
}
|
|
||||||
t.Log("sidechain head:", tmpChain.CurrentBlock().Number, tmpChain.CurrentBlock().Hash())
|
|
||||||
sidechainReceipts := make([]types.Receipts, len(sideblocks))
|
|
||||||
for i, block := range sideblocks {
|
|
||||||
sidechainReceipts[i] = tmpChain.GetReceiptsByHash(block.Hash())
|
|
||||||
}
|
|
||||||
// Get the canon chain receipts.
|
|
||||||
if _, err := tmpChain.InsertChain(canonblocks); err != nil {
|
|
||||||
t.Fatal("processing canon chain failed:", err)
|
|
||||||
}
|
|
||||||
t.Log("canon head:", tmpChain.CurrentBlock().Number, tmpChain.CurrentBlock().Hash())
|
|
||||||
canonReceipts := make([]types.Receipts, len(canonblocks))
|
|
||||||
for i, block := range canonblocks {
|
|
||||||
canonReceipts[i] = tmpChain.GetReceiptsByHash(block.Hash())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up a BlockChain that uses the ancient store.
|
|
||||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
|
||||||
}
|
|
||||||
defer ancientDb.Close()
|
|
||||||
|
|
||||||
ancientChain, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
||||||
defer ancientChain.Stop()
|
|
||||||
|
|
||||||
// Import the canonical header chain.
|
|
||||||
canonHeaders := make([]*types.Header, len(canonblocks))
|
|
||||||
for i, block := range canonblocks {
|
|
||||||
canonHeaders[i] = block.Header()
|
|
||||||
}
|
|
||||||
if _, err = ancientChain.InsertHeaderChain(canonHeaders); err != nil {
|
|
||||||
t.Fatal("can't import canon headers:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to insert blocks/receipts of the side chain.
|
|
||||||
_, err = ancientChain.InsertReceiptChain(sideblocks, sidechainReceipts, uint64(len(sideblocks)))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error from InsertReceiptChain.")
|
|
||||||
}
|
|
||||||
if ancientChain.CurrentSnapBlock().Number.Uint64() != 0 {
|
|
||||||
t.Fatalf("failed to rollback ancient data, want %d, have %d", 0, ancientChain.CurrentSnapBlock().Number)
|
|
||||||
}
|
|
||||||
if frozen, err := ancientChain.db.Ancients(); err != nil || frozen != 1 {
|
|
||||||
t.Fatalf("failed to truncate ancient data, frozen index is %d", frozen)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert blocks/receipts of the canonical chain.
|
|
||||||
_, err = ancientChain.InsertReceiptChain(canonblocks, canonReceipts, uint64(len(canonblocks)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("can't import canon chain receipts: %v", err)
|
|
||||||
}
|
|
||||||
if ancientChain.CurrentSnapBlock().Number.Uint64() != canonblocks[len(canonblocks)-1].NumberU64() {
|
|
||||||
t.Fatalf("failed to insert ancient recept chain after rollback")
|
|
||||||
}
|
|
||||||
if frozen, _ := ancientChain.db.Ancients(); frozen != uint64(len(canonblocks))+1 {
|
|
||||||
t.Fatalf("wrong ancients count %d", frozen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that importing a very large side fork, which is larger than the canon chain,
|
// Tests that importing a very large side fork, which is larger than the canon chain,
|
||||||
// but where the difficulty per block is kept low: this means that it will not
|
// but where the difficulty per block is kept low: this means that it will not
|
||||||
// overtake the 'canon' chain until after it's passed canon by about 200 blocks.
|
// overtake the 'canon' chain until after it's passed canon by about 200 blocks.
|
||||||
|
|
@ -2088,14 +1990,6 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
||||||
}
|
}
|
||||||
} else if typ == "receipts" {
|
} else if typ == "receipts" {
|
||||||
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
||||||
headers := make([]*types.Header, 0, len(blocks))
|
|
||||||
for _, block := range blocks {
|
|
||||||
headers = append(headers, block.Header())
|
|
||||||
}
|
|
||||||
_, err := chain.InsertHeaderChain(headers)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -2262,14 +2156,6 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
||||||
}
|
}
|
||||||
} else if typ == "receipts" {
|
} else if typ == "receipts" {
|
||||||
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
||||||
headers := make([]*types.Header, 0, len(blocks))
|
|
||||||
for _, block := range blocks {
|
|
||||||
headers = append(headers, block.Header())
|
|
||||||
}
|
|
||||||
i, err := chain.InsertHeaderChain(headers)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("index %d: %w", i, err)
|
|
||||||
}
|
|
||||||
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -2605,7 +2491,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := path.Join(datadir, "ancient")
|
ancient := path.Join(datadir, "ancient")
|
||||||
|
|
||||||
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -4265,3 +4151,220 @@ func TestEIP7702(t *testing.T) {
|
||||||
t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual)
|
t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests the scenario that the synchronization target in snap sync has been changed
|
||||||
|
// with a chain reorg at the tip. In this case the reorg'd segment should be unmarked
|
||||||
|
// with canonical flags.
|
||||||
|
func TestChainReorgSnapSync(t *testing.T) {
|
||||||
|
testChainReorgSnapSync(t, 0)
|
||||||
|
testChainReorgSnapSync(t, 32)
|
||||||
|
testChainReorgSnapSync(t, gomath.MaxUint64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) {
|
||||||
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true)))
|
||||||
|
|
||||||
|
// Configure and generate a sample block chain
|
||||||
|
var (
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
funds = big.NewInt(1000000000000000)
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||||
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
|
}
|
||||||
|
signer = types.LatestSigner(gspec.Config)
|
||||||
|
engine = beacon.New(ethash.NewFaker())
|
||||||
|
)
|
||||||
|
genDb, blocks, receipts := GenerateChainWithGenesis(gspec, engine, 32, func(i int, block *BlockGen) {
|
||||||
|
block.SetCoinbase(common.Address{0x00})
|
||||||
|
|
||||||
|
// If the block number is multiple of 3, send a few bonus transactions to the miner
|
||||||
|
if i%3 == 2 {
|
||||||
|
for j := 0; j < i%4+1; j++ {
|
||||||
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
block.AddTx(tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
chainA, receiptsA := GenerateChain(gspec.Config, blocks[len(blocks)-1], engine, genDb, 16, func(i int, gen *BlockGen) {
|
||||||
|
gen.SetCoinbase(common.Address{0: byte(0xa), 19: byte(i)})
|
||||||
|
})
|
||||||
|
chainB, receiptsB := GenerateChain(gspec.Config, blocks[len(blocks)-1], engine, genDb, 20, func(i int, gen *BlockGen) {
|
||||||
|
gen.SetCoinbase(common.Address{0: byte(0xb), 19: byte(i)})
|
||||||
|
})
|
||||||
|
|
||||||
|
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil)
|
||||||
|
defer chain.Stop()
|
||||||
|
|
||||||
|
if n, err := chain.InsertReceiptChain(blocks, receipts, ancientLimit); err != nil {
|
||||||
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertReceiptChain(chainA, receiptsA, ancientLimit); err != nil {
|
||||||
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
|
}
|
||||||
|
// If the common ancestor is below the ancient limit, rewind the chain head.
|
||||||
|
// It's aligned with the behavior in the snap sync
|
||||||
|
ancestor := blocks[len(blocks)-1].NumberU64()
|
||||||
|
if ancestor < ancientLimit {
|
||||||
|
rawdb.WriteLastPivotNumber(db, ancestor)
|
||||||
|
chain.SetHead(ancestor)
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertReceiptChain(chainB, receiptsB, ancientLimit); err != nil {
|
||||||
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
|
}
|
||||||
|
head := chain.CurrentSnapBlock()
|
||||||
|
if head.Hash() != chainB[len(chainB)-1].Hash() {
|
||||||
|
t.Errorf("head snap block #%d: header mismatch: want: %v, got: %v", head.Number, chainB[len(chainB)-1].Hash(), head.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all chain data components, and cross reference
|
||||||
|
for i := 0; i < len(blocks); i++ {
|
||||||
|
num, hash := blocks[i].NumberU64(), blocks[i].Hash()
|
||||||
|
header := chain.GetHeaderByNumber(num)
|
||||||
|
if header.Hash() != hash {
|
||||||
|
t.Errorf("block #%d: header mismatch: want: %v, got: %v", num, hash, header.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(chainA); i++ {
|
||||||
|
num, hash := chainA[i].NumberU64(), chainA[i].Hash()
|
||||||
|
header := chain.GetHeaderByNumber(num)
|
||||||
|
if header == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if header.Hash() == hash {
|
||||||
|
t.Errorf("block #%d: unexpected canonical header: %v", num, hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(chainB); i++ {
|
||||||
|
num, hash := chainB[i].NumberU64(), chainB[i].Hash()
|
||||||
|
header := chain.GetHeaderByNumber(num)
|
||||||
|
if header.Hash() != hash {
|
||||||
|
t.Errorf("block #%d: header mismatch: want: %v, got: %v", num, hash, header.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests the scenario that all the inserted chain segment are with the configured
|
||||||
|
// chain cutoff point. In this case the chain segment before the cutoff should
|
||||||
|
// be persisted without the receipts and bodies; chain after should be persisted
|
||||||
|
// normally.
|
||||||
|
func TestInsertChainWithCutoff(t *testing.T) {
|
||||||
|
testInsertChainWithCutoff(t, 32, 32) // cutoff = 32, ancientLimit = 32
|
||||||
|
testInsertChainWithCutoff(t, 32, 64) // cutoff = 32, ancientLimit = 64 (entire chain in ancient)
|
||||||
|
testInsertChainWithCutoff(t, 32, 65) // cutoff = 32, ancientLimit = 65 (64 blocks in ancient, 1 block in live)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64) {
|
||||||
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true)))
|
||||||
|
|
||||||
|
// Configure and generate a sample block chain
|
||||||
|
var (
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
funds = big.NewInt(1000000000000000)
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||||
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
|
}
|
||||||
|
signer = types.LatestSigner(gspec.Config)
|
||||||
|
engine = beacon.New(ethash.NewFaker())
|
||||||
|
)
|
||||||
|
_, blocks, receipts := GenerateChainWithGenesis(gspec, engine, int(2*cutoff), func(i int, block *BlockGen) {
|
||||||
|
block.SetCoinbase(common.Address{0x00})
|
||||||
|
|
||||||
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
block.AddTx(tx)
|
||||||
|
})
|
||||||
|
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
cutoffBlock := blocks[cutoff-1]
|
||||||
|
config := DefaultCacheConfigWithScheme(rawdb.PathScheme)
|
||||||
|
config.HistoryPruningCutoffNumber = cutoffBlock.NumberU64()
|
||||||
|
config.HistoryPruningCutoffHash = cutoffBlock.Hash()
|
||||||
|
|
||||||
|
chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil)
|
||||||
|
defer chain.Stop()
|
||||||
|
|
||||||
|
var (
|
||||||
|
headersBefore []*types.Header
|
||||||
|
blocksAfter []*types.Block
|
||||||
|
receiptsAfter []types.Receipts
|
||||||
|
)
|
||||||
|
for i, b := range blocks {
|
||||||
|
if b.NumberU64() < cutoffBlock.NumberU64() {
|
||||||
|
headersBefore = append(headersBefore, b.Header())
|
||||||
|
} else {
|
||||||
|
blocksAfter = append(blocksAfter, b)
|
||||||
|
receiptsAfter = append(receiptsAfter, receipts[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil {
|
||||||
|
t.Fatalf("failed to insert headers before cutoff %d: %v", n, err)
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertReceiptChain(blocksAfter, receiptsAfter, ancientLimit); err != nil {
|
||||||
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
|
}
|
||||||
|
headSnap := chain.CurrentSnapBlock()
|
||||||
|
if headSnap.Hash() != blocks[len(blocks)-1].Hash() {
|
||||||
|
t.Errorf("head snap block #%d: header mismatch: want: %v, got: %v", headSnap.Number, blocks[len(blocks)-1].Hash(), headSnap.Hash())
|
||||||
|
}
|
||||||
|
headHeader := chain.CurrentHeader()
|
||||||
|
if headHeader.Hash() != blocks[len(blocks)-1].Hash() {
|
||||||
|
t.Errorf("head header #%d: header mismatch: want: %v, got: %v", headHeader.Number, blocks[len(blocks)-1].Hash(), headHeader.Hash())
|
||||||
|
}
|
||||||
|
headBlock := chain.CurrentBlock()
|
||||||
|
if headBlock.Hash() != gspec.ToBlock().Hash() {
|
||||||
|
t.Errorf("head block #%d: header mismatch: want: %v, got: %v", headBlock.Number, gspec.ToBlock().Hash(), headBlock.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all chain data components, and cross reference
|
||||||
|
for i := 0; i < len(blocks); i++ {
|
||||||
|
num, hash := blocks[i].NumberU64(), blocks[i].Hash()
|
||||||
|
|
||||||
|
// Canonical headers should be visible regardless of cutoff
|
||||||
|
header := chain.GetHeaderByNumber(num)
|
||||||
|
if header.Hash() != hash {
|
||||||
|
t.Errorf("block #%d: header mismatch: want: %v, got: %v", num, hash, header.Hash())
|
||||||
|
}
|
||||||
|
tail, err := db.Tail()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get chain tail, %v", err)
|
||||||
|
}
|
||||||
|
if tail != cutoffBlock.NumberU64() {
|
||||||
|
t.Fatalf("Unexpected chain tail, want: %d, got: %d", cutoffBlock.NumberU64(), tail)
|
||||||
|
}
|
||||||
|
// Block bodies and receipts before the cutoff should be non-existent
|
||||||
|
if num < cutoffBlock.NumberU64() {
|
||||||
|
body := chain.GetBody(hash)
|
||||||
|
if body != nil {
|
||||||
|
t.Fatalf("Unexpected block body: %d, cutoff: %d", num, cutoffBlock.NumberU64())
|
||||||
|
}
|
||||||
|
receipts := chain.GetReceiptsByHash(hash)
|
||||||
|
if receipts != nil {
|
||||||
|
t.Fatalf("Unexpected block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
body := chain.GetBody(hash)
|
||||||
|
if body == nil || len(body.Transactions) != 1 {
|
||||||
|
t.Fatalf("Missed block body: %d, cutoff: %d", num, cutoffBlock.NumberU64())
|
||||||
|
}
|
||||||
|
receipts := chain.GetReceiptsByHash(hash)
|
||||||
|
if receipts == nil || len(receipts) != 1 {
|
||||||
|
t.Fatalf("Missed block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,6 @@ var (
|
||||||
|
|
||||||
// ErrNoGenesis is returned when there is no Genesis Block.
|
// ErrNoGenesis is returned when there is no Genesis Block.
|
||||||
ErrNoGenesis = errors.New("genesis not found in chain")
|
ErrNoGenesis = errors.New("genesis not found in chain")
|
||||||
|
|
||||||
errSideChainReceipts = errors.New("side blocks can't be accepted as ancient chain data")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// List of evm-call-message pre-checking errors. All state transition messages will
|
// List of evm-call-message pre-checking errors. All state transition messages will
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,8 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeHeadersAndSetHead writes a batch of block headers and applies the last
|
// writeHeadersAndSetHead writes a batch of block headers and applies the last
|
||||||
// header as the chain head if the fork choicer says it's ok to update the chain.
|
// header as the chain head.
|
||||||
|
//
|
||||||
// Note: This method is not concurrent-safe with inserting blocks simultaneously
|
// Note: This method is not concurrent-safe with inserting blocks simultaneously
|
||||||
// into the chain, as side effects caused by reorganisations cannot be emulated
|
// into the chain, as side effects caused by reorganisations cannot be emulated
|
||||||
// without the real blocks. Hence, writing headers directly should only be done
|
// without the real blocks. Hence, writing headers directly should only be done
|
||||||
|
|
@ -272,12 +273,14 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header) (*headerW
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateHeaderChain verifies that the supplied header chain is contiguous
|
||||||
|
// and conforms to consensus rules.
|
||||||
func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header) (int, error) {
|
func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header) (int, error) {
|
||||||
// Do a sanity check that the provided chain is actually ordered and linked
|
// Do a sanity check that the provided chain is actually ordered and linked
|
||||||
for i := 1; i < len(chain); i++ {
|
for i := 1; i < len(chain); i++ {
|
||||||
if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 {
|
if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 {
|
||||||
hash := chain[i].Hash()
|
hash, parentHash := chain[i].Hash(), chain[i-1].Hash()
|
||||||
parentHash := chain[i-1].Hash()
|
|
||||||
// Chain broke ancestry, log a message (programming error) and skip insertion
|
// Chain broke ancestry, log a message (programming error) and skip insertion
|
||||||
log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", hash,
|
log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", hash,
|
||||||
"parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", parentHash)
|
"parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", parentHash)
|
||||||
|
|
@ -302,7 +305,6 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header) (int, error) {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -737,6 +737,30 @@ func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *type
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteAncientHeaderChain writes the supplied headers along with nil block
|
||||||
|
// bodies and receipts into the ancient store. It's supposed to be used for
|
||||||
|
// storing chain segment before the chain cutoff.
|
||||||
|
func WriteAncientHeaderChain(db ethdb.AncientWriter, headers []*types.Header) (int64, error) {
|
||||||
|
return db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||||
|
for _, header := range headers {
|
||||||
|
num := header.Number.Uint64()
|
||||||
|
if err := op.AppendRaw(ChainFreezerHashTable, num, header.Hash().Bytes()); err != nil {
|
||||||
|
return fmt.Errorf("can't add block %d hash: %v", num, err)
|
||||||
|
}
|
||||||
|
if err := op.Append(ChainFreezerHeaderTable, num, header); err != nil {
|
||||||
|
return fmt.Errorf("can't append block header %d: %v", num, err)
|
||||||
|
}
|
||||||
|
if err := op.AppendRaw(ChainFreezerBodiesTable, num, nil); err != nil {
|
||||||
|
return fmt.Errorf("can't append block body %d: %v", num, err)
|
||||||
|
}
|
||||||
|
if err := op.AppendRaw(ChainFreezerReceiptTable, num, nil); err != nil {
|
||||||
|
return fmt.Errorf("can't append block %d receipts: %v", num, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteBlock removes all block data associated with a hash.
|
// DeleteBlock removes all block data associated with a hash.
|
||||||
func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||||
DeleteReceipts(db, hash, number)
|
DeleteReceipts(db, hash, number)
|
||||||
|
|
|
||||||
|
|
@ -464,6 +464,48 @@ func TestAncientStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWriteAncientHeaderChain(t *testing.T) {
|
||||||
|
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), t.TempDir(), "", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create database with ancient backend")
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Create a test block
|
||||||
|
var headers []*types.Header
|
||||||
|
headers = append(headers, &types.Header{
|
||||||
|
Number: big.NewInt(0),
|
||||||
|
Extra: []byte("test block"),
|
||||||
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
TxHash: types.EmptyTxsHash,
|
||||||
|
ReceiptHash: types.EmptyReceiptsHash,
|
||||||
|
})
|
||||||
|
headers = append(headers, &types.Header{
|
||||||
|
Number: big.NewInt(1),
|
||||||
|
Extra: []byte("test block"),
|
||||||
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
TxHash: types.EmptyTxsHash,
|
||||||
|
ReceiptHash: types.EmptyReceiptsHash,
|
||||||
|
})
|
||||||
|
// Write and verify the header in the database
|
||||||
|
WriteAncientHeaderChain(db, headers)
|
||||||
|
|
||||||
|
for _, header := range headers {
|
||||||
|
if blob := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); len(blob) == 0 {
|
||||||
|
t.Fatalf("no header returned")
|
||||||
|
}
|
||||||
|
if h := ReadCanonicalHash(db, header.Number.Uint64()); h != header.Hash() {
|
||||||
|
t.Fatalf("no canonical hash returned")
|
||||||
|
}
|
||||||
|
if blob := ReadBodyRLP(db, header.Hash(), header.Number.Uint64()); len(blob) != 0 {
|
||||||
|
t.Fatalf("unexpected body returned")
|
||||||
|
}
|
||||||
|
if blob := ReadReceiptsRLP(db, header.Hash(), header.Number.Uint64()); len(blob) != 0 {
|
||||||
|
t.Fatalf("unexpected body returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCanonicalHashIteration(t *testing.T) {
|
func TestCanonicalHashIteration(t *testing.T) {
|
||||||
var cases = []struct {
|
var cases = []struct {
|
||||||
from, to uint64
|
from, to uint64
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,10 @@ type txIndexer struct {
|
||||||
|
|
||||||
// newTxIndexer initializes the transaction indexer.
|
// newTxIndexer initializes the transaction indexer.
|
||||||
func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
|
func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
|
||||||
|
cutoff, _ := chain.HistoryPruningCutoff()
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
limit: limit,
|
limit: limit,
|
||||||
cutoff: chain.HistoryPruningCutoff(),
|
cutoff: cutoff,
|
||||||
db: chain.db,
|
db: chain.db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
progress: make(chan chan TxIndexProgress),
|
||||||
term: make(chan chan struct{}),
|
term: make(chan chan struct{}),
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
|
||||||
}
|
}
|
||||||
var bn uint64
|
var bn uint64
|
||||||
if number == rpc.EarliestBlockNumber {
|
if number == rpc.EarliestBlockNumber {
|
||||||
bn = b.eth.blockchain.HistoryPruningCutoff()
|
bn = b.HistoryPruningCutoff()
|
||||||
} else {
|
} else {
|
||||||
bn = uint64(number)
|
bn = uint64(number)
|
||||||
}
|
}
|
||||||
|
|
@ -152,10 +152,10 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
|
||||||
}
|
}
|
||||||
bn := uint64(number) // the resolved number
|
bn := uint64(number) // the resolved number
|
||||||
if number == rpc.EarliestBlockNumber {
|
if number == rpc.EarliestBlockNumber {
|
||||||
bn = b.eth.blockchain.HistoryPruningCutoff()
|
bn = b.HistoryPruningCutoff()
|
||||||
}
|
}
|
||||||
block := b.eth.blockchain.GetBlockByNumber(bn)
|
block := b.eth.blockchain.GetBlockByNumber(bn)
|
||||||
if block == nil && bn < b.eth.blockchain.HistoryPruningCutoff() {
|
if block == nil && bn < b.HistoryPruningCutoff() {
|
||||||
return nil, ðconfig.PrunedHistoryError{}
|
return nil, ðconfig.PrunedHistoryError{}
|
||||||
}
|
}
|
||||||
return block, nil
|
return block, nil
|
||||||
|
|
@ -167,7 +167,7 @@ func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
block := b.eth.blockchain.GetBlock(hash, *number)
|
block := b.eth.blockchain.GetBlock(hash, *number)
|
||||||
if block == nil && *number < b.eth.blockchain.HistoryPruningCutoff() {
|
if block == nil && *number < b.HistoryPruningCutoff() {
|
||||||
return nil, ðconfig.PrunedHistoryError{}
|
return nil, ðconfig.PrunedHistoryError{}
|
||||||
}
|
}
|
||||||
return block, nil
|
return block, nil
|
||||||
|
|
@ -180,7 +180,7 @@ func (b *EthAPIBackend) GetBody(ctx context.Context, hash common.Hash, number rp
|
||||||
}
|
}
|
||||||
body := b.eth.blockchain.GetBody(hash)
|
body := b.eth.blockchain.GetBody(hash)
|
||||||
if body == nil {
|
if body == nil {
|
||||||
if uint64(number) < b.eth.blockchain.HistoryPruningCutoff() {
|
if uint64(number) < b.HistoryPruningCutoff() {
|
||||||
return nil, ðconfig.PrunedHistoryError{}
|
return nil, ðconfig.PrunedHistoryError{}
|
||||||
}
|
}
|
||||||
return nil, errors.New("block body not found")
|
return nil, errors.New("block body not found")
|
||||||
|
|
@ -202,7 +202,7 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r
|
||||||
}
|
}
|
||||||
block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64())
|
block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64())
|
||||||
if block == nil {
|
if block == nil {
|
||||||
if header.Number.Uint64() < b.eth.blockchain.HistoryPruningCutoff() {
|
if header.Number.Uint64() < b.HistoryPruningCutoff() {
|
||||||
return nil, ðconfig.PrunedHistoryError{}
|
return nil, ðconfig.PrunedHistoryError{}
|
||||||
}
|
}
|
||||||
return nil, errors.New("header found, but block body is missing")
|
return nil, errors.New("header found, but block body is missing")
|
||||||
|
|
@ -265,7 +265,8 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) HistoryPruningCutoff() uint64 {
|
func (b *EthAPIBackend) HistoryPruningCutoff() uint64 {
|
||||||
return b.eth.blockchain.HistoryPruningCutoff()
|
bn, _ := b.eth.blockchain.HistoryPruningCutoff()
|
||||||
|
return bn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||||
|
|
|
||||||
|
|
@ -154,13 +154,18 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate history pruning configuration.
|
// Validate history pruning configuration.
|
||||||
var historyPruningCutoff uint64
|
var (
|
||||||
|
cutoffNumber uint64
|
||||||
|
cutoffHash common.Hash
|
||||||
|
)
|
||||||
if config.HistoryMode == ethconfig.PostMergeHistory {
|
if config.HistoryMode == ethconfig.PostMergeHistory {
|
||||||
prunecfg, ok := ethconfig.HistoryPrunePoints[genesisHash]
|
prunecfg, ok := ethconfig.HistoryPrunePoints[genesisHash]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("no history pruning point is defined for genesis %x", genesisHash)
|
return nil, fmt.Errorf("no history pruning point is defined for genesis %x", genesisHash)
|
||||||
}
|
}
|
||||||
historyPruningCutoff = prunecfg.BlockNumber
|
cutoffNumber = prunecfg.BlockNumber
|
||||||
|
cutoffHash = prunecfg.BlockHash
|
||||||
|
log.Info("Chain cutoff configured", "number", cutoffNumber, "hash", cutoffHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set networkID to chainID by default.
|
// Set networkID to chainID by default.
|
||||||
|
|
@ -213,7 +218,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
Preimages: config.Preimages,
|
Preimages: config.Preimages,
|
||||||
StateHistory: config.StateHistory,
|
StateHistory: config.StateHistory,
|
||||||
StateScheme: scheme,
|
StateScheme: scheme,
|
||||||
HistoryPruningCutoff: historyPruningCutoff,
|
HistoryPruningCutoffNumber: cutoffNumber,
|
||||||
|
HistoryPruningCutoffHash: cutoffHash,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if config.VMTrace != "" {
|
if config.VMTrace != "" {
|
||||||
|
|
@ -246,7 +252,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
HashScheme: scheme == rawdb.HashScheme,
|
HashScheme: scheme == rawdb.HashScheme,
|
||||||
}
|
}
|
||||||
chainView := eth.newChainView(eth.blockchain.CurrentBlock())
|
chainView := eth.newChainView(eth.blockchain.CurrentBlock())
|
||||||
historyCutoff := eth.blockchain.HistoryPruningCutoff()
|
historyCutoff, _ := eth.blockchain.HistoryPruningCutoff()
|
||||||
var finalBlock uint64
|
var finalBlock uint64
|
||||||
if fb := eth.blockchain.CurrentFinalBlock(); fb != nil {
|
if fb := eth.blockchain.CurrentFinalBlock(); fb != nil {
|
||||||
finalBlock = fb.Number.Uint64()
|
finalBlock = fb.Number.Uint64()
|
||||||
|
|
@ -443,7 +449,7 @@ func (s *Ethereum) updateFilterMapsHeads() {
|
||||||
if head == nil || newHead.Hash() != head.Hash() {
|
if head == nil || newHead.Hash() != head.Hash() {
|
||||||
head = newHead
|
head = newHead
|
||||||
chainView := s.newChainView(head)
|
chainView := s.newChainView(head)
|
||||||
historyCutoff := s.blockchain.HistoryPruningCutoff()
|
historyCutoff, _ := s.blockchain.HistoryPruningCutoff()
|
||||||
var finalBlock uint64
|
var finalBlock uint64
|
||||||
if fb := s.blockchain.CurrentFinalBlock(); fb != nil {
|
if fb := s.blockchain.CurrentFinalBlock(); fb != nil {
|
||||||
finalBlock = fb.Number.Uint64()
|
finalBlock = fb.Number.Uint64()
|
||||||
|
|
|
||||||
|
|
@ -273,8 +273,7 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) {
|
||||||
// fetchHeaders feeds skeleton headers to the downloader queue for scheduling
|
// fetchHeaders feeds skeleton headers to the downloader queue for scheduling
|
||||||
// until sync errors or is finished.
|
// until sync errors or is finished.
|
||||||
func (d *Downloader) fetchHeaders(from uint64) error {
|
func (d *Downloader) fetchHeaders(from uint64) error {
|
||||||
var head *types.Header
|
head, tail, _, err := d.skeleton.Bounds()
|
||||||
_, tail, _, err := d.skeleton.Bounds()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -294,6 +293,27 @@ func (d *Downloader) fetchHeaders(from uint64) error {
|
||||||
fsHeaderContCheckTimer := time.NewTimer(fsHeaderContCheck)
|
fsHeaderContCheckTimer := time.NewTimer(fsHeaderContCheck)
|
||||||
defer fsHeaderContCheckTimer.Stop()
|
defer fsHeaderContCheckTimer.Stop()
|
||||||
|
|
||||||
|
// Verify the header at configured chain cutoff, ensuring it's matched with
|
||||||
|
// the configured hash. Skip the check if the configured cutoff is even higher
|
||||||
|
// than the sync target, which is definitely not a common case.
|
||||||
|
if d.chainCutoffNumber != 0 && d.chainCutoffNumber >= from && d.chainCutoffNumber <= head.Number.Uint64() {
|
||||||
|
h := d.skeleton.Header(d.chainCutoffNumber)
|
||||||
|
if h == nil {
|
||||||
|
if d.chainCutoffNumber < tail.Number.Uint64() {
|
||||||
|
dist := tail.Number.Uint64() - d.chainCutoffNumber
|
||||||
|
if len(localHeaders) >= int(dist) {
|
||||||
|
h = localHeaders[dist-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if h == nil {
|
||||||
|
return fmt.Errorf("header at chain cutoff is not available, cutoff: %d", d.chainCutoffNumber)
|
||||||
|
}
|
||||||
|
if h.Hash() != d.chainCutoffHash {
|
||||||
|
return fmt.Errorf("header at chain cutoff mismatched, want: %v, got: %v", d.chainCutoffHash, h.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Some beacon headers might have appeared since the last cycle, make
|
// Some beacon headers might have appeared since the last cycle, make
|
||||||
// sure we're always syncing to all available ones
|
// sure we're always syncing to all available ones
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ package downloader
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -120,6 +121,12 @@ type Downloader struct {
|
||||||
committed atomic.Bool
|
committed atomic.Bool
|
||||||
ancientLimit uint64 // The maximum block number which can be regarded as ancient data.
|
ancientLimit uint64 // The maximum block number which can be regarded as ancient data.
|
||||||
|
|
||||||
|
// The cutoff block number and hash before which chain segments (bodies
|
||||||
|
// and receipts) are skipped during synchronization. 0 means the entire
|
||||||
|
// chain segment is aimed for synchronization.
|
||||||
|
chainCutoffNumber uint64
|
||||||
|
chainCutoffHash common.Hash
|
||||||
|
|
||||||
// Channels
|
// Channels
|
||||||
headerProcCh chan *headerTask // Channel to feed the header processor new tasks
|
headerProcCh chan *headerTask // Channel to feed the header processor new tasks
|
||||||
|
|
||||||
|
|
@ -163,9 +170,6 @@ type BlockChain interface {
|
||||||
// CurrentHeader retrieves the head header from the local chain.
|
// CurrentHeader retrieves the head header from the local chain.
|
||||||
CurrentHeader() *types.Header
|
CurrentHeader() *types.Header
|
||||||
|
|
||||||
// InsertHeaderChain inserts a batch of headers into the local chain.
|
|
||||||
InsertHeaderChain([]*types.Header) (int, error)
|
|
||||||
|
|
||||||
// SetHead rewinds the local chain to a new head.
|
// SetHead rewinds the local chain to a new head.
|
||||||
SetHead(uint64) error
|
SetHead(uint64) error
|
||||||
|
|
||||||
|
|
@ -187,10 +191,17 @@ type BlockChain interface {
|
||||||
// SnapSyncCommitHead directly commits the head block to a certain entity.
|
// SnapSyncCommitHead directly commits the head block to a certain entity.
|
||||||
SnapSyncCommitHead(common.Hash) error
|
SnapSyncCommitHead(common.Hash) error
|
||||||
|
|
||||||
|
// InsertHeadersBeforeCutoff inserts a batch of headers before the configured
|
||||||
|
// chain cutoff into the ancient store.
|
||||||
|
InsertHeadersBeforeCutoff([]*types.Header) (int, error)
|
||||||
|
|
||||||
// InsertChain inserts a batch of blocks into the local chain.
|
// InsertChain inserts a batch of blocks into the local chain.
|
||||||
InsertChain(types.Blocks) (int, error)
|
InsertChain(types.Blocks) (int, error)
|
||||||
|
|
||||||
// InsertReceiptChain inserts a batch of receipts into the local chain.
|
// InsertReceiptChain inserts a batch of blocks along with their receipts
|
||||||
|
// into the local chain. Blocks older than the specified `ancientLimit`
|
||||||
|
// are stored directly in the ancient store, while newer blocks are stored
|
||||||
|
// in the live key-value store.
|
||||||
InsertReceiptChain(types.Blocks, []types.Receipts, uint64) (int, error)
|
InsertReceiptChain(types.Blocks, []types.Receipts, uint64) (int, error)
|
||||||
|
|
||||||
// Snapshots returns the blockchain snapshot tree to paused it during sync.
|
// Snapshots returns the blockchain snapshot tree to paused it during sync.
|
||||||
|
|
@ -199,16 +210,23 @@ type BlockChain interface {
|
||||||
// TrieDB retrieves the low level trie database used for interacting
|
// TrieDB retrieves the low level trie database used for interacting
|
||||||
// with trie nodes.
|
// with trie nodes.
|
||||||
TrieDB() *triedb.Database
|
TrieDB() *triedb.Database
|
||||||
|
|
||||||
|
// HistoryPruningCutoff returns the configured history pruning point.
|
||||||
|
// Block bodies along with the receipts will be skipped for synchronization.
|
||||||
|
HistoryPruningCutoff() (uint64, common.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new downloader to fetch hashes and blocks from remote peers.
|
// New creates a new downloader to fetch hashes and blocks from remote peers.
|
||||||
func New(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, dropPeer peerDropFn, success func()) *Downloader {
|
func New(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, dropPeer peerDropFn, success func()) *Downloader {
|
||||||
|
cutoffNumber, cutoffHash := chain.HistoryPruningCutoff()
|
||||||
dl := &Downloader{
|
dl := &Downloader{
|
||||||
stateDB: stateDb,
|
stateDB: stateDb,
|
||||||
mux: mux,
|
mux: mux,
|
||||||
queue: newQueue(blockCacheMaxItems, blockCacheInitialItems),
|
queue: newQueue(blockCacheMaxItems, blockCacheInitialItems),
|
||||||
peers: newPeerSet(),
|
peers: newPeerSet(),
|
||||||
blockchain: chain,
|
blockchain: chain,
|
||||||
|
chainCutoffNumber: cutoffNumber,
|
||||||
|
chainCutoffHash: cutoffHash,
|
||||||
dropPeer: dropPeer,
|
dropPeer: dropPeer,
|
||||||
headerProcCh: make(chan *headerTask, 1),
|
headerProcCh: make(chan *headerTask, 1),
|
||||||
quitCh: make(chan struct{}),
|
quitCh: make(chan struct{}),
|
||||||
|
|
@ -503,6 +521,12 @@ func (d *Downloader) syncToHead() (err error) {
|
||||||
} else {
|
} else {
|
||||||
d.ancientLimit = 0
|
d.ancientLimit = 0
|
||||||
}
|
}
|
||||||
|
// Extend the ancient chain segment range if the ancient limit is even
|
||||||
|
// below the pre-configured chain cutoff.
|
||||||
|
if d.chainCutoffNumber != 0 && d.chainCutoffNumber > d.ancientLimit {
|
||||||
|
d.ancientLimit = d.chainCutoffNumber
|
||||||
|
log.Info("Extend the ancient range with configured cutoff", "cutoff", d.chainCutoffNumber)
|
||||||
|
}
|
||||||
frozen, _ := d.stateDB.Ancients() // Ignore the error here since light client can also hit here.
|
frozen, _ := d.stateDB.Ancients() // Ignore the error here since light client can also hit here.
|
||||||
|
|
||||||
// If a part of blockchain data has already been written into active store,
|
// If a part of blockchain data has already been written into active store,
|
||||||
|
|
@ -521,14 +545,23 @@ func (d *Downloader) syncToHead() (err error) {
|
||||||
log.Info("Truncated excess ancient chain segment", "oldhead", frozen-1, "newhead", origin)
|
log.Info("Truncated excess ancient chain segment", "oldhead", frozen-1, "newhead", origin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Skip ancient chain segments if Geth is running with a configured chain cutoff.
|
||||||
|
// These segments are not guaranteed to be available in the network.
|
||||||
|
chainOffset := origin + 1
|
||||||
|
if mode == ethconfig.SnapSync && d.chainCutoffNumber != 0 {
|
||||||
|
if chainOffset < d.chainCutoffNumber {
|
||||||
|
chainOffset = d.chainCutoffNumber
|
||||||
|
log.Info("Skip chain segment before cutoff", "origin", origin, "cutoff", d.chainCutoffNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Initiate the sync using a concurrent header and content retrieval algorithm
|
// Initiate the sync using a concurrent header and content retrieval algorithm
|
||||||
d.queue.Prepare(origin+1, mode)
|
d.queue.Prepare(chainOffset, mode)
|
||||||
|
|
||||||
// In beacon mode, headers are served by the skeleton syncer
|
// In beacon mode, headers are served by the skeleton syncer
|
||||||
fetchers := []func() error{
|
fetchers := []func() error{
|
||||||
func() error { return d.fetchHeaders(origin + 1) }, // Headers are always retrieved
|
func() error { return d.fetchHeaders(origin + 1) }, // Headers are always retrieved
|
||||||
func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and snap sync
|
func() error { return d.fetchBodies(chainOffset) }, // Bodies are retrieved during normal and snap sync
|
||||||
func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during snap sync
|
func() error { return d.fetchReceipts(chainOffset) }, // Receipts are retrieved during snap sync
|
||||||
func() error { return d.processHeaders(origin + 1) },
|
func() error { return d.processHeaders(origin + 1) },
|
||||||
}
|
}
|
||||||
if mode == ethconfig.SnapSync {
|
if mode == ethconfig.SnapSync {
|
||||||
|
|
@ -666,7 +699,7 @@ func (d *Downloader) processHeaders(origin uint64) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Otherwise split the chunk of headers into batches and process them
|
// Otherwise split the chunk of headers into batches and process them
|
||||||
headers, hashes := task.headers, task.hashes
|
headers, hashes, scheduled := task.headers, task.hashes, false
|
||||||
|
|
||||||
for len(headers) > 0 {
|
for len(headers) > 0 {
|
||||||
// Terminate if something failed in between processing chunks
|
// Terminate if something failed in between processing chunks
|
||||||
|
|
@ -683,17 +716,21 @@ func (d *Downloader) processHeaders(origin uint64) error {
|
||||||
chunkHeaders := headers[:limit]
|
chunkHeaders := headers[:limit]
|
||||||
chunkHashes := hashes[:limit]
|
chunkHashes := hashes[:limit]
|
||||||
|
|
||||||
// In case of header only syncing, validate the chunk immediately
|
// Split the headers around the chain cutoff
|
||||||
if mode == ethconfig.SnapSync {
|
var cutoff int
|
||||||
// Although the received headers might be all valid, a legacy
|
if mode == ethconfig.SnapSync && d.chainCutoffNumber != 0 {
|
||||||
// PoW/PoA sync must not accept post-merge headers. Make sure
|
cutoff = sort.Search(len(chunkHeaders), func(i int) bool {
|
||||||
// that any transition is rejected at this point.
|
return chunkHeaders[i].Number.Uint64() >= d.chainCutoffNumber
|
||||||
if len(chunkHeaders) > 0 {
|
})
|
||||||
if n, err := d.blockchain.InsertHeaderChain(chunkHeaders); err != nil {
|
}
|
||||||
log.Warn("Invalid header encountered", "number", chunkHeaders[n].Number, "hash", chunkHashes[n], "parent", chunkHeaders[n].ParentHash, "err", err)
|
// Insert the header chain into the ancient store (with block bodies and
|
||||||
|
// receipts set to nil) if they fall before the cutoff.
|
||||||
|
if mode == ethconfig.SnapSync && cutoff != 0 {
|
||||||
|
if n, err := d.blockchain.InsertHeadersBeforeCutoff(chunkHeaders[:cutoff]); err != nil {
|
||||||
|
log.Warn("Failed to insert ancient header chain", "number", chunkHeaders[n].Number, "hash", chunkHashes[n], "parent", chunkHeaders[n].ParentHash, "err", err)
|
||||||
return fmt.Errorf("%w: %v", errInvalidChain, err)
|
return fmt.Errorf("%w: %v", errInvalidChain, err)
|
||||||
}
|
}
|
||||||
}
|
log.Debug("Inserted headers before cutoff", "number", chunkHeaders[cutoff-1].Number, "hash", chunkHashes[cutoff-1])
|
||||||
}
|
}
|
||||||
// If we've reached the allowed number of pending headers, stall a bit
|
// If we've reached the allowed number of pending headers, stall a bit
|
||||||
for d.queue.PendingBodies() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders {
|
for d.queue.PendingBodies() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders {
|
||||||
|
|
@ -704,12 +741,21 @@ func (d *Downloader) processHeaders(origin uint64) error {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise insert the headers for content retrieval
|
// Otherwise, schedule the headers for content retrieval (block bodies and
|
||||||
inserts := d.queue.Schedule(chunkHeaders, chunkHashes, origin)
|
// potentially receipts in snap sync).
|
||||||
if len(inserts) != len(chunkHeaders) {
|
//
|
||||||
|
// Skip the bodies/receipts retrieval scheduling before the cutoff in snap
|
||||||
|
// sync if chain pruning is configured.
|
||||||
|
if mode == ethconfig.SnapSync && cutoff != 0 {
|
||||||
|
chunkHeaders = chunkHeaders[cutoff:]
|
||||||
|
chunkHashes = chunkHashes[cutoff:]
|
||||||
|
}
|
||||||
|
if len(chunkHeaders) > 0 {
|
||||||
|
scheduled = true
|
||||||
|
if d.queue.Schedule(chunkHeaders, chunkHashes, origin+uint64(cutoff)) != len(chunkHeaders) {
|
||||||
return fmt.Errorf("%w: stale headers", errBadPeer)
|
return fmt.Errorf("%w: stale headers", errBadPeer)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
headers = headers[limit:]
|
headers = headers[limit:]
|
||||||
hashes = hashes[limit:]
|
hashes = hashes[limit:]
|
||||||
origin += uint64(limit)
|
origin += uint64(limit)
|
||||||
|
|
@ -721,7 +767,8 @@ func (d *Downloader) processHeaders(origin uint64) error {
|
||||||
}
|
}
|
||||||
d.syncStatsLock.Unlock()
|
d.syncStatsLock.Unlock()
|
||||||
|
|
||||||
// Signal the content downloaders of the availability of new tasks
|
// Signal the downloader of the availability of new tasks
|
||||||
|
if scheduled {
|
||||||
for _, ch := range []chan bool{d.queue.blockWakeCh, d.queue.receiptWakeCh} {
|
for _, ch := range []chan bool{d.queue.blockWakeCh, d.queue.receiptWakeCh} {
|
||||||
select {
|
select {
|
||||||
case ch <- true:
|
case ch <- true:
|
||||||
|
|
@ -731,6 +778,7 @@ func (d *Downloader) processHeaders(origin uint64) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// processFullSyncContent takes fetch results from the queue and imports them into the chain.
|
// processFullSyncContent takes fetch results from the queue and imports them into the chain.
|
||||||
func (d *Downloader) processFullSyncContent() error {
|
func (d *Downloader) processFullSyncContent() error {
|
||||||
|
|
@ -1085,10 +1133,20 @@ func (d *Downloader) reportSnapSyncProgress(force bool) {
|
||||||
header = d.blockchain.CurrentHeader()
|
header = d.blockchain.CurrentHeader()
|
||||||
block = d.blockchain.CurrentSnapBlock()
|
block = d.blockchain.CurrentSnapBlock()
|
||||||
)
|
)
|
||||||
syncedBlocks := block.Number.Uint64() - d.syncStartBlock
|
// Prevent reporting if nothing has been synchronized yet
|
||||||
if syncedBlocks == 0 {
|
if block.Number.Uint64() <= d.syncStartBlock {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Prevent reporting noise if the actual chain synchronization (headers
|
||||||
|
// and bodies) hasn't started yet. Inserting the ancient header chain is
|
||||||
|
// fast enough and would introduce significant bias if included in the count.
|
||||||
|
if d.chainCutoffNumber != 0 && block.Number.Uint64() <= d.chainCutoffNumber {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fetchedBlocks := block.Number.Uint64() - d.syncStartBlock
|
||||||
|
if d.chainCutoffNumber != 0 && d.chainCutoffNumber > d.syncStartBlock {
|
||||||
|
fetchedBlocks = block.Number.Uint64() - d.chainCutoffNumber
|
||||||
|
}
|
||||||
// Retrieve the current chain head and calculate the ETA
|
// Retrieve the current chain head and calculate the ETA
|
||||||
latest, _, _, err := d.skeleton.Bounds()
|
latest, _, _, err := d.skeleton.Bounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1103,7 +1161,7 @@ func (d *Downloader) reportSnapSyncProgress(force bool) {
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
left = latest.Number.Uint64() - block.Number.Uint64()
|
left = latest.Number.Uint64() - block.Number.Uint64()
|
||||||
eta = time.Since(d.syncStartTime) / time.Duration(syncedBlocks) * time.Duration(left)
|
eta = time.Since(d.syncStartTime) / time.Duration(fetchedBlocks) * time.Duration(left)
|
||||||
|
|
||||||
progress = fmt.Sprintf("%.2f%%", float64(block.Number.Uint64())*100/float64(latest.Number.Uint64()))
|
progress = fmt.Sprintf("%.2f%%", float64(block.Number.Uint64())*100/float64(latest.Number.Uint64()))
|
||||||
headers = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(header.Number.Uint64()), common.StorageSize(headerBytes).TerminalString())
|
headers = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(header.Number.Uint64()), common.StorageSize(headerBytes).TerminalString())
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import (
|
||||||
var (
|
var (
|
||||||
headerInMeter = metrics.NewRegisteredMeter("eth/downloader/headers/in", nil)
|
headerInMeter = metrics.NewRegisteredMeter("eth/downloader/headers/in", nil)
|
||||||
headerReqTimer = metrics.NewRegisteredTimer("eth/downloader/headers/req", nil)
|
headerReqTimer = metrics.NewRegisteredTimer("eth/downloader/headers/req", nil)
|
||||||
headerDropMeter = metrics.NewRegisteredMeter("eth/downloader/headers/drop", nil)
|
|
||||||
headerTimeoutMeter = metrics.NewRegisteredMeter("eth/downloader/headers/timeout", nil)
|
headerTimeoutMeter = metrics.NewRegisteredMeter("eth/downloader/headers/timeout", nil)
|
||||||
|
|
||||||
bodyInMeter = metrics.NewRegisteredMeter("eth/downloader/bodies/in", nil)
|
bodyInMeter = metrics.NewRegisteredMeter("eth/downloader/bodies/in", nil)
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ type fetchResult struct {
|
||||||
Withdrawals types.Withdrawals
|
Withdrawals types.Withdrawals
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFetchResult(header *types.Header, fastSync bool) *fetchResult {
|
func newFetchResult(header *types.Header, snapSync bool) *fetchResult {
|
||||||
item := &fetchResult{
|
item := &fetchResult{
|
||||||
Header: header,
|
Header: header,
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +82,7 @@ func newFetchResult(header *types.Header, fastSync bool) *fetchResult {
|
||||||
} else if header.WithdrawalsHash != nil {
|
} else if header.WithdrawalsHash != nil {
|
||||||
item.Withdrawals = make(types.Withdrawals, 0)
|
item.Withdrawals = make(types.Withdrawals, 0)
|
||||||
}
|
}
|
||||||
if fastSync && !header.EmptyReceipts() {
|
if snapSync && !header.EmptyReceipts() {
|
||||||
item.pending.Store(item.pending.Load() | (1 << receiptType))
|
item.pending.Store(item.pending.Load() | (1 << receiptType))
|
||||||
}
|
}
|
||||||
return item
|
return item
|
||||||
|
|
@ -125,18 +125,7 @@ func (f *fetchResult) Done(kind uint) bool {
|
||||||
// queue represents hashes that are either need fetching or are being fetched
|
// queue represents hashes that are either need fetching or are being fetched
|
||||||
type queue struct {
|
type queue struct {
|
||||||
mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching
|
mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching
|
||||||
|
|
||||||
// Headers are "special", they download in batches, supported by a skeleton chain
|
|
||||||
headerHead common.Hash // Hash of the last queued header to verify order
|
headerHead common.Hash // Hash of the last queued header to verify order
|
||||||
headerTaskPool map[uint64]*types.Header // Pending header retrieval tasks, mapping starting indexes to skeleton headers
|
|
||||||
headerTaskQueue *prque.Prque[int64, uint64] // Priority queue of the skeleton indexes to fetch the filling headers for
|
|
||||||
headerPeerMiss map[string]map[uint64]struct{} // Set of per-peer header batches known to be unavailable
|
|
||||||
headerPendPool map[string]*fetchRequest // Currently pending header retrieval operations
|
|
||||||
headerResults []*types.Header // Result cache accumulating the completed headers
|
|
||||||
headerHashes []common.Hash // Result cache accumulating the completed header hashes
|
|
||||||
headerProced int // Number of headers already processed from the results
|
|
||||||
headerOffset uint64 // Number of the first header in the result cache
|
|
||||||
headerContCh chan bool // Channel to notify when header download finishes
|
|
||||||
|
|
||||||
// All data retrievals below are based on an already assembles header chain
|
// All data retrievals below are based on an already assembles header chain
|
||||||
blockTaskPool map[common.Hash]*types.Header // Pending block (body) retrieval tasks, mapping hashes to headers
|
blockTaskPool map[common.Hash]*types.Header // Pending block (body) retrieval tasks, mapping hashes to headers
|
||||||
|
|
@ -163,7 +152,6 @@ type queue struct {
|
||||||
func newQueue(blockCacheLimit int, thresholdInitialSize int) *queue {
|
func newQueue(blockCacheLimit int, thresholdInitialSize int) *queue {
|
||||||
lock := new(sync.RWMutex)
|
lock := new(sync.RWMutex)
|
||||||
q := &queue{
|
q := &queue{
|
||||||
headerContCh: make(chan bool, 1),
|
|
||||||
blockTaskQueue: prque.New[int64, *types.Header](nil),
|
blockTaskQueue: prque.New[int64, *types.Header](nil),
|
||||||
blockWakeCh: make(chan bool, 1),
|
blockWakeCh: make(chan bool, 1),
|
||||||
receiptTaskQueue: prque.New[int64, *types.Header](nil),
|
receiptTaskQueue: prque.New[int64, *types.Header](nil),
|
||||||
|
|
@ -182,9 +170,7 @@ func (q *queue) Reset(blockCacheLimit int, thresholdInitialSize int) {
|
||||||
|
|
||||||
q.closed = false
|
q.closed = false
|
||||||
q.mode = ethconfig.FullSync
|
q.mode = ethconfig.FullSync
|
||||||
|
|
||||||
q.headerHead = common.Hash{}
|
q.headerHead = common.Hash{}
|
||||||
q.headerPendPool = make(map[string]*fetchRequest)
|
|
||||||
|
|
||||||
q.blockTaskPool = make(map[common.Hash]*types.Header)
|
q.blockTaskPool = make(map[common.Hash]*types.Header)
|
||||||
q.blockTaskQueue.Reset()
|
q.blockTaskQueue.Reset()
|
||||||
|
|
@ -207,14 +193,6 @@ func (q *queue) Close() {
|
||||||
q.lock.Unlock()
|
q.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingHeaders retrieves the number of header requests pending for retrieval.
|
|
||||||
func (q *queue) PendingHeaders() int {
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
|
|
||||||
return q.headerTaskQueue.Size()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PendingBodies retrieves the number of block body requests pending for retrieval.
|
// PendingBodies retrieves the number of block body requests pending for retrieval.
|
||||||
func (q *queue) PendingBodies() int {
|
func (q *queue) PendingBodies() int {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
|
|
@ -260,54 +238,14 @@ func (q *queue) Idle() bool {
|
||||||
return (queued + pending) == 0
|
return (queued + pending) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScheduleSkeleton adds a batch of header retrieval tasks to the queue to fill
|
|
||||||
// up an already retrieved header skeleton.
|
|
||||||
func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) {
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
|
|
||||||
// No skeleton retrieval can be in progress, fail hard if so (huge implementation bug)
|
|
||||||
if q.headerResults != nil {
|
|
||||||
panic("skeleton assembly already in progress")
|
|
||||||
}
|
|
||||||
// Schedule all the header retrieval tasks for the skeleton assembly
|
|
||||||
q.headerTaskPool = make(map[uint64]*types.Header)
|
|
||||||
q.headerTaskQueue = prque.New[int64, uint64](nil)
|
|
||||||
q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains
|
|
||||||
q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch)
|
|
||||||
q.headerHashes = make([]common.Hash, len(skeleton)*MaxHeaderFetch)
|
|
||||||
q.headerProced = 0
|
|
||||||
q.headerOffset = from
|
|
||||||
q.headerContCh = make(chan bool, 1)
|
|
||||||
|
|
||||||
for i, header := range skeleton {
|
|
||||||
index := from + uint64(i*MaxHeaderFetch)
|
|
||||||
|
|
||||||
q.headerTaskPool[index] = header
|
|
||||||
q.headerTaskQueue.Push(index, -int64(index))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveHeaders retrieves the header chain assemble based on the scheduled
|
|
||||||
// skeleton.
|
|
||||||
func (q *queue) RetrieveHeaders() ([]*types.Header, []common.Hash, int) {
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
|
|
||||||
headers, hashes, proced := q.headerResults, q.headerHashes, q.headerProced
|
|
||||||
q.headerResults, q.headerHashes, q.headerProced = nil, nil, 0
|
|
||||||
|
|
||||||
return headers, hashes, proced
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schedule adds a set of headers for the download queue for scheduling, returning
|
// Schedule adds a set of headers for the download queue for scheduling, returning
|
||||||
// the new headers encountered.
|
// the new headers encountered.
|
||||||
func (q *queue) Schedule(headers []*types.Header, hashes []common.Hash, from uint64) []*types.Header {
|
func (q *queue) Schedule(headers []*types.Header, hashes []common.Hash, from uint64) int {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
// Insert all the headers prioritised by the contained block number
|
// Insert all the headers prioritised by the contained block number
|
||||||
inserts := make([]*types.Header, 0, len(headers))
|
var inserts int
|
||||||
for i, header := range headers {
|
for i, header := range headers {
|
||||||
// Make sure chain order is honoured and preserved throughout
|
// Make sure chain order is honoured and preserved throughout
|
||||||
hash := hashes[i]
|
hash := hashes[i]
|
||||||
|
|
@ -337,7 +275,7 @@ func (q *queue) Schedule(headers []*types.Header, hashes []common.Hash, from uin
|
||||||
q.receiptTaskQueue.Push(header, -int64(header.Number.Uint64()))
|
q.receiptTaskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inserts = append(inserts, header)
|
inserts++
|
||||||
q.headerHead = hash
|
q.headerHead = hash
|
||||||
from++
|
from++
|
||||||
}
|
}
|
||||||
|
|
@ -428,46 +366,6 @@ func (q *queue) stats() []interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReserveHeaders reserves a set of headers for the given peer, skipping any
|
|
||||||
// previously failed batches.
|
|
||||||
func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest {
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
|
|
||||||
// Short circuit if the peer's already downloading something (sanity check to
|
|
||||||
// not corrupt state)
|
|
||||||
if _, ok := q.headerPendPool[p.id]; ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Retrieve a batch of hashes, skipping previously failed ones
|
|
||||||
send, skip := uint64(0), []uint64{}
|
|
||||||
for send == 0 && !q.headerTaskQueue.Empty() {
|
|
||||||
from, _ := q.headerTaskQueue.Pop()
|
|
||||||
if q.headerPeerMiss[p.id] != nil {
|
|
||||||
if _, ok := q.headerPeerMiss[p.id][from]; ok {
|
|
||||||
skip = append(skip, from)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
send = from
|
|
||||||
}
|
|
||||||
// Merge all the skipped batches back
|
|
||||||
for _, from := range skip {
|
|
||||||
q.headerTaskQueue.Push(from, -int64(from))
|
|
||||||
}
|
|
||||||
// Assemble and return the block download request
|
|
||||||
if send == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
request := &fetchRequest{
|
|
||||||
Peer: p,
|
|
||||||
From: send,
|
|
||||||
Time: time.Now(),
|
|
||||||
}
|
|
||||||
q.headerPendPool[p.id] = request
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReserveBodies reserves a set of body fetches for the given peer, skipping any
|
// ReserveBodies reserves a set of body fetches for the given peer, skipping any
|
||||||
// previously failed downloads. Beside the next batch of needed fetches, it also
|
// previously failed downloads. Beside the next batch of needed fetches, it also
|
||||||
// returns a flag whether empty blocks were queued requiring processing.
|
// returns a flag whether empty blocks were queued requiring processing.
|
||||||
|
|
@ -594,10 +492,6 @@ func (q *queue) Revoke(peerID string) {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
if request, ok := q.headerPendPool[peerID]; ok {
|
|
||||||
q.headerTaskQueue.Push(request.From, -int64(request.From))
|
|
||||||
delete(q.headerPendPool, peerID)
|
|
||||||
}
|
|
||||||
if request, ok := q.blockPendPool[peerID]; ok {
|
if request, ok := q.blockPendPool[peerID]; ok {
|
||||||
for _, header := range request.Headers {
|
for _, header := range request.Headers {
|
||||||
q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
|
q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
|
|
@ -612,16 +506,6 @@ func (q *queue) Revoke(peerID string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpireHeaders cancels a request that timed out and moves the pending fetch
|
|
||||||
// task back into the queue for rescheduling.
|
|
||||||
func (q *queue) ExpireHeaders(peer string) int {
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
|
|
||||||
headerTimeoutMeter.Mark(1)
|
|
||||||
return q.expire(peer, q.headerPendPool, q.headerTaskQueue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExpireBodies checks for in flight block body requests that exceeded a timeout
|
// ExpireBodies checks for in flight block body requests that exceeded a timeout
|
||||||
// allowance, canceling them and returning the responsible peers for penalisation.
|
// allowance, canceling them and returning the responsible peers for penalisation.
|
||||||
func (q *queue) ExpireBodies(peer string) int {
|
func (q *queue) ExpireBodies(peer string) int {
|
||||||
|
|
@ -670,116 +554,6 @@ func (q *queue) expire(peer string, pendPool map[string]*fetchRequest, taskQueue
|
||||||
return len(req.Headers)
|
return len(req.Headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverHeaders injects a header retrieval response into the header results
|
|
||||||
// cache. This method either accepts all headers it received, or none of them
|
|
||||||
// if they do not map correctly to the skeleton.
|
|
||||||
//
|
|
||||||
// If the headers are accepted, the method makes an attempt to deliver the set
|
|
||||||
// of ready headers to the processor to keep the pipeline full. However, it will
|
|
||||||
// not block to prevent stalling other pending deliveries.
|
|
||||||
func (q *queue) DeliverHeaders(id string, headers []*types.Header, hashes []common.Hash, headerProcCh chan *headerTask) (int, error) {
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
|
|
||||||
var logger log.Logger
|
|
||||||
if len(id) < 16 {
|
|
||||||
// Tests use short IDs, don't choke on them
|
|
||||||
logger = log.New("peer", id)
|
|
||||||
} else {
|
|
||||||
logger = log.New("peer", id[:16])
|
|
||||||
}
|
|
||||||
// Short circuit if the data was never requested
|
|
||||||
request := q.headerPendPool[id]
|
|
||||||
if request == nil {
|
|
||||||
headerDropMeter.Mark(int64(len(headers)))
|
|
||||||
return 0, errNoFetchesPending
|
|
||||||
}
|
|
||||||
delete(q.headerPendPool, id)
|
|
||||||
|
|
||||||
headerReqTimer.UpdateSince(request.Time)
|
|
||||||
headerInMeter.Mark(int64(len(headers)))
|
|
||||||
|
|
||||||
// Ensure headers can be mapped onto the skeleton chain
|
|
||||||
target := q.headerTaskPool[request.From].Hash()
|
|
||||||
|
|
||||||
accepted := len(headers) == MaxHeaderFetch
|
|
||||||
if accepted {
|
|
||||||
if headers[0].Number.Uint64() != request.From {
|
|
||||||
logger.Trace("First header broke chain ordering", "number", headers[0].Number, "hash", hashes[0], "expected", request.From)
|
|
||||||
accepted = false
|
|
||||||
} else if hashes[len(headers)-1] != target {
|
|
||||||
logger.Trace("Last header broke skeleton structure ", "number", headers[len(headers)-1].Number, "hash", hashes[len(headers)-1], "expected", target)
|
|
||||||
accepted = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if accepted {
|
|
||||||
parentHash := hashes[0]
|
|
||||||
for i, header := range headers[1:] {
|
|
||||||
hash := hashes[i+1]
|
|
||||||
if want := request.From + 1 + uint64(i); header.Number.Uint64() != want {
|
|
||||||
logger.Warn("Header broke chain ordering", "number", header.Number, "hash", hash, "expected", want)
|
|
||||||
accepted = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if parentHash != header.ParentHash {
|
|
||||||
logger.Warn("Header broke chain ancestry", "number", header.Number, "hash", hash)
|
|
||||||
accepted = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Set-up parent hash for next round
|
|
||||||
parentHash = hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the batch of headers wasn't accepted, mark as unavailable
|
|
||||||
if !accepted {
|
|
||||||
logger.Trace("Skeleton filling not accepted", "from", request.From)
|
|
||||||
headerDropMeter.Mark(int64(len(headers)))
|
|
||||||
|
|
||||||
miss := q.headerPeerMiss[id]
|
|
||||||
if miss == nil {
|
|
||||||
q.headerPeerMiss[id] = make(map[uint64]struct{})
|
|
||||||
miss = q.headerPeerMiss[id]
|
|
||||||
}
|
|
||||||
miss[request.From] = struct{}{}
|
|
||||||
|
|
||||||
q.headerTaskQueue.Push(request.From, -int64(request.From))
|
|
||||||
return 0, errors.New("delivery not accepted")
|
|
||||||
}
|
|
||||||
// Clean up a successful fetch and try to deliver any sub-results
|
|
||||||
copy(q.headerResults[request.From-q.headerOffset:], headers)
|
|
||||||
copy(q.headerHashes[request.From-q.headerOffset:], hashes)
|
|
||||||
|
|
||||||
delete(q.headerTaskPool, request.From)
|
|
||||||
|
|
||||||
ready := 0
|
|
||||||
for q.headerProced+ready < len(q.headerResults) && q.headerResults[q.headerProced+ready] != nil {
|
|
||||||
ready += MaxHeaderFetch
|
|
||||||
}
|
|
||||||
if ready > 0 {
|
|
||||||
// Headers are ready for delivery, gather them and push forward (non blocking)
|
|
||||||
processHeaders := make([]*types.Header, ready)
|
|
||||||
copy(processHeaders, q.headerResults[q.headerProced:q.headerProced+ready])
|
|
||||||
|
|
||||||
processHashes := make([]common.Hash, ready)
|
|
||||||
copy(processHashes, q.headerHashes[q.headerProced:q.headerProced+ready])
|
|
||||||
|
|
||||||
select {
|
|
||||||
case headerProcCh <- &headerTask{
|
|
||||||
headers: processHeaders,
|
|
||||||
hashes: processHashes,
|
|
||||||
}:
|
|
||||||
logger.Trace("Pre-scheduled new headers", "count", len(processHeaders), "from", processHeaders[0].Number)
|
|
||||||
q.headerProced += len(processHeaders)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check for termination and return
|
|
||||||
if len(q.headerTaskPool) == 0 {
|
|
||||||
q.headerContCh <- false
|
|
||||||
}
|
|
||||||
return len(headers), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeliverBodies injects a block body retrieval response into the results queue.
|
// DeliverBodies injects a block body retrieval response into the results queue.
|
||||||
// The method returns the number of blocks bodies accepted from the delivery and
|
// The method returns the number of blocks bodies accepted from the delivery and
|
||||||
// also wakes any threads waiting for data delivery.
|
// also wakes any threads waiting for data delivery.
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ func (r *resultStore) SetThrottleThreshold(threshold uint64) uint64 {
|
||||||
// throttled - if true, the store is at capacity, this particular header is not prio now
|
// throttled - if true, the store is at capacity, this particular header is not prio now
|
||||||
// item - the result to store data into
|
// item - the result to store data into
|
||||||
// err - any error that occurred
|
// err - any error that occurred
|
||||||
func (r *resultStore) AddFetch(header *types.Header, fastSync bool) (stale, throttled bool, item *fetchResult, err error) {
|
func (r *resultStore) AddFetch(header *types.Header, snapSync bool) (stale, throttled bool, item *fetchResult, err error) {
|
||||||
r.lock.Lock()
|
r.lock.Lock()
|
||||||
defer r.lock.Unlock()
|
defer r.lock.Unlock()
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ func (r *resultStore) AddFetch(header *types.Header, fastSync bool) (stale, thro
|
||||||
return stale, throttled, item, err
|
return stale, throttled, item, err
|
||||||
}
|
}
|
||||||
if item == nil {
|
if item == nil {
|
||||||
item = newFetchResult(header, fastSync)
|
item = newFetchResult(header, snapSync)
|
||||||
r.items[index] = item
|
r.items[index] = item
|
||||||
}
|
}
|
||||||
return stale, throttled, item, err
|
return stale, throttled, item, err
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ func (l panicLogger) Fatalf(format string, args ...interface{}) {
|
||||||
|
|
||||||
// New returns a wrapped pebble DB object. The namespace is the prefix that the
|
// New returns a wrapped pebble DB object. The namespace is the prefix that the
|
||||||
// metrics reporting should use for surfacing internal stats.
|
// metrics reporting should use for surfacing internal stats.
|
||||||
func New(file string, cache int, handles int, namespace string, readonly bool) (*Database, error) {
|
func New(file string, cache int, handles int, namespace string, readonly bool, ephemeral bool) (*Database, error) {
|
||||||
// Ensure we have some minimal caching and file guarantees
|
// Ensure we have some minimal caching and file guarantees
|
||||||
if cache < minCache {
|
if cache < minCache {
|
||||||
cache = minCache
|
cache = minCache
|
||||||
|
|
@ -185,7 +185,7 @@ func New(file string, cache int, handles int, namespace string, readonly bool) (
|
||||||
fn: file,
|
fn: file,
|
||||||
log: logger,
|
log: logger,
|
||||||
quitChan: make(chan chan error),
|
quitChan: make(chan chan error),
|
||||||
writeOptions: &pebble.WriteOptions{Sync: false},
|
writeOptions: &pebble.WriteOptions{Sync: !ephemeral},
|
||||||
}
|
}
|
||||||
opt := &pebble.Options{
|
opt := &pebble.Options{
|
||||||
// Pebble has a single combined cache area and the write
|
// Pebble has a single combined cache area and the write
|
||||||
|
|
|
||||||
|
|
@ -219,3 +219,15 @@ func (r *Reader) FindAll(want uint16) ([]*Entry, error) {
|
||||||
off += int64(headerSize + length)
|
off += int64(headerSize + length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SkipN skips `n` entries starting from `offset` and returns the new offset.
|
||||||
|
func (r *Reader) SkipN(offset int64, n uint64) (int64, error) {
|
||||||
|
for i := uint64(0); i < n; i++ {
|
||||||
|
length, err := r.LengthAt(offset)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
offset += length
|
||||||
|
}
|
||||||
|
return offset, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ func ReadDir(dir, network string) ([]string, error) {
|
||||||
}
|
}
|
||||||
parts := strings.Split(entry.Name(), "-")
|
parts := strings.Split(entry.Name(), "-")
|
||||||
if len(parts) != 3 || parts[0] != network {
|
if len(parts) != 3 || parts[0] != network {
|
||||||
// invalid era1 filename, skip
|
// Invalid era1 filename, skip.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if epoch, err := strconv.ParseUint(parts[1], 10, 64); err != nil {
|
if epoch, err := strconv.ParseUint(parts[1], 10, 64); err != nil {
|
||||||
|
|
@ -126,6 +126,29 @@ func (e *Era) Close() error {
|
||||||
return e.f.Close()
|
return e.f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHeaderByNumber returns the header for the given block number.
|
||||||
|
func (e *Era) GetHeaderByNumber(num uint64) (*types.Header, error) {
|
||||||
|
if e.m.start > num || e.m.start+e.m.count <= num {
|
||||||
|
return nil, errors.New("out-of-bounds")
|
||||||
|
}
|
||||||
|
off, err := e.readOffset(num)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and decompress header.
|
||||||
|
r, _, err := newSnappyReader(e.s, TypeCompressedHeader, off)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var header types.Header
|
||||||
|
if err := rlp.Decode(r, &header); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockByNumber returns the block for the given block number.
|
||||||
func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) {
|
func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) {
|
||||||
if e.m.start > num || e.m.start+e.m.count <= num {
|
if e.m.start > num || e.m.start+e.m.count <= num {
|
||||||
return nil, errors.New("out-of-bounds")
|
return nil, errors.New("out-of-bounds")
|
||||||
|
|
@ -154,6 +177,34 @@ func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) {
|
||||||
return types.NewBlockWithHeader(&header).WithBody(body), nil
|
return types.NewBlockWithHeader(&header).WithBody(body), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetReceiptsByNumber returns the receipts for the given block number.
|
||||||
|
func (e *Era) GetReceiptsByNumber(num uint64) (types.Receipts, error) {
|
||||||
|
if e.m.start > num || e.m.start+e.m.count <= num {
|
||||||
|
return nil, errors.New("out-of-bounds")
|
||||||
|
}
|
||||||
|
off, err := e.readOffset(num)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip over header and body.
|
||||||
|
off, err = e.s.SkipN(off, 2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and decompress receipts.
|
||||||
|
r, _, err := newSnappyReader(e.s, TypeCompressedReceipts, off)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var receipts types.Receipts
|
||||||
|
if err := rlp.Decode(r, &receipts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return receipts, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Accumulator reads the accumulator entry in the Era1 file.
|
// Accumulator reads the accumulator entry in the Era1 file.
|
||||||
func (e *Era) Accumulator() (common.Hash, error) {
|
func (e *Era) Accumulator() (common.Hash, error) {
|
||||||
entry, err := e.s.Find(TypeAccumulator)
|
entry, err := e.s.Find(TypeAccumulator)
|
||||||
|
|
@ -187,14 +238,11 @@ func (e *Era) InitialTD() (*big.Int, error) {
|
||||||
}
|
}
|
||||||
off += n
|
off += n
|
||||||
|
|
||||||
// Skip over next two records.
|
// Skip over header and body.
|
||||||
for i := 0; i < 2; i++ {
|
off, err = e.s.SkipN(off, 2)
|
||||||
length, err := e.s.LengthAt(off)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
off += length
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read total difficulty after first block.
|
// Read total difficulty after first block.
|
||||||
if r, _, err = e.s.ReaderAt(TypeTotalDifficulty, off); err != nil {
|
if r, _, err = e.s.ReaderAt(TypeTotalDifficulty, off); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,15 @@ package era
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testchain struct {
|
type testchain struct {
|
||||||
|
|
@ -48,9 +51,9 @@ func TestEra1Builder(t *testing.T) {
|
||||||
chain = testchain{}
|
chain = testchain{}
|
||||||
)
|
)
|
||||||
for i := 0; i < 128; i++ {
|
for i := 0; i < 128; i++ {
|
||||||
chain.headers = append(chain.headers, []byte{byte('h'), byte(i)})
|
chain.headers = append(chain.headers, mustEncode(&types.Header{Number: big.NewInt(int64(i))}))
|
||||||
chain.bodies = append(chain.bodies, []byte{byte('b'), byte(i)})
|
chain.bodies = append(chain.bodies, mustEncode(&types.Body{Transactions: []*types.Transaction{types.NewTransaction(0, common.Address{byte(i)}, nil, 0, nil, nil)}}))
|
||||||
chain.receipts = append(chain.receipts, []byte{byte('r'), byte(i)})
|
chain.receipts = append(chain.receipts, mustEncode(&types.Receipts{{CumulativeGasUsed: uint64(i)}}))
|
||||||
chain.tds = append(chain.tds, big.NewInt(int64(i)))
|
chain.tds = append(chain.tds, big.NewInt(int64(i)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,13 +94,25 @@ func TestEra1Builder(t *testing.T) {
|
||||||
t.Fatalf("unexpected error %v", it.Error())
|
t.Fatalf("unexpected error %v", it.Error())
|
||||||
}
|
}
|
||||||
// Check headers.
|
// Check headers.
|
||||||
header, err := io.ReadAll(it.Header)
|
rawHeader, err := io.ReadAll(it.Header)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error reading header from iterator: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(rawHeader, chain.headers[i]) {
|
||||||
|
t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], rawHeader)
|
||||||
|
}
|
||||||
|
header, err := e.GetHeaderByNumber(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error reading header: %v", err)
|
t.Fatalf("error reading header: %v", err)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(header, chain.headers[i]) {
|
encHeader, err := rlp.EncodeToBytes(header)
|
||||||
t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], header)
|
if err != nil {
|
||||||
|
t.Fatalf("error encoding header: %v", err)
|
||||||
}
|
}
|
||||||
|
if !bytes.Equal(encHeader, chain.headers[i]) {
|
||||||
|
t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], encHeader)
|
||||||
|
}
|
||||||
|
|
||||||
// Check bodies.
|
// Check bodies.
|
||||||
body, err := io.ReadAll(it.Body)
|
body, err := io.ReadAll(it.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -106,13 +121,25 @@ func TestEra1Builder(t *testing.T) {
|
||||||
if !bytes.Equal(body, chain.bodies[i]) {
|
if !bytes.Equal(body, chain.bodies[i]) {
|
||||||
t.Fatalf("mismatched body: want %s, got %s", chain.bodies[i], body)
|
t.Fatalf("mismatched body: want %s, got %s", chain.bodies[i], body)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check receipts.
|
// Check receipts.
|
||||||
receipts, err := io.ReadAll(it.Receipts)
|
rawReceipts, err := io.ReadAll(it.Receipts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error reading receipts from iterator: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(rawReceipts, chain.receipts[i]) {
|
||||||
|
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], rawReceipts)
|
||||||
|
}
|
||||||
|
receipts, err := e.GetReceiptsByNumber(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error reading receipts: %v", err)
|
t.Fatalf("error reading receipts: %v", err)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(receipts, chain.receipts[i]) {
|
encReceipts, err := rlp.EncodeToBytes(receipts)
|
||||||
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], receipts)
|
if err != nil {
|
||||||
|
t.Fatalf("error encoding receipts: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(encReceipts, chain.receipts[i]) {
|
||||||
|
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], encReceipts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check total difficulty.
|
// Check total difficulty.
|
||||||
|
|
@ -144,3 +171,11 @@ func TestEraFilename(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mustEncode(obj any) []byte {
|
||||||
|
b, err := rlp.EncodeToBytes(obj)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("failed in encode obj: %v", err))
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -623,7 +623,10 @@ func (b testBackend) NewMatcherBackend() filtermaps.MatcherBackend {
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b testBackend) HistoryPruningCutoff() uint64 { return b.chain.HistoryPruningCutoff() }
|
func (b testBackend) HistoryPruningCutoff() uint64 {
|
||||||
|
bn, _ := b.chain.HistoryPruningCutoff()
|
||||||
|
return bn
|
||||||
|
}
|
||||||
|
|
||||||
func TestEstimateGas(t *testing.T) {
|
func TestEstimateGas(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,11 @@ type openOptions struct {
|
||||||
Cache int // the capacity(in megabytes) of the data caching
|
Cache int // the capacity(in megabytes) of the data caching
|
||||||
Handles int // number of files to be open simultaneously
|
Handles int // number of files to be open simultaneously
|
||||||
ReadOnly bool
|
ReadOnly bool
|
||||||
|
|
||||||
|
// Ephemeral means that filesystem sync operations should be avoided:
|
||||||
|
// data integrity in the face of a crash is not important. This option
|
||||||
|
// should typically be used in tests.
|
||||||
|
Ephemeral bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// openDatabase opens both a disk-based key-value database such as leveldb or pebble, but also
|
// openDatabase opens both a disk-based key-value database such as leveldb or pebble, but also
|
||||||
|
|
@ -78,7 +83,7 @@ func openKeyValueDatabase(o openOptions) (ethdb.Database, error) {
|
||||||
}
|
}
|
||||||
if o.Type == rawdb.DBPebble || existingDb == rawdb.DBPebble {
|
if o.Type == rawdb.DBPebble || existingDb == rawdb.DBPebble {
|
||||||
log.Info("Using pebble as the backing database")
|
log.Info("Using pebble as the backing database")
|
||||||
return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
|
||||||
}
|
}
|
||||||
if o.Type == rawdb.DBLeveldb || existingDb == rawdb.DBLeveldb {
|
if o.Type == rawdb.DBLeveldb || existingDb == rawdb.DBLeveldb {
|
||||||
log.Info("Using leveldb as the backing database")
|
log.Info("Using leveldb as the backing database")
|
||||||
|
|
@ -86,7 +91,7 @@ func openKeyValueDatabase(o openOptions) (ethdb.Database, error) {
|
||||||
}
|
}
|
||||||
// No pre-existing database, no user-requested one either. Default to Pebble.
|
// No pre-existing database, no user-requested one either. Default to Pebble.
|
||||||
log.Info("Defaulting to pebble as the backing database")
|
log.Info("Defaulting to pebble as the backing database")
|
||||||
return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newLevelDBDatabase creates a persistent key-value database without a freezer
|
// newLevelDBDatabase creates a persistent key-value database without a freezer
|
||||||
|
|
@ -102,8 +107,8 @@ func newLevelDBDatabase(file string, cache int, handles int, namespace string, r
|
||||||
|
|
||||||
// newPebbleDBDatabase creates a persistent key-value database without a freezer
|
// newPebbleDBDatabase creates a persistent key-value database without a freezer
|
||||||
// moving immutable chain segments into cold storage.
|
// moving immutable chain segments into cold storage.
|
||||||
func newPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
|
func newPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool, ephemeral bool) (ethdb.Database, error) {
|
||||||
db, err := pebble.New(file, cache, handles, namespace, readonly)
|
db, err := pebble.New(file, cache, handles, namespace, readonly, ephemeral)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@ func (n *pmp) AddMapping(protocol string, extport, intport int, name string, lif
|
||||||
if lifetime <= 0 {
|
if lifetime <= 0 {
|
||||||
return 0, errors.New("lifetime must not be <= 0")
|
return 0, errors.New("lifetime must not be <= 0")
|
||||||
}
|
}
|
||||||
|
if extport == 0 {
|
||||||
|
extport = intport
|
||||||
|
}
|
||||||
// Note order of port arguments is switched between our
|
// Note order of port arguments is switched between our
|
||||||
// AddMapping and the client's AddPortMapping.
|
// AddMapping and the client's AddPortMapping.
|
||||||
res, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second))
|
res, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second))
|
||||||
|
|
|
||||||
|
|
@ -86,15 +86,15 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li
|
||||||
}
|
}
|
||||||
protocol = strings.ToUpper(protocol)
|
protocol = strings.ToUpper(protocol)
|
||||||
lifetimeS := uint32(lifetime / time.Second)
|
lifetimeS := uint32(lifetime / time.Second)
|
||||||
n.DeleteMapping(protocol, extport, intport)
|
|
||||||
|
|
||||||
err = n.withRateLimit(func() error {
|
if extport == 0 {
|
||||||
return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
|
extport = intport
|
||||||
})
|
} else {
|
||||||
if err == nil {
|
// Only delete port mapping if the external port was already used by geth.
|
||||||
return uint16(extport), nil
|
n.DeleteMapping(protocol, extport, intport)
|
||||||
}
|
}
|
||||||
// Try addAnyPortMapping if mapping specified port didn't work.
|
|
||||||
|
// Try to add port mapping, preferring the specified external port.
|
||||||
err = n.withRateLimit(func() error {
|
err = n.withRateLimit(func() error {
|
||||||
p, err := n.addAnyPortMapping(protocol, extport, intport, ip, desc, lifetimeS)
|
p, err := n.addAnyPortMapping(protocol, extport, intport, ip, desc, lifetimeS)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -105,19 +105,29 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li
|
||||||
return uint16(extport), err
|
return uint16(extport), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addAnyPortMapping tries to add a port mapping with the specified external port.
|
||||||
|
// If the external port is already in use, it will try to assign another port.
|
||||||
func (n *upnp) addAnyPortMapping(protocol string, extport, intport int, ip net.IP, desc string, lifetimeS uint32) (uint16, error) {
|
func (n *upnp) addAnyPortMapping(protocol string, extport, intport int, ip net.IP, desc string, lifetimeS uint32) (uint16, error) {
|
||||||
if client, ok := n.client.(*internetgateway2.WANIPConnection2); ok {
|
if client, ok := n.client.(*internetgateway2.WANIPConnection2); ok {
|
||||||
return client.AddAnyPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
|
return client.AddAnyPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
|
||||||
}
|
}
|
||||||
// It will retry with a random port number if the client does
|
// For IGDv1 and v1 services we should first try to add with extport.
|
||||||
// not support AddAnyPortMapping.
|
err := n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
|
||||||
|
if err == nil {
|
||||||
|
return uint16(extport), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If above fails, we retry with a random port.
|
||||||
|
// We retry several times because of possible port conflicts.
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
extport = n.randomPort()
|
extport = n.randomPort()
|
||||||
err := n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
|
err := n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return uint16(extport), nil
|
return uint16(extport), nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
func (n *upnp) randomPort() int {
|
func (n *upnp) randomPort() int {
|
||||||
if n.rand == nil {
|
if n.rand == nil {
|
||||||
|
|
|
||||||
|
|
@ -150,14 +150,9 @@ func (srv *Server) portMappingLoop() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
external := m.port
|
log := newLogger(m.protocol, m.extPort, m.port)
|
||||||
if m.extPort != 0 {
|
|
||||||
external = m.extPort
|
|
||||||
}
|
|
||||||
log := newLogger(m.protocol, external, m.port)
|
|
||||||
|
|
||||||
log.Trace("Attempting port mapping")
|
log.Trace("Attempting port mapping")
|
||||||
p, err := srv.NAT.AddMapping(m.protocol, external, m.port, m.name, portMapDuration)
|
p, err := srv.NAT.AddMapping(m.protocol, m.extPort, m.port, m.name, portMapDuration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Couldn't add port mapping", "err", err)
|
log.Debug("Couldn't add port mapping", "err", err)
|
||||||
m.extPort = 0
|
m.extPort = 0
|
||||||
|
|
@ -167,8 +162,8 @@ func (srv *Server) portMappingLoop() {
|
||||||
// It was mapped!
|
// It was mapped!
|
||||||
m.extPort = int(p)
|
m.extPort = int(p)
|
||||||
m.nextTime = srv.clock.Now().Add(portMapRefreshInterval)
|
m.nextTime = srv.clock.Now().Add(portMapRefreshInterval)
|
||||||
if external != m.extPort {
|
|
||||||
log = newLogger(m.protocol, m.extPort, m.port)
|
log = newLogger(m.protocol, m.extPort, m.port)
|
||||||
|
if m.port != m.extPort {
|
||||||
log.Info("NAT mapped alternative port")
|
log.Info("NAT mapped alternative port")
|
||||||
} else {
|
} else {
|
||||||
log.Info("NAT mapped port")
|
log.Info("NAT mapped port")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue