Compare commits
3 Commits
v1.14.11
...
unbork-mas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5c36a8e2c | ||
|
|
afddce396d | ||
|
|
2b5d289b66 |
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -20,6 +20,8 @@ les/ @zsfelfoldi @rjl493456442
|
||||
light/ @zsfelfoldi @rjl493456442
|
||||
node/ @fjl
|
||||
p2p/ @fjl @zsfelfoldi
|
||||
params/ @fjl @holiman @karalabe @gballet @rjl493456442 @zsfelfoldi
|
||||
rpc/ @fjl @holiman
|
||||
p2p/simulations @fjl
|
||||
p2p/protocols @fjl
|
||||
p2p/testing @fjl
|
||||
signer/ @holiman
|
||||
|
||||
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.23.0
|
||||
go-version: 1.21.4
|
||||
cache: false
|
||||
- name: Run tests
|
||||
run: go test -short ./...
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -28,14 +28,6 @@ build/_vendor/pkg
|
||||
/build/bin/
|
||||
/geth*.zip
|
||||
|
||||
# used by the build/ci.go archive + upload tool
|
||||
/geth*.tar.gz
|
||||
/geth*.tar.gz.sig
|
||||
/geth*.tar.gz.asc
|
||||
/geth*.zip.sig
|
||||
/geth*.zip.asc
|
||||
|
||||
|
||||
# travis
|
||||
profile.tmp
|
||||
profile.cov
|
||||
|
||||
@@ -60,14 +60,14 @@ issues:
|
||||
- path: crypto/bn256/
|
||||
linters:
|
||||
- revive
|
||||
- path: cmd/utils/flags.go
|
||||
text: "SA1019: cfg.TxLookupLimit is deprecated: use 'TransactionHistory' instead."
|
||||
- path: cmd/utils/flags.go
|
||||
text: "SA1019: ethconfig.Defaults.TxLookupLimit is deprecated: use 'TransactionHistory' instead."
|
||||
- path: internal/build/pgp.go
|
||||
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
|
||||
- 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.'
|
||||
- path: accounts/usbwallet/trezor.go
|
||||
text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.'
|
||||
- path: accounts/usbwallet/trezor/
|
||||
text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.'
|
||||
exclude:
|
||||
- 'SA1019: event.TypeMux is deprecated: use Feed'
|
||||
- 'SA1019: strings.Title is deprecated'
|
||||
|
||||
54
.travis.yml
54
.travis.yml
@@ -9,13 +9,14 @@ jobs:
|
||||
- azure-osx
|
||||
|
||||
include:
|
||||
# This builder create and push the Docker images for all architectures
|
||||
# These builders create the Docker sub-images for multi-arch push and each
|
||||
# will attempt to push the multi-arch image if they are the last builder
|
||||
- stage: build
|
||||
if: type = push
|
||||
os: linux
|
||||
arch: amd64
|
||||
dist: focal
|
||||
go: 1.23.x
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
env:
|
||||
- docker
|
||||
services:
|
||||
@@ -25,15 +26,32 @@ jobs:
|
||||
before_install:
|
||||
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
script:
|
||||
- go run build/ci.go dockerx -platform "linux/amd64,linux/arm64" -upload ethereum/client-go
|
||||
- go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
|
||||
|
||||
- stage: build
|
||||
if: type = push
|
||||
os: linux
|
||||
arch: arm64
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
env:
|
||||
- docker
|
||||
services:
|
||||
- docker
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
before_install:
|
||||
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
script:
|
||||
- go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
|
||||
|
||||
# This builder does the Linux Azure uploads
|
||||
- stage: build
|
||||
if: type = push
|
||||
os: linux
|
||||
dist: focal
|
||||
dist: noble
|
||||
sudo: required
|
||||
go: 1.23.x
|
||||
go: 1.22.x
|
||||
env:
|
||||
- azure-linux
|
||||
git:
|
||||
@@ -45,7 +63,6 @@ jobs:
|
||||
|
||||
# build 386
|
||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib
|
||||
- git status --porcelain
|
||||
- go run build/ci.go install -dlgo -arch 386
|
||||
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
||||
|
||||
@@ -67,13 +84,12 @@ jobs:
|
||||
if: type = push
|
||||
os: osx
|
||||
osx_image: xcode14.2
|
||||
go: 1.23.1 # See https://github.com/ethereum/go-ethereum/pull/30478
|
||||
go: 1.22.x
|
||||
env:
|
||||
- azure-osx
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
script:
|
||||
- ln -sf /Users/travis/gopath/bin/go1.23.1 /usr/local/bin/go # Work around travis go-setup bug
|
||||
- go run build/ci.go install -dlgo
|
||||
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
||||
- go run build/ci.go install -dlgo -arch arm64
|
||||
@@ -84,16 +100,16 @@ jobs:
|
||||
if: type = push
|
||||
os: linux
|
||||
arch: amd64
|
||||
dist: focal
|
||||
go: 1.23.x
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
script:
|
||||
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
||||
|
||||
- stage: build
|
||||
if: type = push
|
||||
os: linux
|
||||
dist: focal
|
||||
go: 1.22.x
|
||||
dist: noble
|
||||
go: 1.21.x
|
||||
script:
|
||||
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
||||
|
||||
@@ -101,8 +117,8 @@ jobs:
|
||||
- stage: build
|
||||
if: type = cron || (type = push && tag ~= /^v[0-9]/)
|
||||
os: linux
|
||||
dist: focal
|
||||
go: 1.23.x
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
env:
|
||||
- ubuntu-ppa
|
||||
git:
|
||||
@@ -117,8 +133,8 @@ jobs:
|
||||
- stage: build
|
||||
if: type = cron
|
||||
os: linux
|
||||
dist: focal
|
||||
go: 1.23.x
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
env:
|
||||
- azure-purge
|
||||
git:
|
||||
@@ -130,8 +146,8 @@ jobs:
|
||||
- stage: build
|
||||
if: type = cron
|
||||
os: linux
|
||||
dist: focal
|
||||
go: 1.23.x
|
||||
dist: noble
|
||||
go: 1.22.x
|
||||
env:
|
||||
- racetests
|
||||
script:
|
||||
|
||||
@@ -4,7 +4,7 @@ ARG VERSION=""
|
||||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.23-alpine AS builder
|
||||
FROM golang:1.22-alpine as builder
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ ARG VERSION=""
|
||||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.23-alpine AS builder
|
||||
FROM golang:1.22-alpine as builder
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
|
||||
@@ -14,13 +14,6 @@ COPY go.sum /go-ethereum/
|
||||
RUN cd /go-ethereum && go mod download
|
||||
|
||||
ADD . /go-ethereum
|
||||
|
||||
# This is not strictly necessary, but it matches the "Dockerfile" steps, thus
|
||||
# makes it so that under certain circumstances, the docker layer can be cached,
|
||||
# and the builder can jump to the next (build all) command, with the go cache fully loaded.
|
||||
#
|
||||
RUN cd /go-ethereum && go run build/ci.go install -static ./cmd/geth
|
||||
|
||||
RUN cd /go-ethereum && go run build/ci.go install -static
|
||||
|
||||
# Pull all binaries into a second stage deploy alpine container
|
||||
|
||||
2
Makefile
2
Makefile
@@ -42,7 +42,7 @@ clean:
|
||||
devtools:
|
||||
env GOBIN= go install golang.org/x/tools/cmd/stringer@latest
|
||||
env GOBIN= go install github.com/fjl/gencodec@latest
|
||||
env GOBIN= go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
env GOBIN= go install github.com/golang/protobuf/protoc-gen-go@latest
|
||||
env GOBIN= go install ./cmd/abigen
|
||||
@type "solc" 2> /dev/null || echo 'Please install solc'
|
||||
@type "protoc" 2> /dev/null || echo 'Please install protoc'
|
||||
|
||||
14
README.md
14
README.md
@@ -16,7 +16,7 @@ archives are published at https://geth.ethereum.org/downloads/.
|
||||
|
||||
For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/getting-started/installing-geth).
|
||||
|
||||
Building `geth` requires both a Go (version 1.22 or later) and a C compiler. You can install
|
||||
Building `geth` requires both a Go (version 1.21 or later) and a C compiler. You can install
|
||||
them using your favourite package manager. Once the dependencies are installed, run
|
||||
|
||||
```shell
|
||||
@@ -89,7 +89,7 @@ This command will:
|
||||
This tool is optional and if you leave it out you can always attach it to an already running
|
||||
`geth` instance with `geth attach`.
|
||||
|
||||
### A Full node on the Holesky test network
|
||||
### A Full node on the Görli test network
|
||||
|
||||
Transitioning towards developers, if you'd like to play around with creating Ethereum
|
||||
contracts, you almost certainly would like to do that without any real money involved until
|
||||
@@ -98,23 +98,23 @@ network, you want to join the **test** network with your node, which is fully eq
|
||||
the main network, but with play-Ether only.
|
||||
|
||||
```shell
|
||||
$ geth --holesky console
|
||||
$ geth --goerli console
|
||||
```
|
||||
|
||||
The `console` subcommand has the same meaning as above and is equally
|
||||
useful on the testnet too.
|
||||
|
||||
Specifying the `--holesky` flag, however, will reconfigure your `geth` instance a bit:
|
||||
Specifying the `--goerli` flag, however, will reconfigure your `geth` instance a bit:
|
||||
|
||||
* Instead of connecting to the main Ethereum network, the client will connect to the Holesky
|
||||
* Instead of connecting to the main Ethereum network, the client will connect to the Görli
|
||||
test network, which uses different P2P bootnodes, different network IDs and genesis
|
||||
states.
|
||||
* Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth`
|
||||
will nest itself one level deeper into a `holesky` subfolder (`~/.ethereum/holesky` on
|
||||
will nest itself one level deeper into a `goerli` subfolder (`~/.ethereum/goerli` on
|
||||
Linux). Note, on OSX and Linux this also means that attaching to a running testnet node
|
||||
requires the use of a custom endpoint since `geth attach` will try to attach to a
|
||||
production node endpoint by default, e.g.,
|
||||
`geth attach <datadir>/holesky/geth.ipc`. Windows users are not affected by
|
||||
`geth attach <datadir>/goerli/geth.ipc`. Windows users are not affected by
|
||||
this.
|
||||
|
||||
*Note: Although some internal protective measures prevent transactions from
|
||||
|
||||
@@ -171,5 +171,5 @@ i4O1UeWKs9owWttan9+PI47ozBSKOTxmMqLSQ0f56Np9FJsV0ilGxRKfjhzJ4KniOMUBA7mP
|
||||
epy6lH7HmxjjOR7eo0DaSxQGQpThAtFGwkWkFh8yki8j3E42kkrxvEyyYZDXn2YcI3bpqhJx
|
||||
PtwCMZUJ3kc/skOrs6bOI19iBNaEoNX5Dllm7UHjOgWNDQkcCuOCxucKano=
|
||||
=arte
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
-----END PGP PUBLIC KEY BLOCK------
|
||||
```
|
||||
|
||||
@@ -1218,10 +1218,3 @@ func TestUnpackRevert(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInternalContractType(t *testing.T) {
|
||||
jsonData := `[{"inputs":[{"components":[{"internalType":"uint256","name":"dailyLimit","type":"uint256"},{"internalType":"uint256","name":"txLimit","type":"uint256"},{"internalType":"uint256","name":"accountDailyLimit","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"},{"internalType":"bool","name":"onlyWhitelisted","type":"bool"}],"internalType":"struct IMessagePassingBridge.BridgeLimits","name":"bridgeLimits","type":"tuple"},{"components":[{"internalType":"uint256","name":"lastTransferReset","type":"uint256"},{"internalType":"uint256","name":"bridged24Hours","type":"uint256"}],"internalType":"struct IMessagePassingBridge.AccountLimit","name":"accountDailyLimit","type":"tuple"},{"components":[{"internalType":"uint256","name":"lastTransferReset","type":"uint256"},{"internalType":"uint256","name":"bridged24Hours","type":"uint256"}],"internalType":"struct IMessagePassingBridge.BridgeDailyLimit","name":"bridgeDailyLimit","type":"tuple"},{"internalType":"contract INameService","name":"nameService","type":"INameService"},{"internalType":"bool","name":"isClosed","type":"bool"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"canBridge","outputs":[{"internalType":"bool","name":"isWithinLimit","type":"bool"},{"internalType":"string","name":"error","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"normalizeFrom18ToTokenDecimals","outputs":[{"internalType":"uint256","name":"normalized","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"normalizeFromTokenTo18Decimals","outputs":[{"internalType":"uint256","name":"normalized","type":"uint256"}],"stateMutability":"pure","type":"function"}]`
|
||||
if _, err := JSON(strings.NewReader(jsonData)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,12 +59,11 @@ type TransactOpts struct {
|
||||
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
|
||||
Signer SignerFn // Method to use for signing the transaction (mandatory)
|
||||
|
||||
Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds)
|
||||
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
|
||||
GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
||||
GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
||||
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
||||
AccessList types.AccessList // Access list to set for the transaction execution (nil = no access list)
|
||||
Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds)
|
||||
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
|
||||
GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
||||
GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
||||
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
||||
|
||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||
|
||||
@@ -301,21 +300,20 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
||||
return nil, err
|
||||
}
|
||||
baseTx := &types.DynamicFeeTx{
|
||||
To: contract,
|
||||
Nonce: nonce,
|
||||
GasFeeCap: gasFeeCap,
|
||||
GasTipCap: gasTipCap,
|
||||
Gas: gasLimit,
|
||||
Value: value,
|
||||
Data: input,
|
||||
AccessList: opts.AccessList,
|
||||
To: contract,
|
||||
Nonce: nonce,
|
||||
GasFeeCap: gasFeeCap,
|
||||
GasTipCap: gasTipCap,
|
||||
Gas: gasLimit,
|
||||
Value: value,
|
||||
Data: input,
|
||||
}
|
||||
return types.NewTx(baseTx), nil
|
||||
}
|
||||
|
||||
func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
||||
if opts.GasFeeCap != nil || opts.GasTipCap != nil || opts.AccessList != nil {
|
||||
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas or accessList specified but london is not active yet")
|
||||
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
|
||||
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
|
||||
}
|
||||
// Normalize value
|
||||
value := opts.Value
|
||||
|
||||
@@ -82,9 +82,7 @@ func TestWaitDeployed(t *testing.T) {
|
||||
}()
|
||||
|
||||
// Send and mine the transaction.
|
||||
if err := backend.Client().SendTransaction(ctx, tx); err != nil {
|
||||
t.Errorf("test %q: failed to send transaction: %v", name, err)
|
||||
}
|
||||
backend.Client().SendTransaction(ctx, tx)
|
||||
backend.Commit()
|
||||
|
||||
select {
|
||||
@@ -118,9 +116,7 @@ func TestWaitDeployedCornerCases(t *testing.T) {
|
||||
tx, _ = types.SignTx(tx, types.LatestSigner(params.AllDevChainProtocolChanges), testKey)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
if err := backend.Client().SendTransaction(ctx, tx); err != nil {
|
||||
t.Errorf("failed to send transaction: %q", err)
|
||||
}
|
||||
backend.Client().SendTransaction(ctx, tx)
|
||||
backend.Commit()
|
||||
notContractCreation := errors.New("tx is not contract creation")
|
||||
if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != notContractCreation.Error() {
|
||||
@@ -138,8 +134,6 @@ func TestWaitDeployedCornerCases(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
if err := backend.Client().SendTransaction(ctx, tx); err != nil {
|
||||
t.Errorf("failed to send transaction: %q", err)
|
||||
}
|
||||
backend.Client().SendTransaction(ctx, tx)
|
||||
cancel()
|
||||
}
|
||||
|
||||
@@ -219,12 +219,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||
typ.T = FunctionTy
|
||||
typ.Size = 24
|
||||
default:
|
||||
if strings.HasPrefix(internalType, "contract ") {
|
||||
typ.Size = 20
|
||||
typ.T = AddressTy
|
||||
} else {
|
||||
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||
}
|
||||
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -114,7 +114,7 @@ func TestWatchNewFile(t *testing.T) {
|
||||
func TestWatchNoDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Create ks but not the directory that it watches.
|
||||
dir := filepath.Join(t.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
|
||||
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
|
||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||
list := ks.Accounts()
|
||||
if len(list) > 0 {
|
||||
@@ -126,6 +126,7 @@ func TestWatchNoDir(t *testing.T) {
|
||||
}
|
||||
// Create the directory and copy a key file into it.
|
||||
os.MkdirAll(dir, 0700)
|
||||
defer os.RemoveAll(dir)
|
||||
file := filepath.Join(dir, "aaa")
|
||||
if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -33,7 +33,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
// ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
// - Download the latest protoc https://github.com/protocolbuffers/protobuf/releases
|
||||
// - Build with the usual `./configure && make` and ensure it's on your $PATH
|
||||
// - Delete all the .proto and .pb.go files, pull in fresh ones from Trezor
|
||||
// - Grab the latest Go plugin `go get -u google.golang.org/protobuf/cmd/protoc-gen-go`
|
||||
// - Vendor in the latest Go plugin `govendor fetch google.golang.org/protobuf/...`
|
||||
// - Grab the latest Go plugin `go get -u github.com/golang/protobuf/protoc-gen-go`
|
||||
// - Vendor in the latest Go plugin `govendor fetch github.com/golang/protobuf/...`
|
||||
|
||||
//go:generate protoc -I/usr/local/include:. --go_out=paths=source_relative:. messages.proto messages-common.proto messages-management.proto messages-ethereum.proto
|
||||
|
||||
@@ -50,7 +50,7 @@ package trezor
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
// Type returns the protocol buffer type number of a specific message. If the
|
||||
|
||||
@@ -70,10 +70,7 @@ func TestBlockSync(t *testing.T) {
|
||||
t.Helper()
|
||||
var expNumber, headNumber uint64
|
||||
if expHead != nil {
|
||||
p, err := expHead.ExecutionPayload()
|
||||
if err != nil {
|
||||
t.Fatalf("expHead.ExecutionPayload() failed: %v", err)
|
||||
}
|
||||
p, _ := expHead.ExecutionPayload()
|
||||
expNumber = p.NumberU64()
|
||||
}
|
||||
select {
|
||||
|
||||
@@ -56,17 +56,32 @@ var (
|
||||
AddFork("DENEB", 132608, []byte{144, 0, 0, 115}),
|
||||
Checkpoint: common.HexToHash("0x1005a6d9175e96bfbce4d35b80f468e9bff0b674e1e861d16e09e10005a58e81"),
|
||||
}
|
||||
|
||||
GoerliConfig = lightClientConfig{
|
||||
ChainConfig: (&types.ChainConfig{
|
||||
GenesisValidatorsRoot: common.HexToHash("0x043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb"),
|
||||
GenesisTime: 1614588812,
|
||||
}).
|
||||
AddFork("GENESIS", 0, []byte{0, 0, 16, 32}).
|
||||
AddFork("ALTAIR", 36660, []byte{1, 0, 16, 32}).
|
||||
AddFork("BELLATRIX", 112260, []byte{2, 0, 16, 32}).
|
||||
AddFork("CAPELLA", 162304, []byte{3, 0, 16, 32}).
|
||||
AddFork("DENEB", 231680, []byte{4, 0, 16, 32}),
|
||||
Checkpoint: common.HexToHash("0x53a0f4f0a378e2c4ae0a9ee97407eb69d0d737d8d8cd0a5fb1093f42f7b81c49"),
|
||||
}
|
||||
)
|
||||
|
||||
func makeChainConfig(ctx *cli.Context) lightClientConfig {
|
||||
var config lightClientConfig
|
||||
customConfig := ctx.IsSet(utils.BeaconConfigFlag.Name)
|
||||
utils.CheckExclusive(ctx, utils.MainnetFlag, utils.SepoliaFlag, utils.BeaconConfigFlag)
|
||||
utils.CheckExclusive(ctx, utils.MainnetFlag, utils.GoerliFlag, utils.SepoliaFlag, utils.BeaconConfigFlag)
|
||||
switch {
|
||||
case ctx.Bool(utils.MainnetFlag.Name):
|
||||
config = MainnetConfig
|
||||
case ctx.Bool(utils.SepoliaFlag.Name):
|
||||
config = SepoliaConfig
|
||||
case ctx.Bool(utils.GoerliFlag.Name):
|
||||
config = GoerliConfig
|
||||
default:
|
||||
if !customConfig {
|
||||
config = MainnetConfig
|
||||
|
||||
@@ -17,25 +17,23 @@ var _ = (*executableDataMarshaling)(nil)
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||
type ExecutableData struct {
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"`
|
||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||
ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||
Deposits types.Deposits `json:"depositRequests"`
|
||||
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"`
|
||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||
ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||
}
|
||||
var enc ExecutableData
|
||||
enc.ParentHash = e.ParentHash
|
||||
@@ -60,33 +58,29 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||
enc.Withdrawals = e.Withdrawals
|
||||
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
||||
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
||||
enc.Deposits = e.Deposits
|
||||
enc.ExecutionWitness = e.ExecutionWitness
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||
type ExecutableData struct {
|
||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot *common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"`
|
||||
Random *common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
|
||||
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||
ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||
Deposits *types.Deposits `json:"depositRequests"`
|
||||
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot *common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"`
|
||||
Random *common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
|
||||
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||
ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||
}
|
||||
var dec ExecutableData
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
@@ -160,11 +154,5 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||
if dec.ExcessBlobGas != nil {
|
||||
e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
||||
}
|
||||
if dec.Deposits != nil {
|
||||
e.Deposits = *dec.Deposits
|
||||
}
|
||||
if dec.ExecutionWitness != nil {
|
||||
e.ExecutionWitness = dec.ExecutionWitness
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,14 +19,12 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) {
|
||||
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||
Override bool `json:"shouldOverrideBuilder"`
|
||||
Witness *hexutil.Bytes `json:"witness"`
|
||||
}
|
||||
var enc ExecutionPayloadEnvelope
|
||||
enc.ExecutionPayload = e.ExecutionPayload
|
||||
enc.BlockValue = (*hexutil.Big)(e.BlockValue)
|
||||
enc.BlobsBundle = e.BlobsBundle
|
||||
enc.Override = e.Override
|
||||
enc.Witness = e.Witness
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
@@ -37,7 +35,6 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
|
||||
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||
Override *bool `json:"shouldOverrideBuilder"`
|
||||
Witness *hexutil.Bytes `json:"witness"`
|
||||
}
|
||||
var dec ExecutionPayloadEnvelope
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
@@ -57,8 +54,5 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
|
||||
if dec.Override != nil {
|
||||
e.Override = *dec.Override
|
||||
}
|
||||
if dec.Witness != nil {
|
||||
e.Witness = dec.Witness
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
@@ -59,25 +58,23 @@ type payloadAttributesMarshaling struct {
|
||||
|
||||
// ExecutableData is the data necessary to execute an EL payload.
|
||||
type ExecutableData struct {
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
LogsBloom []byte `json:"logsBloom" gencodec:"required"`
|
||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
Number uint64 `json:"blockNumber" gencodec:"required"`
|
||||
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
||||
ExtraData []byte `json:"extraData" gencodec:"required"`
|
||||
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||
Deposits types.Deposits `json:"depositRequests"`
|
||||
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
LogsBloom []byte `json:"logsBloom" gencodec:"required"`
|
||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
Number uint64 `json:"blockNumber" gencodec:"required"`
|
||||
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
||||
ExtraData []byte `json:"extraData" gencodec:"required"`
|
||||
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||
}
|
||||
|
||||
// JSON type overrides for executableData.
|
||||
@@ -94,14 +91,6 @@ type executableDataMarshaling struct {
|
||||
ExcessBlobGas *hexutil.Uint64
|
||||
}
|
||||
|
||||
// StatelessPayloadStatusV1 is the result of a stateless payload execution.
|
||||
type StatelessPayloadStatusV1 struct {
|
||||
Status string `json:"status"`
|
||||
StateRoot common.Hash `json:"stateRoot"`
|
||||
ReceiptsRoot common.Hash `json:"receiptsRoot"`
|
||||
ValidationError *string `json:"validationError"`
|
||||
}
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go
|
||||
|
||||
type ExecutionPayloadEnvelope struct {
|
||||
@@ -109,7 +98,6 @@ type ExecutionPayloadEnvelope struct {
|
||||
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
|
||||
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||
Override bool `json:"shouldOverrideBuilder"`
|
||||
Witness *hexutil.Bytes `json:"witness"`
|
||||
}
|
||||
|
||||
type BlobsBundleV1 struct {
|
||||
@@ -124,10 +112,9 @@ type executionPayloadEnvelopeMarshaling struct {
|
||||
}
|
||||
|
||||
type PayloadStatusV1 struct {
|
||||
Status string `json:"status"`
|
||||
Witness *hexutil.Bytes `json:"witness"`
|
||||
LatestValidHash *common.Hash `json:"latestValidHash"`
|
||||
ValidationError *string `json:"validationError"`
|
||||
Status string `json:"status"`
|
||||
LatestValidHash *common.Hash `json:"latestValidHash"`
|
||||
ValidationError *string `json:"validationError"`
|
||||
}
|
||||
|
||||
type TransitionConfigurationV1 struct {
|
||||
@@ -206,35 +193,21 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||
//
|
||||
// and that the blockhash of the constructed block matches the parameters. Nil
|
||||
// Withdrawals value will propagate through the returned block. Empty
|
||||
// Withdrawals value must be passed via non-nil, length 0 value in data.
|
||||
func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
||||
block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot)
|
||||
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
||||
func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
||||
txs, err := decodeTransactions(params.Transactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if block.Hash() != data.BlockHash {
|
||||
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", data.BlockHash, block.Hash())
|
||||
if len(params.ExtraData) > 32 {
|
||||
return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData))
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used
|
||||
// for stateless execution, so it skips checking if the executable data hashes to
|
||||
// the requested hash (stateless has to *compute* the root hash, it's not given).
|
||||
func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
||||
txs, err := decodeTransactions(data.Transactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data.ExtraData) > int(params.MaximumExtraDataSize) {
|
||||
return nil, fmt.Errorf("invalid extradata length: %v", len(data.ExtraData))
|
||||
}
|
||||
if len(data.LogsBloom) != 256 {
|
||||
return nil, fmt.Errorf("invalid logsBloom length: %v", len(data.LogsBloom))
|
||||
if len(params.LogsBloom) != 256 {
|
||||
return nil, fmt.Errorf("invalid logsBloom length: %v", len(params.LogsBloom))
|
||||
}
|
||||
// Check that baseFeePerGas is not negative or too big
|
||||
if data.BaseFeePerGas != nil && (data.BaseFeePerGas.Sign() == -1 || data.BaseFeePerGas.BitLen() > 256) {
|
||||
return nil, fmt.Errorf("invalid baseFeePerGas: %v", data.BaseFeePerGas)
|
||||
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
||||
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
||||
}
|
||||
var blobHashes = make([]common.Hash, 0, len(txs))
|
||||
for _, tx := range txs {
|
||||
@@ -252,73 +225,59 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
|
||||
// ExecutableData before withdrawals are enabled by marshaling
|
||||
// Withdrawals as the json null value.
|
||||
var withdrawalsRoot *common.Hash
|
||||
if data.Withdrawals != nil {
|
||||
h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil))
|
||||
if params.Withdrawals != nil {
|
||||
h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
|
||||
withdrawalsRoot = &h
|
||||
}
|
||||
// Compute requestsHash if any requests are non-nil.
|
||||
var (
|
||||
requestsHash *common.Hash
|
||||
requests types.Requests
|
||||
)
|
||||
if data.Deposits != nil {
|
||||
requests = make(types.Requests, 0)
|
||||
for _, d := range data.Deposits {
|
||||
requests = append(requests, types.NewRequest(d))
|
||||
}
|
||||
h := types.DeriveSha(requests, trie.NewStackTrie(nil))
|
||||
requestsHash = &h
|
||||
}
|
||||
header := &types.Header{
|
||||
ParentHash: data.ParentHash,
|
||||
ParentHash: params.ParentHash,
|
||||
UncleHash: types.EmptyUncleHash,
|
||||
Coinbase: data.FeeRecipient,
|
||||
Root: data.StateRoot,
|
||||
Coinbase: params.FeeRecipient,
|
||||
Root: params.StateRoot,
|
||||
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
||||
ReceiptHash: data.ReceiptsRoot,
|
||||
Bloom: types.BytesToBloom(data.LogsBloom),
|
||||
ReceiptHash: params.ReceiptsRoot,
|
||||
Bloom: types.BytesToBloom(params.LogsBloom),
|
||||
Difficulty: common.Big0,
|
||||
Number: new(big.Int).SetUint64(data.Number),
|
||||
GasLimit: data.GasLimit,
|
||||
GasUsed: data.GasUsed,
|
||||
Time: data.Timestamp,
|
||||
BaseFee: data.BaseFeePerGas,
|
||||
Extra: data.ExtraData,
|
||||
MixDigest: data.Random,
|
||||
Number: new(big.Int).SetUint64(params.Number),
|
||||
GasLimit: params.GasLimit,
|
||||
GasUsed: params.GasUsed,
|
||||
Time: params.Timestamp,
|
||||
BaseFee: params.BaseFeePerGas,
|
||||
Extra: params.ExtraData,
|
||||
MixDigest: params.Random,
|
||||
WithdrawalsHash: withdrawalsRoot,
|
||||
ExcessBlobGas: data.ExcessBlobGas,
|
||||
BlobGasUsed: data.BlobGasUsed,
|
||||
ExcessBlobGas: params.ExcessBlobGas,
|
||||
BlobGasUsed: params.BlobGasUsed,
|
||||
ParentBeaconRoot: beaconRoot,
|
||||
RequestsHash: requestsHash,
|
||||
}
|
||||
return types.NewBlockWithHeader(header).
|
||||
WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals, Requests: requests}).
|
||||
WithWitness(data.ExecutionWitness),
|
||||
nil
|
||||
block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: params.Withdrawals})
|
||||
if block.Hash() != params.BlockHash {
|
||||
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||
// fields from the given block. It assumes the given block is post-merge block.
|
||||
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar) *ExecutionPayloadEnvelope {
|
||||
data := &ExecutableData{
|
||||
BlockHash: block.Hash(),
|
||||
ParentHash: block.ParentHash(),
|
||||
FeeRecipient: block.Coinbase(),
|
||||
StateRoot: block.Root(),
|
||||
Number: block.NumberU64(),
|
||||
GasLimit: block.GasLimit(),
|
||||
GasUsed: block.GasUsed(),
|
||||
BaseFeePerGas: block.BaseFee(),
|
||||
Timestamp: block.Time(),
|
||||
ReceiptsRoot: block.ReceiptHash(),
|
||||
LogsBloom: block.Bloom().Bytes(),
|
||||
Transactions: encodeTransactions(block.Transactions()),
|
||||
Random: block.MixDigest(),
|
||||
ExtraData: block.Extra(),
|
||||
Withdrawals: block.Withdrawals(),
|
||||
BlobGasUsed: block.BlobGasUsed(),
|
||||
ExcessBlobGas: block.ExcessBlobGas(),
|
||||
ExecutionWitness: block.ExecutionWitness(),
|
||||
BlockHash: block.Hash(),
|
||||
ParentHash: block.ParentHash(),
|
||||
FeeRecipient: block.Coinbase(),
|
||||
StateRoot: block.Root(),
|
||||
Number: block.NumberU64(),
|
||||
GasLimit: block.GasLimit(),
|
||||
GasUsed: block.GasUsed(),
|
||||
BaseFeePerGas: block.BaseFee(),
|
||||
Timestamp: block.Time(),
|
||||
ReceiptsRoot: block.ReceiptHash(),
|
||||
LogsBloom: block.Bloom().Bytes(),
|
||||
Transactions: encodeTransactions(block.Transactions()),
|
||||
Random: block.MixDigest(),
|
||||
ExtraData: block.Extra(),
|
||||
Withdrawals: block.Withdrawals(),
|
||||
BlobGasUsed: block.BlobGasUsed(),
|
||||
ExcessBlobGas: block.ExcessBlobGas(),
|
||||
}
|
||||
bundle := BlobsBundleV1{
|
||||
Commitments: make([]hexutil.Bytes, 0),
|
||||
@@ -332,30 +291,13 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
|
||||
bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(sidecar.Proofs[j][:]))
|
||||
}
|
||||
}
|
||||
setRequests(block.Requests(), data)
|
||||
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &bundle, Override: false}
|
||||
}
|
||||
|
||||
// setRequests differentiates the different request types and
|
||||
// assigns them to the associated fields in ExecutableData.
|
||||
func setRequests(requests types.Requests, data *ExecutableData) {
|
||||
if requests != nil {
|
||||
// If requests is non-nil, it means deposits are available in block and we
|
||||
// should return an empty slice instead of nil if there are no deposits.
|
||||
data.Deposits = make(types.Deposits, 0)
|
||||
}
|
||||
for _, r := range requests {
|
||||
if d, ok := r.Inner().(*types.Deposit); ok {
|
||||
data.Deposits = append(data.Deposits, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ExecutionPayloadBody is used in the response to GetPayloadBodiesByHash and GetPayloadBodiesByRange
|
||||
type ExecutionPayloadBody struct {
|
||||
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1
|
||||
type ExecutionPayloadBodyV1 struct {
|
||||
TransactionData []hexutil.Bytes `json:"transactions"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
Deposits types.Deposits `json:"depositRequests"`
|
||||
}
|
||||
|
||||
// Client identifiers to support ClientVersionV1.
|
||||
|
||||
@@ -23,8 +23,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -122,12 +120,8 @@ func NewBeaconLightApi(url string, customHeaders map[string]string) *BeaconLight
|
||||
}
|
||||
}
|
||||
|
||||
func (api *BeaconLightApi) httpGet(path string, params url.Values) ([]byte, error) {
|
||||
uri, err := api.buildURL(path, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
func (api *BeaconLightApi) httpGet(path string) ([]byte, error) {
|
||||
req, err := http.NewRequest("GET", api.url+path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -151,16 +145,17 @@ func (api *BeaconLightApi) httpGet(path string, params url.Values) ([]byte, erro
|
||||
}
|
||||
}
|
||||
|
||||
func (api *BeaconLightApi) httpGetf(format string, params ...any) ([]byte, error) {
|
||||
return api.httpGet(fmt.Sprintf(format, params...))
|
||||
}
|
||||
|
||||
// GetBestUpdatesAndCommittees fetches and validates LightClientUpdate for given
|
||||
// period and full serialized committee for the next period (committee root hash
|
||||
// equals update.NextSyncCommitteeRoot).
|
||||
// Note that the results are validated but the update signature should be verified
|
||||
// by the caller as its validity depends on the update chain.
|
||||
func (api *BeaconLightApi) GetBestUpdatesAndCommittees(firstPeriod, count uint64) ([]*types.LightClientUpdate, []*types.SerializedSyncCommittee, error) {
|
||||
resp, err := api.httpGet("/eth/v1/beacon/light_client/updates", map[string][]string{
|
||||
"start_period": {strconv.FormatUint(firstPeriod, 10)},
|
||||
"count": {strconv.FormatUint(count, 10)},
|
||||
})
|
||||
resp, err := api.httpGetf("/eth/v1/beacon/light_client/updates?start_period=%d&count=%d", firstPeriod, count)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -197,7 +192,7 @@ func (api *BeaconLightApi) GetBestUpdatesAndCommittees(firstPeriod, count uint64
|
||||
// See data structure definition here:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientoptimisticupdate
|
||||
func (api *BeaconLightApi) GetOptimisticUpdate() (types.OptimisticUpdate, error) {
|
||||
resp, err := api.httpGet("/eth/v1/beacon/light_client/optimistic_update", nil)
|
||||
resp, err := api.httpGet("/eth/v1/beacon/light_client/optimistic_update")
|
||||
if err != nil {
|
||||
return types.OptimisticUpdate{}, err
|
||||
}
|
||||
@@ -250,7 +245,7 @@ func decodeOptimisticUpdate(enc []byte) (types.OptimisticUpdate, error) {
|
||||
// See data structure definition here:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientfinalityupdate
|
||||
func (api *BeaconLightApi) GetFinalityUpdate() (types.FinalityUpdate, error) {
|
||||
resp, err := api.httpGet("/eth/v1/beacon/light_client/finality_update", nil)
|
||||
resp, err := api.httpGet("/eth/v1/beacon/light_client/finality_update")
|
||||
if err != nil {
|
||||
return types.FinalityUpdate{}, err
|
||||
}
|
||||
@@ -316,7 +311,7 @@ func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, bool,
|
||||
} else {
|
||||
blockId = blockRoot.Hex()
|
||||
}
|
||||
resp, err := api.httpGet(fmt.Sprintf("/eth/v1/beacon/headers/%s", blockId), nil)
|
||||
resp, err := api.httpGetf("/eth/v1/beacon/headers/%s", blockId)
|
||||
if err != nil {
|
||||
return types.Header{}, false, false, err
|
||||
}
|
||||
@@ -347,7 +342,7 @@ func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, bool,
|
||||
|
||||
// GetCheckpointData fetches and validates bootstrap data belonging to the given checkpoint.
|
||||
func (api *BeaconLightApi) GetCheckpointData(checkpointHash common.Hash) (*types.BootstrapData, error) {
|
||||
resp, err := api.httpGet(fmt.Sprintf("/eth/v1/beacon/light_client/bootstrap/0x%x", checkpointHash[:]), nil)
|
||||
resp, err := api.httpGetf("/eth/v1/beacon/light_client/bootstrap/0x%x", checkpointHash[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -389,7 +384,7 @@ func (api *BeaconLightApi) GetCheckpointData(checkpointHash common.Hash) (*types
|
||||
}
|
||||
|
||||
func (api *BeaconLightApi) GetBeaconBlock(blockRoot common.Hash) (*types.BeaconBlock, error) {
|
||||
resp, err := api.httpGet(fmt.Sprintf("/eth/v2/beacon/blocks/0x%x", blockRoot), nil)
|
||||
resp, err := api.httpGetf("/eth/v2/beacon/blocks/0x%x", blockRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -550,13 +545,9 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
||||
// established. It can only return nil when the context is canceled.
|
||||
func (api *BeaconLightApi) startEventStream(ctx context.Context, listener *HeadEventListener) *eventsource.Stream {
|
||||
for retry := true; retry; retry = ctxSleep(ctx, 5*time.Second) {
|
||||
path := "/eth/v1/events?topics=head&topics=light_client_finality_update&topics=light_client_optimistic_update"
|
||||
log.Trace("Sending event subscription request")
|
||||
uri, err := api.buildURL("/eth/v1/events", map[string][]string{"topics": {"head", "light_client_finality_update", "light_client_optimistic_update"}})
|
||||
if err != nil {
|
||||
listener.OnError(fmt.Errorf("error creating event subscription URL: %v", err))
|
||||
continue
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", uri, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", api.url+path, nil)
|
||||
if err != nil {
|
||||
listener.OnError(fmt.Errorf("error creating event subscription request: %v", err))
|
||||
continue
|
||||
@@ -585,15 +576,3 @@ func ctxSleep(ctx context.Context, timeout time.Duration) (ok bool) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (api *BeaconLightApi) buildURL(path string, params url.Values) (string, error) {
|
||||
uri, err := url.Parse(api.url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
uri = uri.JoinPath(path)
|
||||
if params != nil {
|
||||
uri.RawQuery = params.Encode()
|
||||
}
|
||||
return uri.String(), nil
|
||||
}
|
||||
|
||||
@@ -201,34 +201,6 @@ func TestUpdateSyncDifferentHeads(t *testing.T) {
|
||||
chain.ExpNextSyncPeriod(t, 17)
|
||||
}
|
||||
|
||||
func TestRangeLock(t *testing.T) {
|
||||
r := make(rangeLock)
|
||||
|
||||
// Lock from 0 to 99.
|
||||
r.lock(0, 100, 1)
|
||||
for i := 0; i < 100; i++ {
|
||||
if v, ok := r[uint64(i)]; v <= 0 || !ok {
|
||||
t.Fatalf("integer space: %d not locked", i)
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock from 0 to 99.
|
||||
r.lock(0, 100, -1)
|
||||
for i := 0; i < 100; i++ {
|
||||
if v, ok := r[uint64(i)]; v > 0 || ok {
|
||||
t.Fatalf("integer space: %d is locked", i)
|
||||
}
|
||||
}
|
||||
|
||||
// Lock from 0 to 99 then unlock from 10 to 59.
|
||||
r.lock(0, 100, 1)
|
||||
r.lock(10, 50, -1)
|
||||
first, count := r.firstUnlocked(0, 100)
|
||||
if first != 10 || count != 50 {
|
||||
t.Fatalf("unexpected first: %d or count: %d", first, count)
|
||||
}
|
||||
}
|
||||
|
||||
func testRespUpdate(request requestWithID) request.Response {
|
||||
var resp RespUpdates
|
||||
if request.request == nil {
|
||||
|
||||
@@ -48,7 +48,7 @@ func BlockFromJSON(forkName string, data []byte) (*BeaconBlock, error) {
|
||||
case "capella":
|
||||
obj = new(capella.BeaconBlock)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported fork: %s", forkName)
|
||||
return nil, fmt.Errorf("unsupported fork: " + forkName)
|
||||
}
|
||||
if err := json.Unmarshal(data, obj); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -46,7 +46,7 @@ func ExecutionHeaderFromJSON(forkName string, data []byte) (*ExecutionHeader, er
|
||||
case "deneb":
|
||||
obj = new(deneb.ExecutionPayloadHeader)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported fork: %s", forkName)
|
||||
return nil, fmt.Errorf("unsupported fork: " + forkName)
|
||||
}
|
||||
if err := json.Unmarshal(data, obj); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -5,56 +5,55 @@
|
||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
||||
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
||||
|
||||
# version:golang 1.23.1
|
||||
# version:golang 1.22.4
|
||||
# https://go.dev/dl/
|
||||
6ee44e298379d146a5e5aa6b1c5b5d5f5d0a3365eabdd70741e6e21340ec3b0d go1.23.1.src.tar.gz
|
||||
f17f2791717c15728ec63213a014e244c35f9c8846fb29f5a1b63d0c0556f756 go1.23.1.aix-ppc64.tar.gz
|
||||
dd9e772686ed908bcff94b6144322d4e2473a7dcd7c696b7e8b6d12f23c887fd go1.23.1.darwin-amd64.pkg
|
||||
488d9e4ca3e3ed513ee4edd91bef3a2360c65fa6d6be59cf79640bf840130a58 go1.23.1.darwin-amd64.tar.gz
|
||||
be34b488157ec69d94e26e1554558219a2c90789bcb7e3686965a7f9c8cfcbe7 go1.23.1.darwin-arm64.pkg
|
||||
e223795ca340e285a760a6446ce57a74500b30e57469a4109961d36184d3c05a go1.23.1.darwin-arm64.tar.gz
|
||||
6af626176923a6ae6c5de6dc1c864f38365793c0e4ecd0d6eab847bdc23953e5 go1.23.1.dragonfly-amd64.tar.gz
|
||||
cc957c1a019702e6cdc2e257202d42799011ebc1968b6c3bcd6b1965952607d5 go1.23.1.freebsd-386.tar.gz
|
||||
a7d57781c50bb80886a8f04066791956d45aa3eea0f83070c5268b6223afb2ff go1.23.1.freebsd-amd64.tar.gz
|
||||
c7b09f3fef456048e596db9bea746eb66796aeb82885622b0388feee18f36a3e go1.23.1.freebsd-arm.tar.gz
|
||||
b05cd6a77995a0c8439d88df124811c725fb78b942d0b6dd1643529d7ba62f1f go1.23.1.freebsd-arm64.tar.gz
|
||||
56236ae70be1613f2915943b94f53c96be5bffc0719314078facd778a89bc57e go1.23.1.freebsd-riscv64.tar.gz
|
||||
8644c52df4e831202114fd67c9fcaf1f7233ad27bf945ac53fa7217cf1a0349f go1.23.1.illumos-amd64.tar.gz
|
||||
cdee2f4e2efa001f7ee75c90f2efc310b63346cfbba7b549987e9139527c6b17 go1.23.1.linux-386.tar.gz
|
||||
49bbb517cfa9eee677e1e7897f7cf9cfdbcf49e05f61984a2789136de359f9bd go1.23.1.linux-amd64.tar.gz
|
||||
faec7f7f8ae53fda0f3d408f52182d942cc89ef5b7d3d9f23ff117437d4b2d2f go1.23.1.linux-arm64.tar.gz
|
||||
6c7832c7dcd8fb6d4eb308f672a725393403c74ee7be1aeccd8a443015df99de go1.23.1.linux-armv6l.tar.gz
|
||||
649ce3856ddc808c00b14a46232eab0bf95e7911cdf497010b17d76656f5ca4e go1.23.1.linux-loong64.tar.gz
|
||||
201911048f234e5a0c51ec94b1a11d4e47062fee4398b1d2faa6c820dc026724 go1.23.1.linux-mips.tar.gz
|
||||
2bce3743df463915e45d2612f9476ffb03d0b3750b1cb3879347de08715b5fc6 go1.23.1.linux-mips64.tar.gz
|
||||
54e301f266e33431b0703136e0bbd4cf02461b1ecedd37b7cbd90cb862a98e5f go1.23.1.linux-mips64le.tar.gz
|
||||
8efd495e93d17408c0803595cdc3bf13cb28e0f957aeabd9cc18245fb8e64019 go1.23.1.linux-mipsle.tar.gz
|
||||
52bd68689095831ad9af7160844c23b28bb8d0acd268de7e300ff5f0662b7a07 go1.23.1.linux-ppc64.tar.gz
|
||||
042888cae54b5fbfd9dd1e3b6bc4a5134879777fe6497fc4c62ec394b5ecf2da go1.23.1.linux-ppc64le.tar.gz
|
||||
1a4a609f0391bea202d9095453cbfaf7368fa88a04c206bf9dd715a738664dc3 go1.23.1.linux-riscv64.tar.gz
|
||||
47dc49ad45c45e192efa0df7dc7bc5403f5f2d15b5d0dc74ef3018154b616f4d go1.23.1.linux-s390x.tar.gz
|
||||
fbfbd5efa6a5d581ea7f5e65015f927db0e52135cab057e43d39d5482da54b61 go1.23.1.netbsd-386.tar.gz
|
||||
e96e1cc5cf36113ee6099d1a7306b22cd9c3f975a36bdff954c59f104f22b853 go1.23.1.netbsd-amd64.tar.gz
|
||||
c394dfc06bfc276a591209a37e09cd39089ec9a9cc3db30b94814ce2e39eb1d4 go1.23.1.netbsd-arm.tar.gz
|
||||
b3b35d64f32821a68b3e2994032dbefb81978f2ec3f218c7a770623b82d36b8e go1.23.1.netbsd-arm64.tar.gz
|
||||
3c775c4c16c182e33c2c4ac090d9a247a93b3fb18a3df01d87d490f29599faff go1.23.1.openbsd-386.tar.gz
|
||||
5edbe53b47c57b32707fd7154536fbe9eaa79053fea01650c93b54cdba13fc0f go1.23.1.openbsd-amd64.tar.gz
|
||||
c30903dd8fa98b8aca8e9db0962ce9f55502aed93e0ef41e5ae148aaa0088de1 go1.23.1.openbsd-arm.tar.gz
|
||||
12da183489e58f9c6b357bc1b626f85ed7d4220cab31a49d6a49e6ac6a718b67 go1.23.1.openbsd-arm64.tar.gz
|
||||
9cc9aad37696a4a10c31dcec9e35a308de0b369dad354d54cf07406ac6fa7c6f go1.23.1.openbsd-ppc64.tar.gz
|
||||
e1d740dda062ce5a276a0c3ed7d8b6353238bc8ff405f63e2e3480bfd26a5ec5 go1.23.1.openbsd-riscv64.tar.gz
|
||||
da2a37f9987f01f096859230aa13ecc4ad2e7884465bce91004bc78c64435d65 go1.23.1.plan9-386.tar.gz
|
||||
fd8fff8b0697d55c4a4d02a8dc998192b80a9dc2a057647373d6ff607cad29de go1.23.1.plan9-amd64.tar.gz
|
||||
52efbc5804c1c86ba7868aa8ebbc31cc8c2a27b62a60fd57944970d48fc67525 go1.23.1.plan9-arm.tar.gz
|
||||
f54205f21e2143f2ada1bf1c00ddf64590f5139d5c3fb77cc06175f0d8cc7567 go1.23.1.solaris-amd64.tar.gz
|
||||
369a17f0cfd29e5c848e58ffe0d772da20abe334d1c7ca01dbcd55bb3db0b440 go1.23.1.windows-386.msi
|
||||
ab866f47d7be56e6b1c67f1d529bf4c23331a339fb0785f435a0552d352cb257 go1.23.1.windows-386.zip
|
||||
e99dac215ee437b9bb8f8b14bbfe0e8756882c1ed291f30818e8363bc9c047a5 go1.23.1.windows-amd64.msi
|
||||
32dedf277c86610e380e1765593edb66876f00223df71690bd6be68ee17675c0 go1.23.1.windows-amd64.zip
|
||||
23169c79dc6b54e0dffb25be6b67425ad9759392a58309bc057430a9bf4c8f6a go1.23.1.windows-arm.msi
|
||||
1a57615a09f13534f88e9f2d7efd5743535d1a5719b19e520eef965a634f8efb go1.23.1.windows-arm.zip
|
||||
313e1a543931ad8735b4df8969e00f5f4c2ef07be21f54015ede961a70263d35 go1.23.1.windows-arm64.msi
|
||||
64ad0954d2c33f556fb1018d62de091254aa6e3a94f1c8a8b16af0d3701d194e go1.23.1.windows-arm64.zip
|
||||
fed720678e728a7ca30ba8d1ded1caafe27d16028fab0232b8ba8e22008fb784 go1.22.4.src.tar.gz
|
||||
b9647fa9fc83a0cc5d4f092a19eaeaecf45f063a5aa7d4962fde65aeb7ae6ce1 go1.22.4.aix-ppc64.tar.gz
|
||||
7788f40f3a46f201df1dc46ca640403eb535d5513fc33449164a90dbd229b761 go1.22.4.darwin-amd64.pkg
|
||||
c95967f50aa4ace34af0c236cbdb49a9a3e80ee2ad09d85775cb4462a5c19ed3 go1.22.4.darwin-amd64.tar.gz
|
||||
4036c88faf57a6b096916f1827edcdbf5290a47cc5f59956e88cdd9b1b71088c go1.22.4.darwin-arm64.pkg
|
||||
242b78dc4c8f3d5435d28a0d2cec9b4c1aa999b601fb8aa59fb4e5a1364bf827 go1.22.4.darwin-arm64.tar.gz
|
||||
f2fbb51af4719d3616efb482d6ed2b96579b474156f85a7ddc6f126764feec4b go1.22.4.dragonfly-amd64.tar.gz
|
||||
7c54884bb9f274884651d41e61d1bc12738863ad1497e97ea19ad0e9aa6bf7b5 go1.22.4.freebsd-386.tar.gz
|
||||
88d44500e1701dd35797619774d6dd51bf60f45a8338b0a82ddc018e4e63fb78 go1.22.4.freebsd-amd64.tar.gz
|
||||
3d9efe47db142a22679aba46b1772e3900b0d87ae13bd2b3bc80dbf2ac0b2cd6 go1.22.4.freebsd-arm.tar.gz
|
||||
726dc093cf020277be45debf03c3b02b43c2efb3e2a5d4fba8f52579d65327dc go1.22.4.freebsd-arm64.tar.gz
|
||||
5f6b67e5e32f1d6ccb2d4dcb44934a5e2e870a877ba7443d86ec43cfc28afa71 go1.22.4.freebsd-riscv64.tar.gz
|
||||
d56ecc2f85b6418a21ef83879594d0c42ab4f65391a676bb12254870e6690d63 go1.22.4.illumos-amd64.tar.gz
|
||||
47a2a8d249a91eb8605c33bceec63aedda0441a43eac47b4721e3975ff916cec go1.22.4.linux-386.tar.gz
|
||||
ba79d4526102575196273416239cca418a651e049c2b099f3159db85e7bade7d go1.22.4.linux-amd64.tar.gz
|
||||
a8e177c354d2e4a1b61020aca3562e27ea3e8f8247eca3170e3fa1e0c2f9e771 go1.22.4.linux-arm64.tar.gz
|
||||
e2b143fbacbc9cbd448e9ef41ac3981f0488ce849af1cf37e2341d09670661de go1.22.4.linux-armv6l.tar.gz
|
||||
e2ff9436e4b34bf6926b06d97916e26d67a909a2effec17967245900f0816f1d go1.22.4.linux-loong64.tar.gz
|
||||
73f0dcc60458c4770593b05a7bc01cc0d31fc98f948c0c2334812c7a1f2fc3f1 go1.22.4.linux-mips.tar.gz
|
||||
417af97fc2630a647052375768be4c38adcc5af946352ea5b28613ea81ca5d45 go1.22.4.linux-mips64.tar.gz
|
||||
7486e2d7dd8c98eb44df815ace35a7fe7f30b7c02326e3741bd934077508139b go1.22.4.linux-mips64le.tar.gz
|
||||
69479c8aad301e459a8365b40cad1074a0dbba5defb9291669f94809c4c4be6e go1.22.4.linux-mipsle.tar.gz
|
||||
dd238847e65bc3e2745caca475a5db6522a2fcf85cf6c38fc36a06642b19efd7 go1.22.4.linux-ppc64.tar.gz
|
||||
a3e5834657ef92523f570f798fed42f1f87bc18222a16815ec76b84169649ec4 go1.22.4.linux-ppc64le.tar.gz
|
||||
56a827ff7dc6245bcd7a1e9288dffaa1d8b0fd7468562264c1523daf3b4f1b4a go1.22.4.linux-riscv64.tar.gz
|
||||
7590c3e278e2dc6040aae0a39da3ca1eb2e3921673a7304cc34d588c45889eec go1.22.4.linux-s390x.tar.gz
|
||||
ddd2eebe34471a2502de6c5dad04ab27c9fc80cbde7a9ad5b3c66ecec4504e1d go1.22.4.netbsd-386.tar.gz
|
||||
33af79f6f935f6fbacc5d23876450b3567b79348fc065beef8e64081127dd234 go1.22.4.netbsd-amd64.tar.gz
|
||||
fa3550ebd5375a70b3bcd342b5a71f4bd271dcbbfaf4eabefa2144ab5d8924b6 go1.22.4.netbsd-arm.tar.gz
|
||||
c9a2971dec9f6d320c6f2b049b2353c6d0a2d35e87b8a4b2d78a2f0d62545f8e go1.22.4.netbsd-arm64.tar.gz
|
||||
d21af022331bfdc2b5b161d616c3a1a4573d33cf7a30416ee509a8f3641deb47 go1.22.4.openbsd-386.tar.gz
|
||||
72c0094c43f7e5722ec49c2a3e9dfa7a1123ac43a5f3a63eecf3e3795d3ff0ae go1.22.4.openbsd-amd64.tar.gz
|
||||
1096831ea3c5ea3ca57d14251d9eda3786889531eb40d7d6775dcaa324d4b065 go1.22.4.openbsd-arm.tar.gz
|
||||
a7ab8d4e0b02bf06ed144ba42c61c0e93ee00f2b433415dfd4ad4b6e79f31650 go1.22.4.openbsd-arm64.tar.gz
|
||||
9716327c8a628358798898dc5148c49dbbeb5196bf2cbf088e550721a6e4f60b go1.22.4.openbsd-ppc64.tar.gz
|
||||
a8dd4503c95c32a502a616ab78870a19889c9325fe9bd31eb16dd69346e4bfa8 go1.22.4.plan9-386.tar.gz
|
||||
5423a25808d76fe5aca8607a2e5ac5673abf45446b168cb5e9d8519ee9fe39a1 go1.22.4.plan9-amd64.tar.gz
|
||||
6af939ad583f5c85c09c53728ab7d38c3cc2b39167562d6c18a07c5c6608b370 go1.22.4.plan9-arm.tar.gz
|
||||
e8cabe69c03085725afdb32a6f9998191a3e55a747b270d835fd05000d56abba go1.22.4.solaris-amd64.tar.gz
|
||||
5c6446e2ea80bc6a971d2b34446f16e6517e638b0ff8d3ea229228d1931790b0 go1.22.4.windows-386.msi
|
||||
aca4e2c37278a10f1c70dd0df142f7d66b50334fcee48978d409202d308d6d25 go1.22.4.windows-386.zip
|
||||
3c21105d7b584759b6e266383b777caf6e87142d304a10b539dbc66ab482bb5f go1.22.4.windows-amd64.msi
|
||||
26321c4d945a0035d8a5bc4a1965b0df401ff8ceac66ce2daadabf9030419a98 go1.22.4.windows-amd64.zip
|
||||
c4303f02b864304eb83dd1db0b4ebf9d2ec9d216e7ef44a7657b166a52889c7f go1.22.4.windows-arm.msi
|
||||
5fcd0671a49cecf39b41021621ee1b6e7aa1370f37122b72e80d4fd4185833b6 go1.22.4.windows-arm.zip
|
||||
553cc6c460f4e3eb4fad5b897c0bb22cd8bbeb20929f0e3eeb939420320292ce go1.22.4.windows-arm64.msi
|
||||
8a2daa9ea28cbdafddc6171aefed384f4e5b6e714fb52116fe9ed25a132f37ed go1.22.4.windows-arm64.zip
|
||||
|
||||
# version:golangci 1.59.0
|
||||
# https://github.com/golangci/golangci-lint/releases/
|
||||
|
||||
233
build/ci.go
233
build/ci.go
@@ -50,6 +50,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -125,6 +126,8 @@ var (
|
||||
"focal", // 20.04, EOL: 04/2030
|
||||
"jammy", // 22.04, EOL: 04/2032
|
||||
"noble", // 24.04, EOL: 04/2034
|
||||
|
||||
"mantic", // 23.10, EOL: 07/2024
|
||||
}
|
||||
|
||||
// This is where the tests should be unpacked.
|
||||
@@ -158,8 +161,8 @@ func main() {
|
||||
doLint(os.Args[2:])
|
||||
case "archive":
|
||||
doArchive(os.Args[2:])
|
||||
case "dockerx":
|
||||
doDockerBuildx(os.Args[2:])
|
||||
case "docker":
|
||||
doDocker(os.Args[2:])
|
||||
case "debsrc":
|
||||
doDebianSource(os.Args[2:])
|
||||
case "nsis":
|
||||
@@ -236,10 +239,6 @@ func doInstall(cmdline []string) {
|
||||
// buildFlags returns the go tool flags for building.
|
||||
func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
|
||||
var ld []string
|
||||
// See https://github.com/golang/go/issues/33772#issuecomment-528176001
|
||||
// We need to set --buildid to the linker here, and also pass --build-id to the
|
||||
// cgo-linker further down.
|
||||
ld = append(ld, "--buildid=none")
|
||||
if env.Commit != "" {
|
||||
ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitCommit="+env.Commit)
|
||||
ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitDate="+env.Date)
|
||||
@@ -252,11 +251,7 @@ func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (
|
||||
if runtime.GOOS == "linux" {
|
||||
// Enforce the stacksize to 8M, which is the case on most platforms apart from
|
||||
// alpine Linux.
|
||||
// See https://sourceware.org/binutils/docs-2.23.1/ld/Options.html#Options
|
||||
// regarding the options --build-id=none and --strip-all. It is needed for
|
||||
// reproducible builds; removing references to temporary files in C-land, and
|
||||
// making build-id reproducably absent.
|
||||
extld := []string{"-Wl,-z,stack-size=0x800000,--build-id=none,--strip-all"}
|
||||
extld := []string{"-Wl,-z,stack-size=0x800000"}
|
||||
if staticLinking {
|
||||
extld = append(extld, "-static")
|
||||
// Under static linking, use of certain glibc features must be
|
||||
@@ -303,7 +298,7 @@ func doTest(cmdline []string) {
|
||||
gotest := tc.Go("test")
|
||||
|
||||
// CI needs a bit more time for the statetests (default 10m).
|
||||
gotest.Args = append(gotest.Args, "-timeout=30m")
|
||||
gotest.Args = append(gotest.Args, "-timeout=20m")
|
||||
|
||||
// Enable CKZG backend in CI.
|
||||
gotest.Args = append(gotest.Args, "-tags=ckzg")
|
||||
@@ -354,10 +349,10 @@ func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
|
||||
return filepath.Join(cachedir, base)
|
||||
}
|
||||
|
||||
// hashAllSourceFiles iterates all files under the top-level project directory
|
||||
// hashSourceFiles iterates all files under the top-level project directory
|
||||
// computing the hash of each file (excluding files within the tests
|
||||
// subrepo)
|
||||
func hashAllSourceFiles() (map[string]common.Hash, error) {
|
||||
func hashSourceFiles() (map[string]common.Hash, error) {
|
||||
res := make(map[string]common.Hash)
|
||||
err := filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error {
|
||||
if strings.HasPrefix(path, filepath.FromSlash("tests/testdata")) {
|
||||
@@ -384,56 +379,6 @@ func hashAllSourceFiles() (map[string]common.Hash, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// hashSourceFiles iterates the provided set of filepaths (relative to the top-level geth project directory)
|
||||
// computing the hash of each file.
|
||||
func hashSourceFiles(files []string) (map[string]common.Hash, error) {
|
||||
res := make(map[string]common.Hash)
|
||||
for _, filePath := range files {
|
||||
f, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hasher := sha256.New()
|
||||
if _, err := io.Copy(hasher, f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res[filePath] = common.Hash(hasher.Sum(nil))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// compareHashedFilesets compares two maps (key is relative file path to top-level geth directory, value is its hash)
|
||||
// and returns the list of file paths whose hashes differed.
|
||||
func compareHashedFilesets(preHashes map[string]common.Hash, postHashes map[string]common.Hash) []string {
|
||||
updates := []string{}
|
||||
for path, postHash := range postHashes {
|
||||
preHash, ok := preHashes[path]
|
||||
if !ok || preHash != postHash {
|
||||
updates = append(updates, path)
|
||||
}
|
||||
}
|
||||
return updates
|
||||
}
|
||||
|
||||
func doGoModTidy() {
|
||||
targetFiles := []string{"go.mod", "go.sum"}
|
||||
preHashes, err := hashSourceFiles(targetFiles)
|
||||
if err != nil {
|
||||
log.Fatal("failed to hash go.mod/go.sum", "err", err)
|
||||
}
|
||||
tc := new(build.GoToolchain)
|
||||
c := tc.Go("mod", "tidy")
|
||||
build.MustRun(c)
|
||||
postHashes, err := hashSourceFiles(targetFiles)
|
||||
updates := compareHashedFilesets(preHashes, postHashes)
|
||||
for _, updatedFile := range updates {
|
||||
fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile)
|
||||
}
|
||||
if len(updates) != 0 {
|
||||
log.Fatal("go.sum and/or go.mod were updated by running 'go mod tidy'")
|
||||
}
|
||||
}
|
||||
|
||||
// doGenerate ensures that re-generating generated files does not cause
|
||||
// any mutations in the source file tree: i.e. all generated files were
|
||||
// updated and committed. Any stale generated files are updated.
|
||||
@@ -450,7 +395,7 @@ func doGenerate() {
|
||||
var preHashes map[string]common.Hash
|
||||
if *verify {
|
||||
var err error
|
||||
preHashes, err = hashAllSourceFiles()
|
||||
preHashes, err = hashSourceFiles()
|
||||
if err != nil {
|
||||
log.Fatal("failed to compute map of source hashes", "err", err)
|
||||
}
|
||||
@@ -465,11 +410,17 @@ func doGenerate() {
|
||||
return
|
||||
}
|
||||
// Check if files were changed.
|
||||
postHashes, err := hashAllSourceFiles()
|
||||
postHashes, err := hashSourceFiles()
|
||||
if err != nil {
|
||||
log.Fatal("error computing source tree file hashes", "err", err)
|
||||
}
|
||||
updates := compareHashedFilesets(preHashes, postHashes)
|
||||
updates := []string{}
|
||||
for path, postHash := range postHashes {
|
||||
preHash, ok := preHashes[path]
|
||||
if !ok || preHash != postHash {
|
||||
updates = append(updates, path)
|
||||
}
|
||||
}
|
||||
for _, updatedFile := range updates {
|
||||
fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile)
|
||||
}
|
||||
@@ -492,8 +443,6 @@ func doLint(cmdline []string) {
|
||||
linter := downloadLinter(*cachedir)
|
||||
lflags := []string{"run", "--config", ".golangci.yml"}
|
||||
build.MustRunCommandWithOutput(linter, append(lflags, packages...)...)
|
||||
|
||||
doGoModTidy()
|
||||
fmt.Println("You have achieved perfection.")
|
||||
}
|
||||
|
||||
@@ -722,9 +671,10 @@ func maybeSkipArchive(env build.Environment) {
|
||||
}
|
||||
|
||||
// Builds the docker images and optionally uploads them to Docker Hub.
|
||||
func doDockerBuildx(cmdline []string) {
|
||||
func doDocker(cmdline []string) {
|
||||
var (
|
||||
platform = flag.String("platform", "", `Push a multi-arch docker image for the specified architectures (usually "linux/amd64,linux/arm64")`)
|
||||
image = flag.Bool("image", false, `Whether to build and push an arch specific docker image`)
|
||||
manifest = flag.String("manifest", "", `Push a multi-arch docker image for the specified architectures (usually "amd64,arm64")`)
|
||||
upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
@@ -759,26 +709,129 @@ func doDockerBuildx(cmdline []string) {
|
||||
case strings.HasPrefix(env.Tag, "v1."):
|
||||
tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), "v" + params.Version}
|
||||
}
|
||||
// Need to create a mult-arch builder
|
||||
build.MustRunCommand("docker", "buildx", "create", "--use", "--name", "multi-arch-builder", "--platform", *platform)
|
||||
// If architecture specific image builds are requested, build and push them
|
||||
if *image {
|
||||
build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:TAG", *upload), ".")
|
||||
build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:alltools-TAG", *upload), "-f", "Dockerfile.alltools", ".")
|
||||
|
||||
for _, spec := range []struct {
|
||||
file string
|
||||
base string
|
||||
}{
|
||||
{file: "Dockerfile", base: fmt.Sprintf("%s:", *upload)},
|
||||
{file: "Dockerfile.alltools", base: fmt.Sprintf("%s:alltools-", *upload)},
|
||||
} {
|
||||
for _, tag := range tags { // latest, stable etc
|
||||
gethImage := fmt.Sprintf("%s%s", spec.base, tag)
|
||||
build.MustRunCommand("docker", "buildx", "build",
|
||||
"--build-arg", "COMMIT="+env.Commit,
|
||||
"--build-arg", "VERSION="+params.VersionWithMeta,
|
||||
"--build-arg", "BUILDNUM="+env.Buildnum,
|
||||
"--tag", gethImage,
|
||||
"--platform", *platform,
|
||||
"--push",
|
||||
"--file", spec.file, ".")
|
||||
// Tag and upload the images to Docker Hub
|
||||
for _, tag := range tags {
|
||||
gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, runtime.GOARCH)
|
||||
toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, runtime.GOARCH)
|
||||
|
||||
// If the image already exists (non version tag), check the build
|
||||
// number to prevent overwriting a newer commit if concurrent builds
|
||||
// are running. This is still a tiny bit racey if two published are
|
||||
// done at the same time, but that's extremely unlikely even on the
|
||||
// master branch.
|
||||
for _, img := range []string{gethImage, toolImage} {
|
||||
if exec.Command("docker", "pull", img).Run() != nil {
|
||||
continue // Generally the only failure is a missing image, which is good
|
||||
}
|
||||
buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
|
||||
}
|
||||
buildnum = bytes.TrimSpace(buildnum)
|
||||
|
||||
if len(buildnum) > 0 && len(env.Buildnum) > 0 {
|
||||
oldnum, err := strconv.Atoi(string(buildnum))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse old image build number: %v", err)
|
||||
}
|
||||
newnum, err := strconv.Atoi(env.Buildnum)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse current build number: %v", err)
|
||||
}
|
||||
if oldnum > newnum {
|
||||
log.Fatalf("Current build number %d not newer than existing %d", newnum, oldnum)
|
||||
} else {
|
||||
log.Printf("Updating %s from build %d to %d", img, oldnum, newnum)
|
||||
}
|
||||
}
|
||||
}
|
||||
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:TAG", *upload), gethImage)
|
||||
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:alltools-TAG", *upload), toolImage)
|
||||
build.MustRunCommand("docker", "push", gethImage)
|
||||
build.MustRunCommand("docker", "push", toolImage)
|
||||
}
|
||||
}
|
||||
// If multi-arch image manifest push is requested, assemble it
|
||||
if len(*manifest) != 0 {
|
||||
// Since different architectures are pushed by different builders, wait
|
||||
// until all required images are updated.
|
||||
var mismatch bool
|
||||
for i := 0; i < 2; i++ { // 2 attempts, second is race check
|
||||
mismatch = false // hope there's no mismatch now
|
||||
|
||||
for _, tag := range tags {
|
||||
for _, arch := range strings.Split(*manifest, ",") {
|
||||
gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, arch)
|
||||
toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, arch)
|
||||
|
||||
for _, img := range []string{gethImage, toolImage} {
|
||||
if out, err := exec.Command("docker", "pull", img).CombinedOutput(); err != nil {
|
||||
log.Printf("Required image %s unavailable: %v\nOutput: %s", img, err, out)
|
||||
mismatch = true
|
||||
break
|
||||
}
|
||||
buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
|
||||
}
|
||||
buildnum = bytes.TrimSpace(buildnum)
|
||||
|
||||
if string(buildnum) != env.Buildnum {
|
||||
log.Printf("Build number mismatch on %s: want %s, have %s", img, env.Buildnum, buildnum)
|
||||
mismatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if mismatch {
|
||||
break
|
||||
}
|
||||
}
|
||||
if mismatch {
|
||||
break
|
||||
}
|
||||
}
|
||||
if mismatch {
|
||||
// Build numbers mismatching, retry in a short time to
|
||||
// avoid concurrent fails in both publisher images. If
|
||||
// however the retry failed too, it means the concurrent
|
||||
// builder is still crunching, let that do the publish.
|
||||
if i == 0 {
|
||||
time.Sleep(30 * time.Second)
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if mismatch {
|
||||
log.Println("Relinquishing publish to other builder")
|
||||
return
|
||||
}
|
||||
// Assemble and push the Geth manifest image
|
||||
for _, tag := range tags {
|
||||
gethImage := fmt.Sprintf("%s:%s", *upload, tag)
|
||||
|
||||
var gethSubImages []string
|
||||
for _, arch := range strings.Split(*manifest, ",") {
|
||||
gethSubImages = append(gethSubImages, gethImage+"-"+arch)
|
||||
}
|
||||
build.MustRunCommand("docker", append([]string{"manifest", "create", gethImage}, gethSubImages...)...)
|
||||
build.MustRunCommand("docker", "manifest", "push", gethImage)
|
||||
}
|
||||
// Assemble and push the alltools manifest image
|
||||
for _, tag := range tags {
|
||||
toolImage := fmt.Sprintf("%s:alltools-%s", *upload, tag)
|
||||
|
||||
var toolSubImages []string
|
||||
for _, arch := range strings.Split(*manifest, ",") {
|
||||
toolSubImages = append(toolSubImages, toolImage+"-"+arch)
|
||||
}
|
||||
build.MustRunCommand("docker", append([]string{"manifest", "create", toolImage}, toolSubImages...)...)
|
||||
build.MustRunCommand("docker", "manifest", "push", toolImage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,6 @@ package tools
|
||||
import (
|
||||
// Tool imports for go:generate.
|
||||
_ "github.com/fjl/gencodec"
|
||||
_ "github.com/golang/protobuf/protoc-gen-go"
|
||||
_ "golang.org/x/tools/cmd/stringer"
|
||||
_ "google.golang.org/protobuf/cmd/protoc-gen-go"
|
||||
)
|
||||
|
||||
@@ -45,6 +45,7 @@ func main() {
|
||||
//TODO datadir for optional permanent database
|
||||
utils.MainnetFlag,
|
||||
utils.SepoliaFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.BlsyncApiFlag,
|
||||
utils.BlsyncJWTSecretFlag,
|
||||
},
|
||||
|
||||
@@ -29,7 +29,7 @@ GLOBAL OPTIONS:
|
||||
--loglevel value log level to emit to the screen (default: 4)
|
||||
--keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore")
|
||||
--configdir value Directory for Clef configuration (default: "$HOME/.clef")
|
||||
--chainid value Chain id to use for signing (1=mainnet, 17000=Holesky) (default: 1)
|
||||
--chainid value Chain id to use for signing (1=mainnet, 5=Goerli) (default: 1)
|
||||
--lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength
|
||||
--nousb Disables monitoring for and managing USB hardware wallets
|
||||
--pcscdpath value Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm")
|
||||
|
||||
@@ -27,8 +27,9 @@ import (
|
||||
// TestImportRaw tests clef --importraw
|
||||
func TestImportRaw(t *testing.T) {
|
||||
t.Parallel()
|
||||
keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
||||
keyPath := filepath.Join(os.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
||||
os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777)
|
||||
t.Cleanup(func() { os.Remove(keyPath) })
|
||||
|
||||
t.Run("happy-path", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -67,8 +68,9 @@ func TestImportRaw(t *testing.T) {
|
||||
// TestListAccounts tests clef --list-accounts
|
||||
func TestListAccounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
||||
keyPath := filepath.Join(os.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
||||
os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777)
|
||||
t.Cleanup(func() { os.Remove(keyPath) })
|
||||
|
||||
t.Run("no-accounts", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -95,8 +97,9 @@ func TestListAccounts(t *testing.T) {
|
||||
// TestListWallets tests clef --list-wallets
|
||||
func TestListWallets(t *testing.T) {
|
||||
t.Parallel()
|
||||
keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
||||
keyPath := filepath.Join(os.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
||||
os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777)
|
||||
t.Cleanup(func() { os.Remove(keyPath) })
|
||||
|
||||
t.Run("no-accounts", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -100,7 +100,7 @@ var (
|
||||
chainIdFlag = &cli.Int64Flag{
|
||||
Name: "chainid",
|
||||
Value: params.MainnetChainConfig.ChainID.Int64(),
|
||||
Usage: "Chain id to use for signing (1=mainnet, 17000=Holesky)",
|
||||
Usage: "Chain id to use for signing (1=mainnet, 5=Goerli)",
|
||||
}
|
||||
rpcPortFlag = &cli.IntFlag{
|
||||
Name: "http.port",
|
||||
|
||||
@@ -44,7 +44,7 @@ set to standard output. The following filters are supported:
|
||||
- `-limit <N>` limits the output set to N entries, taking the top N nodes by score
|
||||
- `-ip <CIDR>` filters nodes by IP subnet
|
||||
- `-min-age <duration>` filters nodes by 'first seen' time
|
||||
- `-eth-network <mainnet/sepolia/holesky>` filters nodes by "eth" ENR entry
|
||||
- `-eth-network <mainnet/goerli/sepolia/holesky>` filters nodes by "eth" ENR entry
|
||||
- `-les-server` filters nodes by LES server support
|
||||
- `-snap` filters nodes by snap protocol support
|
||||
|
||||
|
||||
@@ -88,8 +88,7 @@ func (c *cloudflareClient) checkZone(name string) error {
|
||||
if !strings.HasSuffix(name, "."+zone.Name) {
|
||||
return fmt.Errorf("CloudFlare zone name %q does not match name %q to be deployed", zone.Name, name)
|
||||
}
|
||||
// Necessary permissions for Cloudlare management - Zone:Read, DNS:Read, Zone:Edit, DNS:Edit
|
||||
needPerms := map[string]bool{"#zone:edit": false, "#zone:read": false, "#dns_records:read": false, "#dns_records:edit": false}
|
||||
needPerms := map[string]bool{"#zone:edit": false, "#zone:read": false}
|
||||
for _, perm := range zone.Permissions {
|
||||
if _, ok := needPerms[perm]; ok {
|
||||
needPerms[perm] = true
|
||||
|
||||
@@ -849,16 +849,7 @@ func (s *Suite) TestBlobViolations(t *utesting.T) {
|
||||
if code, _, err := conn.Read(); err != nil {
|
||||
t.Fatalf("expected disconnect on blob violation, got err: %v", err)
|
||||
} else if code != discMsg {
|
||||
if code == protoOffset(ethProto)+eth.NewPooledTransactionHashesMsg {
|
||||
// sometimes we'll get a blob transaction hashes announcement before the disconnect
|
||||
// because blob transactions are scheduled to be fetched right away.
|
||||
if code, _, err = conn.Read(); err != nil {
|
||||
t.Fatalf("expected disconnect on blob violation, got err on second read: %v", err)
|
||||
}
|
||||
}
|
||||
if code != discMsg {
|
||||
t.Fatalf("expected disconnect on blob violation, got msg code: %d", code)
|
||||
}
|
||||
t.Fatalf("expected disconnect on blob violation, got msg code: %d", code)
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
)
|
||||
|
||||
func makeJWTSecret(t *testing.T) (string, [32]byte, error) {
|
||||
func makeJWTSecret() (string, [32]byte, error) {
|
||||
var secret [32]byte
|
||||
if _, err := crand.Read(secret[:]); err != nil {
|
||||
return "", secret, fmt.Errorf("failed to create jwt secret: %v", err)
|
||||
}
|
||||
jwtPath := filepath.Join(t.TempDir(), "jwt_secret")
|
||||
jwtPath := filepath.Join(os.TempDir(), "jwt_secret")
|
||||
if err := os.WriteFile(jwtPath, []byte(hexutil.Encode(secret[:])), 0600); err != nil {
|
||||
return "", secret, fmt.Errorf("failed to prepare jwt secret file: %v", err)
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func makeJWTSecret(t *testing.T) (string, [32]byte, error) {
|
||||
}
|
||||
|
||||
func TestEthSuite(t *testing.T) {
|
||||
jwtPath, secret, err := makeJWTSecret(t)
|
||||
jwtPath, secret, err := makeJWTSecret()
|
||||
if err != nil {
|
||||
t.Fatalf("could not make jwt secret: %v", err)
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func TestEthSuite(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSnapSuite(t *testing.T) {
|
||||
jwtPath, secret, err := makeJWTSecret(t)
|
||||
jwtPath, secret, err := makeJWTSecret()
|
||||
if err != nil {
|
||||
t.Fatalf("could not make jwt secret: %v", err)
|
||||
}
|
||||
|
||||
@@ -230,6 +230,8 @@ func ethFilter(args []string) (nodeFilter, error) {
|
||||
switch args[0] {
|
||||
case "mainnet":
|
||||
filter = forkid.NewStaticFilter(params.MainnetChainConfig, core.DefaultGenesisBlock().ToBlock())
|
||||
case "goerli":
|
||||
filter = forkid.NewStaticFilter(params.GoerliChainConfig, core.DefaultGoerliGenesisBlock().ToBlock())
|
||||
case "sepolia":
|
||||
filter = forkid.NewStaticFilter(params.SepoliaChainConfig, core.DefaultSepoliaGenesisBlock().ToBlock())
|
||||
case "holesky":
|
||||
|
||||
@@ -79,7 +79,7 @@ func rlpxPing(ctx *cli.Context) error {
|
||||
n := getNodeArg(ctx)
|
||||
tcpEndpoint, ok := n.TCPEndpoint()
|
||||
if !ok {
|
||||
return errors.New("node has no TCP endpoint")
|
||||
return fmt.Errorf("node has no TCP endpoint")
|
||||
}
|
||||
fd, err := net.Dial("tcp", tcpEndpoint.String())
|
||||
if err != nil {
|
||||
|
||||
@@ -66,8 +66,6 @@ type ExecutionResult struct {
|
||||
WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"`
|
||||
CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"`
|
||||
CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"`
|
||||
RequestsHash *common.Hash `json:"requestsRoot,omitempty"`
|
||||
DepositRequests *types.Deposits `json:"depositRequests,omitempty"`
|
||||
}
|
||||
|
||||
type ommer struct {
|
||||
@@ -198,14 +196,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
|
||||
}
|
||||
if pre.Env.BlockHashes != nil && chainConfig.IsPrague(new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) {
|
||||
var (
|
||||
prevNumber = pre.Env.Number - 1
|
||||
prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)]
|
||||
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
||||
)
|
||||
core.ProcessParentBlockHash(prevHash, evm, statedb)
|
||||
}
|
||||
|
||||
for i := 0; txIt.Next(); i++ {
|
||||
tx, err := txIt.Tx()
|
||||
if err != nil {
|
||||
@@ -379,31 +370,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(&excessBlobGas)
|
||||
execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed)
|
||||
}
|
||||
if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) {
|
||||
// Parse the requests from the logs
|
||||
var allLogs []*types.Log
|
||||
for _, receipt := range receipts {
|
||||
allLogs = append(allLogs, receipt.Logs...)
|
||||
}
|
||||
requests, err := core.ParseDepositLogs(allLogs, chainConfig)
|
||||
if err != nil {
|
||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
|
||||
}
|
||||
// Calculate the requests root
|
||||
h := types.DeriveSha(requests, trie.NewStackTrie(nil))
|
||||
execRs.RequestsHash = &h
|
||||
// Get the deposits from the requests
|
||||
deposits := make(types.Deposits, 0)
|
||||
for _, req := range requests {
|
||||
if dep, ok := req.Inner().(*types.Deposit); ok {
|
||||
deposits = append(deposits, dep)
|
||||
}
|
||||
}
|
||||
execRs.DepositRequests = &deposits
|
||||
}
|
||||
// Re-create statedb instance with new root upon the updated database
|
||||
// for accessing latest states.
|
||||
statedb, err = state.New(root, statedb.Database())
|
||||
statedb, err = state.New(root, statedb.Database(), nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not reopen state: %v", err))
|
||||
}
|
||||
@@ -412,9 +381,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
}
|
||||
|
||||
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB {
|
||||
tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true})
|
||||
sdb := state.NewDatabase(tdb, nil)
|
||||
statedb, _ := state.New(types.EmptyRootHash, sdb)
|
||||
sdb := state.NewDatabaseWithConfig(db, &triedb.Config{Preimages: true})
|
||||
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
|
||||
for addr, a := range accounts {
|
||||
statedb.SetCode(addr, a.Code)
|
||||
statedb.SetNonce(addr, a.Nonce)
|
||||
@@ -425,7 +393,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB
|
||||
}
|
||||
// Commit and re-open to start with a clean state.
|
||||
root, _ := statedb.Commit(0, false)
|
||||
statedb, _ = state.New(root, sdb)
|
||||
statedb, _ = state.New(root, sdb, nil)
|
||||
return statedb
|
||||
}
|
||||
|
||||
|
||||
@@ -155,13 +155,14 @@ func runCmd(ctx *cli.Context) error {
|
||||
})
|
||||
defer triedb.Close()
|
||||
genesis := genesisConfig.MustCommit(db, triedb)
|
||||
sdb := state.NewDatabase(triedb, nil)
|
||||
statedb, _ = state.New(genesis.Root(), sdb)
|
||||
sdb := state.NewDatabaseWithNodeDB(db, triedb)
|
||||
statedb, _ = state.New(genesis.Root(), sdb, nil)
|
||||
chainConfig = genesisConfig.Config
|
||||
|
||||
if ctx.String(SenderFlag.Name) != "" {
|
||||
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
||||
}
|
||||
statedb.CreateAccount(sender)
|
||||
|
||||
if ctx.String(ReceiverFlag.Name) != "" {
|
||||
receiver = common.HexToAddress(ctx.String(ReceiverFlag.Name))
|
||||
@@ -221,7 +222,6 @@ func runCmd(ctx *cli.Context) error {
|
||||
Time: genesisConfig.Timestamp,
|
||||
Coinbase: genesisConfig.Coinbase,
|
||||
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
|
||||
BaseFee: genesisConfig.BaseFee,
|
||||
BlobHashes: blobHashes,
|
||||
BlobBaseFee: blobBaseFee,
|
||||
EVMConfig: vm.Config{
|
||||
@@ -277,7 +277,7 @@ func runCmd(ctx *cli.Context) error {
|
||||
fmt.Printf("Failed to commit changes %v\n", err)
|
||||
return err
|
||||
}
|
||||
dumpdb, err := state.New(root, sdb)
|
||||
dumpdb, err := state.New(root, sdb, nil)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to open statedb %v\n", err)
|
||||
return err
|
||||
|
||||
@@ -107,7 +107,7 @@ func runStateTest(fname string, cfg vm.Config, dump bool) error {
|
||||
result.Root = &root
|
||||
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
|
||||
if dump { // Dump any state to aid debugging
|
||||
cpy, _ := state.New(root, tstate.StateDB.Database())
|
||||
cpy, _ := state.New(root, tstate.StateDB.Database(), nil)
|
||||
dump := cpy.RawDump(nil)
|
||||
result.State = &dump
|
||||
}
|
||||
|
||||
@@ -221,23 +221,22 @@ func initGenesis(ctx *cli.Context) error {
|
||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||
overrides.OverrideVerkle = &v
|
||||
}
|
||||
for _, name := range []string{"chaindata", "lightchaindata"} {
|
||||
chaindb, err := stack.OpenDatabaseWithFreezer(name, 0, 0, ctx.String(utils.AncientFlag.Name), "", false)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
defer chaindb.Close()
|
||||
|
||||
chaindb, err := stack.OpenDatabaseWithFreezer("chaindata", 0, 0, ctx.String(utils.AncientFlag.Name), "", false)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to open database: %v", err)
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
defer triedb.Close()
|
||||
|
||||
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to write genesis block: %v", err)
|
||||
}
|
||||
log.Info("Successfully wrote genesis state", "database", name, "hash", hash)
|
||||
}
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
defer triedb.Close()
|
||||
|
||||
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to write genesis block: %v", err)
|
||||
}
|
||||
|
||||
log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -259,22 +258,29 @@ func dumpGenesis(ctx *cli.Context) error {
|
||||
|
||||
// dump whatever already exists in the datadir
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
for _, name := range []string{"chaindata", "lightchaindata"} {
|
||||
db, err := stack.OpenDatabase(name, 0, 0, "", true)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
genesis, err := core.ReadGenesis(db)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to read genesis: %s", err)
|
||||
}
|
||||
db.Close()
|
||||
|
||||
db, err := stack.OpenDatabase("chaindata", 0, 0, "", true)
|
||||
if err != nil {
|
||||
return err
|
||||
if err := json.NewEncoder(os.Stdout).Encode(*genesis); err != nil {
|
||||
utils.Fatalf("could not encode stored genesis: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
genesis, err = core.ReadGenesis(db)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to read genesis: %s", err)
|
||||
if ctx.IsSet(utils.DataDirFlag.Name) {
|
||||
utils.Fatalf("no existing datadir at %s", stack.Config().DataDir)
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(os.Stdout).Encode(*genesis); err != nil {
|
||||
utils.Fatalf("could not encode stored genesis: %s", err)
|
||||
}
|
||||
|
||||
utils.Fatalf("no network preset provided, and no genesis exists in the default datadir")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -419,6 +425,8 @@ func importHistory(ctx *cli.Context) error {
|
||||
network = "mainnet"
|
||||
case ctx.Bool(utils.SepoliaFlag.Name):
|
||||
network = "sepolia"
|
||||
case ctx.Bool(utils.GoerliFlag.Name):
|
||||
network = "goerli"
|
||||
}
|
||||
} else {
|
||||
// No network flag set, try to determine network based on files
|
||||
@@ -551,7 +559,7 @@ func parseDumpConfig(ctx *cli.Context, db ethdb.Database) (*state.DumpConfig, co
|
||||
default:
|
||||
return nil, common.Hash{}, fmt.Errorf("invalid start argument: %x. 20 or 32 hex-encoded bytes required", startArg)
|
||||
}
|
||||
conf := &state.DumpConfig{
|
||||
var conf = &state.DumpConfig{
|
||||
SkipCode: ctx.Bool(utils.ExcludeCodeFlag.Name),
|
||||
SkipStorage: ctx.Bool(utils.ExcludeStorageFlag.Name),
|
||||
OnlyWithAddresses: !ctx.Bool(utils.IncludeIncompletesFlag.Name),
|
||||
@@ -578,7 +586,7 @@ func dump(ctx *cli.Context) error {
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, true, true, false) // always enable preimage lookup
|
||||
defer triedb.Close()
|
||||
|
||||
state, err := state.New(root, state.NewDatabase(triedb, nil))
|
||||
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -75,8 +75,8 @@ var tomlSettings = toml.Config{
|
||||
},
|
||||
MissingField: func(rt reflect.Type, field string) error {
|
||||
id := fmt.Sprintf("%s.%s", rt.String(), field)
|
||||
if deprecatedConfigFields[id] {
|
||||
log.Warn(fmt.Sprintf("Config field '%s' is deprecated and won't have any effect.", id))
|
||||
if deprecated(id) {
|
||||
log.Warn("Config field is deprecated and won't have an effect", "name", id)
|
||||
return nil
|
||||
}
|
||||
var link string
|
||||
@@ -87,19 +87,6 @@ var tomlSettings = toml.Config{
|
||||
},
|
||||
}
|
||||
|
||||
var deprecatedConfigFields = map[string]bool{
|
||||
"ethconfig.Config.EVMInterpreter": true,
|
||||
"ethconfig.Config.EWASMInterpreter": true,
|
||||
"ethconfig.Config.TrieCleanCacheJournal": true,
|
||||
"ethconfig.Config.TrieCleanCacheRejournal": true,
|
||||
"ethconfig.Config.LightServ": true,
|
||||
"ethconfig.Config.LightIngress": true,
|
||||
"ethconfig.Config.LightEgress": true,
|
||||
"ethconfig.Config.LightPeers": true,
|
||||
"ethconfig.Config.LightNoPrune": true,
|
||||
"ethconfig.Config.LightNoSyncServe": true,
|
||||
}
|
||||
|
||||
type ethstatsConfig struct {
|
||||
URL string `toml:",omitempty"`
|
||||
}
|
||||
@@ -327,6 +314,21 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
func deprecated(field string) bool {
|
||||
switch field {
|
||||
case "ethconfig.Config.EVMInterpreter":
|
||||
return true
|
||||
case "ethconfig.Config.EWASMInterpreter":
|
||||
return true
|
||||
case "ethconfig.Config.TrieCleanCacheJournal":
|
||||
return true
|
||||
case "ethconfig.Config.TrieCleanCacheRejournal":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func setAccountManagerBackends(conf *node.Config, am *accounts.Manager, keydir string) error {
|
||||
scryptN := keystore.StandardScryptN
|
||||
scryptP := keystore.StandardScryptP
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ipcAPIs = "admin:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0"
|
||||
ipcAPIs = "admin:1.0 clique:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0"
|
||||
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
|
||||
)
|
||||
|
||||
@@ -38,10 +38,10 @@ const (
|
||||
// memory and disk IO. If the args don't set --datadir, the
|
||||
// child g gets a temporary data directory.
|
||||
func runMinimalGeth(t *testing.T, args ...string) *testgeth {
|
||||
// --holesky to make the 'writing genesis to disk' faster (no accounts)
|
||||
// --goerli to make the 'writing genesis to disk' faster (no accounts)
|
||||
// --networkid=1337 to avoid cache bump
|
||||
// --syncmode=full to avoid allocating fast sync bloom
|
||||
allArgs := []string{"--holesky", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
|
||||
allArgs := []string{"--goerli", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
|
||||
"--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64",
|
||||
"--datadir.minfreedisk", "0"}
|
||||
return runGeth(t, append(allArgs, args...)...)
|
||||
@@ -62,7 +62,7 @@ func TestConsoleWelcome(t *testing.T) {
|
||||
geth.SetTemplateFunc("gover", runtime.Version)
|
||||
geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
||||
geth.SetTemplateFunc("niltime", func() string {
|
||||
return time.Unix(1695902100, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
||||
return time.Unix(1548854791, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
||||
})
|
||||
geth.SetTemplateFunc("apis", func() string { return ipcAPIs })
|
||||
|
||||
@@ -131,7 +131,7 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
|
||||
attach.SetTemplateFunc("gover", runtime.Version)
|
||||
attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
||||
attach.SetTemplateFunc("niltime", func() string {
|
||||
return time.Unix(1695902100, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
||||
return time.Unix(1548854791, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
||||
})
|
||||
attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
||||
attach.SetTemplateFunc("datadir", func() string { return geth.Datadir })
|
||||
|
||||
@@ -248,8 +248,7 @@ func removeDB(ctx *cli.Context) error {
|
||||
// Delete state data
|
||||
statePaths := []string{
|
||||
rootDir,
|
||||
filepath.Join(ancientDir, rawdb.MerkleStateFreezerName),
|
||||
filepath.Join(ancientDir, rawdb.VerkleStateFreezerName),
|
||||
filepath.Join(ancientDir, rawdb.StateFreezerName),
|
||||
}
|
||||
confirmAndRemoveDB(statePaths, "state data", ctx, removeStateDataFlag.Name)
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ import (
|
||||
// TestExport does a basic test of "geth export", exporting the test-genesis.
|
||||
func TestExport(t *testing.T) {
|
||||
t.Parallel()
|
||||
outfile := fmt.Sprintf("%v/testExport.out", t.TempDir())
|
||||
outfile := fmt.Sprintf("%v/testExport.out", os.TempDir())
|
||||
defer os.Remove(outfile)
|
||||
geth := runGeth(t, "--datadir", initGeth(t), "export", outfile)
|
||||
geth.WaitExit()
|
||||
if have, want := geth.ExitStatus(), 0; have != want {
|
||||
|
||||
@@ -201,8 +201,9 @@ func TestFileOut(t *testing.T) {
|
||||
var (
|
||||
have, want []byte
|
||||
err error
|
||||
path = fmt.Sprintf("%s/test_file_out-%d", t.TempDir(), rand.Int63())
|
||||
path = fmt.Sprintf("%s/test_file_out-%d", os.TempDir(), rand.Int63())
|
||||
)
|
||||
t.Cleanup(func() { os.Remove(path) })
|
||||
if want, err = runSelf(fmt.Sprintf("--log.file=%s", path), "logtest"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -221,8 +222,9 @@ func TestRotatingFileOut(t *testing.T) {
|
||||
var (
|
||||
have, want []byte
|
||||
err error
|
||||
path = fmt.Sprintf("%s/test_file_out-%d", t.TempDir(), rand.Int63())
|
||||
path = fmt.Sprintf("%s/test_file_out-%d", os.TempDir(), rand.Int63())
|
||||
)
|
||||
t.Cleanup(func() { os.Remove(path) })
|
||||
if want, err = runSelf(fmt.Sprintf("--log.file=%s", path), "--log.rotate", "logtest"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -156,6 +156,7 @@ var (
|
||||
utils.BeaconGenesisRootFlag,
|
||||
utils.BeaconGenesisTimeFlag,
|
||||
utils.BeaconCheckpointFlag,
|
||||
utils.CollectWitnessFlag,
|
||||
}, utils.NetworkFlags, utils.DatabaseFlags)
|
||||
|
||||
rpcFlags = []cli.Flag{
|
||||
@@ -288,6 +289,9 @@ func main() {
|
||||
func prepare(ctx *cli.Context) {
|
||||
// If we're running a known preset, log it for convenience.
|
||||
switch {
|
||||
case ctx.IsSet(utils.GoerliFlag.Name):
|
||||
log.Info("Starting Geth on Görli testnet...")
|
||||
|
||||
case ctx.IsSet(utils.SepoliaFlag.Name):
|
||||
log.Info("Starting Geth on Sepolia testnet...")
|
||||
|
||||
@@ -320,6 +324,7 @@ func prepare(ctx *cli.Context) {
|
||||
// Make sure we're not on any supported preconfigured testnet either
|
||||
if !ctx.IsSet(utils.HoleskyFlag.Name) &&
|
||||
!ctx.IsSet(utils.SepoliaFlag.Name) &&
|
||||
!ctx.IsSet(utils.GoerliFlag.Name) &&
|
||||
!ctx.IsSet(utils.DeveloperFlag.Name) {
|
||||
// Nope, we're really on mainnet. Bump that cache up!
|
||||
log.Info("Bumping default cache on mainnet", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 4096)
|
||||
@@ -355,6 +360,8 @@ func geth(ctx *cli.Context) error {
|
||||
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
||||
// miner.
|
||||
func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
|
||||
debug.Memsize.Add("node", stack)
|
||||
|
||||
// Start up the node itself
|
||||
utils.StartNode(ctx, stack, isConsole)
|
||||
|
||||
|
||||
443
cmd/p2psim/main.go
Normal file
443
cmd/p2psim/main.go
Normal file
@@ -0,0 +1,443 @@
|
||||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// p2psim provides a command-line client for a simulation HTTP API.
|
||||
//
|
||||
// Here is an example of creating a 2 node network with the first node
|
||||
// connected to the second:
|
||||
//
|
||||
// $ p2psim node create
|
||||
// Created node01
|
||||
//
|
||||
// $ p2psim node start node01
|
||||
// Started node01
|
||||
//
|
||||
// $ p2psim node create
|
||||
// Created node02
|
||||
//
|
||||
// $ p2psim node start node02
|
||||
// Started node02
|
||||
//
|
||||
// $ p2psim node connect node01 node02
|
||||
// Connected node01 to node02
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var client *simulations.Client
|
||||
|
||||
var (
|
||||
// global command flags
|
||||
apiFlag = &cli.StringFlag{
|
||||
Name: "api",
|
||||
Value: "http://localhost:8888",
|
||||
Usage: "simulation API URL",
|
||||
EnvVars: []string{"P2PSIM_API_URL"},
|
||||
}
|
||||
|
||||
// events subcommand flags
|
||||
currentFlag = &cli.BoolFlag{
|
||||
Name: "current",
|
||||
Usage: "get existing nodes and conns first",
|
||||
}
|
||||
filterFlag = &cli.StringFlag{
|
||||
Name: "filter",
|
||||
Value: "",
|
||||
Usage: "message filter",
|
||||
}
|
||||
|
||||
// node create subcommand flags
|
||||
nameFlag = &cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "",
|
||||
Usage: "node name",
|
||||
}
|
||||
servicesFlag = &cli.StringFlag{
|
||||
Name: "services",
|
||||
Value: "",
|
||||
Usage: "node services (comma separated)",
|
||||
}
|
||||
keyFlag = &cli.StringFlag{
|
||||
Name: "key",
|
||||
Value: "",
|
||||
Usage: "node private key (hex encoded)",
|
||||
}
|
||||
|
||||
// node rpc subcommand flags
|
||||
subscribeFlag = &cli.BoolFlag{
|
||||
Name: "subscribe",
|
||||
Usage: "method is a subscription",
|
||||
}
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := flags.NewApp("devp2p simulation command-line client")
|
||||
app.Flags = []cli.Flag{
|
||||
apiFlag,
|
||||
}
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
client = simulations.NewClient(ctx.String(apiFlag.Name))
|
||||
return nil
|
||||
}
|
||||
app.Commands = []*cli.Command{
|
||||
{
|
||||
Name: "show",
|
||||
Usage: "show network information",
|
||||
Action: showNetwork,
|
||||
},
|
||||
{
|
||||
Name: "events",
|
||||
Usage: "stream network events",
|
||||
Action: streamNetwork,
|
||||
Flags: []cli.Flag{
|
||||
currentFlag,
|
||||
filterFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "snapshot",
|
||||
Usage: "create a network snapshot to stdout",
|
||||
Action: createSnapshot,
|
||||
},
|
||||
{
|
||||
Name: "load",
|
||||
Usage: "load a network snapshot from stdin",
|
||||
Action: loadSnapshot,
|
||||
},
|
||||
{
|
||||
Name: "node",
|
||||
Usage: "manage simulation nodes",
|
||||
Action: listNodes,
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "list",
|
||||
Usage: "list nodes",
|
||||
Action: listNodes,
|
||||
},
|
||||
{
|
||||
Name: "create",
|
||||
Usage: "create a node",
|
||||
Action: createNode,
|
||||
Flags: []cli.Flag{
|
||||
nameFlag,
|
||||
servicesFlag,
|
||||
keyFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "show",
|
||||
ArgsUsage: "<node>",
|
||||
Usage: "show node information",
|
||||
Action: showNode,
|
||||
},
|
||||
{
|
||||
Name: "start",
|
||||
ArgsUsage: "<node>",
|
||||
Usage: "start a node",
|
||||
Action: startNode,
|
||||
},
|
||||
{
|
||||
Name: "stop",
|
||||
ArgsUsage: "<node>",
|
||||
Usage: "stop a node",
|
||||
Action: stopNode,
|
||||
},
|
||||
{
|
||||
Name: "connect",
|
||||
ArgsUsage: "<node> <peer>",
|
||||
Usage: "connect a node to a peer node",
|
||||
Action: connectNode,
|
||||
},
|
||||
{
|
||||
Name: "disconnect",
|
||||
ArgsUsage: "<node> <peer>",
|
||||
Usage: "disconnect a node from a peer node",
|
||||
Action: disconnectNode,
|
||||
},
|
||||
{
|
||||
Name: "rpc",
|
||||
ArgsUsage: "<node> <method> [<args>]",
|
||||
Usage: "call a node RPC method",
|
||||
Action: rpcNode,
|
||||
Flags: []cli.Flag{
|
||||
subscribeFlag,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func showNetwork(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 0 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
network, err := client.GetNetwork()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := tabwriter.NewWriter(ctx.App.Writer, 1, 2, 2, ' ', 0)
|
||||
defer w.Flush()
|
||||
fmt.Fprintf(w, "NODES\t%d\n", len(network.Nodes))
|
||||
fmt.Fprintf(w, "CONNS\t%d\n", len(network.Conns))
|
||||
return nil
|
||||
}
|
||||
|
||||
func streamNetwork(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 0 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
events := make(chan *simulations.Event)
|
||||
sub, err := client.SubscribeNetwork(events, simulations.SubscribeOpts{
|
||||
Current: ctx.Bool(currentFlag.Name),
|
||||
Filter: ctx.String(filterFlag.Name),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sub.Unsubscribe()
|
||||
enc := json.NewEncoder(ctx.App.Writer)
|
||||
for {
|
||||
select {
|
||||
case event := <-events:
|
||||
if err := enc.Encode(event); err != nil {
|
||||
return err
|
||||
}
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createSnapshot(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 0 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
snap, err := client.CreateSnapshot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.NewEncoder(os.Stdout).Encode(snap)
|
||||
}
|
||||
|
||||
func loadSnapshot(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 0 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
snap := &simulations.Snapshot{}
|
||||
if err := json.NewDecoder(os.Stdin).Decode(snap); err != nil {
|
||||
return err
|
||||
}
|
||||
return client.LoadSnapshot(snap)
|
||||
}
|
||||
|
||||
func listNodes(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 0 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
nodes, err := client.GetNodes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := tabwriter.NewWriter(ctx.App.Writer, 1, 2, 2, ' ', 0)
|
||||
defer w.Flush()
|
||||
fmt.Fprintf(w, "NAME\tPROTOCOLS\tID\n")
|
||||
for _, node := range nodes {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", node.Name, strings.Join(protocolList(node), ","), node.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func protocolList(node *p2p.NodeInfo) []string {
|
||||
protos := make([]string, 0, len(node.Protocols))
|
||||
for name := range node.Protocols {
|
||||
protos = append(protos, name)
|
||||
}
|
||||
return protos
|
||||
}
|
||||
|
||||
func createNode(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 0 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
config := adapters.RandomNodeConfig()
|
||||
config.Name = ctx.String(nameFlag.Name)
|
||||
if key := ctx.String(keyFlag.Name); key != "" {
|
||||
privKey, err := crypto.HexToECDSA(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.ID = enode.PubkeyToIDV4(&privKey.PublicKey)
|
||||
config.PrivateKey = privKey
|
||||
}
|
||||
if services := ctx.String(servicesFlag.Name); services != "" {
|
||||
config.Lifecycles = strings.Split(services, ",")
|
||||
}
|
||||
node, err := client.CreateNode(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(ctx.App.Writer, "Created", node.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func showNode(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 1 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
nodeName := ctx.Args().First()
|
||||
node, err := client.GetNode(nodeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := tabwriter.NewWriter(ctx.App.Writer, 1, 2, 2, ' ', 0)
|
||||
defer w.Flush()
|
||||
fmt.Fprintf(w, "NAME\t%s\n", node.Name)
|
||||
fmt.Fprintf(w, "PROTOCOLS\t%s\n", strings.Join(protocolList(node), ","))
|
||||
fmt.Fprintf(w, "ID\t%s\n", node.ID)
|
||||
fmt.Fprintf(w, "ENODE\t%s\n", node.Enode)
|
||||
for name, proto := range node.Protocols {
|
||||
fmt.Fprintln(w)
|
||||
fmt.Fprintf(w, "--- PROTOCOL INFO: %s\n", name)
|
||||
fmt.Fprintf(w, "%v\n", proto)
|
||||
fmt.Fprintf(w, "---\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func startNode(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 1 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
nodeName := ctx.Args().First()
|
||||
if err := client.StartNode(nodeName); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(ctx.App.Writer, "Started", nodeName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopNode(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 1 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
nodeName := ctx.Args().First()
|
||||
if err := client.StopNode(nodeName); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(ctx.App.Writer, "Stopped", nodeName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func connectNode(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 2 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
args := ctx.Args()
|
||||
nodeName := args.Get(0)
|
||||
peerName := args.Get(1)
|
||||
if err := client.ConnectNode(nodeName, peerName); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(ctx.App.Writer, "Connected", nodeName, "to", peerName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func disconnectNode(ctx *cli.Context) error {
|
||||
args := ctx.Args()
|
||||
if args.Len() != 2 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
nodeName := args.Get(0)
|
||||
peerName := args.Get(1)
|
||||
if err := client.DisconnectNode(nodeName, peerName); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(ctx.App.Writer, "Disconnected", nodeName, "from", peerName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func rpcNode(ctx *cli.Context) error {
|
||||
args := ctx.Args()
|
||||
if args.Len() < 2 {
|
||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
}
|
||||
nodeName := args.Get(0)
|
||||
method := args.Get(1)
|
||||
rpcClient, err := client.RPCClient(context.Background(), nodeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.Bool(subscribeFlag.Name) {
|
||||
return rpcSubscribe(rpcClient, ctx.App.Writer, method, args.Slice()[3:]...)
|
||||
}
|
||||
var result interface{}
|
||||
params := make([]interface{}, len(args.Slice()[3:]))
|
||||
for i, v := range args.Slice()[3:] {
|
||||
params[i] = v
|
||||
}
|
||||
if err := rpcClient.Call(&result, method, params...); err != nil {
|
||||
return err
|
||||
}
|
||||
return json.NewEncoder(ctx.App.Writer).Encode(result)
|
||||
}
|
||||
|
||||
func rpcSubscribe(client *rpc.Client, out io.Writer, method string, args ...string) error {
|
||||
namespace, method, _ := strings.Cut(method, "_")
|
||||
ch := make(chan interface{})
|
||||
subArgs := make([]interface{}, len(args)+1)
|
||||
subArgs[0] = method
|
||||
for i, v := range args {
|
||||
subArgs[i+1] = v
|
||||
}
|
||||
sub, err := client.Subscribe(context.Background(), namespace, ch, subArgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sub.Unsubscribe()
|
||||
enc := json.NewEncoder(out)
|
||||
for {
|
||||
select {
|
||||
case v := <-ch:
|
||||
if err := enc.Encode(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,6 +262,7 @@ func ImportHistory(chain *core.BlockChain, db ethdb.Database, dir string, networ
|
||||
start = time.Now()
|
||||
reported = time.Now()
|
||||
imported = 0
|
||||
forker = core.NewForkChoice(chain, nil)
|
||||
h = sha256.New()
|
||||
buf = bytes.NewBuffer(nil)
|
||||
)
|
||||
@@ -304,7 +305,7 @@ func ImportHistory(chain *core.BlockChain, db ethdb.Database, dir string, networ
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
||||
}
|
||||
if status, err := chain.HeaderChain().InsertHeaderChain([]*types.Header{block.Header()}, start); err != nil {
|
||||
if status, err := chain.HeaderChain().InsertHeaderChain([]*types.Header{block.Header()}, start, forker); 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)
|
||||
|
||||
@@ -29,12 +29,18 @@ import (
|
||||
|
||||
// TestExport does basic sanity checks on the export/import functionality
|
||||
func TestExport(t *testing.T) {
|
||||
f := fmt.Sprintf("%v/tempdump", t.TempDir())
|
||||
f := fmt.Sprintf("%v/tempdump", os.TempDir())
|
||||
defer func() {
|
||||
os.Remove(f)
|
||||
}()
|
||||
testExport(t, f)
|
||||
}
|
||||
|
||||
func TestExportGzip(t *testing.T) {
|
||||
f := fmt.Sprintf("%v/tempdump.gz", t.TempDir())
|
||||
f := fmt.Sprintf("%v/tempdump.gz", os.TempDir())
|
||||
defer func() {
|
||||
os.Remove(f)
|
||||
}()
|
||||
testExport(t, f)
|
||||
}
|
||||
|
||||
@@ -93,14 +99,20 @@ func testExport(t *testing.T, f string) {
|
||||
|
||||
// TestDeletionExport tests if the deletion markers can be exported/imported correctly
|
||||
func TestDeletionExport(t *testing.T) {
|
||||
f := fmt.Sprintf("%v/tempdump", t.TempDir())
|
||||
f := fmt.Sprintf("%v/tempdump", os.TempDir())
|
||||
defer func() {
|
||||
os.Remove(f)
|
||||
}()
|
||||
testDeletion(t, f)
|
||||
}
|
||||
|
||||
// TestDeletionExportGzip tests if the deletion markers can be exported/imported
|
||||
// correctly with gz compression.
|
||||
func TestDeletionExportGzip(t *testing.T) {
|
||||
f := fmt.Sprintf("%v/tempdump.gz", t.TempDir())
|
||||
f := fmt.Sprintf("%v/tempdump.gz", os.TempDir())
|
||||
defer func() {
|
||||
os.Remove(f)
|
||||
}()
|
||||
testDeletion(t, f)
|
||||
}
|
||||
|
||||
@@ -159,7 +171,10 @@ func testDeletion(t *testing.T, f string) {
|
||||
// TestImportFutureFormat tests that we reject unsupported future versions.
|
||||
func TestImportFutureFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
f := fmt.Sprintf("%v/tempdump-future", t.TempDir())
|
||||
f := fmt.Sprintf("%v/tempdump-future", os.TempDir())
|
||||
defer func() {
|
||||
os.Remove(f)
|
||||
}()
|
||||
fh, err := os.OpenFile(f, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -42,7 +42,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/fdlimit"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/txpool/blobpool"
|
||||
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -134,7 +133,7 @@ var (
|
||||
}
|
||||
NetworkIdFlag = &cli.Uint64Flag{
|
||||
Name: "networkid",
|
||||
Usage: "Explicitly set network id (integer)(For testnets: use --sepolia, --holesky instead)",
|
||||
Usage: "Explicitly set network id (integer)(For testnets: use --goerli, --sepolia, --holesky instead)",
|
||||
Value: ethconfig.Defaults.NetworkId,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
@@ -143,6 +142,11 @@ var (
|
||||
Usage: "Ethereum mainnet",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
GoerliFlag = &cli.BoolFlag{
|
||||
Name: "goerli",
|
||||
Usage: "Görli network: pre-configured proof-of-authority test network",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
SepoliaFlag = &cli.BoolFlag{
|
||||
Name: "sepolia",
|
||||
Usage: "Sepolia network: pre-configured proof-of-work test network",
|
||||
@@ -287,7 +291,7 @@ var (
|
||||
}
|
||||
BeaconApiHeaderFlag = &cli.StringSliceFlag{
|
||||
Name: "beacon.api.header",
|
||||
Usage: "Pass custom HTTP header fields to the remote beacon node API in \"key:value\" format. This flag can be given multiple times.",
|
||||
Usage: "Pass custom HTTP header fields to the emote beacon node API in \"key:value\" format. This flag can be given multiple times.",
|
||||
Category: flags.BeaconCategory,
|
||||
}
|
||||
BeaconThresholdFlag = &cli.IntFlag{
|
||||
@@ -600,6 +604,11 @@ var (
|
||||
Usage: "Disables db compaction after import",
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
CollectWitnessFlag = &cli.BoolFlag{
|
||||
Name: "collectwitness",
|
||||
Usage: "Enable state witness generation during block execution. Work in progress flag, don't use.",
|
||||
Category: flags.MiscCategory,
|
||||
}
|
||||
|
||||
// MISC settings
|
||||
SyncTargetFlag = &cli.StringFlag{
|
||||
@@ -802,9 +811,8 @@ var (
|
||||
DiscoveryV5Flag = &cli.BoolFlag{
|
||||
Name: "discovery.v5",
|
||||
Aliases: []string{"discv5"},
|
||||
Usage: "Enables the V5 discovery mechanism",
|
||||
Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism",
|
||||
Category: flags.NetworkingCategory,
|
||||
Value: true,
|
||||
}
|
||||
NetrestrictFlag = &cli.StringFlag{
|
||||
Name: "netrestrict",
|
||||
@@ -956,6 +964,7 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
||||
var (
|
||||
// TestnetFlags is the flag group of all built-in supported testnets.
|
||||
TestnetFlags = []cli.Flag{
|
||||
GoerliFlag,
|
||||
SepoliaFlag,
|
||||
HoleskyFlag,
|
||||
}
|
||||
@@ -978,6 +987,9 @@ var (
|
||||
// then a subdirectory of the specified datadir will be used.
|
||||
func MakeDataDir(ctx *cli.Context) string {
|
||||
if path := ctx.String(DataDirFlag.Name); path != "" {
|
||||
if ctx.Bool(GoerliFlag.Name) {
|
||||
return filepath.Join(path, "goerli")
|
||||
}
|
||||
if ctx.Bool(SepoliaFlag.Name) {
|
||||
return filepath.Join(path, "sepolia")
|
||||
}
|
||||
@@ -1029,7 +1041,7 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
|
||||
//
|
||||
// 1. --bootnodes flag
|
||||
// 2. Config file
|
||||
// 3. Network preset flags (e.g. --holesky)
|
||||
// 3. Network preset flags (e.g. --goerli)
|
||||
// 4. default to mainnet nodes
|
||||
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||
urls := params.MainnetBootnodes
|
||||
@@ -1044,6 +1056,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||
urls = params.HoleskyBootnodes
|
||||
case ctx.Bool(SepoliaFlag.Name):
|
||||
urls = params.SepoliaBootnodes
|
||||
case ctx.Bool(GoerliFlag.Name):
|
||||
urls = params.GoerliBootnodes
|
||||
}
|
||||
}
|
||||
cfg.BootstrapNodes = mustParseBootnodes(urls)
|
||||
@@ -1307,6 +1321,7 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error
|
||||
func setEtherbase(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||
if ctx.IsSet(MinerEtherbaseFlag.Name) {
|
||||
log.Warn("Option --miner.etherbase is deprecated as the etherbase is set by the consensus client post-merge")
|
||||
return
|
||||
}
|
||||
if !ctx.IsSet(MinerPendingFeeRecipientFlag.Name) {
|
||||
return
|
||||
@@ -1468,6 +1483,8 @@ func SetDataDir(ctx *cli.Context, cfg *node.Config) {
|
||||
cfg.DataDir = ctx.String(DataDirFlag.Name)
|
||||
case ctx.Bool(DeveloperFlag.Name):
|
||||
cfg.DataDir = "" // unless explicitly requested, use memory databases
|
||||
case ctx.Bool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
|
||||
case ctx.Bool(SepoliaFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia")
|
||||
case ctx.Bool(HoleskyFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||
@@ -1533,18 +1550,6 @@ func setTxPool(ctx *cli.Context, cfg *legacypool.Config) {
|
||||
}
|
||||
}
|
||||
|
||||
func setBlobPool(ctx *cli.Context, cfg *blobpool.Config) {
|
||||
if ctx.IsSet(BlobPoolDataDirFlag.Name) {
|
||||
cfg.Datadir = ctx.String(BlobPoolDataDirFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(BlobPoolDataCapFlag.Name) {
|
||||
cfg.Datacap = ctx.Uint64(BlobPoolDataCapFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(BlobPoolPriceBumpFlag.Name) {
|
||||
cfg.PriceBump = ctx.Uint64(BlobPoolPriceBumpFlag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func setMiner(ctx *cli.Context, cfg *miner.Config) {
|
||||
if ctx.Bool(MiningEnabledFlag.Name) {
|
||||
log.Warn("The flag --mine is deprecated and will be removed")
|
||||
@@ -1639,14 +1644,13 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
|
||||
// SetEthConfig applies eth-related command line flags to the config.
|
||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
// Avoid conflicting network flags
|
||||
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag)
|
||||
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag, HoleskyFlag)
|
||||
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||
|
||||
// Set configurations from CLI flags
|
||||
setEtherbase(ctx, cfg)
|
||||
setGPO(ctx, &cfg.GPO)
|
||||
setTxPool(ctx, &cfg.TxPool)
|
||||
setBlobPool(ctx, &cfg.BlobPool)
|
||||
setMiner(ctx, &cfg.Miner)
|
||||
setRequiredBlocks(ctx, cfg)
|
||||
setLes(ctx, cfg)
|
||||
@@ -1761,6 +1765,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
// TODO(fjl): force-enable this in --dev mode
|
||||
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(CollectWitnessFlag.Name) {
|
||||
cfg.EnableWitnessCollection = ctx.Bool(CollectWitnessFlag.Name)
|
||||
}
|
||||
|
||||
if ctx.IsSet(RPCGlobalGasCapFlag.Name) {
|
||||
cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name)
|
||||
@@ -1806,6 +1813,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
}
|
||||
cfg.Genesis = core.DefaultSepoliaGenesisBlock()
|
||||
SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash)
|
||||
case ctx.Bool(GoerliFlag.Name):
|
||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 5
|
||||
}
|
||||
cfg.Genesis = core.DefaultGoerliGenesisBlock()
|
||||
SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash)
|
||||
case ctx.Bool(DeveloperFlag.Name):
|
||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 1337
|
||||
@@ -1925,7 +1938,7 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) {
|
||||
|
||||
// RegisterEthService adds an Ethereum client to the stack.
|
||||
// The second return value is the full node instance.
|
||||
func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (*eth.EthAPIBackend, *eth.Ethereum) {
|
||||
func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend, *eth.Ethereum) {
|
||||
backend, err := eth.New(stack, cfg)
|
||||
if err != nil {
|
||||
Fatalf("Failed to register the Ethereum service: %v", err)
|
||||
@@ -1935,7 +1948,7 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (*eth.EthAPIBac
|
||||
}
|
||||
|
||||
// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to the node.
|
||||
func RegisterEthStatsService(stack *node.Node, backend *eth.EthAPIBackend, url string) {
|
||||
func RegisterEthStatsService(stack *node.Node, backend ethapi.Backend, url string) {
|
||||
if err := ethstats.New(stack, backend, backend.Engine(), url); err != nil {
|
||||
Fatalf("Failed to register the Ethereum Stats service: %v", err)
|
||||
}
|
||||
@@ -2061,6 +2074,8 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
|
||||
break
|
||||
}
|
||||
chainDb = remotedb.New(client)
|
||||
case ctx.String(SyncModeFlag.Name) == "light":
|
||||
chainDb, err = stack.OpenDatabase("lightchaindata", cache, handles, "", readonly)
|
||||
default:
|
||||
chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "", readonly)
|
||||
}
|
||||
@@ -2125,6 +2140,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
||||
genesis = core.DefaultHoleskyGenesisBlock()
|
||||
case ctx.Bool(SepoliaFlag.Name):
|
||||
genesis = core.DefaultSepoliaGenesisBlock()
|
||||
case ctx.Bool(GoerliFlag.Name):
|
||||
genesis = core.DefaultGoerliGenesisBlock()
|
||||
case ctx.Bool(DeveloperFlag.Name):
|
||||
Fatalf("Developer chains are ephemeral")
|
||||
}
|
||||
@@ -2183,6 +2200,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
||||
}
|
||||
vmcfg := vm.Config{
|
||||
EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name),
|
||||
EnableWitnessCollection: ctx.Bool(CollectWitnessFlag.Name),
|
||||
}
|
||||
if ctx.IsSet(VMTraceFlag.Name) {
|
||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||
@@ -2198,7 +2216,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
||||
}
|
||||
}
|
||||
// Disable transaction indexing/unindexing by default.
|
||||
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil)
|
||||
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil, nil)
|
||||
if err != nil {
|
||||
Fatalf("Can't create BlockChain: %v", err)
|
||||
}
|
||||
|
||||
@@ -92,21 +92,25 @@ var (
|
||||
LightServeFlag = &cli.IntFlag{
|
||||
Name: "light.serve",
|
||||
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated)",
|
||||
Value: ethconfig.Defaults.LightServ,
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
LightIngressFlag = &cli.IntFlag{
|
||||
Name: "light.ingress",
|
||||
Usage: "Incoming bandwidth limit for serving light clients (deprecated)",
|
||||
Value: ethconfig.Defaults.LightIngress,
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
LightEgressFlag = &cli.IntFlag{
|
||||
Name: "light.egress",
|
||||
Usage: "Outgoing bandwidth limit for serving light clients (deprecated)",
|
||||
Value: ethconfig.Defaults.LightEgress,
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
LightMaxPeersFlag = &cli.IntFlag{
|
||||
Name: "light.maxpeers",
|
||||
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated)",
|
||||
Value: ethconfig.Defaults.LightPeers,
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
LightNoPruneFlag = &cli.BoolFlag{
|
||||
|
||||
@@ -78,7 +78,7 @@ func TestHistoryImportAndExport(t *testing.T) {
|
||||
})
|
||||
|
||||
// Initialize BlockChain.
|
||||
chain, err := core.NewBlockChain(db, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
chain, err := core.NewBlockChain(db, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize chain: %v", err)
|
||||
}
|
||||
@@ -171,7 +171,7 @@ func TestHistoryImportAndExport(t *testing.T) {
|
||||
})
|
||||
|
||||
genesis.MustCommit(db2, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||
imported, err := core.NewBlockChain(db2, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
imported, err := core.NewBlockChain(db2, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize chain: %v", err)
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Blo
|
||||
// (c) the extradata is limited to 32 bytes
|
||||
func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error {
|
||||
// Ensure that the header's extra-data section is of a reasonable size
|
||||
if len(header.Extra) > int(params.MaximumExtraDataSize) {
|
||||
if len(header.Extra) > 32 {
|
||||
return fmt.Errorf("extra-data longer than 32 bytes (%d)", len(header.Extra))
|
||||
}
|
||||
// Verify the seal parts. Ensure the nonce and uncle hash are the expected value.
|
||||
@@ -387,39 +387,8 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
||||
// Assign the final state root to header.
|
||||
header.Root = state.IntermediateRoot(true)
|
||||
|
||||
// Assemble the final block.
|
||||
block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
|
||||
|
||||
// Create the block witness and attach to block.
|
||||
// This step needs to happen as late as possible to catch all access events.
|
||||
if chain.Config().IsVerkle(header.Number, header.Time) {
|
||||
keys := state.AccessEvents().Keys()
|
||||
|
||||
// Open the pre-tree to prove the pre-state against
|
||||
parent := chain.GetHeaderByNumber(header.Number.Uint64() - 1)
|
||||
if parent == nil {
|
||||
return nil, fmt.Errorf("nil parent header for block %d", header.Number)
|
||||
}
|
||||
|
||||
preTrie, err := state.Database().OpenTrie(parent.Root)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error opening pre-state tree root: %w", err)
|
||||
}
|
||||
|
||||
vktPreTrie, okpre := preTrie.(*trie.VerkleTrie)
|
||||
vktPostTrie, okpost := state.GetTrie().(*trie.VerkleTrie)
|
||||
if okpre && okpost {
|
||||
if len(keys) > 0 {
|
||||
verkleProof, stateDiff, err := vktPreTrie.Proof(vktPostTrie, keys, vktPreTrie.FlatdbNodeResolver)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err)
|
||||
}
|
||||
block = block.WithWitness(&types.ExecutionWitness{StateDiff: stateDiff, VerkleProof: verkleProof})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return block, nil
|
||||
// Assemble and return the final block.
|
||||
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), nil
|
||||
}
|
||||
|
||||
// Seal generates a new sealing request for the given input block and pushes
|
||||
|
||||
@@ -30,11 +30,11 @@ import (
|
||||
)
|
||||
|
||||
// This test case is a repro of an annoying bug that took us forever to catch.
|
||||
// In Clique PoA networks, consecutive blocks might have the same state root (no
|
||||
// block subsidy, empty block). If a node crashes, the chain ends up losing the
|
||||
// recent state and needs to regenerate it from blocks already in the database.
|
||||
// The bug was that processing the block *prior* to an empty one **also
|
||||
// completes** the empty one, ending up in a known-block error.
|
||||
// In Clique PoA networks (Görli, etc), consecutive blocks might have
|
||||
// the same state root (no block subsidy, empty block). If a node crashes, the
|
||||
// chain ends up losing the recent state and needs to regenerate it from blocks
|
||||
// already in the database. The bug was that processing the block *prior* to an
|
||||
// empty one **also completes** the empty one, ending up in a known-block error.
|
||||
func TestReimportMirroredState(t *testing.T) {
|
||||
// Initialize a Clique chain with a single signer
|
||||
var (
|
||||
@@ -55,7 +55,7 @@ func TestReimportMirroredState(t *testing.T) {
|
||||
copy(genspec.ExtraData[extraVanity:], addr[:])
|
||||
|
||||
// Generate a batch of blocks, each properly signed
|
||||
chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genspec, nil, engine, vm.Config{}, nil)
|
||||
chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genspec, nil, engine, vm.Config{}, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
_, blocks, _ := core.GenerateChainWithGenesis(genspec, engine, 3, func(i int, block *core.BlockGen) {
|
||||
@@ -87,7 +87,7 @@ func TestReimportMirroredState(t *testing.T) {
|
||||
}
|
||||
// Insert the first two blocks and make sure the chain is valid
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
chain, _ = core.NewBlockChain(db, nil, genspec, nil, engine, vm.Config{}, nil)
|
||||
chain, _ = core.NewBlockChain(db, nil, genspec, nil, engine, vm.Config{}, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
if _, err := chain.InsertChain(blocks[:2]); err != nil {
|
||||
@@ -100,7 +100,7 @@ func TestReimportMirroredState(t *testing.T) {
|
||||
// Simulate a crash by creating a new chain on top of the database, without
|
||||
// flushing the dirty states out. Insert the last block, triggering a sidechain
|
||||
// reimport.
|
||||
chain, _ = core.NewBlockChain(db, nil, genspec, nil, engine, vm.Config{}, nil)
|
||||
chain, _ = core.NewBlockChain(db, nil, genspec, nil, engine, vm.Config{}, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
if _, err := chain.InsertChain(blocks[2:]); err != nil {
|
||||
|
||||
@@ -458,7 +458,7 @@ func (tt *cliqueTest) run(t *testing.T) {
|
||||
batches[len(batches)-1] = append(batches[len(batches)-1], block)
|
||||
}
|
||||
// Pass all the headers through clique and ensure tallying succeeds
|
||||
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genesis, nil, engine, vm.Config{}, nil)
|
||||
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test chain: %v", err)
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||
|
||||
// Time the insertion of the new chain.
|
||||
// State and blocks are stored in the same DB.
|
||||
chainman, _ := NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
chainman, _ := NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer chainman.Stop()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
@@ -312,7 +312,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||
if err != nil {
|
||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||
}
|
||||
chain, err := NewBlockChain(db, &cacheConfig, genesis, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(db, &cacheConfig, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("error creating chain: %v", err)
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/stateless"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
@@ -119,17 +121,14 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||
|
||||
// ValidateState validates the various changes that happen after a state transition,
|
||||
// such as amount of used gas, the receipt roots and the state root itself.
|
||||
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, res *ProcessResult, stateless bool) error {
|
||||
if res == nil {
|
||||
return fmt.Errorf("nil ProcessResult value")
|
||||
}
|
||||
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, stateless bool) error {
|
||||
header := block.Header()
|
||||
if block.GasUsed() != res.GasUsed {
|
||||
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), res.GasUsed)
|
||||
if block.GasUsed() != usedGas {
|
||||
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
|
||||
}
|
||||
// Validate the received block's bloom with the one derived from the generated receipts.
|
||||
// For valid blocks this should always validate to true.
|
||||
rbloom := types.CreateBloom(res.Receipts)
|
||||
rbloom := types.CreateBloom(receipts)
|
||||
if rbloom != header.Bloom {
|
||||
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
|
||||
}
|
||||
@@ -139,17 +138,10 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
|
||||
return nil
|
||||
}
|
||||
// The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
|
||||
receiptSha := types.DeriveSha(res.Receipts, trie.NewStackTrie(nil))
|
||||
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
|
||||
if receiptSha != header.ReceiptHash {
|
||||
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
|
||||
}
|
||||
// Validate the parsed requests match the expected header value.
|
||||
if header.RequestsHash != nil {
|
||||
depositSha := types.DeriveSha(res.Requests, trie.NewStackTrie(nil))
|
||||
if depositSha != *header.RequestsHash {
|
||||
return fmt.Errorf("invalid deposit root hash (remote: %x local: %x)", *header.RequestsHash, depositSha)
|
||||
}
|
||||
}
|
||||
// Validate the state root against the received state root and throw
|
||||
// an error if they don't match.
|
||||
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
|
||||
@@ -158,6 +150,28 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateWitness cross validates a block execution with stateless remote clients.
|
||||
//
|
||||
// Normally we'd distribute the block witness to remote cross validators, wait
|
||||
// for them to respond and then merge the results. For now, however, it's only
|
||||
// Geth, so do an internal stateless run.
|
||||
func (v *BlockValidator) ValidateWitness(witness *stateless.Witness, receiptRoot common.Hash, stateRoot common.Hash) error {
|
||||
// Run the cross client stateless execution
|
||||
// TODO(karalabe): Self-stateless for now, swap with other clients
|
||||
crossReceiptRoot, crossStateRoot, err := ExecuteStateless(v.config, witness)
|
||||
if err != nil {
|
||||
return fmt.Errorf("stateless execution failed: %v", err)
|
||||
}
|
||||
// Stateless cross execution suceeeded, validate the withheld computed fields
|
||||
if crossReceiptRoot != receiptRoot {
|
||||
return fmt.Errorf("cross validator receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, receiptRoot)
|
||||
}
|
||||
if crossStateRoot != stateRoot {
|
||||
return fmt.Errorf("cross validator state root mismatch (cross: %x local: %x)", crossStateRoot, stateRoot)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CalcGasLimit computes the gas limit of the next block after parent. It aims
|
||||
// to keep the baseline gas close to the provided target, and increase it towards
|
||||
// the target if the baseline gas is lower.
|
||||
|
||||
@@ -50,7 +50,7 @@ func testHeaderVerification(t *testing.T, scheme string) {
|
||||
headers[i] = block.Header()
|
||||
}
|
||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
@@ -160,7 +160,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
||||
postHeaders[i] = block.Header()
|
||||
}
|
||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
// Verify the blocks before the merging
|
||||
@@ -201,7 +201,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
||||
t.Fatalf("post-block %d: unexpected result returned: %v", i, result)
|
||||
case <-time.After(25 * time.Millisecond):
|
||||
}
|
||||
chain.InsertBlockWithoutSetHead(postBlocks[i], false)
|
||||
chain.InsertBlockWithoutSetHead(postBlocks[i])
|
||||
}
|
||||
|
||||
// Verify the blocks with pre-merge blocks and post-merge blocks
|
||||
|
||||
@@ -72,17 +72,16 @@ var (
|
||||
storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil)
|
||||
storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil)
|
||||
|
||||
accountReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/account/single/reads", nil)
|
||||
storageReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/storage/single/reads", nil)
|
||||
snapshotAccountReadTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/account/reads", nil)
|
||||
snapshotStorageReadTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/storage/reads", nil)
|
||||
snapshotCommitTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/commits", nil)
|
||||
|
||||
snapshotCommitTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/commits", nil)
|
||||
triedbCommitTimer = metrics.NewRegisteredResettingTimer("chain/triedb/commits", nil)
|
||||
triedbCommitTimer = metrics.NewRegisteredResettingTimer("chain/triedb/commits", nil)
|
||||
|
||||
blockInsertTimer = metrics.NewRegisteredResettingTimer("chain/inserts", nil)
|
||||
blockValidationTimer = metrics.NewRegisteredResettingTimer("chain/validation", nil)
|
||||
blockCrossValidationTimer = metrics.NewRegisteredResettingTimer("chain/crossvalidation", nil)
|
||||
blockExecutionTimer = metrics.NewRegisteredResettingTimer("chain/execution", nil)
|
||||
blockWriteTimer = metrics.NewRegisteredResettingTimer("chain/write", nil)
|
||||
blockInsertTimer = metrics.NewRegisteredResettingTimer("chain/inserts", nil)
|
||||
blockValidationTimer = metrics.NewRegisteredResettingTimer("chain/validation", nil)
|
||||
blockExecutionTimer = metrics.NewRegisteredResettingTimer("chain/execution", nil)
|
||||
blockWriteTimer = metrics.NewRegisteredResettingTimer("chain/write", nil)
|
||||
|
||||
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
|
||||
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
||||
@@ -218,7 +217,7 @@ type BlockChain struct {
|
||||
lastWrite uint64 // Last block when the state was flushed
|
||||
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
|
||||
triedb *triedb.Database // The database handler for maintaining trie nodes.
|
||||
statedb *state.CachingDB // State database to reuse between imports (contains state cache)
|
||||
stateCache state.Database // State database to reuse between imports (contains state cache)
|
||||
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
||||
|
||||
hc *HeaderChain
|
||||
@@ -257,6 +256,7 @@ type BlockChain struct {
|
||||
validator Validator // Block and state validator interface
|
||||
prefetcher Prefetcher
|
||||
processor Processor // Block transaction processor interface
|
||||
forker *ForkChoice
|
||||
vmConfig vm.Config
|
||||
logger *tracing.Hooks
|
||||
}
|
||||
@@ -264,7 +264,7 @@ type BlockChain struct {
|
||||
// NewBlockChain returns a fully initialised block chain using information
|
||||
// available in the database. It initialises the default Ethereum Validator
|
||||
// and Processor.
|
||||
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis, overrides *ChainOverrides, engine consensus.Engine, vmConfig vm.Config, txLookupLimit *uint64) (*BlockChain, error) {
|
||||
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis, overrides *ChainOverrides, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(header *types.Header) bool, txLookupLimit *uint64) (*BlockChain, error) {
|
||||
if cacheConfig == nil {
|
||||
cacheConfig = defaultCacheConfig
|
||||
}
|
||||
@@ -309,7 +309,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
return nil, err
|
||||
}
|
||||
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
|
||||
bc.statedb = state.NewDatabase(bc.triedb, nil)
|
||||
bc.forker = NewForkChoice(bc, shouldPreserve)
|
||||
bc.stateCache = state.NewDatabaseWithNodeDB(bc.db, bc.triedb)
|
||||
bc.validator = NewBlockValidator(chainConfig, bc)
|
||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
|
||||
bc.processor = NewStateProcessor(chainConfig, bc.hc)
|
||||
@@ -447,11 +448,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
AsyncBuild: !bc.cacheConfig.SnapshotWait,
|
||||
}
|
||||
bc.snaps, _ = snapshot.New(snapconfig, bc.db, bc.triedb, head.Root)
|
||||
|
||||
// Re-initialize the state database with snapshot
|
||||
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
|
||||
}
|
||||
|
||||
// Rewind the chain in case of an incompatible config upgrade.
|
||||
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
||||
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
|
||||
@@ -1243,6 +1240,13 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
|
||||
// Rewind may have occurred, skip in that case.
|
||||
if bc.CurrentHeader().Number.Cmp(head.Number()) >= 0 {
|
||||
reorg, err := bc.forker.ReorgNeeded(bc.CurrentSnapBlock(), head.Header())
|
||||
if err != nil {
|
||||
log.Warn("Reorg failed", "err", err)
|
||||
return false
|
||||
} else if !reorg {
|
||||
return false
|
||||
}
|
||||
rawdb.WriteHeadFastBlockHash(bc.db, head.Hash())
|
||||
bc.currentSnapBlock.Store(head.Header())
|
||||
headFastBlockGauge.Update(int64(head.NumberU64()))
|
||||
@@ -1541,30 +1545,42 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types
|
||||
return NonStatTy, err
|
||||
}
|
||||
currentBlock := bc.CurrentBlock()
|
||||
|
||||
// Reorganise the chain if the parent is not the head block
|
||||
if block.ParentHash() != currentBlock.Hash() {
|
||||
if err := bc.reorg(currentBlock, block); err != nil {
|
||||
return NonStatTy, err
|
||||
reorg, err := bc.forker.ReorgNeeded(currentBlock, block.Header())
|
||||
if err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
if reorg {
|
||||
// Reorganise the chain if the parent is not the head block
|
||||
if block.ParentHash() != currentBlock.Hash() {
|
||||
if err := bc.reorg(currentBlock, block); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
}
|
||||
status = CanonStatTy
|
||||
} else {
|
||||
status = SideStatTy
|
||||
}
|
||||
|
||||
// Set new head.
|
||||
bc.writeHeadBlock(block)
|
||||
|
||||
bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
|
||||
if len(logs) > 0 {
|
||||
bc.logsFeed.Send(logs)
|
||||
if status == CanonStatTy {
|
||||
bc.writeHeadBlock(block)
|
||||
}
|
||||
// In theory, we should fire a ChainHeadEvent when we inject
|
||||
// a canonical block, but sometimes we can insert a batch of
|
||||
// canonical blocks. Avoid firing too many ChainHeadEvents,
|
||||
// we will fire an accumulated ChainHeadEvent and disable fire
|
||||
// event here.
|
||||
if emitHeadEvent {
|
||||
bc.chainHeadFeed.Send(ChainHeadEvent{Block: block})
|
||||
if status == CanonStatTy {
|
||||
bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
|
||||
if len(logs) > 0 {
|
||||
bc.logsFeed.Send(logs)
|
||||
}
|
||||
// In theory, we should fire a ChainHeadEvent when we inject
|
||||
// a canonical block, but sometimes we can insert a batch of
|
||||
// canonical blocks. Avoid firing too many ChainHeadEvents,
|
||||
// we will fire an accumulated ChainHeadEvent and disable fire
|
||||
// event here.
|
||||
if emitHeadEvent {
|
||||
bc.chainHeadFeed.Send(ChainHeadEvent{Block: block})
|
||||
}
|
||||
} else {
|
||||
bc.chainSideFeed.Send(ChainSideEvent{Block: block})
|
||||
}
|
||||
return CanonStatTy, nil
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// InsertChain attempts to insert the given batch of blocks in to the canonical
|
||||
@@ -1599,9 +1615,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
return 0, errChainStopped
|
||||
}
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
_, n, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large)
|
||||
return n, err
|
||||
return bc.insertChain(chain, true)
|
||||
}
|
||||
|
||||
// insertChain is the internal implementation of InsertChain, which assumes that
|
||||
@@ -1612,11 +1626,12 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
// racey behaviour. If a sidechain import is in progress, and the historic state
|
||||
// is imported, but then new canon-head is added before the actual sidechain
|
||||
// completes, then the historic state could be pruned again
|
||||
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) {
|
||||
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) {
|
||||
// If the chain is terminating, don't even bother starting up.
|
||||
if bc.insertStopped() {
|
||||
return nil, 0, nil
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
||||
SenderCacher.RecoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number(), chain[0].Time()), chain)
|
||||
|
||||
@@ -1649,10 +1664,24 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
// 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot
|
||||
// from the canonical chain, which has not been verified.
|
||||
// Skip all known blocks that are behind us.
|
||||
current := bc.CurrentBlock()
|
||||
var (
|
||||
reorg bool
|
||||
current = bc.CurrentBlock()
|
||||
)
|
||||
for block != nil && bc.skipBlock(err, it) {
|
||||
if block.NumberU64() > current.Number.Uint64() || bc.GetCanonicalHash(block.NumberU64()) != block.Hash() {
|
||||
break
|
||||
reorg, err = bc.forker.ReorgNeeded(current, block.Header())
|
||||
if err != nil {
|
||||
return it.index, err
|
||||
}
|
||||
if reorg {
|
||||
// Switch to import mode if the forker says the reorg is necessary
|
||||
// and also the block is not on the canonical chain.
|
||||
// In eth2 the forker always returns true for reorg decision (blindly trusting
|
||||
// the external consensus engine), but in order to prevent the unnecessary
|
||||
// reorgs when importing known blocks, the special case is handled here.
|
||||
if block.NumberU64() > current.Number.Uint64() || bc.GetCanonicalHash(block.NumberU64()) != block.Hash() {
|
||||
break
|
||||
}
|
||||
}
|
||||
log.Debug("Ignoring already known block", "number", block.Number(), "hash", block.Hash())
|
||||
stats.ignored++
|
||||
@@ -1670,7 +1699,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
for block != nil && bc.skipBlock(err, it) {
|
||||
log.Debug("Writing previously known block", "number", block.Number(), "hash", block.Hash())
|
||||
if err := bc.writeKnownBlock(block); err != nil {
|
||||
return nil, it.index, err
|
||||
return it.index, err
|
||||
}
|
||||
lastCanon = block
|
||||
|
||||
@@ -1684,12 +1713,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
if setHead {
|
||||
// First block is pruned, insert as sidechain and reorg only if TD grows enough
|
||||
log.Debug("Pruned ancestor, inserting as sidechain", "number", block.Number(), "hash", block.Hash())
|
||||
return bc.insertSideChain(block, it, makeWitness)
|
||||
return bc.insertSideChain(block, it)
|
||||
} else {
|
||||
// We're post-merge and the parent is pruned, try to recover the parent state
|
||||
log.Debug("Pruned ancestor", "number", block.Number(), "hash", block.Hash())
|
||||
_, err := bc.recoverAncestors(block, makeWitness)
|
||||
return nil, it.index, err
|
||||
_, err := bc.recoverAncestors(block)
|
||||
return it.index, err
|
||||
}
|
||||
// Some other error(except ErrKnownBlock) occurred, abort.
|
||||
// ErrKnownBlock is allowed here since some known blocks
|
||||
@@ -1697,7 +1726,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
case err != nil && !errors.Is(err, ErrKnownBlock):
|
||||
stats.ignored += len(it.chain)
|
||||
bc.reportBlock(block, nil, err)
|
||||
return nil, it.index, err
|
||||
return it.index, err
|
||||
}
|
||||
// No validation errors for the first block (or chain prefix skipped)
|
||||
var activeState *state.StateDB
|
||||
@@ -1711,9 +1740,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
}
|
||||
}()
|
||||
|
||||
// Track the singleton witness from this chain insertion (if any)
|
||||
var witness *stateless.Witness
|
||||
|
||||
for ; block != nil && err == nil || errors.Is(err, ErrKnownBlock); block, err = it.next() {
|
||||
// If the chain is terminating, stop processing blocks
|
||||
if bc.insertStopped() {
|
||||
@@ -1750,7 +1776,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
"hash", block.Hash(), "number", block.NumberU64())
|
||||
}
|
||||
if err := bc.writeKnownBlock(block); err != nil {
|
||||
return nil, it.index, err
|
||||
return it.index, err
|
||||
}
|
||||
stats.processed++
|
||||
if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
|
||||
@@ -1761,20 +1787,22 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
Safe: bc.CurrentSafeBlock(),
|
||||
})
|
||||
}
|
||||
|
||||
// We can assume that logs are empty here, since the only way for consecutive
|
||||
// Clique blocks to have the same state is if there are no transactions.
|
||||
lastCanon = block
|
||||
continue
|
||||
}
|
||||
|
||||
// Retrieve the parent block and it's state to execute on top
|
||||
start := time.Now()
|
||||
parent := it.previous()
|
||||
if parent == nil {
|
||||
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||
}
|
||||
statedb, err := state.New(parent.Root, bc.statedb)
|
||||
statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
|
||||
if err != nil {
|
||||
return nil, it.index, err
|
||||
return it.index, err
|
||||
}
|
||||
statedb.SetLogger(bc.logger)
|
||||
|
||||
@@ -1782,13 +1810,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||
// useless due to the intermediate root hashing after each transaction.
|
||||
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||
// Generate witnesses either if we're self-testing, or if it's the
|
||||
// only block being inserted. A bit crude, but witnesses are huge,
|
||||
// so we refuse to make an entire chain of them.
|
||||
if bc.vmConfig.StatelessSelfValidation || (makeWitness && len(chain) == 1) {
|
||||
witness, err = stateless.NewWitness(block.Header(), bc)
|
||||
var witness *stateless.Witness
|
||||
if bc.vmConfig.EnableWitnessCollection {
|
||||
witness, err = stateless.NewWitness(bc, block)
|
||||
if err != nil {
|
||||
return nil, it.index, err
|
||||
return it.index, err
|
||||
}
|
||||
}
|
||||
statedb.StartPrefetcher("chain", witness)
|
||||
@@ -1800,7 +1826,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
var followupInterrupt atomic.Bool
|
||||
if !bc.cacheConfig.TrieCleanNoPrefetch {
|
||||
if followup, err := it.peek(); followup != nil && err == nil {
|
||||
throwaway, _ := state.New(parent.Root, bc.statedb)
|
||||
throwaway, _ := state.New(parent.Root, bc.stateCache, bc.snaps)
|
||||
|
||||
go func(start time.Time, followup *types.Block, throwaway *state.StateDB) {
|
||||
// Disable tracing for prefetcher executions.
|
||||
@@ -1820,7 +1846,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
res, err := bc.processBlock(block, statedb, start, setHead)
|
||||
followupInterrupt.Store(true)
|
||||
if err != nil {
|
||||
return nil, it.index, err
|
||||
return it.index, err
|
||||
}
|
||||
// Report the import stats before returning the various results
|
||||
stats.processed++
|
||||
@@ -1837,7 +1863,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
// After merge we expect few side chains. Simply count
|
||||
// all blocks the CL gives us for GC processing time
|
||||
bc.gcproc += res.procTime
|
||||
return witness, it.index, nil // Direct block insertion of a single block
|
||||
return it.index, nil // Direct block insertion of a single block
|
||||
}
|
||||
switch res.status {
|
||||
case CanonStatTy:
|
||||
@@ -1867,7 +1893,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||
}
|
||||
}
|
||||
stats.ignored += it.remaining()
|
||||
return witness, it.index, err
|
||||
return it.index, err
|
||||
}
|
||||
|
||||
// blockProcessingResult is a summary of block processing
|
||||
@@ -1898,68 +1924,42 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||
|
||||
// Process block using the parent state as reference point
|
||||
pstart := time.Now()
|
||||
res, err := bc.processor.Process(block, statedb, bc.vmConfig)
|
||||
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
|
||||
if err != nil {
|
||||
bc.reportBlock(block, res, err)
|
||||
bc.reportBlock(block, receipts, err)
|
||||
return nil, err
|
||||
}
|
||||
ptime := time.Since(pstart)
|
||||
|
||||
vstart := time.Now()
|
||||
if err := bc.validator.ValidateState(block, statedb, res, false); err != nil {
|
||||
bc.reportBlock(block, res, err)
|
||||
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, false); err != nil {
|
||||
bc.reportBlock(block, receipts, err)
|
||||
return nil, err
|
||||
}
|
||||
vtime := time.Since(vstart)
|
||||
|
||||
// If witnesses was generated and stateless self-validation requested, do
|
||||
// that now. Self validation should *never* run in production, it's more of
|
||||
// a tight integration to enable running *all* consensus tests through the
|
||||
// witness builder/runner, which would otherwise be impossible due to the
|
||||
// various invalid chain states/behaviors being contained in those tests.
|
||||
xvstart := time.Now()
|
||||
if witness := statedb.Witness(); witness != nil && bc.vmConfig.StatelessSelfValidation {
|
||||
log.Warn("Running stateless self-validation", "block", block.Number(), "hash", block.Hash())
|
||||
|
||||
// Remove critical computed fields from the block to force true recalculation
|
||||
context := block.Header()
|
||||
context.Root = common.Hash{}
|
||||
context.ReceiptHash = common.Hash{}
|
||||
|
||||
task := types.NewBlockWithHeader(context).WithBody(*block.Body())
|
||||
|
||||
// Run the stateless self-cross-validation
|
||||
crossStateRoot, crossReceiptRoot, err := ExecuteStateless(bc.chainConfig, task, witness)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stateless self-validation failed: %v", err)
|
||||
}
|
||||
if crossStateRoot != block.Root() {
|
||||
return nil, fmt.Errorf("stateless self-validation root mismatch (cross: %x local: %x)", crossStateRoot, block.Root())
|
||||
}
|
||||
if crossReceiptRoot != block.ReceiptHash() {
|
||||
return nil, fmt.Errorf("stateless self-validation receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, block.ReceiptHash())
|
||||
if witness := statedb.Witness(); witness != nil {
|
||||
if err = bc.validator.ValidateWitness(witness, block.ReceiptHash(), block.Root()); err != nil {
|
||||
bc.reportBlock(block, receipts, err)
|
||||
return nil, fmt.Errorf("cross verification failed: %v", err)
|
||||
}
|
||||
}
|
||||
xvtime := time.Since(xvstart)
|
||||
proctime := time.Since(start) // processing + validation + cross validation
|
||||
proctime := time.Since(start) // processing + validation
|
||||
|
||||
// Update the metrics touched during block processing and validation
|
||||
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
|
||||
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
|
||||
if statedb.AccountLoaded != 0 {
|
||||
accountReadSingleTimer.Update(statedb.AccountReads / time.Duration(statedb.AccountLoaded))
|
||||
}
|
||||
if statedb.StorageLoaded != 0 {
|
||||
storageReadSingleTimer.Update(statedb.StorageReads / time.Duration(statedb.StorageLoaded))
|
||||
}
|
||||
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
|
||||
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
|
||||
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
|
||||
triehash := statedb.AccountHashes // The time spent on tries hashing
|
||||
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
|
||||
blockExecutionTimer.Update(ptime - (statedb.AccountReads + statedb.StorageReads)) // The time spent on EVM processing
|
||||
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
|
||||
blockCrossValidationTimer.Update(xvtime) // The time spent on stateless cross validation
|
||||
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
|
||||
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
|
||||
snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing)
|
||||
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing)
|
||||
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
|
||||
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
|
||||
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
|
||||
triehash := statedb.AccountHashes // The time spent on tries hashing
|
||||
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
|
||||
trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read
|
||||
trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read
|
||||
blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing
|
||||
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
|
||||
|
||||
// Write the block to the chain and get the status.
|
||||
var (
|
||||
@@ -1968,9 +1968,9 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||
)
|
||||
if !setHead {
|
||||
// Don't set the head, only insert the block
|
||||
err = bc.writeBlockWithState(block, res.Receipts, statedb)
|
||||
err = bc.writeBlockWithState(block, receipts, statedb)
|
||||
} else {
|
||||
status, err = bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, false)
|
||||
status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1984,7 +1984,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
||||
blockInsertTimer.UpdateSince(start)
|
||||
|
||||
return &blockProcessingResult{usedGas: res.GasUsed, procTime: proctime, status: status}, nil
|
||||
return &blockProcessingResult{usedGas: usedGas, procTime: proctime, status: status}, nil
|
||||
}
|
||||
|
||||
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
||||
@@ -1994,10 +1994,11 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||
// The method writes all (header-and-body-valid) blocks to disk, then tries to
|
||||
// switch over to the new chain if the TD exceeded the current chain.
|
||||
// insertSideChain is only used pre-merge.
|
||||
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, makeWitness bool) (*stateless.Witness, int, error) {
|
||||
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (int, error) {
|
||||
var (
|
||||
externTd *big.Int
|
||||
current = bc.CurrentBlock()
|
||||
externTd *big.Int
|
||||
lastBlock = block
|
||||
current = bc.CurrentBlock()
|
||||
)
|
||||
// The first sidechain block error is already verified to be ErrPrunedAncestor.
|
||||
// Since we don't import them here, we expect ErrUnknownAncestor for the remaining
|
||||
@@ -2030,7 +2031,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
||||
// If someone legitimately side-mines blocks, they would still be imported as usual. However,
|
||||
// we cannot risk writing unverified blocks to disk when they obviously target the pruning
|
||||
// mechanism.
|
||||
return nil, it.index, errors.New("sidechain ghost-state attack")
|
||||
return it.index, errors.New("sidechain ghost-state attack")
|
||||
}
|
||||
}
|
||||
if externTd == nil {
|
||||
@@ -2041,13 +2042,29 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
||||
if !bc.HasBlock(block.Hash(), block.NumberU64()) {
|
||||
start := time.Now()
|
||||
if err := bc.writeBlockWithoutState(block, externTd); err != nil {
|
||||
return nil, it.index, err
|
||||
return it.index, err
|
||||
}
|
||||
log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(),
|
||||
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
|
||||
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
|
||||
"root", block.Root())
|
||||
}
|
||||
lastBlock = block
|
||||
}
|
||||
// At this point, we've written all sidechain blocks to database. Loop ended
|
||||
// either on some other error or all were processed. If there was some other
|
||||
// error, we can ignore the rest of those blocks.
|
||||
//
|
||||
// If the externTd was larger than our local TD, we now need to reimport the previous
|
||||
// blocks to regenerate the required state
|
||||
reorg, err := bc.forker.ReorgNeeded(current, lastBlock.Header())
|
||||
if err != nil {
|
||||
return it.index, err
|
||||
}
|
||||
if !reorg {
|
||||
localTd := bc.GetTd(current.Hash(), current.Number.Uint64())
|
||||
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
|
||||
return it.index, err
|
||||
}
|
||||
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
||||
var (
|
||||
@@ -2058,7 +2075,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
||||
for parent != nil && !bc.HasState(parent.Root) {
|
||||
if bc.stateRecoverable(parent.Root) {
|
||||
if err := bc.triedb.Recover(parent.Root); err != nil {
|
||||
return nil, 0, err
|
||||
return 0, err
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -2068,7 +2085,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
||||
parent = bc.GetHeader(parent.ParentHash, parent.Number.Uint64()-1)
|
||||
}
|
||||
if parent == nil {
|
||||
return nil, it.index, errors.New("missing parent")
|
||||
return it.index, errors.New("missing parent")
|
||||
}
|
||||
// Import all the pruned blocks to make the state available
|
||||
var (
|
||||
@@ -2087,30 +2104,30 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
||||
// memory here.
|
||||
if len(blocks) >= 2048 || memory > 64*1024*1024 {
|
||||
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
|
||||
if _, _, err := bc.insertChain(blocks, true, false); err != nil {
|
||||
return nil, 0, err
|
||||
if _, err := bc.insertChain(blocks, true); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
blocks, memory = blocks[:0], 0
|
||||
|
||||
// If the chain is terminating, stop processing blocks
|
||||
if bc.insertStopped() {
|
||||
log.Debug("Abort during blocks processing")
|
||||
return nil, 0, nil
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(blocks) > 0 {
|
||||
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
|
||||
return bc.insertChain(blocks, true, makeWitness)
|
||||
return bc.insertChain(blocks, true)
|
||||
}
|
||||
return nil, 0, nil
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// recoverAncestors finds the closest ancestor with available state and re-execute
|
||||
// all the ancestor blocks since that.
|
||||
// recoverAncestors is only used post-merge.
|
||||
// We return the hash of the latest block that we could correctly validate.
|
||||
func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (common.Hash, error) {
|
||||
func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error) {
|
||||
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
||||
var (
|
||||
hashes []common.Hash
|
||||
@@ -2150,7 +2167,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co
|
||||
} else {
|
||||
b = bc.GetBlock(hashes[i], numbers[i])
|
||||
}
|
||||
if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0); err != nil {
|
||||
if _, err := bc.insertChain(types.Blocks{b}, false); err != nil {
|
||||
return b.ParentHash(), err
|
||||
}
|
||||
}
|
||||
@@ -2366,14 +2383,14 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error {
|
||||
// The key difference between the InsertChain is it won't do the canonical chain
|
||||
// updating. It relies on the additional SetCanonical call to finalize the entire
|
||||
// procedure.
|
||||
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*stateless.Witness, error) {
|
||||
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block) error {
|
||||
if !bc.chainmu.TryLock() {
|
||||
return nil, errChainStopped
|
||||
return errChainStopped
|
||||
}
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness)
|
||||
return witness, err
|
||||
_, err := bc.insertChain(types.Blocks{block}, false)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetCanonical rewinds the chain to set the new head block as the specified
|
||||
@@ -2387,7 +2404,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) {
|
||||
|
||||
// Re-execute the reorged chain in case the head state is missing.
|
||||
if !bc.HasState(head.Root()) {
|
||||
if latestValidHash, err := bc.recoverAncestors(head, false); err != nil {
|
||||
if latestValidHash, err := bc.recoverAncestors(head); err != nil {
|
||||
return latestValidHash, err
|
||||
}
|
||||
log.Info("Recovered head state", "number", head.Number(), "hash", head.Hash())
|
||||
@@ -2464,11 +2481,7 @@ func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
|
||||
}
|
||||
|
||||
// reportBlock logs a bad block error.
|
||||
func (bc *BlockChain) reportBlock(block *types.Block, res *ProcessResult, err error) {
|
||||
var receipts types.Receipts
|
||||
if res != nil {
|
||||
receipts = res.Receipts
|
||||
}
|
||||
func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
|
||||
rawdb.WriteBadBlock(bc.db, block)
|
||||
log.Error(summarizeBadBlock(block, receipts, bc.Config(), err))
|
||||
}
|
||||
@@ -2514,7 +2527,7 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header) (int, error) {
|
||||
return 0, errChainStopped
|
||||
}
|
||||
defer bc.chainmu.Unlock()
|
||||
_, err := bc.hc.InsertHeaderChain(chain, start)
|
||||
_, err := bc.hc.InsertHeaderChain(chain, start, bc.forker)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
|
||||
@@ -170,6 +170,11 @@ func (it *insertIterator) current() *types.Header {
|
||||
return it.chain[it.index].Header()
|
||||
}
|
||||
|
||||
// first returns the first block in it.
|
||||
func (it *insertIterator) first() *types.Block {
|
||||
return it.chain[0]
|
||||
}
|
||||
|
||||
// remaining returns the number of remaining blocks.
|
||||
func (it *insertIterator) remaining() int {
|
||||
return len(it.chain) - it.index
|
||||
|
||||
@@ -308,7 +308,7 @@ func (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||
|
||||
// HasState checks if state trie is fully present in the database or not.
|
||||
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
||||
_, err := bc.statedb.OpenTrie(hash)
|
||||
_, err := bc.stateCache.OpenTrie(hash)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
@@ -341,9 +341,12 @@ func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
|
||||
// If the code doesn't exist in the in-memory cache, check the storage with
|
||||
// new code scheme.
|
||||
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
|
||||
type codeReader interface {
|
||||
ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error)
|
||||
}
|
||||
// TODO(rjl493456442) The associated account address is also required
|
||||
// in Verkle scheme. Fix it once snap-sync is supported for Verkle.
|
||||
return bc.statedb.ContractCodeWithPrefix(common.Address{}, hash)
|
||||
return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Address{}, hash)
|
||||
}
|
||||
|
||||
// State returns a new mutable state based on the current HEAD block.
|
||||
@@ -353,7 +356,7 @@ func (bc *BlockChain) State() (*state.StateDB, error) {
|
||||
|
||||
// StateAt returns a new mutable state based on a particular point in time.
|
||||
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||
return state.New(root, bc.statedb)
|
||||
return state.New(root, bc.stateCache, bc.snaps)
|
||||
}
|
||||
|
||||
// Config retrieves the chain's fork configuration.
|
||||
@@ -379,7 +382,7 @@ func (bc *BlockChain) Processor() Processor {
|
||||
|
||||
// StateCache returns the caching database underpinning the blockchain instance.
|
||||
func (bc *BlockChain) StateCache() state.Database {
|
||||
return bc.statedb
|
||||
return bc.stateCache
|
||||
}
|
||||
|
||||
// GasLimit returns the gas limit of the current HEAD block.
|
||||
|
||||
@@ -1757,8 +1757,8 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||
|
||||
func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme string) {
|
||||
// It's hard to follow the test case, visualize the input
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||
// fmt.Println(tt.dump(false))
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
// fmt.Println(tt.dump(true))
|
||||
|
||||
// Create a temporary persistent database
|
||||
datadir := t.TempDir()
|
||||
@@ -1794,7 +1794,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||
config.SnapshotLimit = 256
|
||||
config.SnapshotWait = true
|
||||
}
|
||||
chain, err := NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
@@ -1859,7 +1859,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
newChain, err := NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil)
|
||||
newChain, err := NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
@@ -1908,7 +1908,7 @@ func TestIssue23496(t *testing.T) {
|
||||
|
||||
func testIssue23496(t *testing.T, scheme string) {
|
||||
// It's hard to follow the test case, visualize the input
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
|
||||
// Create a temporary persistent database
|
||||
datadir := t.TempDir()
|
||||
@@ -1931,7 +1931,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||
}
|
||||
engine = ethash.NewFullFaker()
|
||||
)
|
||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
@@ -1981,7 +1981,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
chain, err = NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err = NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
||||
@@ -1961,7 +1961,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||
|
||||
func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme string) {
|
||||
// It's hard to follow the test case, visualize the input
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
// fmt.Println(tt.dump(false))
|
||||
|
||||
// Create a temporary persistent database
|
||||
@@ -1997,7 +1997,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||
config.SnapshotLimit = 256
|
||||
config.SnapshotWait = true
|
||||
}
|
||||
chain, err := NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
@@ -2040,7 +2040,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||
dbconfig.HashDB = hashdb.Defaults
|
||||
}
|
||||
chain.triedb = triedb.NewDatabase(chain.db, dbconfig)
|
||||
chain.statedb = state.NewDatabase(chain.triedb, chain.snaps)
|
||||
chain.stateCache = state.NewDatabaseWithNodeDB(chain.db, chain.triedb)
|
||||
|
||||
// Force run a freeze cycle
|
||||
type freezer interface {
|
||||
|
||||
@@ -81,7 +81,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
|
||||
}
|
||||
engine = ethash.NewFullFaker()
|
||||
)
|
||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(basic.scheme), gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(basic.scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
@@ -222,13 +222,13 @@ type snapshotTest struct {
|
||||
|
||||
func (snaptest *snapshotTest) test(t *testing.T) {
|
||||
// It's hard to follow the test case, visualize the input
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||
// fmt.Println(snaptest.dump())
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
// fmt.Println(tt.dump())
|
||||
chain, blocks := snaptest.prepare(t)
|
||||
|
||||
// Restart the chain normally
|
||||
chain.Stop()
|
||||
newchain, err := NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil)
|
||||
newchain, err := NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
@@ -245,8 +245,8 @@ type crashSnapshotTest struct {
|
||||
|
||||
func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
||||
// It's hard to follow the test case, visualize the input
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||
// fmt.Println(snaptest.dump())
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
// fmt.Println(tt.dump())
|
||||
chain, blocks := snaptest.prepare(t)
|
||||
|
||||
// Pull the plug on the database, simulating a hard crash
|
||||
@@ -270,13 +270,13 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
||||
// the crash, we do restart twice here: one after the crash and one
|
||||
// after the normal stop. It's used to ensure the broken snapshot
|
||||
// can be detected all the time.
|
||||
newchain, err := NewBlockChain(newdb, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil)
|
||||
newchain, err := NewBlockChain(newdb, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
newchain.Stop()
|
||||
|
||||
newchain, err = NewBlockChain(newdb, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil)
|
||||
newchain, err = NewBlockChain(newdb, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
@@ -297,8 +297,8 @@ type gappedSnapshotTest struct {
|
||||
|
||||
func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
||||
// It's hard to follow the test case, visualize the input
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||
// fmt.Println(snaptest.dump())
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
// fmt.Println(tt.dump())
|
||||
chain, blocks := snaptest.prepare(t)
|
||||
|
||||
// Insert blocks without enabling snapshot if gapping is required.
|
||||
@@ -313,7 +313,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
||||
SnapshotLimit: 0,
|
||||
StateScheme: snaptest.scheme,
|
||||
}
|
||||
newchain, err := NewBlockChain(snaptest.db, cacheConfig, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil)
|
||||
newchain, err := NewBlockChain(snaptest.db, cacheConfig, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
@@ -321,7 +321,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
||||
newchain.Stop()
|
||||
|
||||
// Restart the chain with enabling the snapshot
|
||||
newchain, err = NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil)
|
||||
newchain, err = NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
@@ -341,15 +341,15 @@ type setHeadSnapshotTest struct {
|
||||
|
||||
func (snaptest *setHeadSnapshotTest) test(t *testing.T) {
|
||||
// It's hard to follow the test case, visualize the input
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||
// fmt.Println(snaptest.dump())
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
// fmt.Println(tt.dump())
|
||||
chain, blocks := snaptest.prepare(t)
|
||||
|
||||
// Rewind the chain if setHead operation is required.
|
||||
chain.SetHead(snaptest.setHead)
|
||||
chain.Stop()
|
||||
|
||||
newchain, err := NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil)
|
||||
newchain, err := NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
@@ -370,8 +370,8 @@ type wipeCrashSnapshotTest struct {
|
||||
|
||||
func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
||||
// It's hard to follow the test case, visualize the input
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||
// fmt.Println(snaptest.dump())
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
// fmt.Println(tt.dump())
|
||||
chain, blocks := snaptest.prepare(t)
|
||||
|
||||
// Firstly, stop the chain properly, with all snapshot journal
|
||||
@@ -385,7 +385,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
||||
SnapshotLimit: 0,
|
||||
StateScheme: snaptest.scheme,
|
||||
}
|
||||
newchain, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil)
|
||||
newchain, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
@@ -402,7 +402,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
||||
SnapshotWait: false, // Don't wait rebuild
|
||||
StateScheme: snaptest.scheme,
|
||||
}
|
||||
tmp, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil)
|
||||
tmp, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
@@ -411,7 +411,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
||||
tmp.triedb.Close()
|
||||
tmp.stopWithoutSaving()
|
||||
|
||||
newchain, err = NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil)
|
||||
newchain, err = NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -62,7 +61,7 @@ func newCanonical(engine consensus.Engine, n int, full bool, scheme string) (eth
|
||||
}
|
||||
)
|
||||
// Initialize a fresh chain with only a genesis block
|
||||
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
||||
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
|
||||
// Create and inject the requested chain
|
||||
if n == 0 {
|
||||
@@ -160,18 +159,17 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.statedb)
|
||||
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.stateCache, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := blockchain.processor.Process(block, statedb, vm.Config{})
|
||||
receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{})
|
||||
if err != nil {
|
||||
blockchain.reportBlock(block, res, err)
|
||||
blockchain.reportBlock(block, receipts, err)
|
||||
return err
|
||||
}
|
||||
err = blockchain.validator.ValidateState(block, statedb, res, false)
|
||||
if err != nil {
|
||||
blockchain.reportBlock(block, res, err)
|
||||
if err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, false); err != nil {
|
||||
blockchain.reportBlock(block, receipts, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -764,7 +762,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||
})
|
||||
// Import the chain as an archive node for the comparison baseline
|
||||
archiveDb := rawdb.NewMemoryDatabase()
|
||||
archive, _ := NewBlockChain(archiveDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
archive, _ := NewBlockChain(archiveDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer archive.Stop()
|
||||
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
@@ -772,7 +770,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||
}
|
||||
// Fast import the chain as a non-archive node to test
|
||||
fastDb := rawdb.NewMemoryDatabase()
|
||||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer fast.Stop()
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
@@ -792,7 +790,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||
}
|
||||
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, nil)
|
||||
defer ancient.Stop()
|
||||
|
||||
if n, err := ancient.InsertHeaderChain(headers); err != nil {
|
||||
@@ -912,7 +910,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||
archiveCaching.TrieDirtyDisabled = true
|
||||
archiveCaching.StateScheme = scheme
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to process block %d: %v", n, err)
|
||||
}
|
||||
@@ -925,7 +923,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||
fastDb := makeDb()
|
||||
defer fastDb.Close()
|
||||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer fast.Stop()
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
@@ -945,7 +943,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||
// Import the chain as a ancient-first node and ensure all pointers are updated
|
||||
ancientDb := makeDb()
|
||||
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, nil)
|
||||
defer ancient.Stop()
|
||||
|
||||
if n, err := ancient.InsertHeaderChain(headers); err != nil {
|
||||
@@ -964,7 +962,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||
// Import the chain as a light node and ensure all pointers are updated
|
||||
lightDb := makeDb()
|
||||
defer lightDb.Close()
|
||||
light, _ := NewBlockChain(lightDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
light, _ := NewBlockChain(lightDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if n, err := light.InsertHeaderChain(headers); err != nil {
|
||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
||||
}
|
||||
@@ -1037,7 +1035,7 @@ func testChainTxReorgs(t *testing.T, scheme string) {
|
||||
})
|
||||
// Import the chain. This runs all block validation rules.
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
blockchain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
blockchain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
||||
}
|
||||
@@ -1111,7 +1109,7 @@ func testLogReorgs(t *testing.T, scheme string) {
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
rmLogsCh := make(chan RemovedLogsEvent)
|
||||
@@ -1167,7 +1165,7 @@ func testLogRebirth(t *testing.T, scheme string) {
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
engine = ethash.NewFaker()
|
||||
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
||||
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
|
||||
@@ -1248,7 +1246,7 @@ func testSideLogRebirth(t *testing.T, scheme string) {
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
|
||||
@@ -1267,7 +1265,7 @@ func testSideLogRebirth(t *testing.T, scheme string) {
|
||||
}
|
||||
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
||||
|
||||
// Generate side chain with lower difficulty, after the merge, the chain will be accepted even if it is lower difficulty
|
||||
// Generate side chain with lower difficulty
|
||||
genDb, sideChain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 2, func(i int, gen *BlockGen) {
|
||||
if i == 1 {
|
||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, logCode), signer, key1)
|
||||
@@ -1280,14 +1278,14 @@ func testSideLogRebirth(t *testing.T, scheme string) {
|
||||
if _, err := blockchain.InsertChain(sideChain); err != nil {
|
||||
t.Fatalf("failed to insert forked chain: %v", err)
|
||||
}
|
||||
checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
|
||||
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
||||
|
||||
// Generate a new block based on side chain. Should not emit any events anymore.
|
||||
// Generate a new block based on side chain.
|
||||
newBlocks, _ := GenerateChain(gspec.Config, sideChain[len(sideChain)-1], ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {})
|
||||
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
||||
t.Fatalf("failed to insert forked chain: %v", err)
|
||||
}
|
||||
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
||||
checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
|
||||
}
|
||||
|
||||
func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan RemovedLogsEvent, wantNew, wantRemoved int) {
|
||||
@@ -1347,7 +1345,7 @@ func testReorgSideEvent(t *testing.T, scheme string) {
|
||||
}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
_, chain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 3, func(i int, gen *BlockGen) {})
|
||||
@@ -1371,10 +1369,15 @@ func testReorgSideEvent(t *testing.T, scheme string) {
|
||||
t.Fatalf("failed to insert chain: %v", err)
|
||||
}
|
||||
|
||||
// first two block of the secondary chain are for a brief moment considered
|
||||
// side chains because up to that point the first one is considered the
|
||||
// heavier chain.
|
||||
expectedSideHashes := map[common.Hash]bool{
|
||||
chain[0].Hash(): true,
|
||||
chain[1].Hash(): true,
|
||||
chain[2].Hash(): true,
|
||||
replacementBlocks[0].Hash(): true,
|
||||
replacementBlocks[1].Hash(): true,
|
||||
chain[0].Hash(): true,
|
||||
chain[1].Hash(): true,
|
||||
chain[2].Hash(): true,
|
||||
}
|
||||
|
||||
i := 0
|
||||
@@ -1399,7 +1402,7 @@ done:
|
||||
timeout.Reset(timeoutDura)
|
||||
|
||||
case <-timeout.C:
|
||||
t.Fatalf("Timeout. Possibly not all blocks were triggered for sideevent: %v", i)
|
||||
t.Fatal("Timeout. Possibly not all blocks were triggered for sideevent")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1526,7 +1529,7 @@ func testEIP155Transition(t *testing.T, scheme string) {
|
||||
}
|
||||
})
|
||||
|
||||
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
if _, err := blockchain.InsertChain(blocks); err != nil {
|
||||
@@ -1619,7 +1622,7 @@ func testEIP161AccountRemoval(t *testing.T, scheme string) {
|
||||
block.AddTx(tx)
|
||||
})
|
||||
// account must exist pre eip 161
|
||||
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
if _, err := blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil {
|
||||
@@ -1677,7 +1680,7 @@ func testBlockchainHeaderchainReorgConsistency(t *testing.T, scheme string) {
|
||||
}
|
||||
// Import the canonical and fork chain side by side, verifying the current block
|
||||
// and current header consistency
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -1721,7 +1724,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||
forks[i] = fork[0]
|
||||
}
|
||||
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, genesis, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -1767,7 +1770,7 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) {
|
||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
defer db.Close()
|
||||
|
||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -1783,15 +1786,18 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) {
|
||||
if chain.HasState(shared[len(shared)-1].Root()) {
|
||||
t.Fatalf("common-but-old ancestor still cache")
|
||||
}
|
||||
// Import the competitor chain without exceeding the canonical's TD.
|
||||
// Post-merge the side chain should be executed
|
||||
// Import the competitor chain without exceeding the canonical's TD and ensure
|
||||
// we have not processed any of the blocks (protection against malicious blocks)
|
||||
if _, err := chain.InsertChain(competitor[:len(competitor)-2]); err != nil {
|
||||
t.Fatalf("failed to insert competitor chain: %v", err)
|
||||
}
|
||||
if !chain.HasState(competitor[len(competitor)-3].Root()) {
|
||||
t.Fatalf("failed to insert low-TD chain")
|
||||
for i, block := range competitor[:len(competitor)-2] {
|
||||
if chain.HasState(block.Root()) {
|
||||
t.Fatalf("competitor %d: low TD chain became processed", i)
|
||||
}
|
||||
}
|
||||
// Import the head of the competitor chain.
|
||||
// Import the head of the competitor chain, triggering the reorg and ensure we
|
||||
// successfully reprocess all the stashed away blocks.
|
||||
if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
|
||||
t.Fatalf("failed to finalize competitor chain: %v", err)
|
||||
}
|
||||
@@ -1835,7 +1841,7 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
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, nil)
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
@@ -1855,7 +1861,7 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
||||
rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())
|
||||
|
||||
// Reopen broken blockchain again
|
||||
ancient, _ = NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
ancient, _ = NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer ancient.Stop()
|
||||
if num := ancient.CurrentBlock().Number.Uint64(); num != 0 {
|
||||
t.Errorf("head block mismatch: have #%v, want #%v", num, 0)
|
||||
@@ -1907,7 +1913,7 @@ func testInsertReceiptChainRollback(t *testing.T, scheme string) {
|
||||
}
|
||||
defer ancientDb.Close()
|
||||
|
||||
ancientChain, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
ancientChain, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer ancientChain.Stop()
|
||||
|
||||
// Import the canonical header chain.
|
||||
@@ -1974,7 +1980,7 @@ func testLowDiffLongChain(t *testing.T, scheme string) {
|
||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
defer diskdb.Close()
|
||||
|
||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -2035,7 +2041,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||
mergeBlock = math.MaxInt32
|
||||
)
|
||||
// Generate and import the canonical chain
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -2126,9 +2132,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
|
||||
// ^ ^ ^ pruned
|
||||
func TestPrunedImportSide(t *testing.T) {
|
||||
// glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
|
||||
// glogger.Verbosity(3)
|
||||
// log.SetDefault(log.NewLogger(glogger))
|
||||
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
|
||||
//glogger.Verbosity(3)
|
||||
//log.Root().SetHandler(log.Handler(glogger))
|
||||
testSideImport(t, 3, 3, -1)
|
||||
testSideImport(t, 3, -3, -1)
|
||||
testSideImport(t, 10, 0, -1)
|
||||
@@ -2137,9 +2143,9 @@ func TestPrunedImportSide(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPrunedImportSideWithMerging(t *testing.T) {
|
||||
// glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
|
||||
// glogger.Verbosity(3)
|
||||
// log.SetDefault(log.NewLogger(glogger))
|
||||
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
|
||||
//glogger.Verbosity(3)
|
||||
//log.Root().SetHandler(log.Handler(glogger))
|
||||
testSideImport(t, 3, 3, 0)
|
||||
testSideImport(t, 3, -3, 0)
|
||||
testSideImport(t, 10, 0, 0)
|
||||
@@ -2189,7 +2195,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
||||
}
|
||||
defer chaindb.Close()
|
||||
|
||||
chain, err := NewBlockChain(chaindb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(chaindb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -2273,10 +2279,10 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
||||
if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
|
||||
t.Fatalf("failed to insert chain data: %v", err)
|
||||
}
|
||||
// Post-merge the chain should change even if td is lower.
|
||||
asserter(t, blocks2[len(blocks2)-1])
|
||||
// The head shouldn't change.
|
||||
asserter(t, blocks3[len(blocks3)-1])
|
||||
|
||||
// Rollback the heavier chain and re-insert the longer chain again.
|
||||
// Rollback the heavier chain and re-insert the longer chain again
|
||||
chain.SetHead(rollback - 1)
|
||||
if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
|
||||
t.Fatalf("failed to insert chain data: %v", err)
|
||||
@@ -2360,7 +2366,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
||||
}
|
||||
defer chaindb.Close()
|
||||
|
||||
chain, err := NewBlockChain(chaindb, nil, genesis, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(chaindb, nil, genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -2474,7 +2480,7 @@ func getLongAndShortChains(scheme string) (*BlockChain, []*types.Block, []*types
|
||||
genDb, longChain, _ := GenerateChainWithGenesis(genesis, engine, 80, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{1})
|
||||
})
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -2650,7 +2656,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Import the shared chain and the original canonical one
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -2737,21 +2743,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
|
||||
// Generate and import the canonical chain
|
||||
_, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*state.TriesInMemory, nil)
|
||||
|
||||
// Construct a database with freezer enabled
|
||||
datadir := t.TempDir()
|
||||
ancient := path.Join(datadir, "ancient")
|
||||
|
||||
db, err := rawdb.Open(rawdb.OpenOptions{
|
||||
Directory: datadir,
|
||||
AncientsDirectory: ancient,
|
||||
Ephemeral: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create persistent database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -2778,6 +2770,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
|
||||
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
|
||||
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
|
||||
}
|
||||
// Now re-import some old blocks
|
||||
blockToReimport := blocks[5:8]
|
||||
_, err = chain.InsertChain(blockToReimport)
|
||||
if err != nil {
|
||||
@@ -2850,7 +2843,7 @@ func testDeleteCreateRevert(t *testing.T, scheme string) {
|
||||
b.AddTx(tx)
|
||||
})
|
||||
// Import the canonical chain
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -2965,7 +2958,7 @@ func testDeleteRecreateSlots(t *testing.T, scheme string) {
|
||||
// Import the canonical chain
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{
|
||||
Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
||||
}, nil)
|
||||
}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -3047,7 +3040,7 @@ func testDeleteRecreateAccount(t *testing.T, scheme string) {
|
||||
// Import the canonical chain
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{
|
||||
Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
||||
}, nil)
|
||||
}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -3223,7 +3216,7 @@ func testDeleteRecreateSlotsAcrossManyBlocks(t *testing.T, scheme string) {
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{
|
||||
//Debug: true,
|
||||
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
||||
}, nil)
|
||||
}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -3361,7 +3354,7 @@ func testInitThenFailCreateContract(t *testing.T, scheme string) {
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{
|
||||
//Debug: true,
|
||||
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
||||
}, nil)
|
||||
}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -3448,7 +3441,7 @@ func testEIP2718Transition(t *testing.T, scheme string) {
|
||||
})
|
||||
|
||||
// Import the canonical chain
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -3542,7 +3535,7 @@ func testEIP1559Transition(t *testing.T, scheme string) {
|
||||
|
||||
b.AddTx(tx)
|
||||
})
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -3629,7 +3622,7 @@ func TestSetCanonical(t *testing.T) {
|
||||
}
|
||||
|
||||
func testSetCanonical(t *testing.T, scheme string) {
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
@@ -3655,7 +3648,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
defer diskdb.Close()
|
||||
|
||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -3674,7 +3667,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
||||
gen.AddTx(tx)
|
||||
})
|
||||
for _, block := range side {
|
||||
_, err := chain.InsertBlockWithoutSetHead(block, false)
|
||||
err := chain.InsertBlockWithoutSetHead(block)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to insert into chain: %v", err)
|
||||
}
|
||||
@@ -3764,7 +3757,7 @@ func testCanonicalHashMarker(t *testing.T, scheme string) {
|
||||
_, forkB, _ := GenerateChainWithGenesis(gspec, engine, c.forkB, func(i int, gen *BlockGen) {})
|
||||
|
||||
// Initialize test chain
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -3901,7 +3894,7 @@ func testCreateThenDelete(t *testing.T, config *params.ChainConfig) {
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{
|
||||
//Debug: true,
|
||||
//Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
||||
}, nil)
|
||||
}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -4013,7 +4006,7 @@ func TestDeleteThenCreate(t *testing.T) {
|
||||
}
|
||||
})
|
||||
// Import the canonical chain
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -4098,7 +4091,7 @@ func TestTransientStorageReset(t *testing.T) {
|
||||
})
|
||||
|
||||
// Initialize the blockchain with 1153 enabled.
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vmConfig, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vmConfig, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -4193,7 +4186,7 @@ func TestEIP3651(t *testing.T) {
|
||||
|
||||
b.AddTx(tx)
|
||||
})
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil)
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
@@ -4227,90 +4220,3 @@ func TestEIP3651(t *testing.T) {
|
||||
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP6110(t *testing.T) {
|
||||
var (
|
||||
engine = beacon.NewFaker()
|
||||
|
||||
// A sender who makes transactions, has some funds
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
||||
config = *params.AllEthashProtocolChanges
|
||||
gspec = &Genesis{
|
||||
Config: &config,
|
||||
Alloc: types.GenesisAlloc{
|
||||
addr: {Balance: funds},
|
||||
config.DepositContractAddress: {
|
||||
// Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7
|
||||
Code: common.Hex2Bytes("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033"),
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
gspec.Config.BerlinBlock = common.Big0
|
||||
gspec.Config.LondonBlock = common.Big0
|
||||
gspec.Config.TerminalTotalDifficulty = common.Big0
|
||||
gspec.Config.TerminalTotalDifficultyPassed = true
|
||||
gspec.Config.ShanghaiTime = u64(0)
|
||||
gspec.Config.CancunTime = u64(0)
|
||||
gspec.Config.PragueTime = u64(0)
|
||||
signer := types.LatestSigner(gspec.Config)
|
||||
|
||||
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
||||
for i := 0; i < 5; i++ {
|
||||
txdata := &types.DynamicFeeTx{
|
||||
ChainID: gspec.Config.ChainID,
|
||||
Nonce: uint64(i),
|
||||
To: &config.DepositContractAddress,
|
||||
Gas: 500000,
|
||||
GasFeeCap: newGwei(5),
|
||||
GasTipCap: big.NewInt(2),
|
||||
AccessList: nil,
|
||||
Data: []byte{},
|
||||
}
|
||||
tx := types.NewTx(txdata)
|
||||
tx, _ = types.SignTx(tx, signer, key)
|
||||
b.AddTx(tx)
|
||||
}
|
||||
})
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{DisableStack: true}, os.Stderr).Hooks()}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
defer chain.Stop()
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
block := chain.GetBlockByNumber(1)
|
||||
if len(block.Requests()) != 5 {
|
||||
t.Fatalf("failed to retrieve deposits: have %d, want %d", len(block.Requests()), 5)
|
||||
}
|
||||
|
||||
// Verify each index is correct.
|
||||
for want, req := range block.Requests() {
|
||||
d, ok := req.Inner().(*types.Deposit)
|
||||
if !ok {
|
||||
t.Fatalf("expected deposit object")
|
||||
}
|
||||
if got := int(d.PublicKey[0]); got != want {
|
||||
t.Fatalf("invalid pubkey: have %d, want %d", got, want)
|
||||
}
|
||||
if got := int(d.WithdrawalCredentials[0]); got != want {
|
||||
t.Fatalf("invalid withdrawal credentials: have %d, want %d", got, want)
|
||||
}
|
||||
if d.Amount != uint64(want) {
|
||||
t.Fatalf("invalid amounbt: have %d, want %d", d.Amount, want)
|
||||
}
|
||||
if got := int(d.Signature[0]); got != want {
|
||||
t.Fatalf("invalid signature: have %d, want %d", got, want)
|
||||
}
|
||||
if d.Index != uint64(want) {
|
||||
t.Fatalf("invalid index: have %d, want %d", d.Index, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,18 +346,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||
gen(i, b)
|
||||
}
|
||||
|
||||
var requests types.Requests
|
||||
if config.IsPrague(b.header.Number, b.header.Time) {
|
||||
for _, r := range b.receipts {
|
||||
d, err := ParseDepositLogs(r.Logs, config)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse deposit log: %v", err))
|
||||
}
|
||||
requests = append(requests, d...)
|
||||
}
|
||||
}
|
||||
|
||||
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals, Requests: requests}
|
||||
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals}
|
||||
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -379,7 +368,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||
defer triedb.Close()
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
statedb, err := state.New(parent.Root(), state.NewDatabase(triedb, nil))
|
||||
statedb, err := state.New(parent.Root(), state.NewDatabaseWithNodeDB(db, triedb), nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -478,14 +467,15 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
|
||||
panic(fmt.Sprintf("trie write error: %v", err))
|
||||
}
|
||||
|
||||
proofs = append(proofs, block.ExecutionWitness().VerkleProof)
|
||||
keyvals = append(keyvals, block.ExecutionWitness().StateDiff)
|
||||
// TODO uncomment when proof generation is merged
|
||||
// proofs = append(proofs, block.ExecutionWitness().VerkleProof)
|
||||
// keyvals = append(keyvals, block.ExecutionWitness().StateDiff)
|
||||
|
||||
return block, b.receipts
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
statedb, err := state.New(parent.Root(), state.NewDatabase(trdb, nil))
|
||||
statedb, err := state.New(parent.Root(), state.NewDatabaseWithNodeDB(db, trdb), nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||
})
|
||||
|
||||
// Import the chain. This runs all block validation rules.
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec, nil, beacon.NewFaker(), vm.Config{}, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec, nil, beacon.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
if i, err := blockchain.InsertChain(genchain); err != nil {
|
||||
@@ -238,7 +238,7 @@ func ExampleGenerateChain() {
|
||||
})
|
||||
|
||||
// Import the chain. This runs all block validation rules.
|
||||
blockchain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.HashScheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
blockchain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.HashScheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Config: &proConf,
|
||||
}
|
||||
proBc, _ := NewBlockChain(proDb, nil, progspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
proBc, _ := NewBlockChain(proDb, nil, progspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer proBc.Stop()
|
||||
|
||||
conDb := rawdb.NewMemoryDatabase()
|
||||
@@ -62,7 +62,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Config: &conConf,
|
||||
}
|
||||
conBc, _ := NewBlockChain(conDb, nil, congspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
conBc, _ := NewBlockChain(conDb, nil, congspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer conBc.Stop()
|
||||
|
||||
if _, err := proBc.InsertChain(prefix); err != nil {
|
||||
@@ -74,7 +74,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
// Try to expand both pro-fork and non-fork chains iteratively with other camp's blocks
|
||||
for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ {
|
||||
// Create a pro-fork block, and try to feed into the no-fork chain
|
||||
bc, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, congspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
bc, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, congspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
|
||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().Number.Uint64()))
|
||||
for j := 0; j < len(blocks)/2; j++ {
|
||||
@@ -97,7 +97,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err)
|
||||
}
|
||||
// Create a no-fork block, and try to feed into the pro-fork chain
|
||||
bc, _ = NewBlockChain(rawdb.NewMemoryDatabase(), nil, progspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
bc, _ = NewBlockChain(rawdb.NewMemoryDatabase(), nil, progspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
|
||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().Number.Uint64()))
|
||||
for j := 0; j < len(blocks)/2; j++ {
|
||||
@@ -121,7 +121,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
||||
bc, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, congspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
bc, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, congspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().Number.Uint64()))
|
||||
@@ -139,7 +139,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err)
|
||||
}
|
||||
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
||||
bc, _ = NewBlockChain(rawdb.NewMemoryDatabase(), nil, progspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
bc, _ = NewBlockChain(rawdb.NewMemoryDatabase(), nil, progspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().Number.Uint64()))
|
||||
|
||||
113
core/forkchoice.go
Normal file
113
core/forkchoice.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
mrand "math/rand"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// ChainReader defines a small collection of methods needed to access the local
|
||||
// blockchain during header verification. It's implemented by both blockchain
|
||||
// and lightchain.
|
||||
type ChainReader interface {
|
||||
// Config retrieves the header chain's chain configuration.
|
||||
Config() *params.ChainConfig
|
||||
|
||||
// GetTd returns the total difficulty of a local block.
|
||||
GetTd(common.Hash, uint64) *big.Int
|
||||
}
|
||||
|
||||
// ForkChoice is the fork chooser based on the highest total difficulty of the
|
||||
// chain(the fork choice used in the eth1) and the external fork choice (the fork
|
||||
// choice used in the eth2). This main goal of this ForkChoice is not only for
|
||||
// offering fork choice during the eth1/2 merge phase, but also keep the compatibility
|
||||
// for all other proof-of-work networks.
|
||||
type ForkChoice struct {
|
||||
chain ChainReader
|
||||
rand *mrand.Rand
|
||||
|
||||
// preserve is a helper function used in td fork choice.
|
||||
// Miners will prefer to choose the local mined block if the
|
||||
// local td is equal to the extern one. It can be nil for light
|
||||
// client
|
||||
preserve func(header *types.Header) bool
|
||||
}
|
||||
|
||||
func NewForkChoice(chainReader ChainReader, preserve func(header *types.Header) bool) *ForkChoice {
|
||||
// Seed a fast but crypto originating random generator
|
||||
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
|
||||
if err != nil {
|
||||
log.Crit("Failed to initialize random seed", "err", err)
|
||||
}
|
||||
return &ForkChoice{
|
||||
chain: chainReader,
|
||||
rand: mrand.New(mrand.NewSource(seed.Int64())),
|
||||
preserve: preserve,
|
||||
}
|
||||
}
|
||||
|
||||
// ReorgNeeded returns whether the reorg should be applied
|
||||
// based on the given external header and local canonical chain.
|
||||
// In the td mode, the new head is chosen if the corresponding
|
||||
// total difficulty is higher. In the extern mode, the trusted
|
||||
// header is always selected as the head.
|
||||
func (f *ForkChoice) ReorgNeeded(current *types.Header, extern *types.Header) (bool, error) {
|
||||
var (
|
||||
localTD = f.chain.GetTd(current.Hash(), current.Number.Uint64())
|
||||
externTd = f.chain.GetTd(extern.Hash(), extern.Number.Uint64())
|
||||
)
|
||||
if localTD == nil || externTd == nil {
|
||||
return false, errors.New("missing td")
|
||||
}
|
||||
// Accept the new header as the chain head if the transition
|
||||
// is already triggered. We assume all the headers after the
|
||||
// transition come from the trusted consensus layer.
|
||||
if ttd := f.chain.Config().TerminalTotalDifficulty; ttd != nil && ttd.Cmp(externTd) <= 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// If the total difficulty is higher than our known, add it to the canonical chain
|
||||
if diff := externTd.Cmp(localTD); diff > 0 {
|
||||
return true, nil
|
||||
} else if diff < 0 {
|
||||
return false, nil
|
||||
}
|
||||
// Local and external difficulty is identical.
|
||||
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
||||
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
||||
reorg := false
|
||||
externNum, localNum := extern.Number.Uint64(), current.Number.Uint64()
|
||||
if externNum < localNum {
|
||||
reorg = true
|
||||
} else if externNum == localNum {
|
||||
var currentPreserve, externPreserve bool
|
||||
if f.preserve != nil {
|
||||
currentPreserve, externPreserve = f.preserve(current), f.preserve(extern)
|
||||
}
|
||||
reorg = !currentPreserve && (externPreserve || f.rand.Float64() < 0.5)
|
||||
}
|
||||
return reorg, nil
|
||||
}
|
||||
@@ -80,6 +80,25 @@ func TestCreation(t *testing.T) {
|
||||
{50000000, 2000000000, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}}, // Future Cancun block
|
||||
},
|
||||
},
|
||||
// Goerli test cases
|
||||
{
|
||||
params.GoerliChainConfig,
|
||||
core.DefaultGoerliGenesisBlock().ToBlock(),
|
||||
[]testcase{
|
||||
{0, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block
|
||||
{1561650, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Last Petersburg block
|
||||
{1561651, 0, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // First Istanbul block
|
||||
{4460643, 0, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // Last Istanbul block
|
||||
{4460644, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // First Berlin block
|
||||
{5000000, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // Last Berlin block
|
||||
{5062605, 0, ID{Hash: checksumToBytes(0xB8C6299D), Next: 1678832736}}, // First London block
|
||||
{6000000, 1678832735, ID{Hash: checksumToBytes(0xB8C6299D), Next: 1678832736}}, // Last London block
|
||||
{6000001, 1678832736, ID{Hash: checksumToBytes(0xf9843abf), Next: 1705473120}}, // First Shanghai block
|
||||
{6500002, 1705473119, ID{Hash: checksumToBytes(0xf9843abf), Next: 1705473120}}, // Last Shanghai block
|
||||
{6500003, 1705473120, ID{Hash: checksumToBytes(0x70cc14e2), Next: 0}}, // First Cancun block
|
||||
{6500003, 2705473120, ID{Hash: checksumToBytes(0x70cc14e2), Next: 0}}, // Future Cancun block
|
||||
},
|
||||
},
|
||||
// Sepolia test cases
|
||||
{
|
||||
params.SepoliaChainConfig,
|
||||
|
||||
@@ -127,8 +127,8 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
||||
}
|
||||
// Create an ephemeral in-memory database for computing hash,
|
||||
// all the derived states will be discarded to not pollute disk.
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
statedb, err := state.New(types.EmptyRootHash, state.NewDatabase(triedb.NewDatabase(db, config), nil))
|
||||
db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), config)
|
||||
statedb, err := state.New(types.EmptyRootHash, db, nil)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
@@ -145,12 +145,13 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
||||
return statedb.Commit(0, false)
|
||||
}
|
||||
|
||||
// flushAlloc is very similar with hash, but the main difference is all the
|
||||
// generated states will be persisted into the given database.
|
||||
func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database) (common.Hash, error) {
|
||||
statedb, err := state.New(types.EmptyRootHash, state.NewDatabase(triedb, nil))
|
||||
// flushAlloc is very similar with hash, but the main difference is all the generated
|
||||
// states will be persisted into the given database. Also, the genesis state
|
||||
// specification will be flushed as well.
|
||||
func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Database, blockhash common.Hash) error {
|
||||
statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
return err
|
||||
}
|
||||
for addr, account := range *ga {
|
||||
if account.Balance != nil {
|
||||
@@ -166,15 +167,21 @@ func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database) (common.Hash, e
|
||||
}
|
||||
root, err := statedb.Commit(0, false)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
return err
|
||||
}
|
||||
// Commit newly generated states into disk if it's not empty.
|
||||
if root != types.EmptyRootHash {
|
||||
if err := triedb.Commit(root, true); err != nil {
|
||||
return common.Hash{}, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
return root, nil
|
||||
// Marshal the genesis state specification and persist.
|
||||
blob, err := json.Marshal(ga)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawdb.WriteGenesisStateSpec(db, blockhash, blob)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.GenesisAlloc, err error) {
|
||||
@@ -196,6 +203,8 @@ func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.Gene
|
||||
switch blockhash {
|
||||
case params.MainnetGenesisHash:
|
||||
genesis = DefaultGenesisBlock()
|
||||
case params.GoerliGenesisHash:
|
||||
genesis = DefaultGoerliGenesisBlock()
|
||||
case params.SepoliaGenesisHash:
|
||||
genesis = DefaultSepoliaGenesisBlock()
|
||||
case params.HoleskyGenesisHash:
|
||||
@@ -398,6 +407,8 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||
return params.HoleskyChainConfig
|
||||
case ghash == params.SepoliaGenesisHash:
|
||||
return params.SepoliaChainConfig
|
||||
case ghash == params.GoerliGenesisHash:
|
||||
return params.GoerliChainConfig
|
||||
default:
|
||||
return params.AllEthashProtocolChanges
|
||||
}
|
||||
@@ -415,11 +426,6 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return g.toBlockWithRoot(root)
|
||||
}
|
||||
|
||||
// toBlockWithRoot constructs the genesis block with the given genesis state root.
|
||||
func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
|
||||
head := &types.Header{
|
||||
Number: new(big.Int).SetUint64(g.Number),
|
||||
Nonce: types.EncodeNonce(g.Nonce),
|
||||
@@ -447,10 +453,7 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
|
||||
head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee)
|
||||
}
|
||||
}
|
||||
var (
|
||||
withdrawals []*types.Withdrawal
|
||||
requests types.Requests
|
||||
)
|
||||
var withdrawals []*types.Withdrawal
|
||||
if conf := g.Config; conf != nil {
|
||||
num := big.NewInt(int64(g.Number))
|
||||
if conf.IsShanghai(num, g.Timestamp) {
|
||||
@@ -472,18 +475,15 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
|
||||
head.BlobGasUsed = new(uint64)
|
||||
}
|
||||
}
|
||||
if conf.IsPrague(num, g.Timestamp) {
|
||||
head.RequestsHash = &types.EmptyRequestsHash
|
||||
requests = make(types.Requests, 0)
|
||||
}
|
||||
}
|
||||
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals, Requests: requests}, nil, trie.NewStackTrie(nil))
|
||||
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil))
|
||||
}
|
||||
|
||||
// Commit writes the block and state of a genesis specification to the database.
|
||||
// The block is committed as the canonical head block.
|
||||
func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) {
|
||||
if g.Number != 0 {
|
||||
block := g.ToBlock()
|
||||
if block.Number().Sign() != 0 {
|
||||
return nil, errors.New("can't commit genesis block with number > 0")
|
||||
}
|
||||
config := g.Config
|
||||
@@ -493,22 +493,15 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo
|
||||
if err := config.CheckConfigForkOrder(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config.Clique != nil && len(g.ExtraData) < 32+crypto.SignatureLength {
|
||||
if config.Clique != nil && len(block.Extra()) < 32+crypto.SignatureLength {
|
||||
return nil, errors.New("can't start clique chain without signers")
|
||||
}
|
||||
// flush the data to disk and compute the state root
|
||||
root, err := flushAlloc(&g.Alloc, triedb)
|
||||
if err != nil {
|
||||
// All the checks has passed, flushAlloc the states derived from the genesis
|
||||
// specification as well as the specification itself into the provided
|
||||
// database.
|
||||
if err := flushAlloc(&g.Alloc, db, triedb, block.Hash()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block := g.toBlockWithRoot(root)
|
||||
|
||||
// Marshal the genesis state specification and persist.
|
||||
blob, err := json.Marshal(g.Alloc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawdb.WriteGenesisStateSpec(db, block.Hash(), blob)
|
||||
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
|
||||
rawdb.WriteBlock(db, block)
|
||||
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
|
||||
@@ -542,6 +535,18 @@ func DefaultGenesisBlock() *Genesis {
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultGoerliGenesisBlock returns the Görli network genesis block.
|
||||
func DefaultGoerliGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
Config: params.GoerliChainConfig,
|
||||
Timestamp: 1548854791,
|
||||
ExtraData: hexutil.MustDecode("0x22466c6578692069732061207468696e6722202d204166726900000000000000e0a2bd4258d2768837baa26a28fe71dc079f84c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
GasLimit: 10485760,
|
||||
Difficulty: big.NewInt(1),
|
||||
Alloc: decodePrealloc(goerliAllocData),
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultSepoliaGenesisBlock returns the Sepolia network genesis block.
|
||||
func DefaultSepoliaGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
@@ -590,8 +595,6 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
||||
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
|
||||
// Pre-deploy EIP-4788 system contract
|
||||
params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0},
|
||||
// Pre-deploy EIP-2935 history contract.
|
||||
params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0},
|
||||
},
|
||||
}
|
||||
if faucet != nil {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -35,6 +35,15 @@ import (
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
)
|
||||
|
||||
func TestInvalidCliqueConfig(t *testing.T) {
|
||||
block := DefaultGoerliGenesisBlock()
|
||||
block.ExtraData = []byte{}
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
if _, err := block.Commit(db, triedb.NewDatabase(db, nil)); err == nil {
|
||||
t.Fatal("Expected error on invalid clique config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupGenesis(t *testing.T) {
|
||||
testSetupGenesis(t, rawdb.HashScheme)
|
||||
testSetupGenesis(t, rawdb.PathScheme)
|
||||
@@ -96,15 +105,15 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||
wantConfig: customg.Config,
|
||||
},
|
||||
{
|
||||
name: "custom block in DB, genesis == sepolia",
|
||||
name: "custom block in DB, genesis == goerli",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||
customg.Commit(db, tdb)
|
||||
return SetupGenesisBlock(db, tdb, DefaultSepoliaGenesisBlock())
|
||||
return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
|
||||
},
|
||||
wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash},
|
||||
wantHash: params.SepoliaGenesisHash,
|
||||
wantConfig: params.SepoliaChainConfig,
|
||||
wantErr: &GenesisMismatchError{Stored: customghash, New: params.GoerliGenesisHash},
|
||||
wantHash: params.GoerliGenesisHash,
|
||||
wantConfig: params.GoerliChainConfig,
|
||||
},
|
||||
{
|
||||
name: "compatible config in DB",
|
||||
@@ -124,7 +133,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||
oldcustomg.Commit(db, tdb)
|
||||
|
||||
bc, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), &oldcustomg, nil, ethash.NewFullFaker(), vm.Config{}, nil)
|
||||
bc, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), &oldcustomg, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
||||
defer bc.Stop()
|
||||
|
||||
_, blocks, _ := GenerateChainWithGenesis(&oldcustomg, ethash.NewFaker(), 4, nil)
|
||||
@@ -175,6 +184,7 @@ func TestGenesisHashes(t *testing.T) {
|
||||
want common.Hash
|
||||
}{
|
||||
{DefaultGenesisBlock(), params.MainnetGenesisHash},
|
||||
{DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
|
||||
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
|
||||
} {
|
||||
// Test via MustCommit
|
||||
@@ -294,14 +304,14 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
expected := common.FromHex("4a83dc39eb688dbcfaf581d60e82de18f875e38786ebce5833342011d6fef37b")
|
||||
expected := common.FromHex("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b")
|
||||
got := genesis.ToBlock().Root().Bytes()
|
||||
if !bytes.Equal(got, expected) {
|
||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
||||
}
|
||||
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
triedb := triedb.NewDatabase(db, triedb.VerkleDefaults)
|
||||
triedb := triedb.NewDatabase(db, &triedb.Config{IsVerkle: true, PathDB: pathdb.Defaults})
|
||||
block := genesis.MustCommit(db, triedb)
|
||||
if !bytes.Equal(block.Root().Bytes(), expected) {
|
||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, block.Root())
|
||||
@@ -311,8 +321,8 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||
if !triedb.IsVerkle() {
|
||||
t.Fatalf("expected trie to be verkle")
|
||||
}
|
||||
vdb := rawdb.NewTable(db, string(rawdb.VerklePrefix))
|
||||
if !rawdb.HasAccountTrieNode(vdb, nil) {
|
||||
|
||||
if !rawdb.HasAccountTrieNode(db, nil) {
|
||||
t.Fatal("could not find node")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
|
||||
// without the real blocks. Hence, writing headers directly should only be done
|
||||
// in two scenarios: pure-header mode of operation (light clients), or properly
|
||||
// separated header/block phases (non-archive clients).
|
||||
func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header) (*headerWriteResult, error) {
|
||||
func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *ForkChoice) (*headerWriteResult, error) {
|
||||
inserted, err := hc.WriteHeaders(headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -270,6 +270,15 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header) (*headerW
|
||||
lastHeader: lastHeader,
|
||||
}
|
||||
)
|
||||
// Ask the fork choicer if the reorg is necessary
|
||||
if reorg, err := forker.ReorgNeeded(hc.CurrentHeader(), lastHeader); err != nil {
|
||||
return nil, err
|
||||
} else if !reorg {
|
||||
if inserted != 0 {
|
||||
result.status = SideStatTy
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
// Special case, all the inserted headers are already on the canonical
|
||||
// header chain, skip the reorg operation.
|
||||
if hc.GetCanonicalHash(lastHeader.Number.Uint64()) == lastHash && lastHeader.Number.Uint64() <= hc.CurrentHeader().Number.Uint64() {
|
||||
@@ -327,11 +336,11 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header) (int, error) {
|
||||
//
|
||||
// The returned 'write status' says if the inserted headers are part of the canonical chain
|
||||
// or a side chain.
|
||||
func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time) (WriteStatus, error) {
|
||||
func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time, forker *ForkChoice) (WriteStatus, error) {
|
||||
if hc.procInterrupt() {
|
||||
return 0, errors.New("aborted")
|
||||
}
|
||||
res, err := hc.writeHeadersAndSetHead(chain)
|
||||
res, err := hc.writeHeadersAndSetHead(chain, forker)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ func verifyUnbrokenCanonchain(hc *HeaderChain) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func testInsert(t *testing.T, hc *HeaderChain, chain []*types.Header, wantStatus WriteStatus, wantErr error) {
|
||||
func testInsert(t *testing.T, hc *HeaderChain, chain []*types.Header, wantStatus WriteStatus, wantErr error, forker *ForkChoice) {
|
||||
t.Helper()
|
||||
|
||||
status, err := hc.InsertHeaderChain(chain, time.Now())
|
||||
status, err := hc.InsertHeaderChain(chain, time.Now(), forker)
|
||||
if status != wantStatus {
|
||||
t.Errorf("wrong write status from InsertHeaderChain: got %v, want %v", status, wantStatus)
|
||||
}
|
||||
@@ -83,33 +83,34 @@ func TestHeaderInsertion(t *testing.T) {
|
||||
// chain B: G->A1->B1...B128
|
||||
chainB := makeHeaderChain(gspec.Config, chainA[0], 128, ethash.NewFaker(), genDb, 10)
|
||||
|
||||
forker := NewForkChoice(hc, nil)
|
||||
// Inserting 64 headers on an empty chain, expecting
|
||||
// 1 callbacks, 1 canon-status, 0 sidestatus,
|
||||
testInsert(t, hc, chainA[:64], CanonStatTy, nil)
|
||||
testInsert(t, hc, chainA[:64], CanonStatTy, nil, forker)
|
||||
|
||||
// Inserting 64 identical headers, expecting
|
||||
// 0 callbacks, 0 canon-status, 0 sidestatus,
|
||||
testInsert(t, hc, chainA[:64], NonStatTy, nil)
|
||||
testInsert(t, hc, chainA[:64], NonStatTy, nil, forker)
|
||||
|
||||
// Inserting the same some old, some new headers
|
||||
// 1 callbacks, 1 canon, 0 side
|
||||
testInsert(t, hc, chainA[32:96], CanonStatTy, nil)
|
||||
testInsert(t, hc, chainA[32:96], CanonStatTy, nil, forker)
|
||||
|
||||
// Inserting headers from chain B, overtaking the canon chain blindly
|
||||
testInsert(t, hc, chainB[0:32], CanonStatTy, nil)
|
||||
// Inserting side blocks, but not overtaking the canon chain
|
||||
testInsert(t, hc, chainB[0:32], SideStatTy, nil, forker)
|
||||
|
||||
// Inserting more headers on chain B, but we don't have the parent
|
||||
testInsert(t, hc, chainB[34:36], NonStatTy, consensus.ErrUnknownAncestor)
|
||||
// Inserting more side blocks, but we don't have the parent
|
||||
testInsert(t, hc, chainB[34:36], NonStatTy, consensus.ErrUnknownAncestor, forker)
|
||||
|
||||
// Inserting more headers on chain B, extend the canon chain
|
||||
testInsert(t, hc, chainB[32:97], CanonStatTy, nil)
|
||||
// Inserting more sideblocks, overtaking the canon chain
|
||||
testInsert(t, hc, chainB[32:97], CanonStatTy, nil, forker)
|
||||
|
||||
// Inserting more headers on chain A, taking back the canonicality
|
||||
testInsert(t, hc, chainA[90:100], CanonStatTy, nil)
|
||||
// Inserting more A-headers, taking back the canonicality
|
||||
testInsert(t, hc, chainA[90:100], CanonStatTy, nil, forker)
|
||||
|
||||
// And B becomes canon again
|
||||
testInsert(t, hc, chainB[97:107], CanonStatTy, nil)
|
||||
testInsert(t, hc, chainB[97:107], CanonStatTy, nil, forker)
|
||||
|
||||
// And B becomes even longer
|
||||
testInsert(t, hc, chainB[107:128], CanonStatTy, nil)
|
||||
testInsert(t, hc, chainB[107:128], CanonStatTy, nil, forker)
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, has
|
||||
|
||||
// ReadStateScheme reads the state scheme of persistent state, or none
|
||||
// if the state is not present in database.
|
||||
func ReadStateScheme(db ethdb.Database) string {
|
||||
func ReadStateScheme(db ethdb.Reader) string {
|
||||
// Check if state in path-based scheme is present.
|
||||
if HasAccountTrieNode(db, nil) {
|
||||
return PathScheme
|
||||
@@ -255,16 +255,6 @@ func ReadStateScheme(db ethdb.Database) string {
|
||||
if id := ReadPersistentStateID(db); id != 0 {
|
||||
return PathScheme
|
||||
}
|
||||
// Check if verkle state in path-based scheme is present.
|
||||
vdb := NewTable(db, string(VerklePrefix))
|
||||
if HasAccountTrieNode(vdb, nil) {
|
||||
return PathScheme
|
||||
}
|
||||
// The root node of verkle might be deleted during the initial snap sync,
|
||||
// check the persistent state id then.
|
||||
if id := ReadPersistentStateID(vdb); id != 0 {
|
||||
return PathScheme
|
||||
}
|
||||
// In a hash-based scheme, the genesis state is consistently stored
|
||||
// on the disk. To assess the scheme of the persistent state, it
|
||||
// suffices to inspect the scheme of the genesis state.
|
||||
@@ -302,10 +292,6 @@ func ParseStateScheme(provided string, disk ethdb.Database) (string, error) {
|
||||
log.Info("State scheme set to already existing", "scheme", stored)
|
||||
return stored, nil // reuse scheme of persistent scheme
|
||||
}
|
||||
// If state scheme is specified, ensure it's valid.
|
||||
if provided != HashScheme && provided != PathScheme {
|
||||
return "", fmt.Errorf("invalid state scheme %s", provided)
|
||||
}
|
||||
// If state scheme is specified, ensure it's compatible with
|
||||
// persistent state.
|
||||
if stored == "" || provided == stored {
|
||||
|
||||
@@ -72,13 +72,12 @@ var stateFreezerNoSnappy = map[string]bool{
|
||||
|
||||
// The list of identifiers of ancient stores.
|
||||
var (
|
||||
ChainFreezerName = "chain" // the folder name of chain segment ancient store.
|
||||
MerkleStateFreezerName = "state" // the folder name of state history ancient store.
|
||||
VerkleStateFreezerName = "state_verkle" // the folder name of state history ancient store.
|
||||
ChainFreezerName = "chain" // the folder name of chain segment ancient store.
|
||||
StateFreezerName = "state" // the folder name of reverse diff ancient store.
|
||||
)
|
||||
|
||||
// freezers the collections of all builtin freezers.
|
||||
var freezers = []string{ChainFreezerName, MerkleStateFreezerName, VerkleStateFreezerName}
|
||||
var freezers = []string{ChainFreezerName, StateFreezerName}
|
||||
|
||||
// NewStateFreezer initializes the ancient store for state history.
|
||||
//
|
||||
@@ -86,15 +85,9 @@ var freezers = []string{ChainFreezerName, MerkleStateFreezerName, VerkleStateFre
|
||||
// state freezer (e.g. dev mode).
|
||||
// - if non-empty directory is given, initializes the regular file-based
|
||||
// state freezer.
|
||||
func NewStateFreezer(ancientDir string, verkle bool, readOnly bool) (ethdb.ResettableAncientStore, error) {
|
||||
func NewStateFreezer(ancientDir string, readOnly bool) (ethdb.ResettableAncientStore, error) {
|
||||
if ancientDir == "" {
|
||||
return NewMemoryFreezer(readOnly, stateFreezerNoSnappy), nil
|
||||
}
|
||||
var name string
|
||||
if verkle {
|
||||
name = filepath.Join(ancientDir, VerkleStateFreezerName)
|
||||
} else {
|
||||
name = filepath.Join(ancientDir, MerkleStateFreezerName)
|
||||
}
|
||||
return newResettableFreezer(name, "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
|
||||
return newResettableFreezer(filepath.Join(ancientDir, StateFreezerName), "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
|
||||
}
|
||||
|
||||
@@ -88,12 +88,12 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) {
|
||||
}
|
||||
infos = append(infos, info)
|
||||
|
||||
case MerkleStateFreezerName, VerkleStateFreezerName:
|
||||
case StateFreezerName:
|
||||
datadir, err := db.AncientDatadir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := NewStateFreezer(datadir, freezer == VerkleStateFreezerName, true)
|
||||
f, err := NewStateFreezer(datadir, true)
|
||||
if err != nil {
|
||||
continue // might be possible the state freezer is not existent
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func InspectFreezerTable(ancient string, freezerName string, tableName string, s
|
||||
switch freezerName {
|
||||
case ChainFreezerName:
|
||||
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
|
||||
case MerkleStateFreezerName, VerkleStateFreezerName:
|
||||
case StateFreezerName:
|
||||
path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
|
||||
default:
|
||||
return fmt.Errorf("unknown freezer, supported ones: %v", freezers)
|
||||
|
||||
@@ -149,6 +149,12 @@ func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReaderOp) error)
|
||||
return fn(db)
|
||||
}
|
||||
|
||||
// MigrateTable processes the entries in a given table in sequence
|
||||
// converting them to a new format if they're of an old format.
|
||||
func (db *nofreezedb) MigrateTable(kind string, convert convertLegacyFn) error {
|
||||
return errNotSupported
|
||||
}
|
||||
|
||||
// AncientDatadir returns an error as we don't have a backing chain freezer.
|
||||
func (db *nofreezedb) AncientDatadir() (string, error) {
|
||||
return "", errNotSupported
|
||||
@@ -475,10 +481,6 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
beaconHeaders stat
|
||||
cliqueSnaps stat
|
||||
|
||||
// Verkle statistics
|
||||
verkleTries stat
|
||||
verkleStateLookups stat
|
||||
|
||||
// Les statistic
|
||||
chtTrieNodes stat
|
||||
bloomTrieNodes stat
|
||||
@@ -548,24 +550,6 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
bytes.HasPrefix(key, BloomTrieIndexPrefix) ||
|
||||
bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub
|
||||
bloomTrieNodes.Add(size)
|
||||
|
||||
// Verkle trie data is detected, determine the sub-category
|
||||
case bytes.HasPrefix(key, VerklePrefix):
|
||||
remain := key[len(VerklePrefix):]
|
||||
switch {
|
||||
case IsAccountTrieNode(remain):
|
||||
verkleTries.Add(size)
|
||||
case bytes.HasPrefix(remain, stateIDPrefix) && len(remain) == len(stateIDPrefix)+common.HashLength:
|
||||
verkleStateLookups.Add(size)
|
||||
case bytes.Equal(remain, persistentStateIDKey):
|
||||
metadata.Add(size)
|
||||
case bytes.Equal(remain, trieJournalKey):
|
||||
metadata.Add(size)
|
||||
case bytes.Equal(remain, snapSyncStatusFlagKey):
|
||||
metadata.Add(size)
|
||||
default:
|
||||
unaccounted.Add(size)
|
||||
}
|
||||
default:
|
||||
var accounted bool
|
||||
for _, meta := range [][]byte{
|
||||
@@ -606,8 +590,6 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
{"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()},
|
||||
{"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()},
|
||||
{"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()},
|
||||
{"Key-Value store", "Verkle trie nodes", verkleTries.Size(), verkleTries.Count()},
|
||||
{"Key-Value store", "Verkle trie state lookups", verkleStateLookups.Size(), verkleStateLookups.Count()},
|
||||
{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
|
||||
{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
|
||||
{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
|
||||
|
||||
@@ -24,7 +24,9 @@ import (
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
@@ -52,11 +54,13 @@ var (
|
||||
// freezerTableSize defines the maximum size of freezer data files.
|
||||
const freezerTableSize = 2 * 1000 * 1000 * 1000
|
||||
|
||||
// Freezer is an append-only database to store immutable ordered data into
|
||||
// flat files:
|
||||
// Freezer is a memory mapped append-only database to store immutable ordered
|
||||
// data into flat files:
|
||||
//
|
||||
// - The append-only nature ensures that disk writes are minimized.
|
||||
// - The in-order data ensures that disk reads are always optimized.
|
||||
// - The append-only nature ensures that disk writes are minimized.
|
||||
// - The memory mapping ensures we can max out system memory for caching without
|
||||
// reserving it for go-ethereum. This would also reduce the memory requirements
|
||||
// of Geth, and thus also GC overhead.
|
||||
type Freezer struct {
|
||||
frozen atomic.Uint64 // Number of items already frozen
|
||||
tail atomic.Uint64 // Number of the first stored item in the freezer
|
||||
@@ -150,7 +154,7 @@ func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize ui
|
||||
return freezer, nil
|
||||
}
|
||||
|
||||
// Close terminates the chain freezer, closing all the data files.
|
||||
// Close terminates the chain freezer, unmapping all the data files.
|
||||
func (f *Freezer) Close() error {
|
||||
f.writeLock.Lock()
|
||||
defer f.writeLock.Unlock()
|
||||
@@ -385,3 +389,115 @@ func (f *Freezer) repair() error {
|
||||
f.tail.Store(tail)
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertLegacyFn takes a raw freezer entry in an older format and
|
||||
// returns it in the new format.
|
||||
type convertLegacyFn = func([]byte) ([]byte, error)
|
||||
|
||||
// MigrateTable processes the entries in a given table in sequence
|
||||
// converting them to a new format if they're of an old format.
|
||||
func (f *Freezer) MigrateTable(kind string, convert convertLegacyFn) error {
|
||||
if f.readonly {
|
||||
return errReadOnly
|
||||
}
|
||||
f.writeLock.Lock()
|
||||
defer f.writeLock.Unlock()
|
||||
|
||||
table, ok := f.tables[kind]
|
||||
if !ok {
|
||||
return errUnknownTable
|
||||
}
|
||||
// forEach iterates every entry in the table serially and in order, calling `fn`
|
||||
// with the item as argument. If `fn` returns an error the iteration stops
|
||||
// and that error will be returned.
|
||||
forEach := func(t *freezerTable, offset uint64, fn func(uint64, []byte) error) error {
|
||||
var (
|
||||
items = t.items.Load()
|
||||
batchSize = uint64(1024)
|
||||
maxBytes = uint64(1024 * 1024)
|
||||
)
|
||||
for i := offset; i < items; {
|
||||
if i+batchSize > items {
|
||||
batchSize = items - i
|
||||
}
|
||||
data, err := t.RetrieveItems(i, batchSize, maxBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for j, item := range data {
|
||||
if err := fn(i+uint64(j), item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
i += uint64(len(data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// TODO(s1na): This is a sanity-check since as of now no process does tail-deletion. But the migration
|
||||
// process assumes no deletion at tail and needs to be modified to account for that.
|
||||
if table.itemOffset.Load() > 0 || table.itemHidden.Load() > 0 {
|
||||
return errors.New("migration not supported for tail-deleted freezers")
|
||||
}
|
||||
ancientsPath := filepath.Dir(table.index.Name())
|
||||
// Set up new dir for the migrated table, the content of which
|
||||
// we'll at the end move over to the ancients dir.
|
||||
migrationPath := filepath.Join(ancientsPath, "migration")
|
||||
newTable, err := newFreezerTable(migrationPath, kind, table.noCompression, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
batch = newTable.newBatch()
|
||||
out []byte
|
||||
start = time.Now()
|
||||
logged = time.Now()
|
||||
offset = newTable.items.Load()
|
||||
)
|
||||
if offset > 0 {
|
||||
log.Info("found previous migration attempt", "migrated", offset)
|
||||
}
|
||||
// Iterate through entries and transform them
|
||||
if err := forEach(table, offset, func(i uint64, blob []byte) error {
|
||||
if i%10000 == 0 && time.Since(logged) > 16*time.Second {
|
||||
log.Info("Processing legacy elements", "count", i, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
logged = time.Now()
|
||||
}
|
||||
out, err = convert(blob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batch.AppendRaw(i, out); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batch.commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Replacing old table files with migrated ones", "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
// Release and delete old table files. Note this won't
|
||||
// delete the index file.
|
||||
table.releaseFilesAfter(0, true)
|
||||
|
||||
if err := newTable.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
files, err := os.ReadDir(migrationPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Move migrated files to ancients dir.
|
||||
for _, f := range files {
|
||||
// This will replace the old index file as a side-effect.
|
||||
if err := os.Rename(filepath.Join(migrationPath, f.Name()), filepath.Join(ancientsPath, f.Name())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Delete by now empty dir.
|
||||
if err := os.Remove(migrationPath); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -394,6 +394,13 @@ func (f *MemoryFreezer) Sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MigrateTable processes and migrates entries of a given table to a new format.
|
||||
// The second argument is a function that takes a raw entry and returns it
|
||||
// in the newest format.
|
||||
func (f *MemoryFreezer) MigrateTable(string, func([]byte) ([]byte, error)) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// Close releases all the sources held by the memory freezer. It will panic if
|
||||
// any following invocation is made to a closed freezer.
|
||||
func (f *MemoryFreezer) Close() error {
|
||||
|
||||
@@ -22,11 +22,10 @@ import (
|
||||
)
|
||||
|
||||
func TestReadWriteFreezerTableMeta(t *testing.T) {
|
||||
f, err := os.CreateTemp(t.TempDir(), "*")
|
||||
f, err := os.CreateTemp(os.TempDir(), "*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create file %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
err = writeMetadata(f, newMetadata(100))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write metadata %v", err)
|
||||
@@ -44,11 +43,10 @@ func TestReadWriteFreezerTableMeta(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInitializeFreezerTableMeta(t *testing.T) {
|
||||
f, err := os.CreateTemp(t.TempDir(), "*")
|
||||
f, err := os.CreateTemp(os.TempDir(), "*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create file %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
meta, err := loadMetadata(f, uint64(100))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read metadata %v", err)
|
||||
|
||||
@@ -202,6 +202,15 @@ func (f *resettableFreezer) Sync() error {
|
||||
return f.freezer.Sync()
|
||||
}
|
||||
|
||||
// MigrateTable processes the entries in a given table in sequence
|
||||
// converting them to a new format if they're of an old format.
|
||||
func (f *resettableFreezer) MigrateTable(kind string, convert convertLegacyFn) error {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
|
||||
return f.freezer.MigrateTable(kind, convert)
|
||||
}
|
||||
|
||||
// cleanup removes the directory located in the specified path
|
||||
// has the name with deletion marker suffix.
|
||||
func cleanup(path string) error {
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
@@ -377,6 +379,87 @@ func checkAncientCount(t *testing.T, f *Freezer, kind string, n uint64) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenameWindows(t *testing.T) {
|
||||
var (
|
||||
fname = "file.bin"
|
||||
fname2 = "file2.bin"
|
||||
data = []byte{1, 2, 3, 4}
|
||||
data2 = []byte{2, 3, 4, 5}
|
||||
data3 = []byte{3, 5, 6, 7}
|
||||
dataLen = 4
|
||||
)
|
||||
|
||||
// Create 2 temp dirs
|
||||
dir1 := t.TempDir()
|
||||
dir2 := t.TempDir()
|
||||
|
||||
// Create file in dir1 and fill with data
|
||||
f, err := os.Create(filepath.Join(dir1, fname))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f2, err := os.Create(filepath.Join(dir1, fname2))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f3, err := os.Create(filepath.Join(dir2, fname2))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := f.Write(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := f2.Write(data2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := f3.Write(data3); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f2.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f3.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Rename(f.Name(), filepath.Join(dir2, fname)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Rename(f2.Name(), filepath.Join(dir2, fname2)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check file contents
|
||||
f, err = os.Open(filepath.Join(dir2, fname))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
buf := make([]byte, dataLen)
|
||||
if _, err := f.Read(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(buf, data) {
|
||||
t.Errorf("unexpected file contents. Got %v\n", buf)
|
||||
}
|
||||
|
||||
f, err = os.Open(filepath.Join(dir2, fname2))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
if _, err := f.Read(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(buf, data2) {
|
||||
t.Errorf("unexpected file contents. Got %v\n", buf)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFreezerCloseSync(t *testing.T) {
|
||||
t.Parallel()
|
||||
f, _ := newFreezerForTesting(t, map[string]bool{"a": true, "b": true})
|
||||
|
||||
@@ -117,13 +117,6 @@ var (
|
||||
TrieNodeStoragePrefix = []byte("O") // TrieNodeStoragePrefix + accountHash + hexPath -> trie node
|
||||
stateIDPrefix = []byte("L") // stateIDPrefix + state root -> state id
|
||||
|
||||
// VerklePrefix is the database prefix for Verkle trie data, which includes:
|
||||
// (a) Trie nodes
|
||||
// (b) In-memory trie node journal
|
||||
// (c) Persistent state ID
|
||||
// (d) State ID lookups, etc.
|
||||
VerklePrefix = []byte("v")
|
||||
|
||||
PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage
|
||||
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
||||
genesisPrefix = []byte("ethereum-genesis-") // genesis state prefix for the db
|
||||
|
||||
@@ -113,6 +113,12 @@ func (t *table) Sync() error {
|
||||
return t.db.Sync()
|
||||
}
|
||||
|
||||
// MigrateTable processes the entries in a given table in sequence
|
||||
// converting them to a new format if they're of an old format.
|
||||
func (t *table) MigrateTable(kind string, convert convertLegacyFn) error {
|
||||
return t.db.MigrateTable(kind, convert)
|
||||
}
|
||||
|
||||
// AncientDatadir returns the ancient datadir of the underlying database.
|
||||
func (t *table) AncientDatadir() (string, error) {
|
||||
return t.db.AncientDatadir()
|
||||
@@ -194,6 +200,13 @@ func (t *table) NewBatchWithSize(size int) ethdb.Batch {
|
||||
return &tableBatch{t.db.NewBatchWithSize(size), t.prefix}
|
||||
}
|
||||
|
||||
// NewSnapshot creates a database snapshot based on the current state.
|
||||
// The created snapshot will not be affected by all following mutations
|
||||
// happened on the database.
|
||||
func (t *table) NewSnapshot() (ethdb.Snapshot, error) {
|
||||
return t.db.NewSnapshot()
|
||||
}
|
||||
|
||||
// tableBatch is a wrapper around a database batch that prefixes each key access
|
||||
// with a pre-configured string.
|
||||
type tableBatch struct {
|
||||
|
||||
@@ -28,8 +28,8 @@ import (
|
||||
|
||||
// mode specifies how a tree location has been accessed
|
||||
// for the byte value:
|
||||
// * the first bit is set if the branch has been read
|
||||
// * the second bit is set if the branch has been edited
|
||||
// * the first bit is set if the branch has been edited
|
||||
// * the second bit is set if the branch has been read
|
||||
type mode byte
|
||||
|
||||
const (
|
||||
@@ -94,8 +94,11 @@ func (ae *AccessEvents) Copy() *AccessEvents {
|
||||
// member fields of an account.
|
||||
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
||||
var gas uint64
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite)
|
||||
return gas
|
||||
}
|
||||
|
||||
@@ -104,7 +107,8 @@ func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
||||
// call to that account.
|
||||
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
||||
var gas uint64
|
||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.VersionLeafKey, false)
|
||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.CodeSizeLeafKey, false)
|
||||
return gas
|
||||
}
|
||||
|
||||
@@ -112,43 +116,41 @@ func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
||||
// cold balance member fields of the caller and the callee accounts.
|
||||
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
|
||||
var gas uint64
|
||||
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||
return gas
|
||||
}
|
||||
|
||||
// ContractCreateCPreheck charges access costs before
|
||||
// a contract creation is initiated. It is just reads, because the
|
||||
// address collision is done before the transfer, and so no write
|
||||
// are guaranteed to happen at this point.
|
||||
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address) uint64 {
|
||||
var gas uint64
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
||||
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
||||
return gas
|
||||
}
|
||||
|
||||
// ContractCreateInitGas returns the access gas costs for the initialization of
|
||||
// a contract creation.
|
||||
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address) uint64 {
|
||||
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 {
|
||||
var gas uint64
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, true)
|
||||
if createSendsValue {
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
||||
}
|
||||
return gas
|
||||
}
|
||||
|
||||
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey, false)
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey, true)
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
|
||||
}
|
||||
|
||||
// AddTxDestination adds the member fields of the sender account to the access event list,
|
||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue)
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false)
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, sendsValue)
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, false)
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
|
||||
}
|
||||
|
||||
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
||||
@@ -273,12 +275,39 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
||||
return statelessGasCharged
|
||||
}
|
||||
|
||||
// BasicDataGas adds the account's basic data to the accessed data, and returns the
|
||||
// VersionGas adds the account's version to the accessed data, and returns the
|
||||
// amount of gas that it costs.
|
||||
// Note that an access in write mode implies an access in read mode, whereas an
|
||||
// access in read mode does not imply an access in write mode.
|
||||
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
||||
func (ae *AccessEvents) VersionGas(addr common.Address, isWrite bool) uint64 {
|
||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
|
||||
}
|
||||
|
||||
// BalanceGas adds the account's balance to the accessed data, and returns the
|
||||
// amount of gas that it costs.
|
||||
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||
// read mode does not imply an access in write mode.
|
||||
func (ae *AccessEvents) BalanceGas(addr common.Address, isWrite bool) uint64 {
|
||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite)
|
||||
}
|
||||
|
||||
// NonceGas adds the account's nonce to the accessed data, and returns the
|
||||
// amount of gas that it costs.
|
||||
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||
// read mode does not imply an access in write mode.
|
||||
func (ae *AccessEvents) NonceGas(addr common.Address, isWrite bool) uint64 {
|
||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite)
|
||||
}
|
||||
|
||||
// CodeSizeGas adds the account's code size to the accessed data, and returns the
|
||||
// amount of gas that it costs.
|
||||
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||
// read mode does not imply an access in write mode.
|
||||
func (ae *AccessEvents) CodeSizeGas(addr common.Address, isWrite bool) uint64 {
|
||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite)
|
||||
}
|
||||
|
||||
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
||||
@@ -287,5 +316,5 @@ func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||
// read mode does not imply an access in write mode.
|
||||
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
|
||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
|
||||
}
|
||||
|
||||
@@ -40,43 +40,55 @@ func TestAccountHeaderGas(t *testing.T) {
|
||||
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||
|
||||
// Check cold read cost
|
||||
gas := ae.BasicDataGas(testAddr, false)
|
||||
gas := ae.VersionGas(testAddr, false)
|
||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||
}
|
||||
|
||||
// Check warm read cost
|
||||
gas = ae.BasicDataGas(testAddr, false)
|
||||
gas = ae.VersionGas(testAddr, false)
|
||||
if gas != 0 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||
}
|
||||
|
||||
// Check cold read costs in the same group no longer incur the branch read cost
|
||||
gas = ae.BalanceGas(testAddr, false)
|
||||
if gas != params.WitnessChunkReadCost {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||
}
|
||||
gas = ae.NonceGas(testAddr, false)
|
||||
if gas != params.WitnessChunkReadCost {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||
}
|
||||
gas = ae.CodeSizeGas(testAddr, false)
|
||||
if gas != params.WitnessChunkReadCost {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||
}
|
||||
gas = ae.CodeHashGas(testAddr, false)
|
||||
if gas != params.WitnessChunkReadCost {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||
}
|
||||
|
||||
// Check cold write cost
|
||||
gas = ae.BasicDataGas(testAddr, true)
|
||||
gas = ae.VersionGas(testAddr, true)
|
||||
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||
}
|
||||
|
||||
// Check warm write cost
|
||||
gas = ae.BasicDataGas(testAddr, true)
|
||||
gas = ae.VersionGas(testAddr, true)
|
||||
if gas != 0 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||
}
|
||||
|
||||
// Check a write without a read charges both read and write costs
|
||||
gas = ae.BasicDataGas(testAddr2, true)
|
||||
gas = ae.BalanceGas(testAddr2, true)
|
||||
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||
}
|
||||
|
||||
// Check that a write followed by a read charges nothing
|
||||
gas = ae.BasicDataGas(testAddr2, false)
|
||||
gas = ae.BalanceGas(testAddr2, false)
|
||||
if gas != 0 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||
}
|
||||
@@ -100,13 +112,13 @@ func TestContractCreateInitGas(t *testing.T) {
|
||||
}
|
||||
|
||||
// Check cold read cost, without a value
|
||||
gas := ae.ContractCreateInitGas(testAddr)
|
||||
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + 2*params.WitnessChunkWriteCost + 2*params.WitnessChunkReadCost; gas != want {
|
||||
gas := ae.ContractCreateInitGas(testAddr, false)
|
||||
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + params.WitnessChunkWriteCost*2 + params.WitnessChunkReadCost*2; gas != want {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||
}
|
||||
|
||||
// Check warm read cost
|
||||
gas = ae.ContractCreateInitGas(testAddr)
|
||||
gas = ae.ContractCreateInitGas(testAddr, false)
|
||||
if gas != 0 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||
}
|
||||
@@ -119,17 +131,17 @@ func TestMessageCallGas(t *testing.T) {
|
||||
|
||||
// Check cold read cost, without a value
|
||||
gas := ae.MessageCallGas(testAddr)
|
||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost*2; gas != want {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||
}
|
||||
|
||||
// Check that reading the basic data and code hash of the same account does not incur the branch read cost
|
||||
gas = ae.BasicDataGas(testAddr, false)
|
||||
// Check that reading the version and code size of the same account does not incur the branch read cost
|
||||
gas = ae.VersionGas(testAddr, false)
|
||||
if gas != 0 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||
}
|
||||
gas = ae.CodeHashGas(testAddr, false)
|
||||
if gas != params.WitnessChunkReadCost {
|
||||
gas = ae.CodeSizeGas(testAddr, false)
|
||||
if gas != 0 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
@@ -46,29 +45,29 @@ const (
|
||||
|
||||
// Database wraps access to tries and contract code.
|
||||
type Database interface {
|
||||
// Reader returns a state reader associated with the specified state root.
|
||||
Reader(root common.Hash) (Reader, error)
|
||||
|
||||
// OpenTrie opens the main account trie.
|
||||
OpenTrie(root common.Hash) (Trie, error)
|
||||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie Trie) (Trie, error)
|
||||
|
||||
// CopyTrie returns an independent copy of the given trie.
|
||||
CopyTrie(Trie) Trie
|
||||
|
||||
// ContractCode retrieves a particular contract's code.
|
||||
ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error)
|
||||
|
||||
// ContractCodeSize retrieves a particular contracts code's size.
|
||||
ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error)
|
||||
|
||||
// DiskDB returns the underlying key-value disk database.
|
||||
DiskDB() ethdb.KeyValueStore
|
||||
|
||||
// PointCache returns the cache holding points used in verkle tree key computation
|
||||
PointCache() *utils.PointCache
|
||||
|
||||
// TrieDB returns the underlying trie database for managing trie nodes.
|
||||
TrieDB() *triedb.Database
|
||||
|
||||
// Snapshot returns the underlying state snapshot.
|
||||
Snapshot() *snapshot.Tree
|
||||
}
|
||||
|
||||
// Trie is a Ethereum Merkle Patricia trie.
|
||||
@@ -95,7 +94,7 @@ type Trie interface {
|
||||
// UpdateAccount abstracts an account write to the trie. It encodes the
|
||||
// provided account object with associated algorithm and then updates it
|
||||
// in the trie with provided address.
|
||||
UpdateAccount(address common.Address, account *types.StateAccount, codeLen int) error
|
||||
UpdateAccount(address common.Address, account *types.StateAccount) error
|
||||
|
||||
// UpdateStorage associates key with value in the trie. If value has length zero,
|
||||
// any existing value is deleted from the trie. The value bytes must not be modified
|
||||
@@ -148,62 +147,47 @@ type Trie interface {
|
||||
IsVerkle() bool
|
||||
}
|
||||
|
||||
// CachingDB is an implementation of Database interface. It leverages both trie and
|
||||
// state snapshot to provide functionalities for state access. It's meant to be a
|
||||
// long-live object and has a few caches inside for sharing between blocks.
|
||||
type CachingDB struct {
|
||||
disk ethdb.KeyValueStore
|
||||
triedb *triedb.Database
|
||||
snap *snapshot.Tree
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
pointCache *utils.PointCache
|
||||
// NewDatabase creates a backing store for state. The returned database is safe for
|
||||
// concurrent use, but does not retain any recent trie nodes in memory. To keep some
|
||||
// historical state in memory, use the NewDatabaseWithConfig constructor.
|
||||
func NewDatabase(db ethdb.Database) Database {
|
||||
return NewDatabaseWithConfig(db, nil)
|
||||
}
|
||||
|
||||
// NewDatabase creates a state database with the provided data sources.
|
||||
func NewDatabase(triedb *triedb.Database, snap *snapshot.Tree) *CachingDB {
|
||||
return &CachingDB{
|
||||
disk: triedb.Disk(),
|
||||
triedb: triedb,
|
||||
snap: snap,
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
// NewDatabaseWithConfig creates a backing store for state. The returned database
|
||||
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
|
||||
// large memory cache.
|
||||
func NewDatabaseWithConfig(db ethdb.Database, config *triedb.Config) Database {
|
||||
return &cachingDB{
|
||||
disk: db,
|
||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
triedb: triedb.NewDatabase(db, config),
|
||||
pointCache: utils.NewPointCache(pointCacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
// NewDatabaseForTesting is similar to NewDatabase, but it initializes the caching
|
||||
// db by using an ephemeral memory db with default config for testing.
|
||||
func NewDatabaseForTesting() *CachingDB {
|
||||
return NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
|
||||
// NewDatabaseWithNodeDB creates a state database with an already initialized node database.
|
||||
func NewDatabaseWithNodeDB(db ethdb.Database, triedb *triedb.Database) Database {
|
||||
return &cachingDB{
|
||||
disk: db,
|
||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
triedb: triedb,
|
||||
pointCache: utils.NewPointCache(pointCacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Reader returns a state reader associated with the specified state root.
|
||||
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||
var readers []Reader
|
||||
|
||||
// Set up the state snapshot reader if available. This feature
|
||||
// is optional and may be partially useful if it's not fully
|
||||
// generated.
|
||||
if db.snap != nil {
|
||||
sr, err := newStateReader(stateRoot, db.snap)
|
||||
if err == nil {
|
||||
readers = append(readers, sr) // snap reader is optional
|
||||
}
|
||||
}
|
||||
// Set up the trie reader, which is expected to always be available
|
||||
// as the gatekeeper unless the state is corrupted.
|
||||
tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
readers = append(readers, tr)
|
||||
|
||||
return newMultiReader(readers...)
|
||||
type cachingDB struct {
|
||||
disk ethdb.KeyValueStore
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
triedb *triedb.Database
|
||||
pointCache *utils.PointCache
|
||||
}
|
||||
|
||||
// OpenTrie opens the main account trie at a specific root hash.
|
||||
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
if db.triedb.IsVerkle() {
|
||||
return trie.NewVerkleTrie(root, db.triedb, db.pointCache)
|
||||
}
|
||||
@@ -215,7 +199,7 @@ func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
}
|
||||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
|
||||
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
|
||||
// In the verkle case, there is only one tree. But the two-tree structure
|
||||
// is hardcoded in the codebase. So we need to return the same trie in this
|
||||
// case.
|
||||
@@ -229,8 +213,20 @@ func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// CopyTrie returns an independent copy of the given trie.
|
||||
func (db *cachingDB) CopyTrie(t Trie) Trie {
|
||||
switch t := t.(type) {
|
||||
case *trie.StateTrie:
|
||||
return t.Copy()
|
||||
case *trie.VerkleTrie:
|
||||
return t.Copy()
|
||||
default:
|
||||
panic(fmt.Errorf("unknown trie type %T", t))
|
||||
}
|
||||
}
|
||||
|
||||
// ContractCode retrieves a particular contract's code.
|
||||
func (db *CachingDB) ContractCode(address common.Address, codeHash common.Hash) ([]byte, error) {
|
||||
func (db *cachingDB) ContractCode(address common.Address, codeHash common.Hash) ([]byte, error) {
|
||||
code, _ := db.codeCache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
return code, nil
|
||||
@@ -247,7 +243,7 @@ func (db *CachingDB) ContractCode(address common.Address, codeHash common.Hash)
|
||||
// ContractCodeWithPrefix retrieves a particular contract's code. If the
|
||||
// code can't be found in the cache, then check the existence with **new**
|
||||
// db scheme.
|
||||
func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error) {
|
||||
func (db *cachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error) {
|
||||
code, _ := db.codeCache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
return code, nil
|
||||
@@ -262,7 +258,7 @@ func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash com
|
||||
}
|
||||
|
||||
// ContractCodeSize retrieves a particular contracts code's size.
|
||||
func (db *CachingDB) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
||||
func (db *cachingDB) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
||||
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
|
||||
return cached, nil
|
||||
}
|
||||
@@ -270,29 +266,17 @@ func (db *CachingDB) ContractCodeSize(addr common.Address, codeHash common.Hash)
|
||||
return len(code), err
|
||||
}
|
||||
|
||||
// DiskDB returns the underlying key-value disk database.
|
||||
func (db *cachingDB) DiskDB() ethdb.KeyValueStore {
|
||||
return db.disk
|
||||
}
|
||||
|
||||
// TrieDB retrieves any intermediate trie-node caching layer.
|
||||
func (db *CachingDB) TrieDB() *triedb.Database {
|
||||
func (db *cachingDB) TrieDB() *triedb.Database {
|
||||
return db.triedb
|
||||
}
|
||||
|
||||
// PointCache returns the cache of evaluated curve points.
|
||||
func (db *CachingDB) PointCache() *utils.PointCache {
|
||||
func (db *cachingDB) PointCache() *utils.PointCache {
|
||||
return db.pointCache
|
||||
}
|
||||
|
||||
// Snapshot returns the underlying state snapshot.
|
||||
func (db *CachingDB) Snapshot() *snapshot.Tree {
|
||||
return db.snap
|
||||
}
|
||||
|
||||
// mustCopyTrie returns a deep-copied trie.
|
||||
func mustCopyTrie(t Trie) Trie {
|
||||
switch t := t.(type) {
|
||||
case *trie.StateTrie:
|
||||
return t.Copy()
|
||||
case *trie.VerkleTrie:
|
||||
return t.Copy()
|
||||
default:
|
||||
panic(fmt.Errorf("unknown trie type %T", t))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func testNodeIteratorCoverage(t *testing.T, scheme string) {
|
||||
db, sdb, ndb, root, _ := makeTestState(scheme)
|
||||
ndb.Commit(root, false)
|
||||
|
||||
state, err := New(root, sdb)
|
||||
state, err := New(root, sdb, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create state trie at %x: %v", root, err)
|
||||
}
|
||||
|
||||
@@ -17,21 +17,12 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
type revision struct {
|
||||
id int
|
||||
journalIndex int
|
||||
}
|
||||
|
||||
// journalEntry is a modification entry in the state change journal that can be
|
||||
// reverted on demand.
|
||||
type journalEntry interface {
|
||||
@@ -51,9 +42,6 @@ type journalEntry interface {
|
||||
type journal struct {
|
||||
entries []journalEntry // Current changes tracked by the journal
|
||||
dirties map[common.Address]int // Dirty accounts and the number of changes
|
||||
|
||||
validRevisions []revision
|
||||
nextRevisionId int
|
||||
}
|
||||
|
||||
// newJournal creates a new initialized journal.
|
||||
@@ -63,40 +51,6 @@ func newJournal() *journal {
|
||||
}
|
||||
}
|
||||
|
||||
// reset clears the journal, after this operation the journal can be used anew.
|
||||
// It is semantically similar to calling 'newJournal', but the underlying slices
|
||||
// can be reused.
|
||||
func (j *journal) reset() {
|
||||
j.entries = j.entries[:0]
|
||||
j.validRevisions = j.validRevisions[:0]
|
||||
clear(j.dirties)
|
||||
j.nextRevisionId = 0
|
||||
}
|
||||
|
||||
// snapshot returns an identifier for the current revision of the state.
|
||||
func (j *journal) snapshot() int {
|
||||
id := j.nextRevisionId
|
||||
j.nextRevisionId++
|
||||
j.validRevisions = append(j.validRevisions, revision{id, j.length()})
|
||||
return id
|
||||
}
|
||||
|
||||
// revertToSnapshot reverts all state changes made since the given revision.
|
||||
func (j *journal) revertToSnapshot(revid int, s *StateDB) {
|
||||
// Find the snapshot in the stack of valid snapshots.
|
||||
idx := sort.Search(len(j.validRevisions), func(i int) bool {
|
||||
return j.validRevisions[i].id >= revid
|
||||
})
|
||||
if idx == len(j.validRevisions) || j.validRevisions[idx].id != revid {
|
||||
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
|
||||
}
|
||||
snapshot := j.validRevisions[idx].journalIndex
|
||||
|
||||
// Replay the journal to undo changes and remove invalidated snapshots
|
||||
j.revert(s, snapshot)
|
||||
j.validRevisions = j.validRevisions[:idx]
|
||||
}
|
||||
|
||||
// append inserts a new modification entry to the end of the change journal.
|
||||
func (j *journal) append(entry journalEntry) {
|
||||
j.entries = append(j.entries, entry)
|
||||
@@ -141,122 +95,48 @@ func (j *journal) copy() *journal {
|
||||
entries = append(entries, j.entries[i].copy())
|
||||
}
|
||||
return &journal{
|
||||
entries: entries,
|
||||
dirties: maps.Clone(j.dirties),
|
||||
validRevisions: slices.Clone(j.validRevisions),
|
||||
nextRevisionId: j.nextRevisionId,
|
||||
entries: entries,
|
||||
dirties: maps.Clone(j.dirties),
|
||||
}
|
||||
}
|
||||
|
||||
func (j *journal) logChange(txHash common.Hash) {
|
||||
j.append(addLogChange{txhash: txHash})
|
||||
}
|
||||
|
||||
func (j *journal) createObject(addr common.Address) {
|
||||
j.append(createObjectChange{account: addr})
|
||||
}
|
||||
|
||||
func (j *journal) createContract(addr common.Address) {
|
||||
j.append(createContractChange{account: addr})
|
||||
}
|
||||
|
||||
func (j *journal) destruct(addr common.Address) {
|
||||
j.append(selfDestructChange{account: addr})
|
||||
}
|
||||
|
||||
func (j *journal) storageChange(addr common.Address, key, prev, origin common.Hash) {
|
||||
j.append(storageChange{
|
||||
account: addr,
|
||||
key: key,
|
||||
prevvalue: prev,
|
||||
origvalue: origin,
|
||||
})
|
||||
}
|
||||
|
||||
func (j *journal) transientStateChange(addr common.Address, key, prev common.Hash) {
|
||||
j.append(transientStorageChange{
|
||||
account: addr,
|
||||
key: key,
|
||||
prevalue: prev,
|
||||
})
|
||||
}
|
||||
|
||||
func (j *journal) refundChange(previous uint64) {
|
||||
j.append(refundChange{prev: previous})
|
||||
}
|
||||
|
||||
func (j *journal) balanceChange(addr common.Address, previous *uint256.Int) {
|
||||
j.append(balanceChange{
|
||||
account: addr,
|
||||
prev: previous.Clone(),
|
||||
})
|
||||
}
|
||||
|
||||
func (j *journal) setCode(address common.Address) {
|
||||
j.append(codeChange{account: address})
|
||||
}
|
||||
|
||||
func (j *journal) nonceChange(address common.Address, prev uint64) {
|
||||
j.append(nonceChange{
|
||||
account: address,
|
||||
prev: prev,
|
||||
})
|
||||
}
|
||||
|
||||
func (j *journal) touchChange(address common.Address) {
|
||||
j.append(touchChange{
|
||||
account: address,
|
||||
})
|
||||
if address == ripemd {
|
||||
// Explicitly put it in the dirty-cache, which is otherwise generated from
|
||||
// flattened journals.
|
||||
j.dirty(address)
|
||||
}
|
||||
}
|
||||
|
||||
func (j *journal) accessListAddAccount(addr common.Address) {
|
||||
j.append(accessListAddAccountChange{addr})
|
||||
}
|
||||
|
||||
func (j *journal) accessListAddSlot(addr common.Address, slot common.Hash) {
|
||||
j.append(accessListAddSlotChange{
|
||||
address: addr,
|
||||
slot: slot,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
// Changes to the account trie.
|
||||
createObjectChange struct {
|
||||
account common.Address
|
||||
account *common.Address
|
||||
}
|
||||
|
||||
// createContractChange represents an account becoming a contract-account.
|
||||
// This event happens prior to executing initcode. The journal-event simply
|
||||
// manages the created-flag, in order to allow same-tx destruction.
|
||||
createContractChange struct {
|
||||
account common.Address
|
||||
}
|
||||
|
||||
selfDestructChange struct {
|
||||
account common.Address
|
||||
account *common.Address
|
||||
prev bool // whether account had already self-destructed
|
||||
prevbalance *uint256.Int
|
||||
}
|
||||
|
||||
// Changes to individual accounts.
|
||||
balanceChange struct {
|
||||
account common.Address
|
||||
account *common.Address
|
||||
prev *uint256.Int
|
||||
}
|
||||
nonceChange struct {
|
||||
account common.Address
|
||||
account *common.Address
|
||||
prev uint64
|
||||
}
|
||||
storageChange struct {
|
||||
account common.Address
|
||||
account *common.Address
|
||||
key common.Hash
|
||||
prevvalue common.Hash
|
||||
origvalue common.Hash
|
||||
}
|
||||
codeChange struct {
|
||||
account common.Address
|
||||
account *common.Address
|
||||
prevcode, prevhash []byte
|
||||
}
|
||||
|
||||
// Changes to other state values.
|
||||
@@ -266,32 +146,35 @@ type (
|
||||
addLogChange struct {
|
||||
txhash common.Hash
|
||||
}
|
||||
addPreimageChange struct {
|
||||
hash common.Hash
|
||||
}
|
||||
touchChange struct {
|
||||
account common.Address
|
||||
account *common.Address
|
||||
}
|
||||
|
||||
// Changes to the access list
|
||||
accessListAddAccountChange struct {
|
||||
address common.Address
|
||||
address *common.Address
|
||||
}
|
||||
accessListAddSlotChange struct {
|
||||
address common.Address
|
||||
slot common.Hash
|
||||
address *common.Address
|
||||
slot *common.Hash
|
||||
}
|
||||
|
||||
// Changes to transient storage
|
||||
transientStorageChange struct {
|
||||
account common.Address
|
||||
account *common.Address
|
||||
key, prevalue common.Hash
|
||||
}
|
||||
)
|
||||
|
||||
func (ch createObjectChange) revert(s *StateDB) {
|
||||
delete(s.stateObjects, ch.account)
|
||||
delete(s.stateObjects, *ch.account)
|
||||
}
|
||||
|
||||
func (ch createObjectChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch createObjectChange) copy() journalEntry {
|
||||
@@ -315,19 +198,22 @@ func (ch createContractChange) copy() journalEntry {
|
||||
}
|
||||
|
||||
func (ch selfDestructChange) revert(s *StateDB) {
|
||||
obj := s.getStateObject(ch.account)
|
||||
obj := s.getStateObject(*ch.account)
|
||||
if obj != nil {
|
||||
obj.selfDestructed = false
|
||||
obj.selfDestructed = ch.prev
|
||||
obj.setBalance(ch.prevbalance)
|
||||
}
|
||||
}
|
||||
|
||||
func (ch selfDestructChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch selfDestructChange) copy() journalEntry {
|
||||
return selfDestructChange{
|
||||
account: ch.account,
|
||||
account: ch.account,
|
||||
prev: ch.prev,
|
||||
prevbalance: new(uint256.Int).Set(ch.prevbalance),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,7 +223,7 @@ func (ch touchChange) revert(s *StateDB) {
|
||||
}
|
||||
|
||||
func (ch touchChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch touchChange) copy() journalEntry {
|
||||
@@ -347,11 +233,11 @@ func (ch touchChange) copy() journalEntry {
|
||||
}
|
||||
|
||||
func (ch balanceChange) revert(s *StateDB) {
|
||||
s.getStateObject(ch.account).setBalance(ch.prev)
|
||||
s.getStateObject(*ch.account).setBalance(ch.prev)
|
||||
}
|
||||
|
||||
func (ch balanceChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch balanceChange) copy() journalEntry {
|
||||
@@ -362,11 +248,11 @@ func (ch balanceChange) copy() journalEntry {
|
||||
}
|
||||
|
||||
func (ch nonceChange) revert(s *StateDB) {
|
||||
s.getStateObject(ch.account).setNonce(ch.prev)
|
||||
s.getStateObject(*ch.account).setNonce(ch.prev)
|
||||
}
|
||||
|
||||
func (ch nonceChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch nonceChange) copy() journalEntry {
|
||||
@@ -377,23 +263,27 @@ func (ch nonceChange) copy() journalEntry {
|
||||
}
|
||||
|
||||
func (ch codeChange) revert(s *StateDB) {
|
||||
s.getStateObject(ch.account).setCode(types.EmptyCodeHash, nil)
|
||||
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
|
||||
}
|
||||
|
||||
func (ch codeChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch codeChange) copy() journalEntry {
|
||||
return codeChange{account: ch.account}
|
||||
return codeChange{
|
||||
account: ch.account,
|
||||
prevhash: common.CopyBytes(ch.prevhash),
|
||||
prevcode: common.CopyBytes(ch.prevcode),
|
||||
}
|
||||
}
|
||||
|
||||
func (ch storageChange) revert(s *StateDB) {
|
||||
s.getStateObject(ch.account).setState(ch.key, ch.prevvalue, ch.origvalue)
|
||||
s.getStateObject(*ch.account).setState(ch.key, ch.prevvalue, ch.origvalue)
|
||||
}
|
||||
|
||||
func (ch storageChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch storageChange) copy() journalEntry {
|
||||
@@ -405,7 +295,7 @@ func (ch storageChange) copy() journalEntry {
|
||||
}
|
||||
|
||||
func (ch transientStorageChange) revert(s *StateDB) {
|
||||
s.setTransientState(ch.account, ch.key, ch.prevalue)
|
||||
s.setTransientState(*ch.account, ch.key, ch.prevalue)
|
||||
}
|
||||
|
||||
func (ch transientStorageChange) dirtied() *common.Address {
|
||||
@@ -454,6 +344,20 @@ func (ch addLogChange) copy() journalEntry {
|
||||
}
|
||||
}
|
||||
|
||||
func (ch addPreimageChange) revert(s *StateDB) {
|
||||
delete(s.preimages, ch.hash)
|
||||
}
|
||||
|
||||
func (ch addPreimageChange) dirtied() *common.Address {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch addPreimageChange) copy() journalEntry {
|
||||
return addPreimageChange{
|
||||
hash: ch.hash,
|
||||
}
|
||||
}
|
||||
|
||||
func (ch accessListAddAccountChange) revert(s *StateDB) {
|
||||
/*
|
||||
One important invariant here, is that whenever a (addr, slot) is added, if the
|
||||
@@ -464,7 +368,7 @@ func (ch accessListAddAccountChange) revert(s *StateDB) {
|
||||
(addr) at this point, since no storage adds can remain when come upon
|
||||
a single (addr) change.
|
||||
*/
|
||||
s.accessList.DeleteAddress(ch.address)
|
||||
s.accessList.DeleteAddress(*ch.address)
|
||||
}
|
||||
|
||||
func (ch accessListAddAccountChange) dirtied() *common.Address {
|
||||
@@ -478,7 +382,7 @@ func (ch accessListAddAccountChange) copy() journalEntry {
|
||||
}
|
||||
|
||||
func (ch accessListAddSlotChange) revert(s *StateDB) {
|
||||
s.accessList.DeleteSlot(ch.address, ch.slot)
|
||||
s.accessList.DeleteSlot(*ch.address, *ch.slot)
|
||||
}
|
||||
|
||||
func (ch accessListAddSlotChange) dirtied() *common.Address {
|
||||
|
||||
@@ -19,8 +19,6 @@ package state
|
||||
import "github.com/ethereum/go-ethereum/metrics"
|
||||
|
||||
var (
|
||||
accountReadMeters = metrics.NewRegisteredMeter("state/read/accounts", nil)
|
||||
storageReadMeters = metrics.NewRegisteredMeter("state/read/storage", nil)
|
||||
accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
|
||||
storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
|
||||
accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
|
||||
@@ -29,4 +27,10 @@ var (
|
||||
storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil)
|
||||
accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil)
|
||||
storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil)
|
||||
|
||||
slotDeletionMaxCount = metrics.NewRegisteredGauge("state/delete/storage/max/slot", nil)
|
||||
slotDeletionMaxSize = metrics.NewRegisteredGauge("state/delete/storage/max/size", nil)
|
||||
slotDeletionTimer = metrics.NewRegisteredResettingTimer("state/delete/storage/timer", nil)
|
||||
slotDeletionCount = metrics.NewRegisteredMeter("state/delete/storage/slot", nil)
|
||||
slotDeletionSize = metrics.NewRegisteredMeter("state/delete/storage/size", nil)
|
||||
)
|
||||
|
||||
@@ -270,10 +270,11 @@ func (p *Pruner) Prune(root common.Hash) error {
|
||||
// is the presence of root can indicate the presence of the
|
||||
// entire trie.
|
||||
if !rawdb.HasLegacyTrieNode(p.db, root) {
|
||||
// The special case is for clique based networks, it's possible
|
||||
// that two consecutive blocks will have same root. In this case
|
||||
// snapshot difflayer won't be created. So HEAD-127 may not paired
|
||||
// with head-127 layer. Instead the paired layer is higher than the
|
||||
// The special case is for clique based networks(goerli
|
||||
// and some other private networks), it's possible that two
|
||||
// consecutive blocks will have same root. In this case snapshot
|
||||
// difflayer won't be created. So HEAD-127 may not paired with
|
||||
// head-127 layer. Instead the paired layer is higher than the
|
||||
// bottom-most diff layer. Try to find the bottom-most snapshot
|
||||
// layer with state available.
|
||||
//
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
// Copyright 2024 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"maps"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/utils"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
// Reader defines the interface for accessing accounts and storage slots
|
||||
// associated with a specific state.
|
||||
type Reader interface {
|
||||
// Account retrieves the account associated with a particular address.
|
||||
//
|
||||
// - Returns a nil account if it does not exist
|
||||
// - Returns an error only if an unexpected issue occurs
|
||||
// - The returned account is safe to modify after the call
|
||||
Account(addr common.Address) (*types.StateAccount, error)
|
||||
|
||||
// Storage retrieves the storage slot associated with a particular account
|
||||
// address and slot key.
|
||||
//
|
||||
// - Returns an empty slot if it does not exist
|
||||
// - Returns an error only if an unexpected issue occurs
|
||||
// - The returned storage slot is safe to modify after the call
|
||||
Storage(addr common.Address, slot common.Hash) (common.Hash, error)
|
||||
|
||||
// Copy returns a deep-copied state reader.
|
||||
Copy() Reader
|
||||
}
|
||||
|
||||
// stateReader is a wrapper over the state snapshot and implements the Reader
|
||||
// interface. It provides an efficient way to access flat state.
|
||||
type stateReader struct {
|
||||
snap snapshot.Snapshot
|
||||
buff crypto.KeccakState
|
||||
}
|
||||
|
||||
// newStateReader constructs a flat state reader with on the specified state root.
|
||||
func newStateReader(root common.Hash, snaps *snapshot.Tree) (*stateReader, error) {
|
||||
snap := snaps.Snapshot(root)
|
||||
if snap == nil {
|
||||
return nil, errors.New("snapshot is not available")
|
||||
}
|
||||
return &stateReader{
|
||||
snap: snap,
|
||||
buff: crypto.NewKeccakState(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Account implements Reader, retrieving the account specified by the address.
|
||||
//
|
||||
// An error will be returned if the associated snapshot is already stale or
|
||||
// the requested account is not yet covered by the snapshot.
|
||||
//
|
||||
// The returned account might be nil if it's not existent.
|
||||
func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
ret, err := r.snap.Account(crypto.HashData(r.buff, addr.Bytes()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret == nil {
|
||||
return nil, nil
|
||||
}
|
||||
acct := &types.StateAccount{
|
||||
Nonce: ret.Nonce,
|
||||
Balance: ret.Balance,
|
||||
CodeHash: ret.CodeHash,
|
||||
Root: common.BytesToHash(ret.Root),
|
||||
}
|
||||
if len(acct.CodeHash) == 0 {
|
||||
acct.CodeHash = types.EmptyCodeHash.Bytes()
|
||||
}
|
||||
if acct.Root == (common.Hash{}) {
|
||||
acct.Root = types.EmptyRootHash
|
||||
}
|
||||
return acct, nil
|
||||
}
|
||||
|
||||
// Storage implements Reader, retrieving the storage slot specified by the
|
||||
// address and slot key.
|
||||
//
|
||||
// An error will be returned if the associated snapshot is already stale or
|
||||
// the requested storage slot is not yet covered by the snapshot.
|
||||
//
|
||||
// The returned storage slot might be empty if it's not existent.
|
||||
func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
addrHash := crypto.HashData(r.buff, addr.Bytes())
|
||||
slotHash := crypto.HashData(r.buff, key.Bytes())
|
||||
ret, err := r.snap.Storage(addrHash, slotHash)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
if len(ret) == 0 {
|
||||
return common.Hash{}, nil
|
||||
}
|
||||
// Perform the rlp-decode as the slot value is RLP-encoded in the state
|
||||
// snapshot.
|
||||
_, content, _, err := rlp.Split(ret)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
var value common.Hash
|
||||
value.SetBytes(content)
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Copy implements Reader, returning a deep-copied snap reader.
|
||||
func (r *stateReader) Copy() Reader {
|
||||
return &stateReader{
|
||||
snap: r.snap,
|
||||
buff: crypto.NewKeccakState(),
|
||||
}
|
||||
}
|
||||
|
||||
// trieReader implements the Reader interface, providing functions to access
|
||||
// state from the referenced trie.
|
||||
type trieReader struct {
|
||||
root common.Hash // State root which uniquely represent a state
|
||||
db *triedb.Database // Database for loading trie
|
||||
buff crypto.KeccakState // Buffer for keccak256 hashing
|
||||
mainTrie Trie // Main trie, resolved in constructor
|
||||
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved
|
||||
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved
|
||||
}
|
||||
|
||||
// trieReader constructs a trie reader of the specific state. An error will be
|
||||
// returned if the associated trie specified by root is not existent.
|
||||
func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache) (*trieReader, error) {
|
||||
var (
|
||||
tr Trie
|
||||
err error
|
||||
)
|
||||
if !db.IsVerkle() {
|
||||
tr, err = trie.NewStateTrie(trie.StateTrieID(root), db)
|
||||
} else {
|
||||
tr, err = trie.NewVerkleTrie(root, db, cache)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &trieReader{
|
||||
root: root,
|
||||
db: db,
|
||||
buff: crypto.NewKeccakState(),
|
||||
mainTrie: tr,
|
||||
subRoots: make(map[common.Address]common.Hash),
|
||||
subTries: make(map[common.Address]Trie),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Account implements Reader, retrieving the account specified by the address.
|
||||
//
|
||||
// An error will be returned if the trie state is corrupted. An nil account
|
||||
// will be returned if it's not existent in the trie.
|
||||
func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
account, err := r.mainTrie.GetAccount(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if account == nil {
|
||||
r.subRoots[addr] = types.EmptyRootHash
|
||||
} else {
|
||||
r.subRoots[addr] = account.Root
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// Storage implements Reader, retrieving the storage slot specified by the
|
||||
// address and slot key.
|
||||
//
|
||||
// An error will be returned if the trie state is corrupted. An empty storage
|
||||
// slot will be returned if it's not existent in the trie.
|
||||
func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
var (
|
||||
tr Trie
|
||||
found bool
|
||||
value common.Hash
|
||||
)
|
||||
if r.db.IsVerkle() {
|
||||
tr = r.mainTrie
|
||||
} else {
|
||||
tr, found = r.subTries[addr]
|
||||
if !found {
|
||||
root, ok := r.subRoots[addr]
|
||||
|
||||
// The storage slot is accessed without account caching. It's unexpected
|
||||
// behavior but try to resolve the account first anyway.
|
||||
if !ok {
|
||||
_, err := r.Account(addr)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
root = r.subRoots[addr]
|
||||
}
|
||||
var err error
|
||||
tr, err = trie.NewStateTrie(trie.StorageTrieID(r.root, crypto.HashData(r.buff, addr.Bytes()), root), r.db)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
r.subTries[addr] = tr
|
||||
}
|
||||
}
|
||||
ret, err := tr.GetStorage(addr, key.Bytes())
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
value.SetBytes(ret)
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Copy implements Reader, returning a deep-copied trie reader.
|
||||
func (r *trieReader) Copy() Reader {
|
||||
tries := make(map[common.Address]Trie)
|
||||
for addr, tr := range r.subTries {
|
||||
tries[addr] = mustCopyTrie(tr)
|
||||
}
|
||||
return &trieReader{
|
||||
root: r.root,
|
||||
db: r.db,
|
||||
buff: crypto.NewKeccakState(),
|
||||
mainTrie: mustCopyTrie(r.mainTrie),
|
||||
subRoots: maps.Clone(r.subRoots),
|
||||
subTries: tries,
|
||||
}
|
||||
}
|
||||
|
||||
// multiReader is the aggregation of a list of Reader interface, providing state
|
||||
// access by leveraging all readers. The checking priority is determined by the
|
||||
// position in the reader list.
|
||||
type multiReader struct {
|
||||
readers []Reader // List of readers, sorted by checking priority
|
||||
}
|
||||
|
||||
// newMultiReader constructs a multiReader instance with the given readers. The
|
||||
// priority among readers is assumed to be sorted. Note, it must contain at least
|
||||
// one reader for constructing a multiReader.
|
||||
func newMultiReader(readers ...Reader) (*multiReader, error) {
|
||||
if len(readers) == 0 {
|
||||
return nil, errors.New("empty reader set")
|
||||
}
|
||||
return &multiReader{
|
||||
readers: readers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Account implementing Reader interface, retrieving the account associated with
|
||||
// a particular address.
|
||||
//
|
||||
// - Returns a nil account if it does not exist
|
||||
// - Returns an error only if an unexpected issue occurs
|
||||
// - The returned account is safe to modify after the call
|
||||
func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
var errs []error
|
||||
for _, reader := range r.readers {
|
||||
acct, err := reader.Account(addr)
|
||||
if err == nil {
|
||||
return acct, nil
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
|
||||
// Storage implementing Reader interface, retrieving the storage slot associated
|
||||
// with a particular account address and slot key.
|
||||
//
|
||||
// - Returns an empty slot if it does not exist
|
||||
// - Returns an error only if an unexpected issue occurs
|
||||
// - The returned storage slot is safe to modify after the call
|
||||
func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||
var errs []error
|
||||
for _, reader := range r.readers {
|
||||
slot, err := reader.Storage(addr, slot)
|
||||
if err == nil {
|
||||
return slot, nil
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return common.Hash{}, errors.Join(errs...)
|
||||
}
|
||||
|
||||
// Copy implementing Reader interface, returning a deep-copied state reader.
|
||||
func (r *multiReader) Copy() Reader {
|
||||
var readers []Reader
|
||||
for _, reader := range r.readers {
|
||||
readers = append(readers, reader.Copy())
|
||||
}
|
||||
return &multiReader{readers: readers}
|
||||
}
|
||||
@@ -74,14 +74,6 @@ func (dl *diskLayer) Stale() bool {
|
||||
return dl.stale
|
||||
}
|
||||
|
||||
// markStale sets the stale flag as true.
|
||||
func (dl *diskLayer) markStale() {
|
||||
dl.lock.Lock()
|
||||
defer dl.lock.Unlock()
|
||||
|
||||
dl.stale = true
|
||||
}
|
||||
|
||||
// Account directly retrieves the account associated with a particular hash in
|
||||
// the snapshot slim data format.
|
||||
func (dl *diskLayer) Account(hash common.Hash) (*types.SlimAccount, error) {
|
||||
@@ -183,18 +175,3 @@ func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro
|
||||
func (dl *diskLayer) Update(blockHash common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
|
||||
return newDiffLayer(dl, blockHash, destructs, accounts, storage)
|
||||
}
|
||||
|
||||
// stopGeneration aborts the state snapshot generation if it is currently running.
|
||||
func (dl *diskLayer) stopGeneration() {
|
||||
dl.lock.RLock()
|
||||
generating := dl.genMarker != nil
|
||||
dl.lock.RUnlock()
|
||||
if !generating {
|
||||
return
|
||||
}
|
||||
if dl.genAbort != nil {
|
||||
abort := make(chan *generatorStats)
|
||||
dl.genAbort <- abort
|
||||
<-abort
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,10 +631,16 @@ func generateAccounts(ctx *generatorContext, dl *diskLayer, accMarker []byte) er
|
||||
accMarker = nil
|
||||
return nil
|
||||
}
|
||||
// Always reset the initial account range as 1 whenever recover from the
|
||||
// interruption. TODO(rjl493456442) can we remove it?
|
||||
var accountRange = accountCheckRange
|
||||
if len(accMarker) > 0 {
|
||||
accountRange = 1
|
||||
}
|
||||
origin := common.CopyBytes(accMarker)
|
||||
for {
|
||||
id := trie.StateTrieID(dl.root)
|
||||
exhausted, last, err := dl.generateRange(ctx, id, rawdb.SnapshotAccountPrefix, snapAccount, origin, accountCheckRange, onAccount, types.FullAccountRLP)
|
||||
exhausted, last, err := dl.generateRange(ctx, id, rawdb.SnapshotAccountPrefix, snapAccount, origin, accountRange, onAccount, types.FullAccountRLP)
|
||||
if err != nil {
|
||||
return err // The procedure it aborted, either by external signal or internal error.
|
||||
}
|
||||
@@ -646,6 +652,7 @@ func generateAccounts(ctx *generatorContext, dl *diskLayer, accMarker []byte) er
|
||||
ctx.removeStorageLeft()
|
||||
break
|
||||
}
|
||||
accountRange = accountCheckRange
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -258,11 +258,24 @@ func (t *Tree) Disable() {
|
||||
for _, layer := range t.layers {
|
||||
switch layer := layer.(type) {
|
||||
case *diskLayer:
|
||||
// TODO this function will hang if it's called twice. Will
|
||||
// fix it in the following PRs.
|
||||
layer.stopGeneration()
|
||||
layer.markStale()
|
||||
layer.Release()
|
||||
|
||||
layer.lock.RLock()
|
||||
generating := layer.genMarker != nil
|
||||
layer.lock.RUnlock()
|
||||
if !generating {
|
||||
// Generator is already aborted or finished
|
||||
break
|
||||
}
|
||||
// If the base layer is generating, abort it
|
||||
if layer.genAbort != nil {
|
||||
abort := make(chan *generatorStats)
|
||||
layer.genAbort <- abort
|
||||
<-abort
|
||||
}
|
||||
// Layer should be inactive now, mark it as stale
|
||||
layer.lock.Lock()
|
||||
layer.stale = true
|
||||
layer.lock.Unlock()
|
||||
|
||||
case *diffLayer:
|
||||
// If the layer is a simple diff, simply mark as stale
|
||||
@@ -717,11 +730,16 @@ func (t *Tree) Rebuild(root common.Hash) {
|
||||
for _, layer := range t.layers {
|
||||
switch layer := layer.(type) {
|
||||
case *diskLayer:
|
||||
// TODO this function will hang if it's called twice. Will
|
||||
// fix it in the following PRs.
|
||||
layer.stopGeneration()
|
||||
layer.markStale()
|
||||
layer.Release()
|
||||
// If the base layer is generating, abort it and save
|
||||
if layer.genAbort != nil {
|
||||
abort := make(chan *generatorStats)
|
||||
layer.genAbort <- abort
|
||||
<-abort
|
||||
}
|
||||
// Layer should be inactive now, mark it as stale
|
||||
layer.lock.Lock()
|
||||
layer.stale = true
|
||||
layer.lock.Unlock()
|
||||
|
||||
case *diffLayer:
|
||||
// If the layer is a simple diff, simply mark as stale
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user