mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
Merge branch 'master' into multicall
This commit is contained in:
commit
6ec5083421
164 changed files with 2709 additions and 9008 deletions
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
|
|
@ -21,7 +21,4 @@ light/ @zsfelfoldi @rjl493456442
|
||||||
node/ @fjl
|
node/ @fjl
|
||||||
p2p/ @fjl @zsfelfoldi
|
p2p/ @fjl @zsfelfoldi
|
||||||
rpc/ @fjl @holiman
|
rpc/ @fjl @holiman
|
||||||
p2p/simulations @fjl
|
|
||||||
p2p/protocols @fjl
|
|
||||||
p2p/testing @fjl
|
|
||||||
signer/ @holiman
|
signer/ @holiman
|
||||||
|
|
|
||||||
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.21.4
|
go-version: 1.23.0
|
||||||
cache: false
|
cache: false
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: go test -short ./...
|
run: go test -short ./...
|
||||||
|
|
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -28,6 +28,14 @@ build/_vendor/pkg
|
||||||
/build/bin/
|
/build/bin/
|
||||||
/geth*.zip
|
/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
|
# travis
|
||||||
profile.tmp
|
profile.tmp
|
||||||
profile.cov
|
profile.cov
|
||||||
|
|
|
||||||
|
|
@ -60,14 +60,14 @@ issues:
|
||||||
- path: crypto/bn256/
|
- path: crypto/bn256/
|
||||||
linters:
|
linters:
|
||||||
- revive
|
- 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
|
- path: internal/build/pgp.go
|
||||||
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
|
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
|
||||||
- path: core/vm/contracts.go
|
- path: core/vm/contracts.go
|
||||||
text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.'
|
text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.'
|
||||||
- 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:
|
exclude:
|
||||||
- 'SA1019: event.TypeMux is deprecated: use Feed'
|
- 'SA1019: event.TypeMux is deprecated: use Feed'
|
||||||
- 'SA1019: strings.Title is deprecated'
|
- 'SA1019: strings.Title is deprecated'
|
||||||
|
|
|
||||||
35
.travis.yml
35
.travis.yml
|
|
@ -15,8 +15,8 @@ jobs:
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
arch: amd64
|
arch: amd64
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- docker
|
- docker
|
||||||
services:
|
services:
|
||||||
|
|
@ -32,8 +32,8 @@ jobs:
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
arch: arm64
|
arch: arm64
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- docker
|
- docker
|
||||||
services:
|
services:
|
||||||
|
|
@ -49,9 +49,9 @@ jobs:
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
dist: noble
|
dist: focal
|
||||||
sudo: required
|
sudo: required
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- azure-linux
|
- azure-linux
|
||||||
git:
|
git:
|
||||||
|
|
@ -63,6 +63,7 @@ jobs:
|
||||||
|
|
||||||
# build 386
|
# build 386
|
||||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib
|
- 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 install -dlgo -arch 386
|
||||||
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
||||||
|
|
||||||
|
|
@ -84,7 +85,7 @@ jobs:
|
||||||
if: type = push
|
if: type = push
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode14.2
|
osx_image: xcode14.2
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
git:
|
git:
|
||||||
|
|
@ -100,16 +101,16 @@ jobs:
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
arch: amd64
|
arch: amd64
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
script:
|
script:
|
||||||
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
||||||
|
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.21.x
|
go: 1.22.x
|
||||||
script:
|
script:
|
||||||
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
||||||
|
|
||||||
|
|
@ -117,8 +118,8 @@ jobs:
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = cron || (type = push && tag ~= /^v[0-9]/)
|
if: type = cron || (type = push && tag ~= /^v[0-9]/)
|
||||||
os: linux
|
os: linux
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- ubuntu-ppa
|
- ubuntu-ppa
|
||||||
git:
|
git:
|
||||||
|
|
@ -133,8 +134,8 @@ jobs:
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = cron
|
if: type = cron
|
||||||
os: linux
|
os: linux
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- azure-purge
|
- azure-purge
|
||||||
git:
|
git:
|
||||||
|
|
@ -146,8 +147,8 @@ jobs:
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = cron
|
if: type = cron
|
||||||
os: linux
|
os: linux
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- racetests
|
- racetests
|
||||||
script:
|
script:
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ ARG VERSION=""
|
||||||
ARG BUILDNUM=""
|
ARG BUILDNUM=""
|
||||||
|
|
||||||
# Build Geth in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.22-alpine as builder
|
FROM golang:1.23-alpine as builder
|
||||||
|
|
||||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ ARG VERSION=""
|
||||||
ARG BUILDNUM=""
|
ARG BUILDNUM=""
|
||||||
|
|
||||||
# Build Geth in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.22-alpine as builder
|
FROM golang:1.23-alpine as builder
|
||||||
|
|
||||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||||
|
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -42,7 +42,7 @@ clean:
|
||||||
devtools:
|
devtools:
|
||||||
env GOBIN= go install golang.org/x/tools/cmd/stringer@latest
|
env GOBIN= go install golang.org/x/tools/cmd/stringer@latest
|
||||||
env GOBIN= go install github.com/fjl/gencodec@latest
|
env GOBIN= go install github.com/fjl/gencodec@latest
|
||||||
env GOBIN= go install github.com/golang/protobuf/protoc-gen-go@latest
|
env GOBIN= go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||||
env GOBIN= go install ./cmd/abigen
|
env GOBIN= go install ./cmd/abigen
|
||||||
@type "solc" 2> /dev/null || echo 'Please install solc'
|
@type "solc" 2> /dev/null || echo 'Please install solc'
|
||||||
@type "protoc" 2> /dev/null || echo 'Please install protoc'
|
@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).
|
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.21 or later) and a C compiler. You can install
|
Building `geth` requires both a Go (version 1.22 or later) and a C compiler. You can install
|
||||||
them using your favourite package manager. Once the dependencies are installed, run
|
them using your favourite package manager. Once the dependencies are installed, run
|
||||||
|
|
||||||
```shell
|
```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
|
This tool is optional and if you leave it out you can always attach it to an already running
|
||||||
`geth` instance with `geth attach`.
|
`geth` instance with `geth attach`.
|
||||||
|
|
||||||
### A Full node on the Görli test network
|
### A Full node on the Holesky test network
|
||||||
|
|
||||||
Transitioning towards developers, if you'd like to play around with creating Ethereum
|
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
|
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.
|
the main network, but with play-Ether only.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ geth --goerli console
|
$ geth --holesky console
|
||||||
```
|
```
|
||||||
|
|
||||||
The `console` subcommand has the same meaning as above and is equally
|
The `console` subcommand has the same meaning as above and is equally
|
||||||
useful on the testnet too.
|
useful on the testnet too.
|
||||||
|
|
||||||
Specifying the `--goerli` flag, however, will reconfigure your `geth` instance a bit:
|
Specifying the `--holesky` flag, however, will reconfigure your `geth` instance a bit:
|
||||||
|
|
||||||
* Instead of connecting to the main Ethereum network, the client will connect to the Görli
|
* Instead of connecting to the main Ethereum network, the client will connect to the Holesky
|
||||||
test network, which uses different P2P bootnodes, different network IDs and genesis
|
test network, which uses different P2P bootnodes, different network IDs and genesis
|
||||||
states.
|
states.
|
||||||
* Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth`
|
* Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth`
|
||||||
will nest itself one level deeper into a `goerli` subfolder (`~/.ethereum/goerli` on
|
will nest itself one level deeper into a `holesky` subfolder (`~/.ethereum/holesky` on
|
||||||
Linux). Note, on OSX and Linux this also means that attaching to a running testnet node
|
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
|
requires the use of a custom endpoint since `geth attach` will try to attach to a
|
||||||
production node endpoint by default, e.g.,
|
production node endpoint by default, e.g.,
|
||||||
`geth attach <datadir>/goerli/geth.ipc`. Windows users are not affected by
|
`geth attach <datadir>/holesky/geth.ipc`. Windows users are not affected by
|
||||||
this.
|
this.
|
||||||
|
|
||||||
*Note: Although some internal protective measures prevent transactions from
|
*Note: Although some internal protective measures prevent transactions from
|
||||||
|
|
|
||||||
|
|
@ -1218,3 +1218,10 @@ 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ type TransactOpts struct {
|
||||||
GasFeeCap *big.Int // Gas fee cap to use for the 1559 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)
|
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)
|
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)
|
||||||
|
|
||||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||||
|
|
||||||
|
|
@ -307,13 +308,14 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
||||||
Gas: gasLimit,
|
Gas: gasLimit,
|
||||||
Value: value,
|
Value: value,
|
||||||
Data: input,
|
Data: input,
|
||||||
|
AccessList: opts.AccessList,
|
||||||
}
|
}
|
||||||
return types.NewTx(baseTx), nil
|
return types.NewTx(baseTx), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
||||||
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
|
if opts.GasFeeCap != nil || opts.GasTipCap != nil || opts.AccessList != nil {
|
||||||
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
|
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas or accessList specified but london is not active yet")
|
||||||
}
|
}
|
||||||
// Normalize value
|
// Normalize value
|
||||||
value := opts.Value
|
value := opts.Value
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,9 @@ func TestWaitDeployed(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Send and mine the transaction.
|
// Send and mine the transaction.
|
||||||
backend.Client().SendTransaction(ctx, tx)
|
if err := backend.Client().SendTransaction(ctx, tx); err != nil {
|
||||||
|
t.Errorf("test %q: failed to send transaction: %v", name, err)
|
||||||
|
}
|
||||||
backend.Commit()
|
backend.Commit()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|
@ -116,7 +118,9 @@ func TestWaitDeployedCornerCases(t *testing.T) {
|
||||||
tx, _ = types.SignTx(tx, types.LatestSigner(params.AllDevChainProtocolChanges), testKey)
|
tx, _ = types.SignTx(tx, types.LatestSigner(params.AllDevChainProtocolChanges), testKey)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
backend.Client().SendTransaction(ctx, tx)
|
if err := backend.Client().SendTransaction(ctx, tx); err != nil {
|
||||||
|
t.Errorf("failed to send transaction: %q", err)
|
||||||
|
}
|
||||||
backend.Commit()
|
backend.Commit()
|
||||||
notContractCreation := errors.New("tx is not contract creation")
|
notContractCreation := errors.New("tx is not contract creation")
|
||||||
if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != notContractCreation.Error() {
|
if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != notContractCreation.Error() {
|
||||||
|
|
@ -134,6 +138,8 @@ func TestWaitDeployedCornerCases(t *testing.T) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
backend.Client().SendTransaction(ctx, tx)
|
if err := backend.Client().SendTransaction(ctx, tx); err != nil {
|
||||||
|
t.Errorf("failed to send transaction: %q", err)
|
||||||
|
}
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -219,8 +219,13 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
typ.T = FunctionTy
|
typ.T = FunctionTy
|
||||||
typ.Size = 24
|
typ.Size = 24
|
||||||
default:
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/golang/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
|
// 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
|
// - Download the latest protoc https://github.com/protocolbuffers/protobuf/releases
|
||||||
// - Build with the usual `./configure && make` and ensure it's on your $PATH
|
// - 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
|
// - Delete all the .proto and .pb.go files, pull in fresh ones from Trezor
|
||||||
// - Grab the latest Go plugin `go get -u github.com/golang/protobuf/protoc-gen-go`
|
// - Grab the latest Go plugin `go get -u google.golang.org/protobuf/cmd/protoc-gen-go`
|
||||||
// - Vendor in the latest Go plugin `govendor fetch github.com/golang/protobuf/...`
|
// - Vendor in the latest Go plugin `govendor fetch google.golang.org/protobuf/...`
|
||||||
|
|
||||||
//go:generate protoc -I/usr/local/include:. --go_out=paths=source_relative:. messages.proto messages-common.proto messages-management.proto messages-ethereum.proto
|
//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 (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type returns the protocol buffer type number of a specific message. If the
|
// Type returns the protocol buffer type number of a specific message. If the
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,10 @@ func TestBlockSync(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var expNumber, headNumber uint64
|
var expNumber, headNumber uint64
|
||||||
if expHead != nil {
|
if expHead != nil {
|
||||||
p, _ := expHead.ExecutionPayload()
|
p, err := expHead.ExecutionPayload()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expHead.ExecutionPayload() failed: %v", err)
|
||||||
|
}
|
||||||
expNumber = p.NumberU64()
|
expNumber = p.NumberU64()
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
|
|
||||||
|
|
@ -56,32 +56,17 @@ var (
|
||||||
AddFork("DENEB", 132608, []byte{144, 0, 0, 115}),
|
AddFork("DENEB", 132608, []byte{144, 0, 0, 115}),
|
||||||
Checkpoint: common.HexToHash("0x1005a6d9175e96bfbce4d35b80f468e9bff0b674e1e861d16e09e10005a58e81"),
|
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 {
|
func makeChainConfig(ctx *cli.Context) lightClientConfig {
|
||||||
var config lightClientConfig
|
var config lightClientConfig
|
||||||
customConfig := ctx.IsSet(utils.BeaconConfigFlag.Name)
|
customConfig := ctx.IsSet(utils.BeaconConfigFlag.Name)
|
||||||
utils.CheckExclusive(ctx, utils.MainnetFlag, utils.GoerliFlag, utils.SepoliaFlag, utils.BeaconConfigFlag)
|
utils.CheckExclusive(ctx, utils.MainnetFlag, utils.SepoliaFlag, utils.BeaconConfigFlag)
|
||||||
switch {
|
switch {
|
||||||
case ctx.Bool(utils.MainnetFlag.Name):
|
case ctx.Bool(utils.MainnetFlag.Name):
|
||||||
config = MainnetConfig
|
config = MainnetConfig
|
||||||
case ctx.Bool(utils.SepoliaFlag.Name):
|
case ctx.Bool(utils.SepoliaFlag.Name):
|
||||||
config = SepoliaConfig
|
config = SepoliaConfig
|
||||||
case ctx.Bool(utils.GoerliFlag.Name):
|
|
||||||
config = GoerliConfig
|
|
||||||
default:
|
default:
|
||||||
if !customConfig {
|
if !customConfig {
|
||||||
config = MainnetConfig
|
config = MainnetConfig
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
|
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||||
}
|
}
|
||||||
var enc ExecutableData
|
var enc ExecutableData
|
||||||
enc.ParentHash = e.ParentHash
|
enc.ParentHash = e.ParentHash
|
||||||
|
|
@ -58,6 +59,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
enc.Withdrawals = e.Withdrawals
|
enc.Withdrawals = e.Withdrawals
|
||||||
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
||||||
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
||||||
|
enc.ExecutionWitness = e.ExecutionWitness
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,6 +83,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
|
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||||
}
|
}
|
||||||
var dec ExecutableData
|
var dec ExecutableData
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -154,5 +157,8 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
if dec.ExcessBlobGas != nil {
|
if dec.ExcessBlobGas != nil {
|
||||||
e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
||||||
}
|
}
|
||||||
|
if dec.ExecutionWitness != nil {
|
||||||
|
e.ExecutionWitness = dec.ExecutionWitness
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -75,6 +76,7 @@ type ExecutableData struct {
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||||
|
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON type overrides for executableData.
|
// JSON type overrides for executableData.
|
||||||
|
|
@ -193,21 +195,21 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||||
//
|
//
|
||||||
// and that the blockhash of the constructed block matches the parameters. Nil
|
// and that the blockhash of the constructed block matches the parameters. Nil
|
||||||
// Withdrawals value will propagate through the returned block. Empty
|
// Withdrawals value will propagate through the returned block. Empty
|
||||||
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
// Withdrawals value must be passed via non-nil, length 0 value in data.
|
||||||
func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
||||||
txs, err := decodeTransactions(params.Transactions)
|
txs, err := decodeTransactions(data.Transactions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(params.ExtraData) > 32 {
|
if len(data.ExtraData) > int(params.MaximumExtraDataSize) {
|
||||||
return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData))
|
return nil, fmt.Errorf("invalid extradata length: %v", len(data.ExtraData))
|
||||||
}
|
}
|
||||||
if len(params.LogsBloom) != 256 {
|
if len(data.LogsBloom) != 256 {
|
||||||
return nil, fmt.Errorf("invalid logsBloom length: %v", len(params.LogsBloom))
|
return nil, fmt.Errorf("invalid logsBloom length: %v", len(data.LogsBloom))
|
||||||
}
|
}
|
||||||
// Check that baseFeePerGas is not negative or too big
|
// Check that baseFeePerGas is not negative or too big
|
||||||
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
if data.BaseFeePerGas != nil && (data.BaseFeePerGas.Sign() == -1 || data.BaseFeePerGas.BitLen() > 256) {
|
||||||
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
return nil, fmt.Errorf("invalid baseFeePerGas: %v", data.BaseFeePerGas)
|
||||||
}
|
}
|
||||||
var blobHashes = make([]common.Hash, 0, len(txs))
|
var blobHashes = make([]common.Hash, 0, len(txs))
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
|
|
@ -225,34 +227,36 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash,
|
||||||
// ExecutableData before withdrawals are enabled by marshaling
|
// ExecutableData before withdrawals are enabled by marshaling
|
||||||
// Withdrawals as the json null value.
|
// Withdrawals as the json null value.
|
||||||
var withdrawalsRoot *common.Hash
|
var withdrawalsRoot *common.Hash
|
||||||
if params.Withdrawals != nil {
|
if data.Withdrawals != nil {
|
||||||
h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
|
h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil))
|
||||||
withdrawalsRoot = &h
|
withdrawalsRoot = &h
|
||||||
}
|
}
|
||||||
header := &types.Header{
|
header := &types.Header{
|
||||||
ParentHash: params.ParentHash,
|
ParentHash: data.ParentHash,
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
Coinbase: params.FeeRecipient,
|
Coinbase: data.FeeRecipient,
|
||||||
Root: params.StateRoot,
|
Root: data.StateRoot,
|
||||||
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
||||||
ReceiptHash: params.ReceiptsRoot,
|
ReceiptHash: data.ReceiptsRoot,
|
||||||
Bloom: types.BytesToBloom(params.LogsBloom),
|
Bloom: types.BytesToBloom(data.LogsBloom),
|
||||||
Difficulty: common.Big0,
|
Difficulty: common.Big0,
|
||||||
Number: new(big.Int).SetUint64(params.Number),
|
Number: new(big.Int).SetUint64(data.Number),
|
||||||
GasLimit: params.GasLimit,
|
GasLimit: data.GasLimit,
|
||||||
GasUsed: params.GasUsed,
|
GasUsed: data.GasUsed,
|
||||||
Time: params.Timestamp,
|
Time: data.Timestamp,
|
||||||
BaseFee: params.BaseFeePerGas,
|
BaseFee: data.BaseFeePerGas,
|
||||||
Extra: params.ExtraData,
|
Extra: data.ExtraData,
|
||||||
MixDigest: params.Random,
|
MixDigest: data.Random,
|
||||||
WithdrawalsHash: withdrawalsRoot,
|
WithdrawalsHash: withdrawalsRoot,
|
||||||
ExcessBlobGas: params.ExcessBlobGas,
|
ExcessBlobGas: data.ExcessBlobGas,
|
||||||
BlobGasUsed: params.BlobGasUsed,
|
BlobGasUsed: data.BlobGasUsed,
|
||||||
ParentBeaconRoot: beaconRoot,
|
ParentBeaconRoot: beaconRoot,
|
||||||
}
|
}
|
||||||
block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: params.Withdrawals})
|
block := types.NewBlockWithHeader(header)
|
||||||
if block.Hash() != params.BlockHash {
|
block = block.WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals})
|
||||||
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
|
block = block.WithWitness(data.ExecutionWitness)
|
||||||
|
if block.Hash() != data.BlockHash {
|
||||||
|
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", data.BlockHash, block.Hash())
|
||||||
}
|
}
|
||||||
return block, nil
|
return block, nil
|
||||||
}
|
}
|
||||||
|
|
@ -278,6 +282,7 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
|
||||||
Withdrawals: block.Withdrawals(),
|
Withdrawals: block.Withdrawals(),
|
||||||
BlobGasUsed: block.BlobGasUsed(),
|
BlobGasUsed: block.BlobGasUsed(),
|
||||||
ExcessBlobGas: block.ExcessBlobGas(),
|
ExcessBlobGas: block.ExcessBlobGas(),
|
||||||
|
ExecutionWitness: block.ExecutionWitness(),
|
||||||
}
|
}
|
||||||
bundle := BlobsBundleV1{
|
bundle := BlobsBundleV1{
|
||||||
Commitments: make([]hexutil.Bytes, 0),
|
Commitments: make([]hexutil.Bytes, 0),
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -121,7 +122,11 @@ func NewBeaconLightApi(url string, customHeaders map[string]string) *BeaconLight
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *BeaconLightApi) httpGet(path string) ([]byte, error) {
|
func (api *BeaconLightApi) httpGet(path string) ([]byte, error) {
|
||||||
req, err := http.NewRequest("GET", api.url+path, nil)
|
uri, err := api.buildURL(path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("GET", uri, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -545,9 +550,13 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
||||||
// established. It can only return nil when the context is canceled.
|
// established. It can only return nil when the context is canceled.
|
||||||
func (api *BeaconLightApi) startEventStream(ctx context.Context, listener *HeadEventListener) *eventsource.Stream {
|
func (api *BeaconLightApi) startEventStream(ctx context.Context, listener *HeadEventListener) *eventsource.Stream {
|
||||||
for retry := true; retry; retry = ctxSleep(ctx, 5*time.Second) {
|
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")
|
log.Trace("Sending event subscription request")
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", api.url+path, nil)
|
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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
listener.OnError(fmt.Errorf("error creating event subscription request: %v", err))
|
listener.OnError(fmt.Errorf("error creating event subscription request: %v", err))
|
||||||
continue
|
continue
|
||||||
|
|
@ -576,3 +585,15 @@ func ctxSleep(ctx context.Context, timeout time.Duration) (ok bool) {
|
||||||
return false
|
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,6 +201,34 @@ func TestUpdateSyncDifferentHeads(t *testing.T) {
|
||||||
chain.ExpNextSyncPeriod(t, 17)
|
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 {
|
func testRespUpdate(request requestWithID) request.Response {
|
||||||
var resp RespUpdates
|
var resp RespUpdates
|
||||||
if request.request == nil {
|
if request.request == nil {
|
||||||
|
|
|
||||||
|
|
@ -5,55 +5,55 @@
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
||||||
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
||||||
|
|
||||||
# version:golang 1.22.5
|
# version:golang 1.23.0
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
ac9c723f224969aee624bc34fd34c9e13f2a212d75c71c807de644bb46e112f6 go1.22.5.src.tar.gz
|
42b7a8e80d805daa03022ed3fde4321d4c3bf2c990a144165d01eeecd6f699c6 go1.23.0.src.tar.gz
|
||||||
c82ba3403c45a4aa4b84b08244656a51e55b86fb130dcc500f5291d0f3b12222 go1.22.5.aix-ppc64.tar.gz
|
257f8560bb4001fb81a5e0ee84f32fecbe18d4450343c9556557d296786847b6 go1.23.0.aix-ppc64.tar.gz
|
||||||
8a8872b1bac959b3b76f2e3978c46406d22a54a99c83ca55840ca08b4f2960bc go1.22.5.darwin-amd64.pkg
|
bc91d2573939a01731413fac0884c329606c1c168883692131ce772669caf27b go1.23.0.darwin-amd64.pkg
|
||||||
95d9933cdcf45f211243c42c7705c37353cccd99f27eb4d8e2d1bf2f4165cb50 go1.22.5.darwin-amd64.tar.gz
|
ffd070acf59f054e8691b838f274d540572db0bd09654af851e4e76ab88403dc go1.23.0.darwin-amd64.tar.gz
|
||||||
8c943512d1fa4e849f0078b03721df02aac19d8bb872dd17ab3ee7127ae6b732 go1.22.5.darwin-arm64.pkg
|
d73ae741ed449ea842238f76f4b02935277eb867689f84ace0640965b2caf700 go1.23.0.darwin-arm64.pkg
|
||||||
4cd1bcb05be03cecb77bccd765785d5ff69d79adf4dd49790471d00c06b41133 go1.22.5.darwin-arm64.tar.gz
|
b770812aef17d7b2ea406588e2b97689e9557aac7e646fe76218b216e2c51406 go1.23.0.darwin-arm64.tar.gz
|
||||||
1f1f035e968a877cd8ed62adae6edb2feeee62470660b7587ddcb904a3877a21 go1.22.5.dragonfly-amd64.tar.gz
|
8fd2ab5ac8629fc97d25a056693e23f332446603dd3c2b764ccb496872004b0c go1.23.0.dragonfly-amd64.tar.gz
|
||||||
d660698411465531d475ec1c617fdb415df68740f3511138a8d15506665a06f9 go1.22.5.freebsd-386.tar.gz
|
2c9b76ead3c44f5b3e40e10b980075addb837f2dd05dafe7c0e4c611fd239753 go1.23.0.freebsd-386.tar.gz
|
||||||
75f43ef46c2ad46c534ded25d26fba9bef036fc07074dfa45c0b3b90856a8151 go1.22.5.freebsd-amd64.tar.gz
|
2c2252902b87ba605fdc0b12b4c860fe6553c0c5483c12cc471756ebdd8249fe go1.23.0.freebsd-amd64.tar.gz
|
||||||
75614714e7e4a4dd721f0eddd6555b3f6afc4c07e59c1b9b769cf663996165f9 go1.22.5.freebsd-arm.tar.gz
|
8ec48b8d99a515644ae00e79d093ad3b7645dcaf2a19c0a9c0d97916187f4514 go1.23.0.freebsd-arm.tar.gz
|
||||||
1377d0d7233f1b8f4cb8e3456f2e7ed44aca4a95daab79ae09605d34aa967c6b go1.22.5.freebsd-arm64.tar.gz
|
f476bbe8efb0db18155671840545370bfb73903fec04ea897d510569dab16d9c go1.23.0.freebsd-arm64.tar.gz
|
||||||
07baf198587abc05ea789dbe5810a2d6612ad56a51718bbf74de2c93bdbe676a go1.22.5.freebsd-riscv64.tar.gz
|
b0e254b2ea5752b4f1c69934ae43a44bbabf98e0c2843af44e1b6d12390eb551 go1.23.0.freebsd-riscv64.tar.gz
|
||||||
c0bd4f0d44252f3ec93ca850a41b167bb868179c7c283f8af9439e73b2654b17 go1.22.5.illumos-amd64.tar.gz
|
09716dcc7a2e19891b3d1e2ea68a1aab22838fc664cdc5f82d5f8eef05db78cf go1.23.0.illumos-amd64.tar.gz
|
||||||
3ea4c78e6fa52978ae1ed2e5927ad17495da440c9fae7787b1ebc1d0572f7f43 go1.22.5.linux-386.tar.gz
|
0e8a7340c2632e6fb5088d60f95b52be1f8303143e04cd34e9b2314fafc24edd go1.23.0.linux-386.tar.gz
|
||||||
904b924d435eaea086515bc63235b192ea441bd8c9b198c507e85009e6e4c7f0 go1.22.5.linux-amd64.tar.gz
|
905a297f19ead44780548933e0ff1a1b86e8327bb459e92f9c0012569f76f5e3 go1.23.0.linux-amd64.tar.gz
|
||||||
8d21325bfcf431be3660527c1a39d3d9ad71535fabdf5041c826e44e31642b5a go1.22.5.linux-arm64.tar.gz
|
62788056693009bcf7020eedc778cdd1781941c6145eab7688bd087bce0f8659 go1.23.0.linux-arm64.tar.gz
|
||||||
8c4587cf3e63c9aefbcafa92818c4d9d51683af93ea687bf6c7508d6fa36f85e go1.22.5.linux-armv6l.tar.gz
|
0efa1338e644d7f74064fa7f1016b5da7872b2df0070ea3b56e4fef63192e35b go1.23.0.linux-armv6l.tar.gz
|
||||||
780e2eeb6376a763c564f776eaac6700f33f95e29302faa54b040b19cb1f6fd2 go1.22.5.linux-loong64.tar.gz
|
dc8f723ce1a236e85c8b56d1e6749e270314e99dd41b80a58355e7ffcf9ea857 go1.23.0.linux-loong64.tar.gz
|
||||||
f784aa1adfb605da3bfe8cd534b545bddae3eb893e9302f7c2f5d44656b1cae2 go1.22.5.linux-mips.tar.gz
|
3332cc76c73c05b3413cdecccffc29aaa3469f87db8ed9f9b784ebb527ca5352 go1.23.0.linux-mips.tar.gz
|
||||||
aaa3756571467768388f2ab641a02ff54f98f1684808cda047a7be3026e4b438 go1.22.5.linux-mips64.tar.gz
|
0ed5cee92433d09fd0816ec5adfbf4b16d712944e833f6342bbe2df18f7826ae go1.23.0.linux-mips64.tar.gz
|
||||||
b7956d925c9ef5a4dc53017feaed2d78dba5d0a1036bad5ea513f1f15ba08fbc go1.22.5.linux-mips64le.tar.gz
|
06a579dd6d1f9a84bc43cab063e7c759a92a6d4dd01fec3d860f22a32df93406 go1.23.0.linux-mips64le.tar.gz
|
||||||
7baf605be9b787acd750b6b48a91818a5590ec9289b14aea5696a46b41853888 go1.22.5.linux-mipsle.tar.gz
|
d522770d32d6ee963f61331a695c4f8a730f2445b965d8d56db0a2e75c62af57 go1.23.0.linux-mipsle.tar.gz
|
||||||
f09b2a6c1a409662e8e8fe267e1eabeba0a1fd00eb1422fd88297b013803952e go1.22.5.linux-ppc64.tar.gz
|
8c884cb4f2593d897f58ec1b0f23f303acf5c78fd101e76cb48d6cb1fe5e90e7 go1.23.0.linux-ppc64.tar.gz
|
||||||
5312bb420ac0b59175a58927e70b4660b14ab7319aab54398b6071fabcbfbb09 go1.22.5.linux-ppc64le.tar.gz
|
8b26e20d4d43a4d7641cddbdc0298d7ba3804d910a9e06cda7672970dbf2829d go1.23.0.linux-ppc64le.tar.gz
|
||||||
f8d0c7d96b336f4133409ff9da7241cfe91e65723c2e8e7c7f9b58a9f9603476 go1.22.5.linux-riscv64.tar.gz
|
a87726205f1a283247f877ccae8ce147ff4e77ac802382647ac52256eb5642c7 go1.23.0.linux-riscv64.tar.gz
|
||||||
24c6c5c9d515adea5d58ae78388348c97614a0c21ac4d4f4c0dab75e893b0b5d go1.22.5.linux-s390x.tar.gz
|
003722971de02d97131a4dca2496abdab5cb175a6ee0ed9c8227c5ae9b883e69 go1.23.0.linux-s390x.tar.gz
|
||||||
39144c62acbaa85e4f1ab57bad8f5b3dc67d6fa24b711ec1fa593f4a0ea1fe91 go1.22.5.netbsd-386.tar.gz
|
b203fa2354874c66c40d828e96a6cce1f4e4db192414050a600d0a09b16cafd3 go1.23.0.netbsd-386.tar.gz
|
||||||
118f79640588eb878529b46cdf56599012da6575f0ac07069ec1e9a8e78ddd0b go1.22.5.netbsd-amd64.tar.gz
|
1502c82c3ba663959df99c2cc3ca5e7a5e1a75a1495fd26bef697d63bf1f291c go1.23.0.netbsd-amd64.tar.gz
|
||||||
d39c2b94ae3fd0a6399e545cbecb673496293075291bd98ef15f24d21625a490 go1.22.5.netbsd-arm.tar.gz
|
dd50c05c7f613522c8d3d74f598bfc1862c0fee9182b738225820c9b458c7be5 go1.23.0.netbsd-arm.tar.gz
|
||||||
f7fb617d10c39248996521d72370db82d50724fa894089c76ae4298fbbe1fb0b go1.22.5.netbsd-arm64.tar.gz
|
728a94a648f9502cd6175adaac2b770acde6b26f5f92dcbd8c5a1a43cc44bb10 go1.23.0.netbsd-arm64.tar.gz
|
||||||
e0f778a34746587ae7c18e8a24cfaba1b2eaabce75d0ceb470adf576ad1cd90f go1.22.5.openbsd-386.tar.gz
|
e1ff3584778257778a4e3f0093b09044072423aebedf2015a550537853c46745 go1.23.0.openbsd-386.tar.gz
|
||||||
b417311df26ef7ae8b34fcb991519a5c496010561c12386d9469aea03c1bdf0b go1.22.5.openbsd-amd64.tar.gz
|
d2e30cdb0de256360b51a43f5e551587a7369d8c248120010d5e9432f698a6e8 go1.23.0.openbsd-amd64.tar.gz
|
||||||
e78e8ad05605d530a4f79e55031c7c65f2020a9d442e05d490bd08f0d947a34f go1.22.5.openbsd-arm.tar.gz
|
bd5224c8a5f195f4128c866c0d418f1b61db865a1042913fd07714ed85da28db go1.23.0.openbsd-arm.tar.gz
|
||||||
8027898948f17742717786ead2ff2e960ee1fc82995d6edbad0050d551710f59 go1.22.5.openbsd-arm64.tar.gz
|
fc0e0af3a1b4b7168455e8492a5bb6aa96ceaf46321cef1fc04187301c058890 go1.23.0.openbsd-arm64.tar.gz
|
||||||
99c5b81d75bcc0d83d25dedc9535682c42c0e761276c88bcc4db6340344644fd go1.22.5.openbsd-ppc64.tar.gz
|
ce7ea9343c7c2ef2700b55b80c45549ce39d164031e4d7bb98bec7ca593ed93d go1.23.0.openbsd-ppc64.tar.gz
|
||||||
30d5dacdee0481f0b8cabb75b706465e2177c3a4a1d1c46293332f4b90a3d199 go1.22.5.plan9-386.tar.gz
|
93b970a8a41f6c89113daaea12e39f2580038af155e823550d0a94a5502c5e2c go1.23.0.plan9-386.tar.gz
|
||||||
65628650cd7665387cfe6fa386c381f4de1ef7b03a12067ae9ccf06d2feaea2c go1.22.5.plan9-amd64.tar.gz
|
6231862acbb6c1e02b1455b35446b9789b0b4b3230d249953e6957c393a53011 go1.23.0.plan9-amd64.tar.gz
|
||||||
322541cbfc9ae95b48b9eec4eb45df48299784592e23121084f790cf1082787e go1.22.5.plan9-arm.tar.gz
|
632bdd3a1f84b2fe691203423dd2c3f536d4ab250bb52a48e9b05ebf327ae594 go1.23.0.plan9-arm.tar.gz
|
||||||
87c590e3eb81fcffa3dc1524c03c2847f0890e95c2a43586e82b56c262eb03d8 go1.22.5.solaris-amd64.tar.gz
|
16773f85003d9e610960f9af67e00bc6c02359d7914de7224079538cc9c1e93d go1.23.0.solaris-amd64.tar.gz
|
||||||
3ec89ed822b38f4483977a90913fbe39d0857f0ed16c4642dec1950ddbe8c943 go1.22.5.windows-386.msi
|
803ef1d4f431d37ac8572ad9b0b65a4f945798208cd16b7f0588960e6b0949ba go1.23.0.windows-386.msi
|
||||||
c44fc421075022add78fbf8db38519dd5520a11832749be2189e64b3cf4f02f9 go1.22.5.windows-386.zip
|
09448fedec0cdf98ad12397222e0c8bfc835b1d0894c0015ced653534b8d7427 go1.23.0.windows-386.zip
|
||||||
86b0299ab8cb9c44882a9080dac03f7f4d9546f51ed1ba1015599114bcbc66d0 go1.22.5.windows-amd64.msi
|
93e1cf580893303d0f6ac10647335de9f0768199d7027d8a361639cae6ab3145 go1.23.0.windows-amd64.msi
|
||||||
59968438b8d90f108fd240d4d2f95b037e59716995f7409e0a322dcb996e9f42 go1.22.5.windows-amd64.zip
|
d4be481ef73079ee0ad46081d278923aa3fd78db1b3cf147172592f73e14c1ac go1.23.0.windows-amd64.zip
|
||||||
013d3b300e6b8f26482d6dd17b02830b83ee63795498bd8c0c9d80bb2c4d6cf7 go1.22.5.windows-arm.msi
|
2a361c94879258309343e88c5de5df17f6425df4d74bdf7e333b7298b29f6f29 go1.23.0.windows-arm.msi
|
||||||
8cc860630a84e2dbff3e84280f46a571741f26f8a1819aa4fbcb3164fdd51312 go1.22.5.windows-arm.zip
|
006d93712246a672bdb57906dd5bffcab62facc36169e51a27d52340cdac661f go1.23.0.windows-arm.zip
|
||||||
8f90519d9f305f2caa05d1d4fb0656b50f1bf89d76e194279f480e5a484c891f go1.22.5.windows-arm64.msi
|
a876ed2bb130d9146152aaf391638abd79dcb3a4f2e9cc59b78709dcef29ced3 go1.23.0.windows-arm64.msi
|
||||||
6717d5841162aa8c05f932eb74a643f1310b8a88f80f0830e86d194289734bbf go1.22.5.windows-arm64.zip
|
0be62073ef8f5a2d3b9adcefddf18c417dab0a7975c71488ac2694856e2ff976 go1.23.0.windows-arm64.zip
|
||||||
|
|
||||||
# version:golangci 1.59.0
|
# version:golangci 1.59.0
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
|
|
|
||||||
80
build/ci.go
80
build/ci.go
|
|
@ -126,8 +126,6 @@ var (
|
||||||
"focal", // 20.04, EOL: 04/2030
|
"focal", // 20.04, EOL: 04/2030
|
||||||
"jammy", // 22.04, EOL: 04/2032
|
"jammy", // 22.04, EOL: 04/2032
|
||||||
"noble", // 24.04, EOL: 04/2034
|
"noble", // 24.04, EOL: 04/2034
|
||||||
|
|
||||||
"mantic", // 23.10, EOL: 07/2024
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is where the tests should be unpacked.
|
// This is where the tests should be unpacked.
|
||||||
|
|
@ -239,6 +237,10 @@ func doInstall(cmdline []string) {
|
||||||
// buildFlags returns the go tool flags for building.
|
// buildFlags returns the go tool flags for building.
|
||||||
func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
|
func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
|
||||||
var ld []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 != "" {
|
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.gitCommit="+env.Commit)
|
||||||
ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitDate="+env.Date)
|
ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitDate="+env.Date)
|
||||||
|
|
@ -251,7 +253,11 @@ func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
// Enforce the stacksize to 8M, which is the case on most platforms apart from
|
// Enforce the stacksize to 8M, which is the case on most platforms apart from
|
||||||
// alpine Linux.
|
// alpine Linux.
|
||||||
extld := []string{"-Wl,-z,stack-size=0x800000"}
|
// 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"}
|
||||||
if staticLinking {
|
if staticLinking {
|
||||||
extld = append(extld, "-static")
|
extld = append(extld, "-static")
|
||||||
// Under static linking, use of certain glibc features must be
|
// Under static linking, use of certain glibc features must be
|
||||||
|
|
@ -349,10 +355,10 @@ func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
|
||||||
return filepath.Join(cachedir, base)
|
return filepath.Join(cachedir, base)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hashSourceFiles iterates all files under the top-level project directory
|
// hashAllSourceFiles iterates all files under the top-level project directory
|
||||||
// computing the hash of each file (excluding files within the tests
|
// computing the hash of each file (excluding files within the tests
|
||||||
// subrepo)
|
// subrepo)
|
||||||
func hashSourceFiles() (map[string]common.Hash, error) {
|
func hashAllSourceFiles() (map[string]common.Hash, error) {
|
||||||
res := make(map[string]common.Hash)
|
res := make(map[string]common.Hash)
|
||||||
err := filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error {
|
err := filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error {
|
||||||
if strings.HasPrefix(path, filepath.FromSlash("tests/testdata")) {
|
if strings.HasPrefix(path, filepath.FromSlash("tests/testdata")) {
|
||||||
|
|
@ -379,6 +385,56 @@ func hashSourceFiles() (map[string]common.Hash, error) {
|
||||||
return res, nil
|
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
|
// doGenerate ensures that re-generating generated files does not cause
|
||||||
// any mutations in the source file tree: i.e. all generated files were
|
// any mutations in the source file tree: i.e. all generated files were
|
||||||
// updated and committed. Any stale generated files are updated.
|
// updated and committed. Any stale generated files are updated.
|
||||||
|
|
@ -395,7 +451,7 @@ func doGenerate() {
|
||||||
var preHashes map[string]common.Hash
|
var preHashes map[string]common.Hash
|
||||||
if *verify {
|
if *verify {
|
||||||
var err error
|
var err error
|
||||||
preHashes, err = hashSourceFiles()
|
preHashes, err = hashAllSourceFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("failed to compute map of source hashes", "err", err)
|
log.Fatal("failed to compute map of source hashes", "err", err)
|
||||||
}
|
}
|
||||||
|
|
@ -410,17 +466,11 @@ func doGenerate() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Check if files were changed.
|
// Check if files were changed.
|
||||||
postHashes, err := hashSourceFiles()
|
postHashes, err := hashAllSourceFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("error computing source tree file hashes", "err", err)
|
log.Fatal("error computing source tree file hashes", "err", err)
|
||||||
}
|
}
|
||||||
updates := []string{}
|
updates := compareHashedFilesets(preHashes, postHashes)
|
||||||
for path, postHash := range postHashes {
|
|
||||||
preHash, ok := preHashes[path]
|
|
||||||
if !ok || preHash != postHash {
|
|
||||||
updates = append(updates, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, updatedFile := range updates {
|
for _, updatedFile := range updates {
|
||||||
fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile)
|
fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile)
|
||||||
}
|
}
|
||||||
|
|
@ -443,6 +493,8 @@ func doLint(cmdline []string) {
|
||||||
linter := downloadLinter(*cachedir)
|
linter := downloadLinter(*cachedir)
|
||||||
lflags := []string{"run", "--config", ".golangci.yml"}
|
lflags := []string{"run", "--config", ".golangci.yml"}
|
||||||
build.MustRunCommandWithOutput(linter, append(lflags, packages...)...)
|
build.MustRunCommandWithOutput(linter, append(lflags, packages...)...)
|
||||||
|
|
||||||
|
doGoModTidy()
|
||||||
fmt.Println("You have achieved perfection.")
|
fmt.Println("You have achieved perfection.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,6 @@ package tools
|
||||||
import (
|
import (
|
||||||
// Tool imports for go:generate.
|
// Tool imports for go:generate.
|
||||||
_ "github.com/fjl/gencodec"
|
_ "github.com/fjl/gencodec"
|
||||||
_ "github.com/golang/protobuf/protoc-gen-go"
|
|
||||||
_ "golang.org/x/tools/cmd/stringer"
|
_ "golang.org/x/tools/cmd/stringer"
|
||||||
|
_ "google.golang.org/protobuf/cmd/protoc-gen-go"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ func main() {
|
||||||
//TODO datadir for optional permanent database
|
//TODO datadir for optional permanent database
|
||||||
utils.MainnetFlag,
|
utils.MainnetFlag,
|
||||||
utils.SepoliaFlag,
|
utils.SepoliaFlag,
|
||||||
utils.GoerliFlag,
|
|
||||||
utils.BlsyncApiFlag,
|
utils.BlsyncApiFlag,
|
||||||
utils.BlsyncJWTSecretFlag,
|
utils.BlsyncJWTSecretFlag,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ GLOBAL OPTIONS:
|
||||||
--loglevel value log level to emit to the screen (default: 4)
|
--loglevel value log level to emit to the screen (default: 4)
|
||||||
--keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore")
|
--keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore")
|
||||||
--configdir value Directory for Clef configuration (default: "$HOME/.clef")
|
--configdir value Directory for Clef configuration (default: "$HOME/.clef")
|
||||||
--chainid value Chain id to use for signing (1=mainnet, 5=Goerli) (default: 1)
|
--chainid value Chain id to use for signing (1=mainnet, 17000=Holesky) (default: 1)
|
||||||
--lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength
|
--lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength
|
||||||
--nousb Disables monitoring for and managing USB hardware wallets
|
--nousb Disables monitoring for and managing USB hardware wallets
|
||||||
--pcscdpath value Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm")
|
--pcscdpath value Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm")
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ var (
|
||||||
chainIdFlag = &cli.Int64Flag{
|
chainIdFlag = &cli.Int64Flag{
|
||||||
Name: "chainid",
|
Name: "chainid",
|
||||||
Value: params.MainnetChainConfig.ChainID.Int64(),
|
Value: params.MainnetChainConfig.ChainID.Int64(),
|
||||||
Usage: "Chain id to use for signing (1=mainnet, 5=Goerli)",
|
Usage: "Chain id to use for signing (1=mainnet, 17000=Holesky)",
|
||||||
}
|
}
|
||||||
rpcPortFlag = &cli.IntFlag{
|
rpcPortFlag = &cli.IntFlag{
|
||||||
Name: "http.port",
|
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
|
- `-limit <N>` limits the output set to N entries, taking the top N nodes by score
|
||||||
- `-ip <CIDR>` filters nodes by IP subnet
|
- `-ip <CIDR>` filters nodes by IP subnet
|
||||||
- `-min-age <duration>` filters nodes by 'first seen' time
|
- `-min-age <duration>` filters nodes by 'first seen' time
|
||||||
- `-eth-network <mainnet/goerli/sepolia/holesky>` filters nodes by "eth" ENR entry
|
- `-eth-network <mainnet/sepolia/holesky>` filters nodes by "eth" ENR entry
|
||||||
- `-les-server` filters nodes by LES server support
|
- `-les-server` filters nodes by LES server support
|
||||||
- `-snap` filters nodes by snap protocol support
|
- `-snap` filters nodes by snap protocol support
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,8 @@ func (c *cloudflareClient) checkZone(name string) error {
|
||||||
if !strings.HasSuffix(name, "."+zone.Name) {
|
if !strings.HasSuffix(name, "."+zone.Name) {
|
||||||
return fmt.Errorf("CloudFlare zone name %q does not match name %q to be deployed", zone.Name, name)
|
return fmt.Errorf("CloudFlare zone name %q does not match name %q to be deployed", zone.Name, name)
|
||||||
}
|
}
|
||||||
needPerms := map[string]bool{"#zone:edit": false, "#zone:read": false}
|
// 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}
|
||||||
for _, perm := range zone.Permissions {
|
for _, perm := range zone.Permissions {
|
||||||
if _, ok := needPerms[perm]; ok {
|
if _, ok := needPerms[perm]; ok {
|
||||||
needPerms[perm] = true
|
needPerms[perm] = true
|
||||||
|
|
|
||||||
|
|
@ -230,8 +230,6 @@ func ethFilter(args []string) (nodeFilter, error) {
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "mainnet":
|
case "mainnet":
|
||||||
filter = forkid.NewStaticFilter(params.MainnetChainConfig, core.DefaultGenesisBlock().ToBlock())
|
filter = forkid.NewStaticFilter(params.MainnetChainConfig, core.DefaultGenesisBlock().ToBlock())
|
||||||
case "goerli":
|
|
||||||
filter = forkid.NewStaticFilter(params.GoerliChainConfig, core.DefaultGoerliGenesisBlock().ToBlock())
|
|
||||||
case "sepolia":
|
case "sepolia":
|
||||||
filter = forkid.NewStaticFilter(params.SepoliaChainConfig, core.DefaultSepoliaGenesisBlock().ToBlock())
|
filter = forkid.NewStaticFilter(params.SepoliaChainConfig, core.DefaultSepoliaGenesisBlock().ToBlock())
|
||||||
case "holesky":
|
case "holesky":
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
|
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++ {
|
for i := 0; txIt.Next(); i++ {
|
||||||
tx, err := txIt.Tx()
|
tx, err := txIt.Tx()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,6 @@ func runCmd(ctx *cli.Context) error {
|
||||||
if ctx.String(SenderFlag.Name) != "" {
|
if ctx.String(SenderFlag.Name) != "" {
|
||||||
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
||||||
}
|
}
|
||||||
statedb.CreateAccount(sender)
|
|
||||||
|
|
||||||
if ctx.String(ReceiverFlag.Name) != "" {
|
if ctx.String(ReceiverFlag.Name) != "" {
|
||||||
receiver = common.HexToAddress(ctx.String(ReceiverFlag.Name))
|
receiver = common.HexToAddress(ctx.String(ReceiverFlag.Name))
|
||||||
|
|
@ -222,6 +221,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
Time: genesisConfig.Timestamp,
|
Time: genesisConfig.Timestamp,
|
||||||
Coinbase: genesisConfig.Coinbase,
|
Coinbase: genesisConfig.Coinbase,
|
||||||
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
|
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
|
||||||
|
BaseFee: genesisConfig.BaseFee,
|
||||||
BlobHashes: blobHashes,
|
BlobHashes: blobHashes,
|
||||||
BlobBaseFee: blobBaseFee,
|
BlobBaseFee: blobBaseFee,
|
||||||
EVMConfig: vm.Config{
|
EVMConfig: vm.Config{
|
||||||
|
|
|
||||||
|
|
@ -425,8 +425,6 @@ func importHistory(ctx *cli.Context) error {
|
||||||
network = "mainnet"
|
network = "mainnet"
|
||||||
case ctx.Bool(utils.SepoliaFlag.Name):
|
case ctx.Bool(utils.SepoliaFlag.Name):
|
||||||
network = "sepolia"
|
network = "sepolia"
|
||||||
case ctx.Bool(utils.GoerliFlag.Name):
|
|
||||||
network = "goerli"
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No network flag set, try to determine network based on files
|
// No network flag set, try to determine network based on files
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,8 @@ var tomlSettings = toml.Config{
|
||||||
},
|
},
|
||||||
MissingField: func(rt reflect.Type, field string) error {
|
MissingField: func(rt reflect.Type, field string) error {
|
||||||
id := fmt.Sprintf("%s.%s", rt.String(), field)
|
id := fmt.Sprintf("%s.%s", rt.String(), field)
|
||||||
if deprecated(id) {
|
if deprecatedConfigFields[id] {
|
||||||
log.Warn("Config field is deprecated and won't have an effect", "name", id)
|
log.Warn(fmt.Sprintf("Config field '%s' is deprecated and won't have any effect.", id))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var link string
|
var link string
|
||||||
|
|
@ -87,6 +87,19 @@ 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 {
|
type ethstatsConfig struct {
|
||||||
URL string `toml:",omitempty"`
|
URL string `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
@ -314,21 +327,6 @@ 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 {
|
func setAccountManagerBackends(conf *node.Config, am *accounts.Manager, keydir string) error {
|
||||||
scryptN := keystore.StandardScryptN
|
scryptN := keystore.StandardScryptN
|
||||||
scryptP := keystore.StandardScryptP
|
scryptP := keystore.StandardScryptP
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
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"
|
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"
|
||||||
httpAPIs = "eth:1.0 net:1.0 rpc: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
|
// memory and disk IO. If the args don't set --datadir, the
|
||||||
// child g gets a temporary data directory.
|
// child g gets a temporary data directory.
|
||||||
func runMinimalGeth(t *testing.T, args ...string) *testgeth {
|
func runMinimalGeth(t *testing.T, args ...string) *testgeth {
|
||||||
// --goerli to make the 'writing genesis to disk' faster (no accounts)
|
// --holesky to make the 'writing genesis to disk' faster (no accounts)
|
||||||
// --networkid=1337 to avoid cache bump
|
// --networkid=1337 to avoid cache bump
|
||||||
// --syncmode=full to avoid allocating fast sync bloom
|
// --syncmode=full to avoid allocating fast sync bloom
|
||||||
allArgs := []string{"--goerli", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
|
allArgs := []string{"--holesky", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
|
||||||
"--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64",
|
"--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64",
|
||||||
"--datadir.minfreedisk", "0"}
|
"--datadir.minfreedisk", "0"}
|
||||||
return runGeth(t, append(allArgs, args...)...)
|
return runGeth(t, append(allArgs, args...)...)
|
||||||
|
|
@ -62,7 +62,7 @@ func TestConsoleWelcome(t *testing.T) {
|
||||||
geth.SetTemplateFunc("gover", runtime.Version)
|
geth.SetTemplateFunc("gover", runtime.Version)
|
||||||
geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
||||||
geth.SetTemplateFunc("niltime", func() string {
|
geth.SetTemplateFunc("niltime", func() string {
|
||||||
return time.Unix(1548854791, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
return time.Unix(1695902100, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
||||||
})
|
})
|
||||||
geth.SetTemplateFunc("apis", func() string { return ipcAPIs })
|
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("gover", runtime.Version)
|
||||||
attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
||||||
attach.SetTemplateFunc("niltime", func() string {
|
attach.SetTemplateFunc("niltime", func() string {
|
||||||
return time.Unix(1548854791, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
return time.Unix(1695902100, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
||||||
})
|
})
|
||||||
attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
||||||
attach.SetTemplateFunc("datadir", func() string { return geth.Datadir })
|
attach.SetTemplateFunc("datadir", func() string { return geth.Datadir })
|
||||||
|
|
|
||||||
|
|
@ -289,9 +289,6 @@ func main() {
|
||||||
func prepare(ctx *cli.Context) {
|
func prepare(ctx *cli.Context) {
|
||||||
// If we're running a known preset, log it for convenience.
|
// If we're running a known preset, log it for convenience.
|
||||||
switch {
|
switch {
|
||||||
case ctx.IsSet(utils.GoerliFlag.Name):
|
|
||||||
log.Info("Starting Geth on Görli testnet...")
|
|
||||||
|
|
||||||
case ctx.IsSet(utils.SepoliaFlag.Name):
|
case ctx.IsSet(utils.SepoliaFlag.Name):
|
||||||
log.Info("Starting Geth on Sepolia testnet...")
|
log.Info("Starting Geth on Sepolia testnet...")
|
||||||
|
|
||||||
|
|
@ -324,7 +321,6 @@ func prepare(ctx *cli.Context) {
|
||||||
// Make sure we're not on any supported preconfigured testnet either
|
// Make sure we're not on any supported preconfigured testnet either
|
||||||
if !ctx.IsSet(utils.HoleskyFlag.Name) &&
|
if !ctx.IsSet(utils.HoleskyFlag.Name) &&
|
||||||
!ctx.IsSet(utils.SepoliaFlag.Name) &&
|
!ctx.IsSet(utils.SepoliaFlag.Name) &&
|
||||||
!ctx.IsSet(utils.GoerliFlag.Name) &&
|
|
||||||
!ctx.IsSet(utils.DeveloperFlag.Name) {
|
!ctx.IsSet(utils.DeveloperFlag.Name) {
|
||||||
// Nope, we're really on mainnet. Bump that cache up!
|
// 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)
|
log.Info("Bumping default cache on mainnet", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 4096)
|
||||||
|
|
@ -360,8 +356,6 @@ func geth(ctx *cli.Context) error {
|
||||||
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
||||||
// miner.
|
// miner.
|
||||||
func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
|
func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
|
||||||
debug.Memsize.Add("node", stack)
|
|
||||||
|
|
||||||
// Start up the node itself
|
// Start up the node itself
|
||||||
utils.StartNode(ctx, stack, isConsole)
|
utils.StartNode(ctx, stack, isConsole)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,443 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -134,7 +134,7 @@ var (
|
||||||
}
|
}
|
||||||
NetworkIdFlag = &cli.Uint64Flag{
|
NetworkIdFlag = &cli.Uint64Flag{
|
||||||
Name: "networkid",
|
Name: "networkid",
|
||||||
Usage: "Explicitly set network id (integer)(For testnets: use --goerli, --sepolia, --holesky instead)",
|
Usage: "Explicitly set network id (integer)(For testnets: use --sepolia, --holesky instead)",
|
||||||
Value: ethconfig.Defaults.NetworkId,
|
Value: ethconfig.Defaults.NetworkId,
|
||||||
Category: flags.EthCategory,
|
Category: flags.EthCategory,
|
||||||
}
|
}
|
||||||
|
|
@ -143,11 +143,6 @@ var (
|
||||||
Usage: "Ethereum mainnet",
|
Usage: "Ethereum mainnet",
|
||||||
Category: flags.EthCategory,
|
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{
|
SepoliaFlag = &cli.BoolFlag{
|
||||||
Name: "sepolia",
|
Name: "sepolia",
|
||||||
Usage: "Sepolia network: pre-configured proof-of-work test network",
|
Usage: "Sepolia network: pre-configured proof-of-work test network",
|
||||||
|
|
@ -812,8 +807,9 @@ var (
|
||||||
DiscoveryV5Flag = &cli.BoolFlag{
|
DiscoveryV5Flag = &cli.BoolFlag{
|
||||||
Name: "discovery.v5",
|
Name: "discovery.v5",
|
||||||
Aliases: []string{"discv5"},
|
Aliases: []string{"discv5"},
|
||||||
Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism",
|
Usage: "Enables the V5 discovery mechanism",
|
||||||
Category: flags.NetworkingCategory,
|
Category: flags.NetworkingCategory,
|
||||||
|
Value: true,
|
||||||
}
|
}
|
||||||
NetrestrictFlag = &cli.StringFlag{
|
NetrestrictFlag = &cli.StringFlag{
|
||||||
Name: "netrestrict",
|
Name: "netrestrict",
|
||||||
|
|
@ -965,7 +961,6 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
||||||
var (
|
var (
|
||||||
// TestnetFlags is the flag group of all built-in supported testnets.
|
// TestnetFlags is the flag group of all built-in supported testnets.
|
||||||
TestnetFlags = []cli.Flag{
|
TestnetFlags = []cli.Flag{
|
||||||
GoerliFlag,
|
|
||||||
SepoliaFlag,
|
SepoliaFlag,
|
||||||
HoleskyFlag,
|
HoleskyFlag,
|
||||||
}
|
}
|
||||||
|
|
@ -988,9 +983,6 @@ var (
|
||||||
// then a subdirectory of the specified datadir will be used.
|
// then a subdirectory of the specified datadir will be used.
|
||||||
func MakeDataDir(ctx *cli.Context) string {
|
func MakeDataDir(ctx *cli.Context) string {
|
||||||
if path := ctx.String(DataDirFlag.Name); path != "" {
|
if path := ctx.String(DataDirFlag.Name); path != "" {
|
||||||
if ctx.Bool(GoerliFlag.Name) {
|
|
||||||
return filepath.Join(path, "goerli")
|
|
||||||
}
|
|
||||||
if ctx.Bool(SepoliaFlag.Name) {
|
if ctx.Bool(SepoliaFlag.Name) {
|
||||||
return filepath.Join(path, "sepolia")
|
return filepath.Join(path, "sepolia")
|
||||||
}
|
}
|
||||||
|
|
@ -1042,7 +1034,7 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
|
||||||
//
|
//
|
||||||
// 1. --bootnodes flag
|
// 1. --bootnodes flag
|
||||||
// 2. Config file
|
// 2. Config file
|
||||||
// 3. Network preset flags (e.g. --goerli)
|
// 3. Network preset flags (e.g. --holesky)
|
||||||
// 4. default to mainnet nodes
|
// 4. default to mainnet nodes
|
||||||
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
urls := params.MainnetBootnodes
|
urls := params.MainnetBootnodes
|
||||||
|
|
@ -1057,8 +1049,6 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
urls = params.HoleskyBootnodes
|
urls = params.HoleskyBootnodes
|
||||||
case ctx.Bool(SepoliaFlag.Name):
|
case ctx.Bool(SepoliaFlag.Name):
|
||||||
urls = params.SepoliaBootnodes
|
urls = params.SepoliaBootnodes
|
||||||
case ctx.Bool(GoerliFlag.Name):
|
|
||||||
urls = params.GoerliBootnodes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cfg.BootstrapNodes = mustParseBootnodes(urls)
|
cfg.BootstrapNodes = mustParseBootnodes(urls)
|
||||||
|
|
@ -1484,8 +1474,6 @@ func SetDataDir(ctx *cli.Context, cfg *node.Config) {
|
||||||
cfg.DataDir = ctx.String(DataDirFlag.Name)
|
cfg.DataDir = ctx.String(DataDirFlag.Name)
|
||||||
case ctx.Bool(DeveloperFlag.Name):
|
case ctx.Bool(DeveloperFlag.Name):
|
||||||
cfg.DataDir = "" // unless explicitly requested, use memory databases
|
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():
|
case ctx.Bool(SepoliaFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia")
|
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia")
|
||||||
case ctx.Bool(HoleskyFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
case ctx.Bool(HoleskyFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||||
|
|
@ -1657,7 +1645,7 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
|
||||||
// SetEthConfig applies eth-related command line flags to the config.
|
// SetEthConfig applies eth-related command line flags to the config.
|
||||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
// Avoid conflicting network flags
|
// Avoid conflicting network flags
|
||||||
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag, HoleskyFlag)
|
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag)
|
||||||
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||||
|
|
||||||
// Set configurations from CLI flags
|
// Set configurations from CLI flags
|
||||||
|
|
@ -1827,12 +1815,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
}
|
}
|
||||||
cfg.Genesis = core.DefaultSepoliaGenesisBlock()
|
cfg.Genesis = core.DefaultSepoliaGenesisBlock()
|
||||||
SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash)
|
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):
|
case ctx.Bool(DeveloperFlag.Name):
|
||||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||||
cfg.NetworkId = 1337
|
cfg.NetworkId = 1337
|
||||||
|
|
@ -2154,8 +2136,6 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
||||||
genesis = core.DefaultHoleskyGenesisBlock()
|
genesis = core.DefaultHoleskyGenesisBlock()
|
||||||
case ctx.Bool(SepoliaFlag.Name):
|
case ctx.Bool(SepoliaFlag.Name):
|
||||||
genesis = core.DefaultSepoliaGenesisBlock()
|
genesis = core.DefaultSepoliaGenesisBlock()
|
||||||
case ctx.Bool(GoerliFlag.Name):
|
|
||||||
genesis = core.DefaultGoerliGenesisBlock()
|
|
||||||
case ctx.Bool(DeveloperFlag.Name):
|
case ctx.Bool(DeveloperFlag.Name):
|
||||||
Fatalf("Developer chains are ephemeral")
|
Fatalf("Developer chains are ephemeral")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,25 +92,21 @@ var (
|
||||||
LightServeFlag = &cli.IntFlag{
|
LightServeFlag = &cli.IntFlag{
|
||||||
Name: "light.serve",
|
Name: "light.serve",
|
||||||
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated)",
|
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated)",
|
||||||
Value: ethconfig.Defaults.LightServ,
|
|
||||||
Category: flags.DeprecatedCategory,
|
Category: flags.DeprecatedCategory,
|
||||||
}
|
}
|
||||||
LightIngressFlag = &cli.IntFlag{
|
LightIngressFlag = &cli.IntFlag{
|
||||||
Name: "light.ingress",
|
Name: "light.ingress",
|
||||||
Usage: "Incoming bandwidth limit for serving light clients (deprecated)",
|
Usage: "Incoming bandwidth limit for serving light clients (deprecated)",
|
||||||
Value: ethconfig.Defaults.LightIngress,
|
|
||||||
Category: flags.DeprecatedCategory,
|
Category: flags.DeprecatedCategory,
|
||||||
}
|
}
|
||||||
LightEgressFlag = &cli.IntFlag{
|
LightEgressFlag = &cli.IntFlag{
|
||||||
Name: "light.egress",
|
Name: "light.egress",
|
||||||
Usage: "Outgoing bandwidth limit for serving light clients (deprecated)",
|
Usage: "Outgoing bandwidth limit for serving light clients (deprecated)",
|
||||||
Value: ethconfig.Defaults.LightEgress,
|
|
||||||
Category: flags.DeprecatedCategory,
|
Category: flags.DeprecatedCategory,
|
||||||
}
|
}
|
||||||
LightMaxPeersFlag = &cli.IntFlag{
|
LightMaxPeersFlag = &cli.IntFlag{
|
||||||
Name: "light.maxpeers",
|
Name: "light.maxpeers",
|
||||||
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated)",
|
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated)",
|
||||||
Value: ethconfig.Defaults.LightPeers,
|
|
||||||
Category: flags.DeprecatedCategory,
|
Category: flags.DeprecatedCategory,
|
||||||
}
|
}
|
||||||
LightNoPruneFlag = &cli.BoolFlag{
|
LightNoPruneFlag = &cli.BoolFlag{
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Blo
|
||||||
// (c) the extradata is limited to 32 bytes
|
// (c) the extradata is limited to 32 bytes
|
||||||
func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error {
|
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
|
// Ensure that the header's extra-data section is of a reasonable size
|
||||||
if len(header.Extra) > 32 {
|
if len(header.Extra) > int(params.MaximumExtraDataSize) {
|
||||||
return fmt.Errorf("extra-data longer than 32 bytes (%d)", len(header.Extra))
|
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.
|
// Verify the seal parts. Ensure the nonce and uncle hash are the expected value.
|
||||||
|
|
@ -387,8 +387,39 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
||||||
// Assign the final state root to header.
|
// Assign the final state root to header.
|
||||||
header.Root = state.IntermediateRoot(true)
|
header.Root = state.IntermediateRoot(true)
|
||||||
|
|
||||||
// Assemble and return the final block.
|
// Assemble the final block.
|
||||||
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), nil
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seal generates a new sealing request for the given input block and pushes
|
// 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.
|
// This test case is a repro of an annoying bug that took us forever to catch.
|
||||||
// In Clique PoA networks (Görli, etc), consecutive blocks might have
|
// In Clique PoA networks, consecutive blocks might have the same state root (no
|
||||||
// the same state root (no block subsidy, empty block). If a node crashes, the
|
// block subsidy, empty block). If a node crashes, the chain ends up losing the
|
||||||
// chain ends up losing the recent state and needs to regenerate it from blocks
|
// recent state and needs to regenerate it from blocks already in the database.
|
||||||
// already in the database. The bug was that processing the block *prior* to an
|
// The bug was that processing the block *prior* to an empty one **also
|
||||||
// empty one **also completes** the empty one, ending up in a known-block error.
|
// completes** the empty one, ending up in a known-block error.
|
||||||
func TestReimportMirroredState(t *testing.T) {
|
func TestReimportMirroredState(t *testing.T) {
|
||||||
// Initialize a Clique chain with a single signer
|
// Initialize a Clique chain with a single signer
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,9 @@ var (
|
||||||
snapshotStorageReadTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/storage/reads", nil)
|
snapshotStorageReadTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/storage/reads", nil)
|
||||||
snapshotCommitTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/commits", nil)
|
snapshotCommitTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/commits", nil)
|
||||||
|
|
||||||
|
accountReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/account/single/reads", nil)
|
||||||
|
storageReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/storage/single/reads", nil)
|
||||||
|
|
||||||
triedbCommitTimer = metrics.NewRegisteredResettingTimer("chain/triedb/commits", nil)
|
triedbCommitTimer = metrics.NewRegisteredResettingTimer("chain/triedb/commits", nil)
|
||||||
|
|
||||||
blockInsertTimer = metrics.NewRegisteredResettingTimer("chain/inserts", nil)
|
blockInsertTimer = metrics.NewRegisteredResettingTimer("chain/inserts", nil)
|
||||||
|
|
@ -1951,14 +1954,21 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||||
storageReadTimer.Update(statedb.StorageReads) // Storage 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)
|
snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing)
|
||||||
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing)
|
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing)
|
||||||
|
|
||||||
|
accountRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read
|
||||||
|
storageRead := statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read
|
||||||
|
if statedb.AccountLoaded != 0 {
|
||||||
|
accountReadSingleTimer.Update(accountRead / time.Duration(statedb.AccountLoaded))
|
||||||
|
}
|
||||||
|
if statedb.StorageLoaded != 0 {
|
||||||
|
storageReadSingleTimer.Update(storageRead / time.Duration(statedb.StorageLoaded))
|
||||||
|
}
|
||||||
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
|
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
|
||||||
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage 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)
|
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
|
||||||
triehash := statedb.AccountHashes // The time spent on tries hashing
|
triehash := statedb.AccountHashes // The time spent on tries hashing
|
||||||
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
|
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
|
||||||
trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read
|
blockExecutionTimer.Update(ptime - (accountRead + storageRead)) // The time spent on EVM processing
|
||||||
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
|
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
|
||||||
|
|
||||||
// Write the block to the chain and get the status.
|
// Write the block to the chain and get the status.
|
||||||
|
|
|
||||||
|
|
@ -467,9 +467,8 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
|
||||||
panic(fmt.Sprintf("trie write error: %v", err))
|
panic(fmt.Sprintf("trie write error: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO uncomment when proof generation is merged
|
proofs = append(proofs, block.ExecutionWitness().VerkleProof)
|
||||||
// proofs = append(proofs, block.ExecutionWitness().VerkleProof)
|
keyvals = append(keyvals, block.ExecutionWitness().StateDiff)
|
||||||
// keyvals = append(keyvals, block.ExecutionWitness().StateDiff)
|
|
||||||
|
|
||||||
return block, b.receipts
|
return block, b.receipts
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,25 +80,6 @@ func TestCreation(t *testing.T) {
|
||||||
{50000000, 2000000000, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}}, // Future Cancun block
|
{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
|
// Sepolia test cases
|
||||||
{
|
{
|
||||||
params.SepoliaChainConfig,
|
params.SepoliaChainConfig,
|
||||||
|
|
|
||||||
|
|
@ -145,13 +145,12 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
||||||
return statedb.Commit(0, false)
|
return statedb.Commit(0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// flushAlloc is very similar with hash, but the main difference is all the generated
|
// flushAlloc is very similar with hash, but the main difference is all the
|
||||||
// states will be persisted into the given database. Also, the genesis state
|
// generated states will be persisted into the given database.
|
||||||
// specification will be flushed as well.
|
func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Database) (common.Hash, error) {
|
||||||
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)
|
statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
for addr, account := range *ga {
|
for addr, account := range *ga {
|
||||||
if account.Balance != nil {
|
if account.Balance != nil {
|
||||||
|
|
@ -167,21 +166,15 @@ func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Databa
|
||||||
}
|
}
|
||||||
root, err := statedb.Commit(0, false)
|
root, err := statedb.Commit(0, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
// Commit newly generated states into disk if it's not empty.
|
// Commit newly generated states into disk if it's not empty.
|
||||||
if root != types.EmptyRootHash {
|
if root != types.EmptyRootHash {
|
||||||
if err := triedb.Commit(root, true); err != nil {
|
if err := triedb.Commit(root, true); err != nil {
|
||||||
return err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Marshal the genesis state specification and persist.
|
return root, nil
|
||||||
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) {
|
func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.GenesisAlloc, err error) {
|
||||||
|
|
@ -203,8 +196,6 @@ func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.Gene
|
||||||
switch blockhash {
|
switch blockhash {
|
||||||
case params.MainnetGenesisHash:
|
case params.MainnetGenesisHash:
|
||||||
genesis = DefaultGenesisBlock()
|
genesis = DefaultGenesisBlock()
|
||||||
case params.GoerliGenesisHash:
|
|
||||||
genesis = DefaultGoerliGenesisBlock()
|
|
||||||
case params.SepoliaGenesisHash:
|
case params.SepoliaGenesisHash:
|
||||||
genesis = DefaultSepoliaGenesisBlock()
|
genesis = DefaultSepoliaGenesisBlock()
|
||||||
case params.HoleskyGenesisHash:
|
case params.HoleskyGenesisHash:
|
||||||
|
|
@ -407,8 +398,6 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||||
return params.HoleskyChainConfig
|
return params.HoleskyChainConfig
|
||||||
case ghash == params.SepoliaGenesisHash:
|
case ghash == params.SepoliaGenesisHash:
|
||||||
return params.SepoliaChainConfig
|
return params.SepoliaChainConfig
|
||||||
case ghash == params.GoerliGenesisHash:
|
|
||||||
return params.GoerliChainConfig
|
|
||||||
default:
|
default:
|
||||||
return params.AllEthashProtocolChanges
|
return params.AllEthashProtocolChanges
|
||||||
}
|
}
|
||||||
|
|
@ -426,6 +415,11 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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{
|
head := &types.Header{
|
||||||
Number: new(big.Int).SetUint64(g.Number),
|
Number: new(big.Int).SetUint64(g.Number),
|
||||||
Nonce: types.EncodeNonce(g.Nonce),
|
Nonce: types.EncodeNonce(g.Nonce),
|
||||||
|
|
@ -482,8 +476,7 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||||
// Commit writes the block and state of a genesis specification to the database.
|
// Commit writes the block and state of a genesis specification to the database.
|
||||||
// The block is committed as the canonical head block.
|
// The block is committed as the canonical head block.
|
||||||
func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) {
|
func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) {
|
||||||
block := g.ToBlock()
|
if g.Number != 0 {
|
||||||
if block.Number().Sign() != 0 {
|
|
||||||
return nil, errors.New("can't commit genesis block with number > 0")
|
return nil, errors.New("can't commit genesis block with number > 0")
|
||||||
}
|
}
|
||||||
config := g.Config
|
config := g.Config
|
||||||
|
|
@ -493,15 +486,22 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo
|
||||||
if err := config.CheckConfigForkOrder(); err != nil {
|
if err := config.CheckConfigForkOrder(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if config.Clique != nil && len(block.Extra()) < 32+crypto.SignatureLength {
|
if config.Clique != nil && len(g.ExtraData) < 32+crypto.SignatureLength {
|
||||||
return nil, errors.New("can't start clique chain without signers")
|
return nil, errors.New("can't start clique chain without signers")
|
||||||
}
|
}
|
||||||
// All the checks has passed, flushAlloc the states derived from the genesis
|
// flush the data to disk and compute the state root
|
||||||
// specification as well as the specification itself into the provided
|
root, err := flushAlloc(&g.Alloc, db, triedb)
|
||||||
// database.
|
if err != nil {
|
||||||
if err := flushAlloc(&g.Alloc, db, triedb, block.Hash()); err != nil {
|
|
||||||
return nil, err
|
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.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
|
||||||
rawdb.WriteBlock(db, block)
|
rawdb.WriteBlock(db, block)
|
||||||
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
|
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
|
||||||
|
|
@ -535,18 +535,6 @@ 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.
|
// DefaultSepoliaGenesisBlock returns the Sepolia network genesis block.
|
||||||
func DefaultSepoliaGenesisBlock() *Genesis {
|
func DefaultSepoliaGenesisBlock() *Genesis {
|
||||||
return &Genesis{
|
return &Genesis{
|
||||||
|
|
@ -595,6 +583,8 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
||||||
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
|
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
|
||||||
// Pre-deploy EIP-4788 system contract
|
// Pre-deploy EIP-4788 system contract
|
||||||
params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0},
|
params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0},
|
||||||
|
// Pre-deploy EIP-2935 history contract.
|
||||||
|
params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if faucet != nil {
|
if faucet != nil {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -35,15 +35,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
"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) {
|
func TestSetupGenesis(t *testing.T) {
|
||||||
testSetupGenesis(t, rawdb.HashScheme)
|
testSetupGenesis(t, rawdb.HashScheme)
|
||||||
testSetupGenesis(t, rawdb.PathScheme)
|
testSetupGenesis(t, rawdb.PathScheme)
|
||||||
|
|
@ -105,15 +96,15 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
wantConfig: customg.Config,
|
wantConfig: customg.Config,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "custom block in DB, genesis == goerli",
|
name: "custom block in DB, genesis == sepolia",
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||||
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||||
customg.Commit(db, tdb)
|
customg.Commit(db, tdb)
|
||||||
return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
|
return SetupGenesisBlock(db, tdb, DefaultSepoliaGenesisBlock())
|
||||||
},
|
},
|
||||||
wantErr: &GenesisMismatchError{Stored: customghash, New: params.GoerliGenesisHash},
|
wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash},
|
||||||
wantHash: params.GoerliGenesisHash,
|
wantHash: params.SepoliaGenesisHash,
|
||||||
wantConfig: params.GoerliChainConfig,
|
wantConfig: params.SepoliaChainConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "compatible config in DB",
|
name: "compatible config in DB",
|
||||||
|
|
@ -184,7 +175,6 @@ func TestGenesisHashes(t *testing.T) {
|
||||||
want common.Hash
|
want common.Hash
|
||||||
}{
|
}{
|
||||||
{DefaultGenesisBlock(), params.MainnetGenesisHash},
|
{DefaultGenesisBlock(), params.MainnetGenesisHash},
|
||||||
{DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
|
|
||||||
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
|
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
|
||||||
} {
|
} {
|
||||||
// Test via MustCommit
|
// Test via MustCommit
|
||||||
|
|
@ -304,7 +294,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := common.FromHex("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b")
|
expected := common.FromHex("4a83dc39eb688dbcfaf581d60e82de18f875e38786ebce5833342011d6fef37b")
|
||||||
got := genesis.ToBlock().Root().Bytes()
|
got := genesis.ToBlock().Root().Bytes()
|
||||||
if !bytes.Equal(got, expected) {
|
if !bytes.Equal(got, expected) {
|
||||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
||||||
|
|
|
||||||
|
|
@ -149,12 +149,6 @@ func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReaderOp) error)
|
||||||
return fn(db)
|
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.
|
// AncientDatadir returns an error as we don't have a backing chain freezer.
|
||||||
func (db *nofreezedb) AncientDatadir() (string, error) {
|
func (db *nofreezedb) AncientDatadir() (string, error) {
|
||||||
return "", errNotSupported
|
return "", errNotSupported
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
|
@ -389,115 +387,3 @@ func (f *Freezer) repair() error {
|
||||||
f.tail.Store(tail)
|
f.tail.Store(tail)
|
||||||
return nil
|
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,13 +394,6 @@ func (f *MemoryFreezer) Sync() error {
|
||||||
return nil
|
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
|
// Close releases all the sources held by the memory freezer. It will panic if
|
||||||
// any following invocation is made to a closed freezer.
|
// any following invocation is made to a closed freezer.
|
||||||
func (f *MemoryFreezer) Close() error {
|
func (f *MemoryFreezer) Close() error {
|
||||||
|
|
|
||||||
|
|
@ -202,15 +202,6 @@ func (f *resettableFreezer) Sync() error {
|
||||||
return f.freezer.Sync()
|
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
|
// cleanup removes the directory located in the specified path
|
||||||
// has the name with deletion marker suffix.
|
// has the name with deletion marker suffix.
|
||||||
func cleanup(path string) error {
|
func cleanup(path string) error {
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -379,87 +377,6 @@ 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) {
|
func TestFreezerCloseSync(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
f, _ := newFreezerForTesting(t, map[string]bool{"a": true, "b": true})
|
f, _ := newFreezerForTesting(t, map[string]bool{"a": true, "b": true})
|
||||||
|
|
|
||||||
|
|
@ -113,12 +113,6 @@ func (t *table) Sync() error {
|
||||||
return t.db.Sync()
|
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.
|
// AncientDatadir returns the ancient datadir of the underlying database.
|
||||||
func (t *table) AncientDatadir() (string, error) {
|
func (t *table) AncientDatadir() (string, error) {
|
||||||
return t.db.AncientDatadir()
|
return t.db.AncientDatadir()
|
||||||
|
|
|
||||||
|
|
@ -94,11 +94,8 @@ func (ae *AccessEvents) Copy() *AccessEvents {
|
||||||
// member fields of an account.
|
// member fields of an account.
|
||||||
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite)
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, 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
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,8 +104,7 @@ func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
||||||
// call to that account.
|
// call to that account.
|
||||||
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.VersionLeafKey, false)
|
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
||||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.CodeSizeLeafKey, false)
|
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,8 +112,8 @@ func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
||||||
// cold balance member fields of the caller and the callee accounts.
|
// cold balance member fields of the caller and the callee accounts.
|
||||||
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
|
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||||
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,32 +121,22 @@ func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address)
|
||||||
// a contract creation.
|
// a contract creation.
|
||||||
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 {
|
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true)
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, true)
|
|
||||||
if createSendsValue {
|
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
|
||||||
}
|
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
// 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.
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey, false)
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||||
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,
|
// 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.
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
|
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false)
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue)
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, sendsValue)
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||||
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.
|
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
||||||
|
|
@ -275,39 +261,12 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
||||||
return statelessGasCharged
|
return statelessGasCharged
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionGas adds the account's version to the accessed data, and returns the
|
// BasicDataGas adds the account's basic data to the accessed data, and returns the
|
||||||
// amount of gas that it costs.
|
// amount of gas that it costs.
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an
|
// 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.
|
// access in read mode does not imply an access in write mode.
|
||||||
func (ae *AccessEvents) VersionGas(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, 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
|
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
||||||
|
|
@ -316,5 +275,5 @@ func (ae *AccessEvents) CodeSizeGas(addr common.Address, isWrite bool) uint64 {
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
// 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.
|
// read mode does not imply an access in write mode.
|
||||||
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,55 +40,43 @@ func TestAccountHeaderGas(t *testing.T) {
|
||||||
ae := NewAccessEvents(utils.NewPointCache(1024))
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
// Check cold read cost
|
// Check cold read cost
|
||||||
gas := ae.VersionGas(testAddr, false)
|
gas := ae.BasicDataGas(testAddr, false)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.VersionGas(testAddr, false)
|
gas = ae.BasicDataGas(testAddr, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", 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
|
// 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)
|
gas = ae.CodeHashGas(testAddr, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold write cost
|
// Check cold write cost
|
||||||
gas = ae.VersionGas(testAddr, true)
|
gas = ae.BasicDataGas(testAddr, true)
|
||||||
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm write cost
|
// Check warm write cost
|
||||||
gas = ae.VersionGas(testAddr, true)
|
gas = ae.BasicDataGas(testAddr, true)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", 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
|
// Check a write without a read charges both read and write costs
|
||||||
gas = ae.BalanceGas(testAddr2, true)
|
gas = ae.BasicDataGas(testAddr2, true)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that a write followed by a read charges nothing
|
// Check that a write followed by a read charges nothing
|
||||||
gas = ae.BalanceGas(testAddr2, false)
|
gas = ae.BasicDataGas(testAddr2, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +101,7 @@ func TestContractCreateInitGas(t *testing.T) {
|
||||||
|
|
||||||
// Check cold read cost, without a value
|
// Check cold read cost, without a value
|
||||||
gas := ae.ContractCreateInitGas(testAddr, false)
|
gas := ae.ContractCreateInitGas(testAddr, false)
|
||||||
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + params.WitnessChunkWriteCost*2 + params.WitnessChunkReadCost*2; gas != want {
|
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,17 +119,17 @@ func TestMessageCallGas(t *testing.T) {
|
||||||
|
|
||||||
// Check cold read cost, without a value
|
// Check cold read cost, without a value
|
||||||
gas := ae.MessageCallGas(testAddr)
|
gas := ae.MessageCallGas(testAddr)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost*2; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that reading the version and code size of the same account does not incur the branch read cost
|
// Check that reading the basic data and code hash of the same account does not incur the branch read cost
|
||||||
gas = ae.VersionGas(testAddr, false)
|
gas = ae.BasicDataGas(testAddr, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
gas = ae.CodeSizeGas(testAddr, false)
|
gas = ae.CodeHashGas(testAddr, false)
|
||||||
if gas != 0 {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ type Trie interface {
|
||||||
// UpdateAccount abstracts an account write to the trie. It encodes the
|
// UpdateAccount abstracts an account write to the trie. It encodes the
|
||||||
// provided account object with associated algorithm and then updates it
|
// provided account object with associated algorithm and then updates it
|
||||||
// in the trie with provided address.
|
// in the trie with provided address.
|
||||||
UpdateAccount(address common.Address, account *types.StateAccount) error
|
UpdateAccount(address common.Address, account *types.StateAccount, codeLen int) error
|
||||||
|
|
||||||
// UpdateStorage associates key with value in the trie. If value has length zero,
|
// 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
|
// any existing value is deleted from the trie. The value bytes must not be modified
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,21 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
|
"slices"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type revision struct {
|
||||||
|
id int
|
||||||
|
journalIndex int
|
||||||
|
}
|
||||||
|
|
||||||
// journalEntry is a modification entry in the state change journal that can be
|
// journalEntry is a modification entry in the state change journal that can be
|
||||||
// reverted on demand.
|
// reverted on demand.
|
||||||
type journalEntry interface {
|
type journalEntry interface {
|
||||||
|
|
@ -42,6 +51,9 @@ type journalEntry interface {
|
||||||
type journal struct {
|
type journal struct {
|
||||||
entries []journalEntry // Current changes tracked by the journal
|
entries []journalEntry // Current changes tracked by the journal
|
||||||
dirties map[common.Address]int // Dirty accounts and the number of changes
|
dirties map[common.Address]int // Dirty accounts and the number of changes
|
||||||
|
|
||||||
|
validRevisions []revision
|
||||||
|
nextRevisionId int
|
||||||
}
|
}
|
||||||
|
|
||||||
// newJournal creates a new initialized journal.
|
// newJournal creates a new initialized journal.
|
||||||
|
|
@ -51,6 +63,40 @@ 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.
|
// append inserts a new modification entry to the end of the change journal.
|
||||||
func (j *journal) append(entry journalEntry) {
|
func (j *journal) append(entry journalEntry) {
|
||||||
j.entries = append(j.entries, entry)
|
j.entries = append(j.entries, entry)
|
||||||
|
|
@ -97,9 +143,88 @@ func (j *journal) copy() *journal {
|
||||||
return &journal{
|
return &journal{
|
||||||
entries: entries,
|
entries: entries,
|
||||||
dirties: maps.Clone(j.dirties),
|
dirties: maps.Clone(j.dirties),
|
||||||
|
validRevisions: slices.Clone(j.validRevisions),
|
||||||
|
nextRevisionId: j.nextRevisionId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (
|
type (
|
||||||
// Changes to the account trie.
|
// Changes to the account trie.
|
||||||
createObjectChange struct {
|
createObjectChange struct {
|
||||||
|
|
@ -115,8 +240,6 @@ type (
|
||||||
|
|
||||||
selfDestructChange struct {
|
selfDestructChange struct {
|
||||||
account *common.Address
|
account *common.Address
|
||||||
prev bool // whether account had already self-destructed
|
|
||||||
prevbalance *uint256.Int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes to individual accounts.
|
// Changes to individual accounts.
|
||||||
|
|
@ -136,7 +259,6 @@ type (
|
||||||
}
|
}
|
||||||
codeChange struct {
|
codeChange struct {
|
||||||
account *common.Address
|
account *common.Address
|
||||||
prevcode, prevhash []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes to other state values.
|
// Changes to other state values.
|
||||||
|
|
@ -146,9 +268,6 @@ type (
|
||||||
addLogChange struct {
|
addLogChange struct {
|
||||||
txhash common.Hash
|
txhash common.Hash
|
||||||
}
|
}
|
||||||
addPreimageChange struct {
|
|
||||||
hash common.Hash
|
|
||||||
}
|
|
||||||
touchChange struct {
|
touchChange struct {
|
||||||
account *common.Address
|
account *common.Address
|
||||||
}
|
}
|
||||||
|
|
@ -200,8 +319,7 @@ func (ch createContractChange) copy() journalEntry {
|
||||||
func (ch selfDestructChange) revert(s *StateDB) {
|
func (ch selfDestructChange) revert(s *StateDB) {
|
||||||
obj := s.getStateObject(*ch.account)
|
obj := s.getStateObject(*ch.account)
|
||||||
if obj != nil {
|
if obj != nil {
|
||||||
obj.selfDestructed = ch.prev
|
obj.selfDestructed = false
|
||||||
obj.setBalance(ch.prevbalance)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,8 +330,6 @@ func (ch selfDestructChange) dirtied() *common.Address {
|
||||||
func (ch selfDestructChange) copy() journalEntry {
|
func (ch selfDestructChange) copy() journalEntry {
|
||||||
return selfDestructChange{
|
return selfDestructChange{
|
||||||
account: ch.account,
|
account: ch.account,
|
||||||
prev: ch.prev,
|
|
||||||
prevbalance: new(uint256.Int).Set(ch.prevbalance),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,7 +379,7 @@ func (ch nonceChange) copy() journalEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch codeChange) revert(s *StateDB) {
|
func (ch codeChange) revert(s *StateDB) {
|
||||||
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
|
s.getStateObject(*ch.account).setCode(types.EmptyCodeHash, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch codeChange) dirtied() *common.Address {
|
func (ch codeChange) dirtied() *common.Address {
|
||||||
|
|
@ -271,11 +387,7 @@ func (ch codeChange) dirtied() *common.Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch codeChange) copy() journalEntry {
|
func (ch codeChange) copy() journalEntry {
|
||||||
return codeChange{
|
return codeChange{account: ch.account}
|
||||||
account: ch.account,
|
|
||||||
prevhash: common.CopyBytes(ch.prevhash),
|
|
||||||
prevcode: common.CopyBytes(ch.prevcode),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch storageChange) revert(s *StateDB) {
|
func (ch storageChange) revert(s *StateDB) {
|
||||||
|
|
@ -344,20 +456,6 @@ 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) {
|
func (ch accessListAddAccountChange) revert(s *StateDB) {
|
||||||
/*
|
/*
|
||||||
One important invariant here, is that whenever a (addr, slot) is added, if the
|
One important invariant here, is that whenever a (addr, slot) is added, if the
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ package state
|
||||||
import "github.com/ethereum/go-ethereum/metrics"
|
import "github.com/ethereum/go-ethereum/metrics"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
accountReadMeters = metrics.NewRegisteredMeter("state/read/accounts", nil)
|
||||||
|
storageReadMeters = metrics.NewRegisteredMeter("state/read/storage", nil)
|
||||||
accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
|
accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
|
||||||
storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
|
storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
|
||||||
accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
|
accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
|
||||||
|
|
|
||||||
|
|
@ -270,11 +270,10 @@ func (p *Pruner) Prune(root common.Hash) error {
|
||||||
// is the presence of root can indicate the presence of the
|
// is the presence of root can indicate the presence of the
|
||||||
// entire trie.
|
// entire trie.
|
||||||
if !rawdb.HasLegacyTrieNode(p.db, root) {
|
if !rawdb.HasLegacyTrieNode(p.db, root) {
|
||||||
// The special case is for clique based networks(goerli
|
// The special case is for clique based networks, it's possible
|
||||||
// and some other private networks), it's possible that two
|
// that two consecutive blocks will have same root. In this case
|
||||||
// consecutive blocks will have same root. In this case snapshot
|
// snapshot difflayer won't be created. So HEAD-127 may not paired
|
||||||
// difflayer won't be created. So HEAD-127 may not paired with
|
// with head-127 layer. Instead the paired layer is higher than the
|
||||||
// head-127 layer. Instead the paired layer is higher than the
|
|
||||||
// bottom-most diff layer. Try to find the bottom-most snapshot
|
// bottom-most diff layer. Try to find the bottom-most snapshot
|
||||||
// layer with state available.
|
// layer with state available.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -114,14 +114,7 @@ func (s *stateObject) markSelfdestructed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) touch() {
|
func (s *stateObject) touch() {
|
||||||
s.db.journal.append(touchChange{
|
s.db.journal.touchChange(s.address)
|
||||||
account: &s.address,
|
|
||||||
})
|
|
||||||
if s.address == ripemd {
|
|
||||||
// Explicitly put it in the dirty-cache, which is otherwise generated from
|
|
||||||
// flattened journals.
|
|
||||||
s.db.journal.dirty(s.address)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTrie returns the associated storage trie. The trie will be opened if it's
|
// getTrie returns the associated storage trie. The trie will be opened if it's
|
||||||
|
|
@ -150,7 +143,7 @@ func (s *stateObject) getTrie() (Trie, error) {
|
||||||
func (s *stateObject) getPrefetchedTrie() Trie {
|
func (s *stateObject) getPrefetchedTrie() Trie {
|
||||||
// If there's nothing to meaningfully return, let the user figure it out by
|
// If there's nothing to meaningfully return, let the user figure it out by
|
||||||
// pulling the trie from disk.
|
// pulling the trie from disk.
|
||||||
if s.data.Root == types.EmptyRootHash || s.db.prefetcher == nil {
|
if (s.data.Root == types.EmptyRootHash && !s.db.db.TrieDB().IsVerkle()) || s.db.prefetcher == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Attempt to retrieve the trie from the prefetcher
|
// Attempt to retrieve the trie from the prefetcher
|
||||||
|
|
@ -239,6 +232,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.originStorage[key] = value
|
s.originStorage[key] = value
|
||||||
|
s.db.StorageLoaded++
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,16 +245,11 @@ func (s *stateObject) SetState(key, value common.Hash) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// New value is different, update and journal the change
|
// New value is different, update and journal the change
|
||||||
s.db.journal.append(storageChange{
|
s.db.journal.storageChange(s.address, key, prev, origin)
|
||||||
account: &s.address,
|
s.setState(key, value, origin)
|
||||||
key: key,
|
|
||||||
prevvalue: prev,
|
|
||||||
origvalue: origin,
|
|
||||||
})
|
|
||||||
if s.db.logger != nil && s.db.logger.OnStorageChange != nil {
|
if s.db.logger != nil && s.db.logger.OnStorageChange != nil {
|
||||||
s.db.logger.OnStorageChange(s.address, key, prev, value)
|
s.db.logger.OnStorageChange(s.address, key, prev, value)
|
||||||
}
|
}
|
||||||
s.setState(key, value, origin)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setState updates a value in account dirty storage. The dirtiness will be
|
// setState updates a value in account dirty storage. The dirtiness will be
|
||||||
|
|
@ -510,10 +499,7 @@ func (s *stateObject) SubBalance(amount *uint256.Int, reason tracing.BalanceChan
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) SetBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) {
|
func (s *stateObject) SetBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) {
|
||||||
s.db.journal.append(balanceChange{
|
s.db.journal.balanceChange(s.address, s.data.Balance)
|
||||||
account: &s.address,
|
|
||||||
prev: new(uint256.Int).Set(s.data.Balance),
|
|
||||||
})
|
|
||||||
if s.db.logger != nil && s.db.logger.OnBalanceChange != nil {
|
if s.db.logger != nil && s.db.logger.OnBalanceChange != nil {
|
||||||
s.db.logger.OnBalanceChange(s.address, s.Balance().ToBig(), amount.ToBig(), reason)
|
s.db.logger.OnBalanceChange(s.address, s.Balance().ToBig(), amount.ToBig(), reason)
|
||||||
}
|
}
|
||||||
|
|
@ -589,14 +575,10 @@ func (s *stateObject) CodeSize() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
||||||
prevcode := s.Code()
|
s.db.journal.setCode(s.address)
|
||||||
s.db.journal.append(codeChange{
|
|
||||||
account: &s.address,
|
|
||||||
prevhash: s.CodeHash(),
|
|
||||||
prevcode: prevcode,
|
|
||||||
})
|
|
||||||
if s.db.logger != nil && s.db.logger.OnCodeChange != nil {
|
if s.db.logger != nil && s.db.logger.OnCodeChange != nil {
|
||||||
s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), prevcode, codeHash, code)
|
// TODO remove prevcode from this callback
|
||||||
|
s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), nil, codeHash, code)
|
||||||
}
|
}
|
||||||
s.setCode(codeHash, code)
|
s.setCode(codeHash, code)
|
||||||
}
|
}
|
||||||
|
|
@ -608,10 +590,7 @@ func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) SetNonce(nonce uint64) {
|
func (s *stateObject) SetNonce(nonce uint64) {
|
||||||
s.db.journal.append(nonceChange{
|
s.db.journal.nonceChange(s.address, s.data.Nonce)
|
||||||
account: &s.address,
|
|
||||||
prev: s.data.Nonce,
|
|
||||||
})
|
|
||||||
if s.db.logger != nil && s.db.logger.OnNonceChange != nil {
|
if s.db.logger != nil && s.db.logger.OnNonceChange != nil {
|
||||||
s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce)
|
s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"maps"
|
"maps"
|
||||||
"math/big"
|
"math/big"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -48,11 +47,6 @@ import (
|
||||||
// TriesInMemory represents the number of layers that are kept in RAM.
|
// TriesInMemory represents the number of layers that are kept in RAM.
|
||||||
const TriesInMemory = 128
|
const TriesInMemory = 128
|
||||||
|
|
||||||
type revision struct {
|
|
||||||
id int
|
|
||||||
journalIndex int
|
|
||||||
}
|
|
||||||
|
|
||||||
type mutationType int
|
type mutationType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -137,6 +131,7 @@ type StateDB struct {
|
||||||
|
|
||||||
// Per-transaction access list
|
// Per-transaction access list
|
||||||
accessList *accessList
|
accessList *accessList
|
||||||
|
accessEvents *AccessEvents
|
||||||
|
|
||||||
// Transient storage
|
// Transient storage
|
||||||
transientStorage transientStorage
|
transientStorage transientStorage
|
||||||
|
|
@ -144,8 +139,6 @@ type StateDB struct {
|
||||||
// Journal of state modifications. This is the backbone of
|
// Journal of state modifications. This is the backbone of
|
||||||
// Snapshot and RevertToSnapshot.
|
// Snapshot and RevertToSnapshot.
|
||||||
journal *journal
|
journal *journal
|
||||||
validRevisions []revision
|
|
||||||
nextRevisionId int
|
|
||||||
|
|
||||||
// State witness if cross validation is needed
|
// State witness if cross validation is needed
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
|
|
@ -163,10 +156,12 @@ type StateDB struct {
|
||||||
SnapshotCommits time.Duration
|
SnapshotCommits time.Duration
|
||||||
TrieDBCommits time.Duration
|
TrieDBCommits time.Duration
|
||||||
|
|
||||||
AccountUpdated int
|
AccountLoaded int // Number of accounts retrieved from the database during the state transition
|
||||||
StorageUpdated atomic.Int64
|
AccountUpdated int // Number of accounts updated during the state transition
|
||||||
AccountDeleted int
|
AccountDeleted int // Number of accounts deleted during the state transition
|
||||||
StorageDeleted atomic.Int64
|
StorageLoaded int // Number of storage slots retrieved from the database during the state transition
|
||||||
|
StorageUpdated atomic.Int64 // Number of storage slots updated during the state transition
|
||||||
|
StorageDeleted atomic.Int64 // Number of storage slots deleted during the state transition
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new state from a given trie.
|
// New creates a new state from a given trie.
|
||||||
|
|
@ -190,6 +185,9 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
|
||||||
transientStorage: newTransientStorage(),
|
transientStorage: newTransientStorage(),
|
||||||
hasher: crypto.NewKeccakState(),
|
hasher: crypto.NewKeccakState(),
|
||||||
}
|
}
|
||||||
|
if db.TrieDB().IsVerkle() {
|
||||||
|
sdb.accessEvents = NewAccessEvents(db.(*cachingDB).pointCache)
|
||||||
|
}
|
||||||
if sdb.snaps != nil {
|
if sdb.snaps != nil {
|
||||||
sdb.snap = sdb.snaps.Snapshot(root)
|
sdb.snap = sdb.snaps.Snapshot(root)
|
||||||
}
|
}
|
||||||
|
|
@ -256,7 +254,7 @@ func (s *StateDB) Error() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) AddLog(log *types.Log) {
|
func (s *StateDB) AddLog(log *types.Log) {
|
||||||
s.journal.append(addLogChange{txhash: s.thash})
|
s.journal.logChange(s.thash)
|
||||||
|
|
||||||
log.TxHash = s.thash
|
log.TxHash = s.thash
|
||||||
log.TxIndex = uint(s.txIndex)
|
log.TxIndex = uint(s.txIndex)
|
||||||
|
|
@ -290,7 +288,6 @@ func (s *StateDB) Logs() []*types.Log {
|
||||||
// AddPreimage records a SHA3 preimage seen by the VM.
|
// AddPreimage records a SHA3 preimage seen by the VM.
|
||||||
func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
|
func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
|
||||||
if _, ok := s.preimages[hash]; !ok {
|
if _, ok := s.preimages[hash]; !ok {
|
||||||
s.journal.append(addPreimageChange{hash: hash})
|
|
||||||
s.preimages[hash] = slices.Clone(preimage)
|
s.preimages[hash] = slices.Clone(preimage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -302,14 +299,14 @@ func (s *StateDB) Preimages() map[common.Hash][]byte {
|
||||||
|
|
||||||
// AddRefund adds gas to the refund counter
|
// AddRefund adds gas to the refund counter
|
||||||
func (s *StateDB) AddRefund(gas uint64) {
|
func (s *StateDB) AddRefund(gas uint64) {
|
||||||
s.journal.append(refundChange{prev: s.refund})
|
s.journal.refundChange(s.refund)
|
||||||
s.refund += gas
|
s.refund += gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubRefund removes gas from the refund counter.
|
// SubRefund removes gas from the refund counter.
|
||||||
// This method will panic if the refund counter goes below zero
|
// This method will panic if the refund counter goes below zero
|
||||||
func (s *StateDB) SubRefund(gas uint64) {
|
func (s *StateDB) SubRefund(gas uint64) {
|
||||||
s.journal.append(refundChange{prev: s.refund})
|
s.journal.refundChange(s.refund)
|
||||||
if gas > s.refund {
|
if gas > s.refund {
|
||||||
panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund))
|
panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund))
|
||||||
}
|
}
|
||||||
|
|
@ -506,20 +503,17 @@ func (s *StateDB) SelfDestruct(addr common.Address) {
|
||||||
if stateObject == nil {
|
if stateObject == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var (
|
// Regardless of whether it is already destructed or not, we do have to
|
||||||
prev = new(uint256.Int).Set(stateObject.Balance())
|
// journal the balance-change, if we set it to zero here.
|
||||||
n = new(uint256.Int)
|
if !stateObject.Balance().IsZero() {
|
||||||
)
|
stateObject.SetBalance(new(uint256.Int), tracing.BalanceDecreaseSelfdestruct)
|
||||||
s.journal.append(selfDestructChange{
|
|
||||||
account: &addr,
|
|
||||||
prev: stateObject.selfDestructed,
|
|
||||||
prevbalance: prev,
|
|
||||||
})
|
|
||||||
if s.logger != nil && s.logger.OnBalanceChange != nil && prev.Sign() > 0 {
|
|
||||||
s.logger.OnBalanceChange(addr, prev.ToBig(), n.ToBig(), tracing.BalanceDecreaseSelfdestruct)
|
|
||||||
}
|
}
|
||||||
|
// If it is already marked as self-destructed, we do not need to add it
|
||||||
|
// for journalling a second time.
|
||||||
|
if !stateObject.selfDestructed {
|
||||||
|
s.journal.destruct(addr)
|
||||||
stateObject.markSelfdestructed()
|
stateObject.markSelfdestructed()
|
||||||
stateObject.data.Balance = n
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) Selfdestruct6780(addr common.Address) {
|
func (s *StateDB) Selfdestruct6780(addr common.Address) {
|
||||||
|
|
@ -540,11 +534,7 @@ func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash)
|
||||||
if prev == value {
|
if prev == value {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.journal.append(transientStorageChange{
|
s.journal.transientStateChange(addr, key, prev)
|
||||||
account: &addr,
|
|
||||||
key: key,
|
|
||||||
prevalue: prev,
|
|
||||||
})
|
|
||||||
s.setTransientState(addr, key, value)
|
s.setTransientState(addr, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -567,7 +557,7 @@ func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common
|
||||||
func (s *StateDB) updateStateObject(obj *stateObject) {
|
func (s *StateDB) updateStateObject(obj *stateObject) {
|
||||||
// Encode the account and update the account trie
|
// Encode the account and update the account trie
|
||||||
addr := obj.Address()
|
addr := obj.Address()
|
||||||
if err := s.trie.UpdateAccount(addr, &obj.data); err != nil {
|
if err := s.trie.UpdateAccount(addr, &obj.data, len(obj.code)); err != nil {
|
||||||
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err))
|
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err))
|
||||||
}
|
}
|
||||||
if obj.dirtyCode {
|
if obj.dirtyCode {
|
||||||
|
|
@ -601,6 +591,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
||||||
s.SnapshotAccountReads += time.Since(start)
|
s.SnapshotAccountReads += time.Since(start)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if acc == nil {
|
if acc == nil {
|
||||||
|
s.AccountLoaded++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
data = &types.StateAccount{
|
data = &types.StateAccount{
|
||||||
|
|
@ -629,6 +620,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if data == nil {
|
if data == nil {
|
||||||
|
s.AccountLoaded++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -643,6 +635,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
||||||
// Insert into the live set
|
// Insert into the live set
|
||||||
obj := newObject(s, addr, data)
|
obj := newObject(s, addr, data)
|
||||||
s.setStateObject(obj)
|
s.setStateObject(obj)
|
||||||
|
s.AccountLoaded++
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -663,7 +656,7 @@ func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject {
|
||||||
// existing account with the given address, otherwise it will be silently overwritten.
|
// existing account with the given address, otherwise it will be silently overwritten.
|
||||||
func (s *StateDB) createObject(addr common.Address) *stateObject {
|
func (s *StateDB) createObject(addr common.Address) *stateObject {
|
||||||
obj := newObject(s, addr, nil)
|
obj := newObject(s, addr, nil)
|
||||||
s.journal.append(createObjectChange{account: &addr})
|
s.journal.createObject(addr)
|
||||||
s.setStateObject(obj)
|
s.setStateObject(obj)
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
@ -685,7 +678,7 @@ func (s *StateDB) CreateContract(addr common.Address) {
|
||||||
obj := s.getStateObject(addr)
|
obj := s.getStateObject(addr)
|
||||||
if !obj.newContract {
|
if !obj.newContract {
|
||||||
obj.newContract = true
|
obj.newContract = true
|
||||||
s.journal.append(createContractChange{account: addr})
|
s.journal.createContract(addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -709,8 +702,6 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
logSize: s.logSize,
|
logSize: s.logSize,
|
||||||
preimages: maps.Clone(s.preimages),
|
preimages: maps.Clone(s.preimages),
|
||||||
journal: s.journal.copy(),
|
journal: s.journal.copy(),
|
||||||
validRevisions: slices.Clone(s.validRevisions),
|
|
||||||
nextRevisionId: s.nextRevisionId,
|
|
||||||
|
|
||||||
// In order for the block producer to be able to use and make additions
|
// In order for the block producer to be able to use and make additions
|
||||||
// to the snapshot tree, we need to copy that as well. Otherwise, any
|
// to the snapshot tree, we need to copy that as well. Otherwise, any
|
||||||
|
|
@ -722,6 +713,9 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
if s.witness != nil {
|
if s.witness != nil {
|
||||||
state.witness = s.witness.Copy()
|
state.witness = s.witness.Copy()
|
||||||
}
|
}
|
||||||
|
if s.accessEvents != nil {
|
||||||
|
state.accessEvents = s.accessEvents.Copy()
|
||||||
|
}
|
||||||
// Deep copy cached state objects.
|
// Deep copy cached state objects.
|
||||||
for addr, obj := range s.stateObjects {
|
for addr, obj := range s.stateObjects {
|
||||||
state.stateObjects[addr] = obj.deepCopy(state)
|
state.stateObjects[addr] = obj.deepCopy(state)
|
||||||
|
|
@ -756,26 +750,12 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
|
|
||||||
// Snapshot returns an identifier for the current revision of the state.
|
// Snapshot returns an identifier for the current revision of the state.
|
||||||
func (s *StateDB) Snapshot() int {
|
func (s *StateDB) Snapshot() int {
|
||||||
id := s.nextRevisionId
|
return s.journal.snapshot()
|
||||||
s.nextRevisionId++
|
|
||||||
s.validRevisions = append(s.validRevisions, revision{id, s.journal.length()})
|
|
||||||
return id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevertToSnapshot reverts all state changes made since the given revision.
|
// RevertToSnapshot reverts all state changes made since the given revision.
|
||||||
func (s *StateDB) RevertToSnapshot(revid int) {
|
func (s *StateDB) RevertToSnapshot(revid int) {
|
||||||
// Find the snapshot in the stack of valid snapshots.
|
s.journal.revertToSnapshot(revid, s)
|
||||||
idx := sort.Search(len(s.validRevisions), func(i int) bool {
|
|
||||||
return s.validRevisions[i].id >= revid
|
|
||||||
})
|
|
||||||
if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid {
|
|
||||||
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
|
|
||||||
}
|
|
||||||
snapshot := s.validRevisions[idx].journalIndex
|
|
||||||
|
|
||||||
// Replay the journal to undo changes and remove invalidated snapshots
|
|
||||||
s.journal.revert(s, snapshot)
|
|
||||||
s.validRevisions = s.validRevisions[:idx]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRefund returns the current value of the refund counter.
|
// GetRefund returns the current value of the refund counter.
|
||||||
|
|
@ -882,8 +862,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// If witness building is enabled, gather all the read-only accesses
|
// If witness building is enabled, gather all the read-only accesses.
|
||||||
if s.witness != nil {
|
// Skip witness collection in Verkle mode, they will be gathered
|
||||||
|
// together at the end.
|
||||||
|
if s.witness != nil && !s.db.TrieDB().IsVerkle() {
|
||||||
// Pull in anything that has been accessed before destruction
|
// Pull in anything that has been accessed before destruction
|
||||||
for _, obj := range s.stateObjectsDestruct {
|
for _, obj := range s.stateObjectsDestruct {
|
||||||
// Skip any objects that haven't touched their storage
|
// Skip any objects that haven't touched their storage
|
||||||
|
|
@ -924,7 +906,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
// only a single trie is used for state hashing. Replacing a non-nil verkle tree
|
// only a single trie is used for state hashing. Replacing a non-nil verkle tree
|
||||||
// here could result in losing uncommitted changes from storage.
|
// here could result in losing uncommitted changes from storage.
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
if s.prefetcher != nil && (s.trie == nil || !s.trie.IsVerkle()) {
|
if s.prefetcher != nil {
|
||||||
if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil {
|
if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil {
|
||||||
log.Error("Failed to retrieve account pre-fetcher trie")
|
log.Error("Failed to retrieve account pre-fetcher trie")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -989,12 +971,9 @@ func (s *StateDB) SetTxContext(thash common.Hash, ti int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) clearJournalAndRefund() {
|
func (s *StateDB) clearJournalAndRefund() {
|
||||||
if len(s.journal.entries) > 0 {
|
s.journal.reset()
|
||||||
s.journal = newJournal()
|
|
||||||
s.refund = 0
|
s.refund = 0
|
||||||
}
|
}
|
||||||
s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entries
|
|
||||||
}
|
|
||||||
|
|
||||||
// fastDeleteStorage is the function that efficiently deletes the storage trie
|
// fastDeleteStorage is the function that efficiently deletes the storage trie
|
||||||
// of a specific account. It leverages the associated state snapshot for fast
|
// of a specific account. It leverages the associated state snapshot for fast
|
||||||
|
|
@ -1286,6 +1265,8 @@ func (s *StateDB) commit(deleteEmptyObjects bool) (*stateUpdate, error) {
|
||||||
if err := workers.Wait(); err != nil {
|
if err := workers.Wait(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
accountReadMeters.Mark(int64(s.AccountLoaded))
|
||||||
|
storageReadMeters.Mark(int64(s.StorageLoaded))
|
||||||
accountUpdatedMeter.Mark(int64(s.AccountUpdated))
|
accountUpdatedMeter.Mark(int64(s.AccountUpdated))
|
||||||
storageUpdatedMeter.Mark(s.StorageUpdated.Load())
|
storageUpdatedMeter.Mark(s.StorageUpdated.Load())
|
||||||
accountDeletedMeter.Mark(int64(s.AccountDeleted))
|
accountDeletedMeter.Mark(int64(s.AccountDeleted))
|
||||||
|
|
@ -1294,7 +1275,10 @@ func (s *StateDB) commit(deleteEmptyObjects bool) (*stateUpdate, error) {
|
||||||
accountTrieDeletedMeter.Mark(int64(accountTrieNodesDeleted))
|
accountTrieDeletedMeter.Mark(int64(accountTrieNodesDeleted))
|
||||||
storageTriesUpdatedMeter.Mark(int64(storageTrieNodesUpdated))
|
storageTriesUpdatedMeter.Mark(int64(storageTrieNodesUpdated))
|
||||||
storageTriesDeletedMeter.Mark(int64(storageTrieNodesDeleted))
|
storageTriesDeletedMeter.Mark(int64(storageTrieNodesDeleted))
|
||||||
s.AccountUpdated, s.AccountDeleted = 0, 0
|
|
||||||
|
// Clear the metric markers
|
||||||
|
s.AccountLoaded, s.AccountUpdated, s.AccountDeleted = 0, 0, 0
|
||||||
|
s.StorageLoaded = 0
|
||||||
s.StorageUpdated.Store(0)
|
s.StorageUpdated.Store(0)
|
||||||
s.StorageDeleted.Store(0)
|
s.StorageDeleted.Store(0)
|
||||||
|
|
||||||
|
|
@ -1419,7 +1403,7 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d
|
||||||
// AddAddressToAccessList adds the given address to the access list
|
// AddAddressToAccessList adds the given address to the access list
|
||||||
func (s *StateDB) AddAddressToAccessList(addr common.Address) {
|
func (s *StateDB) AddAddressToAccessList(addr common.Address) {
|
||||||
if s.accessList.AddAddress(addr) {
|
if s.accessList.AddAddress(addr) {
|
||||||
s.journal.append(accessListAddAccountChange{&addr})
|
s.journal.accessListAddAccount(addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1431,13 +1415,10 @@ func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
|
||||||
// scope of 'address' without having the 'address' become already added
|
// scope of 'address' without having the 'address' become already added
|
||||||
// to the access list (via call-variant, create, etc).
|
// to the access list (via call-variant, create, etc).
|
||||||
// Better safe than sorry, though
|
// Better safe than sorry, though
|
||||||
s.journal.append(accessListAddAccountChange{&addr})
|
s.journal.accessListAddAccount(addr)
|
||||||
}
|
}
|
||||||
if slotMod {
|
if slotMod {
|
||||||
s.journal.append(accessListAddSlotChange{
|
s.journal.accessListAddSlot(addr, slot)
|
||||||
address: &addr,
|
|
||||||
slot: &slot,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1478,3 +1459,7 @@ func (s *StateDB) PointCache() *utils.PointCache {
|
||||||
func (s *StateDB) Witness() *stateless.Witness {
|
func (s *StateDB) Witness() *stateless.Witness {
|
||||||
return s.witness
|
return s.witness
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) AccessEvents() *AccessEvents {
|
||||||
|
return s.accessEvents
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction
|
||||||
args: make([]int64, 1),
|
args: make([]int64, 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SetState",
|
name: "SetStorage",
|
||||||
fn: func(a testAction, s *StateDB) {
|
fn: func(a testAction, s *StateDB) {
|
||||||
var key, val common.Hash
|
var key, val common.Hash
|
||||||
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
||||||
|
|
|
||||||
|
|
@ -360,7 +360,7 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
||||||
args: make([]int64, 1),
|
args: make([]int64, 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SetState",
|
name: "SetStorage",
|
||||||
fn: func(a testAction, s *StateDB) {
|
fn: func(a testAction, s *StateDB) {
|
||||||
var key, val common.Hash
|
var key, val common.Hash
|
||||||
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
||||||
|
|
@ -372,6 +372,12 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
||||||
{
|
{
|
||||||
name: "SetCode",
|
name: "SetCode",
|
||||||
fn: func(a testAction, s *StateDB) {
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
// SetCode can only be performed in case the addr does
|
||||||
|
// not already hold code
|
||||||
|
if c := s.GetCode(addr); len(c) > 0 {
|
||||||
|
// no-op
|
||||||
|
return
|
||||||
|
}
|
||||||
code := make([]byte, 16)
|
code := make([]byte, 16)
|
||||||
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
||||||
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ var (
|
||||||
//
|
//
|
||||||
// Note, the prefetcher's API is not thread safe.
|
// Note, the prefetcher's API is not thread safe.
|
||||||
type triePrefetcher struct {
|
type triePrefetcher struct {
|
||||||
|
verkle bool // Flag whether the prefetcher is in verkle mode
|
||||||
db Database // Database to fetch trie nodes through
|
db Database // Database to fetch trie nodes through
|
||||||
root common.Hash // Root hash of the account trie for metrics
|
root common.Hash // Root hash of the account trie for metrics
|
||||||
fetchers map[string]*subfetcher // Subfetchers for each trie
|
fetchers map[string]*subfetcher // Subfetchers for each trie
|
||||||
|
|
@ -66,6 +67,7 @@ type triePrefetcher struct {
|
||||||
func newTriePrefetcher(db Database, root common.Hash, namespace string, noreads bool) *triePrefetcher {
|
func newTriePrefetcher(db Database, root common.Hash, namespace string, noreads bool) *triePrefetcher {
|
||||||
prefix := triePrefetchMetricsPrefix + namespace
|
prefix := triePrefetchMetricsPrefix + namespace
|
||||||
return &triePrefetcher{
|
return &triePrefetcher{
|
||||||
|
verkle: db.TrieDB().IsVerkle(),
|
||||||
db: db,
|
db: db,
|
||||||
root: root,
|
root: root,
|
||||||
fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map
|
fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map
|
||||||
|
|
@ -196,12 +198,18 @@ func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie {
|
||||||
func (p *triePrefetcher) used(owner common.Hash, root common.Hash, used [][]byte) {
|
func (p *triePrefetcher) used(owner common.Hash, root common.Hash, used [][]byte) {
|
||||||
if fetcher := p.fetchers[p.trieID(owner, root)]; fetcher != nil {
|
if fetcher := p.fetchers[p.trieID(owner, root)]; fetcher != nil {
|
||||||
fetcher.wait() // ensure the fetcher's idle before poking in its internals
|
fetcher.wait() // ensure the fetcher's idle before poking in its internals
|
||||||
fetcher.used = used
|
fetcher.used = append(fetcher.used, used...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// trieID returns an unique trie identifier consists the trie owner and root hash.
|
// trieID returns an unique trie identifier consists the trie owner and root hash.
|
||||||
func (p *triePrefetcher) trieID(owner common.Hash, root common.Hash) string {
|
func (p *triePrefetcher) trieID(owner common.Hash, root common.Hash) string {
|
||||||
|
// The trie in verkle is only identified by state root
|
||||||
|
if p.verkle {
|
||||||
|
return p.root.Hex()
|
||||||
|
}
|
||||||
|
// The trie in merkle is either identified by state root (account trie),
|
||||||
|
// or identified by the owner and trie root (storage trie)
|
||||||
trieID := make([]byte, common.HashLength*2)
|
trieID := make([]byte, common.HashLength*2)
|
||||||
copy(trieID, owner.Bytes())
|
copy(trieID, owner.Bytes())
|
||||||
copy(trieID[common.HashLength:], root.Bytes())
|
copy(trieID[common.HashLength:], root.Bytes())
|
||||||
|
|
@ -320,29 +328,47 @@ func (sf *subfetcher) terminate(async bool) {
|
||||||
<-sf.term
|
<-sf.term
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// openTrie resolves the target trie from database for prefetching.
|
||||||
|
func (sf *subfetcher) openTrie() error {
|
||||||
|
// Open the verkle tree if the sub-fetcher is in verkle mode. Note, there is
|
||||||
|
// only a single fetcher for verkle.
|
||||||
|
if sf.db.TrieDB().IsVerkle() {
|
||||||
|
tr, err := sf.db.OpenTrie(sf.state)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Trie prefetcher failed opening verkle trie", "root", sf.root, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf.trie = tr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Open the merkle tree if the sub-fetcher is in merkle mode
|
||||||
|
if sf.owner == (common.Hash{}) {
|
||||||
|
tr, err := sf.db.OpenTrie(sf.state)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Trie prefetcher failed opening account trie", "root", sf.root, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf.trie = tr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tr, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Trie prefetcher failed opening storage trie", "root", sf.root, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf.trie = tr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// loop loads newly-scheduled trie tasks as they are received and loads them, stopping
|
// loop loads newly-scheduled trie tasks as they are received and loads them, stopping
|
||||||
// when requested.
|
// when requested.
|
||||||
func (sf *subfetcher) loop() {
|
func (sf *subfetcher) loop() {
|
||||||
// No matter how the loop stops, signal anyone waiting that it's terminated
|
// No matter how the loop stops, signal anyone waiting that it's terminated
|
||||||
defer close(sf.term)
|
defer close(sf.term)
|
||||||
|
|
||||||
// Start by opening the trie and stop processing if it fails
|
if err := sf.openTrie(); err != nil {
|
||||||
if sf.owner == (common.Hash{}) {
|
|
||||||
trie, err := sf.db.OpenTrie(sf.root)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sf.trie = trie
|
|
||||||
} else {
|
|
||||||
trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sf.trie = trie
|
|
||||||
}
|
|
||||||
// Trie opened successfully, keep prefetching items
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-sf.wake:
|
case <-sf.wake:
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/testrand"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -62,3 +65,44 @@ func TestUseAfterTerminate(t *testing.T) {
|
||||||
t.Errorf("Prefetcher returned nil trie after terminate")
|
t.Errorf("Prefetcher returned nil trie after terminate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVerklePrefetcher(t *testing.T) {
|
||||||
|
db := NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), triedb.VerkleDefaults)
|
||||||
|
state, err := New(types.EmptyRootHash, db, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to initialize state: %v", err)
|
||||||
|
}
|
||||||
|
// Create an account and check if the retrieved balance is correct
|
||||||
|
addr := testrand.Address()
|
||||||
|
skey := testrand.Hash()
|
||||||
|
sval := testrand.Hash()
|
||||||
|
|
||||||
|
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
||||||
|
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||||
|
state.SetState(addr, skey, sval) // Change the storage trie
|
||||||
|
root, _ := state.Commit(0, true)
|
||||||
|
|
||||||
|
state, _ = New(root, db, nil)
|
||||||
|
sRoot := state.GetStorageRoot(addr)
|
||||||
|
fetcher := newTriePrefetcher(db, root, "", false)
|
||||||
|
|
||||||
|
// Read account
|
||||||
|
fetcher.prefetch(common.Hash{}, root, common.Address{}, [][]byte{
|
||||||
|
addr.Bytes(),
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
// Read storage slot
|
||||||
|
fetcher.prefetch(crypto.Keccak256Hash(addr.Bytes()), sRoot, addr, [][]byte{
|
||||||
|
skey.Bytes(),
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
fetcher.terminate(false)
|
||||||
|
accountTrie := fetcher.trie(common.Hash{}, root)
|
||||||
|
storageTrie := fetcher.trie(crypto.Keccak256Hash(addr.Bytes()), sRoot)
|
||||||
|
|
||||||
|
rootA := accountTrie.Hash()
|
||||||
|
rootB := storageTrie.Hash()
|
||||||
|
if rootA != rootB {
|
||||||
|
t.Fatal("Two different tries are retrieved")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
|
@ -78,6 +77,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
||||||
}
|
}
|
||||||
|
if p.config.IsPrague(block.Number(), block.Time()) {
|
||||||
|
ProcessParentBlockHash(block.ParentHash(), vmenv, statedb)
|
||||||
|
}
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||||
|
|
@ -93,11 +95,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
// Fail if Shanghai not enabled and len(withdrawals) is non-zero.
|
|
||||||
withdrawals := block.Withdrawals()
|
|
||||||
if len(withdrawals) > 0 && !p.config.IsShanghai(block.Number(), block.Time()) {
|
|
||||||
return nil, nil, 0, errors.New("withdrawals before shanghai")
|
|
||||||
}
|
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
p.chain.engine.Finalize(p.chain, header, statedb, block.Body())
|
p.chain.engine.Finalize(p.chain, header, statedb, block.Body())
|
||||||
|
|
||||||
|
|
@ -161,6 +158,12 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
|
||||||
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge the tx-local access event into the "block-local" one, in order to collect
|
||||||
|
// all values, so that the witness can be built.
|
||||||
|
if statedb.GetTrie().IsVerkle() {
|
||||||
|
statedb.AccessEvents().Merge(evm.AccessEvents)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the receipt logs and create the bloom filter.
|
// Set the receipt logs and create the bloom filter.
|
||||||
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
|
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
|
||||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
|
|
@ -189,11 +192,13 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
||||||
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
||||||
// contract. This method is exported to be used in tests.
|
// contract. This method is exported to be used in tests.
|
||||||
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
|
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
|
||||||
if vmenv.Config.Tracer != nil && vmenv.Config.Tracer.OnSystemCallStart != nil {
|
if tracer := vmenv.Config.Tracer; tracer != nil {
|
||||||
vmenv.Config.Tracer.OnSystemCallStart()
|
if tracer.OnSystemCallStart != nil {
|
||||||
|
tracer.OnSystemCallStart()
|
||||||
|
}
|
||||||
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
defer tracer.OnSystemCallEnd()
|
||||||
}
|
}
|
||||||
if vmenv.Config.Tracer != nil && vmenv.Config.Tracer.OnSystemCallEnd != nil {
|
|
||||||
defer vmenv.Config.Tracer.OnSystemCallEnd()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with
|
// If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with
|
||||||
|
|
@ -212,3 +217,30 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
|
||||||
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
|
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||||
statedb.Finalise(true)
|
statedb.Finalise(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProcessParentBlockHash stores the parent block hash in the history storage contract
|
||||||
|
// as per EIP-2935.
|
||||||
|
func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
|
||||||
|
if tracer := vmenv.Config.Tracer; tracer != nil {
|
||||||
|
if tracer.OnSystemCallStart != nil {
|
||||||
|
tracer.OnSystemCallStart()
|
||||||
|
}
|
||||||
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
defer tracer.OnSystemCallEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &Message{
|
||||||
|
From: params.SystemAddress,
|
||||||
|
GasLimit: 30_000_000,
|
||||||
|
GasPrice: common.Big0,
|
||||||
|
GasFeeCap: common.Big0,
|
||||||
|
GasTipCap: common.Big0,
|
||||||
|
To: ¶ms.HistoryStorageAddress,
|
||||||
|
Data: prevHash.Bytes(),
|
||||||
|
}
|
||||||
|
vmenv.Reset(NewEVMTxContext(msg), statedb)
|
||||||
|
statedb.AddAddressToAccessList(params.HistoryStorageAddress)
|
||||||
|
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||||
|
statedb.Finalise(true)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"encoding/binary"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -29,11 +30,15 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
"github.com/ethereum/go-verkle"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
@ -481,13 +486,40 @@ func TestProcessVerkle(t *testing.T) {
|
||||||
|
|
||||||
txCost1 := params.TxGas
|
txCost1 := params.TxGas
|
||||||
txCost2 := params.TxGas
|
txCost2 := params.TxGas
|
||||||
contractCreationCost := intrinsicContractCreationGas + uint64(2039 /* execution costs */)
|
contractCreationCost := intrinsicContractCreationGas +
|
||||||
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(57444 /* execution costs */)
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */
|
||||||
|
739 /* execution costs */
|
||||||
|
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas +
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */
|
||||||
|
params.WitnessChunkReadCost + /* SLOAD in constructor */
|
||||||
|
params.WitnessChunkWriteCost + /* SSTORE in constructor */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at PC=0x121) */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */
|
||||||
|
params.WitnessChunkReadCost + /* SLOAD in constructor */
|
||||||
|
params.WitnessChunkWriteCost + /* SSTORE in constructor */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash for tx creation */
|
||||||
|
15*(params.WitnessChunkReadCost+params.WitnessChunkWriteCost) + /* code chunks #0..#14 */
|
||||||
|
4844 /* execution costs */
|
||||||
blockGasUsagesExpected := []uint64{
|
blockGasUsagesExpected := []uint64{
|
||||||
txCost1*2 + txCost2,
|
txCost1*2 + txCost2,
|
||||||
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
|
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
|
||||||
}
|
}
|
||||||
_, chain, _, _, _ := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) {
|
_, chain, _, proofs, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) {
|
||||||
gen.SetPoS()
|
gen.SetPoS()
|
||||||
|
|
||||||
// TODO need to check that the tx cost provided is the exact amount used (no remaining left-over)
|
// TODO need to check that the tx cost provided is the exact amount used (no remaining left-over)
|
||||||
|
|
@ -508,7 +540,17 @@ func TestProcessVerkle(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Log("inserting blocks into the chain")
|
// Check proof for both blocks
|
||||||
|
err := verkle.Verify(proofs[0], gspec.ToBlock().Root().Bytes(), chain[0].Root().Bytes(), statediffs[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = verkle.Verify(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), statediffs[1])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("verified verkle proof, inserting blocks into the chain")
|
||||||
|
|
||||||
endnum, err := blockchain.InsertChain(chain)
|
endnum, err := blockchain.InsertChain(chain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -528,3 +570,54 @@ func TestProcessVerkle(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProcessParentBlockHash(t *testing.T) {
|
||||||
|
var (
|
||||||
|
chainConfig = params.MergedTestChainConfig
|
||||||
|
hashA = common.Hash{0x01}
|
||||||
|
hashB = common.Hash{0x02}
|
||||||
|
header = &types.Header{ParentHash: hashA, Number: big.NewInt(2), Difficulty: big.NewInt(0)}
|
||||||
|
parent = &types.Header{ParentHash: hashB, Number: big.NewInt(1), Difficulty: big.NewInt(0)}
|
||||||
|
coinbase = common.Address{}
|
||||||
|
)
|
||||||
|
test := func(statedb *state.StateDB) {
|
||||||
|
statedb.SetNonce(params.HistoryStorageAddress, 1)
|
||||||
|
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
|
||||||
|
statedb.IntermediateRoot(true)
|
||||||
|
|
||||||
|
vmContext := NewEVMBlockContext(header, nil, &coinbase)
|
||||||
|
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
||||||
|
ProcessParentBlockHash(header.ParentHash, evm, statedb)
|
||||||
|
|
||||||
|
vmContext = NewEVMBlockContext(parent, nil, &coinbase)
|
||||||
|
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
||||||
|
ProcessParentBlockHash(parent.ParentHash, evm, statedb)
|
||||||
|
|
||||||
|
// make sure that the state is correct
|
||||||
|
if have := getParentBlockHash(statedb, 1); have != hashA {
|
||||||
|
t.Errorf("want parent hash %v, have %v", hashA, have)
|
||||||
|
}
|
||||||
|
if have := getParentBlockHash(statedb, 0); have != hashB {
|
||||||
|
t.Errorf("want parent hash %v, have %v", hashB, have)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Run("MPT", func(t *testing.T) {
|
||||||
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
|
||||||
|
test(statedb)
|
||||||
|
})
|
||||||
|
t.Run("Verkle", func(t *testing.T) {
|
||||||
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme)
|
||||||
|
cacheConfig.SnapshotLimit = 0
|
||||||
|
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
|
||||||
|
statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
||||||
|
test(statedb)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash {
|
||||||
|
ringIndex := number % params.HistoryServeWindow
|
||||||
|
var key common.Hash
|
||||||
|
binary.BigEndian.PutUint64(key[24:], ringIndex)
|
||||||
|
return statedb.GetState(params.HistoryStorageAddress, key)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -476,7 +476,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
|
|
||||||
// add the coinbase to the witness iff the fee is greater than 0
|
// add the coinbase to the witness iff the fee is greater than 0
|
||||||
if rules.IsEIP4762 && fee.Sign() != 0 {
|
if rules.IsEIP4762 && fee.Sign() != 0 {
|
||||||
st.evm.AccessEvents.BalanceGas(st.evm.Context.Coinbase, true)
|
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1155,11 +1155,11 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error {
|
||||||
)
|
)
|
||||||
switch {
|
switch {
|
||||||
case tx.GasFeeCapIntCmp(minGasFeeCap.ToBig()) < 0:
|
case tx.GasFeeCapIntCmp(minGasFeeCap.ToBig()) < 0:
|
||||||
return fmt.Errorf("%w: new tx gas fee cap %v <= %v queued + %d%% replacement penalty", txpool.ErrReplaceUnderpriced, tx.GasFeeCap(), prev.execFeeCap, p.config.PriceBump)
|
return fmt.Errorf("%w: new tx gas fee cap %v < %v queued + %d%% replacement penalty", txpool.ErrReplaceUnderpriced, tx.GasFeeCap(), prev.execFeeCap, p.config.PriceBump)
|
||||||
case tx.GasTipCapIntCmp(minGasTipCap.ToBig()) < 0:
|
case tx.GasTipCapIntCmp(minGasTipCap.ToBig()) < 0:
|
||||||
return fmt.Errorf("%w: new tx gas tip cap %v <= %v queued + %d%% replacement penalty", txpool.ErrReplaceUnderpriced, tx.GasTipCap(), prev.execTipCap, p.config.PriceBump)
|
return fmt.Errorf("%w: new tx gas tip cap %v < %v queued + %d%% replacement penalty", txpool.ErrReplaceUnderpriced, tx.GasTipCap(), prev.execTipCap, p.config.PriceBump)
|
||||||
case tx.BlobGasFeeCapIntCmp(minBlobGasFeeCap.ToBig()) < 0:
|
case tx.BlobGasFeeCapIntCmp(minBlobGasFeeCap.ToBig()) < 0:
|
||||||
return fmt.Errorf("%w: new tx blob gas fee cap %v <= %v queued + %d%% replacement penalty", txpool.ErrReplaceUnderpriced, tx.BlobGasFeeCap(), prev.blobFeeCap, p.config.PriceBump)
|
return fmt.Errorf("%w: new tx blob gas fee cap %v < %v queued + %d%% replacement penalty", txpool.ErrReplaceUnderpriced, tx.BlobGasFeeCap(), prev.blobFeeCap, p.config.PriceBump)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-verkle"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A BlockNonce is a 64-bit hash which proves (combined with the
|
// A BlockNonce is a 64-bit hash which proves (combined with the
|
||||||
|
|
@ -59,6 +60,13 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
|
||||||
return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
|
return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecutionWitness represents the witness + proof used in a verkle context,
|
||||||
|
// to provide the ability to execute a block statelessly.
|
||||||
|
type ExecutionWitness struct {
|
||||||
|
StateDiff verkle.StateDiff `json:"stateDiff"`
|
||||||
|
VerkleProof *verkle.VerkleProof `json:"verkleProof"`
|
||||||
|
}
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
|
//go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
|
||||||
//go:generate go run ../../rlp/rlpgen -type Header -out gen_header_rlp.go
|
//go:generate go run ../../rlp/rlpgen -type Header -out gen_header_rlp.go
|
||||||
|
|
||||||
|
|
@ -197,6 +205,11 @@ type Block struct {
|
||||||
transactions Transactions
|
transactions Transactions
|
||||||
withdrawals Withdrawals
|
withdrawals Withdrawals
|
||||||
|
|
||||||
|
// witness is not an encoded part of the block body.
|
||||||
|
// It is held in Block in order for easy relaying to the places
|
||||||
|
// that process it.
|
||||||
|
witness *ExecutionWitness
|
||||||
|
|
||||||
// caches
|
// caches
|
||||||
hash atomic.Pointer[common.Hash]
|
hash atomic.Pointer[common.Hash]
|
||||||
size atomic.Uint64
|
size atomic.Uint64
|
||||||
|
|
@ -401,6 +414,9 @@ func (b *Block) BlobGasUsed() *uint64 {
|
||||||
return blobGasUsed
|
return blobGasUsed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecutionWitness returns the verkle execution witneess + proof for a block
|
||||||
|
func (b *Block) ExecutionWitness() *ExecutionWitness { return b.witness }
|
||||||
|
|
||||||
// Size returns the true RLP encoded storage size of the block, either by encoding
|
// Size returns the true RLP encoded storage size of the block, either by encoding
|
||||||
// and returning it, or returning a previously cached value.
|
// and returning it, or returning a previously cached value.
|
||||||
func (b *Block) Size() uint64 {
|
func (b *Block) Size() uint64 {
|
||||||
|
|
@ -448,6 +464,7 @@ func (b *Block) WithSeal(header *Header) *Block {
|
||||||
transactions: b.transactions,
|
transactions: b.transactions,
|
||||||
uncles: b.uncles,
|
uncles: b.uncles,
|
||||||
withdrawals: b.withdrawals,
|
withdrawals: b.withdrawals,
|
||||||
|
witness: b.witness,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -459,6 +476,7 @@ func (b *Block) WithBody(body Body) *Block {
|
||||||
transactions: slices.Clone(body.Transactions),
|
transactions: slices.Clone(body.Transactions),
|
||||||
uncles: make([]*Header, len(body.Uncles)),
|
uncles: make([]*Header, len(body.Uncles)),
|
||||||
withdrawals: slices.Clone(body.Withdrawals),
|
withdrawals: slices.Clone(body.Withdrawals),
|
||||||
|
witness: b.witness,
|
||||||
}
|
}
|
||||||
for i := range body.Uncles {
|
for i := range body.Uncles {
|
||||||
block.uncles[i] = CopyHeader(body.Uncles[i])
|
block.uncles[i] = CopyHeader(body.Uncles[i])
|
||||||
|
|
@ -466,6 +484,16 @@ func (b *Block) WithBody(body Body) *Block {
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Block) WithWitness(witness *ExecutionWitness) *Block {
|
||||||
|
return &Block{
|
||||||
|
header: b.header,
|
||||||
|
transactions: b.transactions,
|
||||||
|
uncles: b.uncles,
|
||||||
|
withdrawals: b.withdrawals,
|
||||||
|
witness: witness,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Hash returns the keccak256 hash of b's header.
|
// Hash returns the keccak256 hash of b's header.
|
||||||
// The hash is computed on the first call and cached thereafter.
|
// The hash is computed on the first call and cached thereafter.
|
||||||
func (b *Block) Hash() common.Hash {
|
func (b *Block) Hash() common.Hash {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
|
@ -48,6 +49,12 @@ type Withdrawals []*Withdrawal
|
||||||
// Len returns the length of s.
|
// Len returns the length of s.
|
||||||
func (s Withdrawals) Len() int { return len(s) }
|
func (s Withdrawals) Len() int { return len(s) }
|
||||||
|
|
||||||
|
var withdrawalSize = int(reflect.TypeOf(Withdrawal{}).Size())
|
||||||
|
|
||||||
|
func (s Withdrawals) Size() int {
|
||||||
|
return withdrawalSize * len(s)
|
||||||
|
}
|
||||||
|
|
||||||
// EncodeIndex encodes the i'th withdrawal to w. Note that this does not check for errors
|
// EncodeIndex encodes the i'th withdrawal to w. Note that this does not check for errors
|
||||||
// because we assume that *Withdrawal will only ever contain valid withdrawals that were either
|
// because we assume that *Withdrawal will only ever contain valid withdrawals that were either
|
||||||
// constructed by decoding or via public API in this package.
|
// constructed by decoding or via public API in this package.
|
||||||
|
|
|
||||||
|
|
@ -492,66 +492,61 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
contract.SetCodeOptionalHash(&address, codeAndHash)
|
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||||
contract.IsDeployment = true
|
contract.IsDeployment = true
|
||||||
|
|
||||||
// Charge the contract creation init gas in verkle mode
|
ret, err = evm.initNewContract(contract, address, value)
|
||||||
if evm.chainRules.IsEIP4762 {
|
|
||||||
if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) {
|
|
||||||
err = ErrOutOfGas
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
ret, err = evm.interpreter.Run(contract, nil, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the max code size has been exceeded, assign err if the case.
|
|
||||||
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
|
|
||||||
err = ErrMaxCodeSizeExceeded
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reject code starting with 0xEF if EIP-3541 is enabled.
|
|
||||||
if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon {
|
|
||||||
err = ErrInvalidCode
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the contract creation ran successfully and no errors were returned
|
|
||||||
// calculate the gas required to store the code. If the code could not
|
|
||||||
// be stored due to not enough gas set an error and let it be handled
|
|
||||||
// by the error checking condition below.
|
|
||||||
if err == nil {
|
|
||||||
if !evm.chainRules.IsEIP4762 {
|
|
||||||
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
|
||||||
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
|
||||||
err = ErrCodeStoreOutOfGas
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Contract creation completed, touch the missing fields in the contract
|
|
||||||
if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) {
|
|
||||||
err = ErrCodeStoreOutOfGas
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil && len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) {
|
|
||||||
err = ErrCodeStoreOutOfGas
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
evm.StateDB.SetCode(address, ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When an error was returned by the EVM or when setting the creation code
|
|
||||||
// above we revert to the snapshot and consume any gas remaining. Additionally,
|
|
||||||
// when we're in homestead this also counts for code storage gas errors.
|
|
||||||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, address, contract.Gas, err
|
return ret, address, contract.Gas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initNewContract runs a new contract's creation code, performs checks on the
|
||||||
|
// resulting code that is to be deployed, and consumes necessary gas.
|
||||||
|
func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int) ([]byte, error) {
|
||||||
|
// Charge the contract creation init gas in verkle mode
|
||||||
|
if evm.chainRules.IsEIP4762 {
|
||||||
|
if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) {
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := evm.interpreter.Run(contract, nil, false)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the max code size has been exceeded, assign err if the case.
|
||||||
|
if evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
|
||||||
|
return ret, ErrMaxCodeSizeExceeded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject code starting with 0xEF if EIP-3541 is enabled.
|
||||||
|
if len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon {
|
||||||
|
return ret, ErrInvalidCode
|
||||||
|
}
|
||||||
|
|
||||||
|
if !evm.chainRules.IsEIP4762 {
|
||||||
|
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
||||||
|
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
||||||
|
return ret, ErrCodeStoreOutOfGas
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Contract creation completed, touch the missing fields in the contract
|
||||||
|
if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) {
|
||||||
|
return ret, ErrCodeStoreOutOfGas
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) {
|
||||||
|
return ret, ErrCodeStoreOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evm.StateDB.SetCode(address, ret)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Create creates a new contract using code as deployment code.
|
// Create creates a new contract using code as deployment code.
|
||||||
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||||
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
|
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
|
||||||
|
|
||||||
func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
||||||
data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||||
|
|
||||||
if interpreter.hasher == nil {
|
if interpreter.hasher == nil {
|
||||||
interpreter.hasher = crypto.NewKeccakState()
|
interpreter.hasher = crypto.NewKeccakState()
|
||||||
|
|
@ -502,7 +502,7 @@ func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
|
||||||
|
|
||||||
func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
v := scope.Stack.peek()
|
v := scope.Stack.peek()
|
||||||
offset := int64(v.Uint64())
|
offset := v.Uint64()
|
||||||
v.SetBytes(scope.Memory.GetPtr(offset, 32))
|
v.SetBytes(scope.Memory.GetPtr(offset, 32))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -583,6 +583,86 @@ func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func opSwap1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap1()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap2()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap3()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap4(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap4()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap5(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap5()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap6(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap6()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap7(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap7()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap8()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap9(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap9()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap10(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap10()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap11(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap11()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap12(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap12()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap13(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap13()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap14(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap14()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap15(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap15()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap16(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap16()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
if interpreter.readOnly {
|
if interpreter.readOnly {
|
||||||
return nil, ErrWriteProtection
|
return nil, ErrWriteProtection
|
||||||
|
|
@ -590,7 +670,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
||||||
var (
|
var (
|
||||||
value = scope.Stack.pop()
|
value = scope.Stack.pop()
|
||||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||||
gas = scope.Contract.Gas
|
gas = scope.Contract.Gas
|
||||||
)
|
)
|
||||||
if interpreter.evm.chainRules.IsEIP150 {
|
if interpreter.evm.chainRules.IsEIP150 {
|
||||||
|
|
@ -634,7 +714,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
||||||
endowment = scope.Stack.pop()
|
endowment = scope.Stack.pop()
|
||||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||||
salt = scope.Stack.pop()
|
salt = scope.Stack.pop()
|
||||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||||
gas = scope.Contract.Gas
|
gas = scope.Contract.Gas
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -672,7 +752,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
||||||
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||||
toAddr := common.Address(addr.Bytes20())
|
toAddr := common.Address(addr.Bytes20())
|
||||||
// Get the arguments from the memory.
|
// Get the arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
if interpreter.readOnly && !value.IsZero() {
|
if interpreter.readOnly && !value.IsZero() {
|
||||||
return nil, ErrWriteProtection
|
return nil, ErrWriteProtection
|
||||||
|
|
@ -708,7 +788,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
||||||
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||||
toAddr := common.Address(addr.Bytes20())
|
toAddr := common.Address(addr.Bytes20())
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
if !value.IsZero() {
|
if !value.IsZero() {
|
||||||
gas += params.CallStipend
|
gas += params.CallStipend
|
||||||
|
|
@ -741,7 +821,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||||
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||||
toAddr := common.Address(addr.Bytes20())
|
toAddr := common.Address(addr.Bytes20())
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas)
|
ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -770,7 +850,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
||||||
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||||
toAddr := common.Address(addr.Bytes20())
|
toAddr := common.Address(addr.Bytes20())
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
|
ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -791,14 +871,14 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
||||||
|
|
||||||
func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||||
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||||
|
|
||||||
return ret, errStopToken
|
return ret, errStopToken
|
||||||
}
|
}
|
||||||
|
|
||||||
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||||
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||||
|
|
||||||
interpreter.returnData = ret
|
interpreter.returnData = ret
|
||||||
return ret, ErrExecutionReverted
|
return ret, ErrExecutionReverted
|
||||||
|
|
@ -867,7 +947,7 @@ func makeLog(size int) executionFunc {
|
||||||
topics[i] = addr.Bytes32()
|
topics[i] = addr.Bytes32()
|
||||||
}
|
}
|
||||||
|
|
||||||
d := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
|
d := scope.Memory.GetCopy(mStart.Uint64(), mSize.Uint64())
|
||||||
interpreter.evm.StateDB.AddLog(&types.Log{
|
interpreter.evm.StateDB.AddLog(&types.Log{
|
||||||
Address: scope.Contract.Address(),
|
Address: scope.Contract.Address(),
|
||||||
Topics: topics,
|
Topics: topics,
|
||||||
|
|
@ -923,13 +1003,3 @@ func makeDup(size int64) executionFunc {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make swap instruction function
|
|
||||||
func makeSwap(size int64) executionFunc {
|
|
||||||
// switch n + 1 otherwise n would be swapped with n
|
|
||||||
size++
|
|
||||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
scope.Stack.swap(int(size))
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
// they are returned to the pools
|
// they are returned to the pools
|
||||||
defer func() {
|
defer func() {
|
||||||
returnStack(stack)
|
returnStack(stack)
|
||||||
|
mem.Free()
|
||||||
}()
|
}()
|
||||||
contract.Input = input
|
contract.Input = input
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -892,97 +892,97 @@ func newFrontierInstructionSet() JumpTable {
|
||||||
maxStack: maxDupStack(16),
|
maxStack: maxDupStack(16),
|
||||||
},
|
},
|
||||||
SWAP1: {
|
SWAP1: {
|
||||||
execute: makeSwap(1),
|
execute: opSwap1,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(2),
|
minStack: minSwapStack(2),
|
||||||
maxStack: maxSwapStack(2),
|
maxStack: maxSwapStack(2),
|
||||||
},
|
},
|
||||||
SWAP2: {
|
SWAP2: {
|
||||||
execute: makeSwap(2),
|
execute: opSwap2,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(3),
|
minStack: minSwapStack(3),
|
||||||
maxStack: maxSwapStack(3),
|
maxStack: maxSwapStack(3),
|
||||||
},
|
},
|
||||||
SWAP3: {
|
SWAP3: {
|
||||||
execute: makeSwap(3),
|
execute: opSwap3,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(4),
|
minStack: minSwapStack(4),
|
||||||
maxStack: maxSwapStack(4),
|
maxStack: maxSwapStack(4),
|
||||||
},
|
},
|
||||||
SWAP4: {
|
SWAP4: {
|
||||||
execute: makeSwap(4),
|
execute: opSwap4,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(5),
|
minStack: minSwapStack(5),
|
||||||
maxStack: maxSwapStack(5),
|
maxStack: maxSwapStack(5),
|
||||||
},
|
},
|
||||||
SWAP5: {
|
SWAP5: {
|
||||||
execute: makeSwap(5),
|
execute: opSwap5,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(6),
|
minStack: minSwapStack(6),
|
||||||
maxStack: maxSwapStack(6),
|
maxStack: maxSwapStack(6),
|
||||||
},
|
},
|
||||||
SWAP6: {
|
SWAP6: {
|
||||||
execute: makeSwap(6),
|
execute: opSwap6,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(7),
|
minStack: minSwapStack(7),
|
||||||
maxStack: maxSwapStack(7),
|
maxStack: maxSwapStack(7),
|
||||||
},
|
},
|
||||||
SWAP7: {
|
SWAP7: {
|
||||||
execute: makeSwap(7),
|
execute: opSwap7,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(8),
|
minStack: minSwapStack(8),
|
||||||
maxStack: maxSwapStack(8),
|
maxStack: maxSwapStack(8),
|
||||||
},
|
},
|
||||||
SWAP8: {
|
SWAP8: {
|
||||||
execute: makeSwap(8),
|
execute: opSwap8,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(9),
|
minStack: minSwapStack(9),
|
||||||
maxStack: maxSwapStack(9),
|
maxStack: maxSwapStack(9),
|
||||||
},
|
},
|
||||||
SWAP9: {
|
SWAP9: {
|
||||||
execute: makeSwap(9),
|
execute: opSwap9,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(10),
|
minStack: minSwapStack(10),
|
||||||
maxStack: maxSwapStack(10),
|
maxStack: maxSwapStack(10),
|
||||||
},
|
},
|
||||||
SWAP10: {
|
SWAP10: {
|
||||||
execute: makeSwap(10),
|
execute: opSwap10,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(11),
|
minStack: minSwapStack(11),
|
||||||
maxStack: maxSwapStack(11),
|
maxStack: maxSwapStack(11),
|
||||||
},
|
},
|
||||||
SWAP11: {
|
SWAP11: {
|
||||||
execute: makeSwap(11),
|
execute: opSwap11,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(12),
|
minStack: minSwapStack(12),
|
||||||
maxStack: maxSwapStack(12),
|
maxStack: maxSwapStack(12),
|
||||||
},
|
},
|
||||||
SWAP12: {
|
SWAP12: {
|
||||||
execute: makeSwap(12),
|
execute: opSwap12,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(13),
|
minStack: minSwapStack(13),
|
||||||
maxStack: maxSwapStack(13),
|
maxStack: maxSwapStack(13),
|
||||||
},
|
},
|
||||||
SWAP13: {
|
SWAP13: {
|
||||||
execute: makeSwap(13),
|
execute: opSwap13,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(14),
|
minStack: minSwapStack(14),
|
||||||
maxStack: maxSwapStack(14),
|
maxStack: maxSwapStack(14),
|
||||||
},
|
},
|
||||||
SWAP14: {
|
SWAP14: {
|
||||||
execute: makeSwap(14),
|
execute: opSwap14,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(15),
|
minStack: minSwapStack(15),
|
||||||
maxStack: maxSwapStack(15),
|
maxStack: maxSwapStack(15),
|
||||||
},
|
},
|
||||||
SWAP15: {
|
SWAP15: {
|
||||||
execute: makeSwap(15),
|
execute: opSwap15,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(16),
|
minStack: minSwapStack(16),
|
||||||
maxStack: maxSwapStack(16),
|
maxStack: maxSwapStack(16),
|
||||||
},
|
},
|
||||||
SWAP16: {
|
SWAP16: {
|
||||||
execute: makeSwap(16),
|
execute: opSwap16,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(17),
|
minStack: minSwapStack(17),
|
||||||
maxStack: maxSwapStack(17),
|
maxStack: maxSwapStack(17),
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,17 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var memoryPool = sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
return &Memory{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Memory implements a simple memory model for the ethereum virtual machine.
|
// Memory implements a simple memory model for the ethereum virtual machine.
|
||||||
type Memory struct {
|
type Memory struct {
|
||||||
store []byte
|
store []byte
|
||||||
|
|
@ -28,7 +36,18 @@ type Memory struct {
|
||||||
|
|
||||||
// NewMemory returns a new memory model.
|
// NewMemory returns a new memory model.
|
||||||
func NewMemory() *Memory {
|
func NewMemory() *Memory {
|
||||||
return &Memory{}
|
return memoryPool.Get().(*Memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free returns the memory to the pool.
|
||||||
|
func (m *Memory) Free() {
|
||||||
|
// To reduce peak allocation, return only smaller memory instances to the pool.
|
||||||
|
const maxBufferSize = 16 << 10
|
||||||
|
if cap(m.store) <= maxBufferSize {
|
||||||
|
m.store = m.store[:0]
|
||||||
|
m.lastGasCost = 0
|
||||||
|
memoryPool.Put(m)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets offset + size to value
|
// Set sets offset + size to value
|
||||||
|
|
@ -66,34 +85,27 @@ func (m *Memory) Resize(size uint64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCopy returns offset + size as a new slice
|
// GetCopy returns offset + size as a new slice
|
||||||
func (m *Memory) GetCopy(offset, size int64) (cpy []byte) {
|
func (m *Memory) GetCopy(offset, size uint64) (cpy []byte) {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(m.store) > int(offset) {
|
// memory is always resized before being accessed, no need to check bounds
|
||||||
cpy = make([]byte, size)
|
cpy = make([]byte, size)
|
||||||
copy(cpy, m.store[offset:offset+size])
|
copy(cpy, m.store[offset:offset+size])
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPtr returns the offset + size
|
// GetPtr returns the offset + size
|
||||||
func (m *Memory) GetPtr(offset, size int64) []byte {
|
func (m *Memory) GetPtr(offset, size uint64) []byte {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(m.store) > int(offset) {
|
// memory is always resized before being accessed, no need to check bounds
|
||||||
return m.store[offset : offset+size]
|
return m.store[offset : offset+size]
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the length of the backing slice
|
// Len returns the length of the backing slice
|
||||||
func (m *Memory) Len() int {
|
func (m *Memory) Len() int {
|
||||||
return len(m.store)
|
return len(m.store)
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
|
||||||
|
|
||||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
address := stack.peek().Bytes20()
|
address := stack.peek().Bytes20()
|
||||||
gas := evm.AccessEvents.BalanceGas(address, false)
|
gas := evm.AccessEvents.BasicDataGas(address, false)
|
||||||
if gas == 0 {
|
if gas == 0 {
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
gas = params.WarmStorageReadCostEIP2929
|
||||||
}
|
}
|
||||||
|
|
@ -52,8 +52,7 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
gas := evm.AccessEvents.VersionGas(address, false)
|
gas := evm.AccessEvents.BasicDataGas(address, false)
|
||||||
gas += evm.AccessEvents.CodeSizeGas(address, false)
|
|
||||||
if gas == 0 {
|
if gas == 0 {
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
gas = params.WarmStorageReadCostEIP2929
|
||||||
}
|
}
|
||||||
|
|
@ -102,17 +101,15 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
contractAddr := contract.Address()
|
contractAddr := contract.Address()
|
||||||
statelessGas := evm.AccessEvents.VersionGas(contractAddr, false)
|
statelessGas := evm.AccessEvents.BasicDataGas(contractAddr, false)
|
||||||
statelessGas += evm.AccessEvents.CodeSizeGas(contractAddr, false)
|
|
||||||
statelessGas += evm.AccessEvents.BalanceGas(contractAddr, false)
|
|
||||||
if contractAddr != beneficiaryAddr {
|
if contractAddr != beneficiaryAddr {
|
||||||
statelessGas += evm.AccessEvents.BalanceGas(beneficiaryAddr, false)
|
statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, false)
|
||||||
}
|
}
|
||||||
// Charge write costs if it transfers value
|
// Charge write costs if it transfers value
|
||||||
if evm.StateDB.GetBalance(contractAddr).Sign() != 0 {
|
if evm.StateDB.GetBalance(contractAddr).Sign() != 0 {
|
||||||
statelessGas += evm.AccessEvents.BalanceGas(contractAddr, true)
|
statelessGas += evm.AccessEvents.BasicDataGas(contractAddr, true)
|
||||||
if contractAddr != beneficiaryAddr {
|
if contractAddr != beneficiaryAddr {
|
||||||
statelessGas += evm.AccessEvents.BalanceGas(beneficiaryAddr, true)
|
statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return statelessGas, nil
|
return statelessGas, nil
|
||||||
|
|
@ -145,8 +142,7 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
addr := common.Address(stack.peek().Bytes20())
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
wgas := evm.AccessEvents.VersionGas(addr, false)
|
wgas := evm.AccessEvents.BasicDataGas(addr, false)
|
||||||
wgas += evm.AccessEvents.CodeSizeGas(addr, false)
|
|
||||||
if wgas == 0 {
|
if wgas == 0 {
|
||||||
wgas = params.WarmStorageReadCostEIP2929
|
wgas = params.WarmStorageReadCostEIP2929
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -38,7 +40,6 @@ import (
|
||||||
|
|
||||||
// force-load js tracers to trigger registration
|
// force-load js tracers to trigger registration
|
||||||
_ "github.com/ethereum/go-ethereum/eth/tracers/js"
|
_ "github.com/ethereum/go-ethereum/eth/tracers/js"
|
||||||
"github.com/holiman/uint256"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDefaults(t *testing.T) {
|
func TestDefaults(t *testing.T) {
|
||||||
|
|
@ -213,6 +214,70 @@ func BenchmarkEVM_CREATE2_1200(bench *testing.B) {
|
||||||
benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056")
|
benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkEVM_SWAP1(b *testing.B) {
|
||||||
|
// returns a contract that does n swaps (SWAP1)
|
||||||
|
swapContract := func(n uint64) []byte {
|
||||||
|
contract := []byte{
|
||||||
|
byte(vm.PUSH0), // PUSH0
|
||||||
|
byte(vm.PUSH0), // PUSH0
|
||||||
|
}
|
||||||
|
for i := uint64(0); i < n; i++ {
|
||||||
|
contract = append(contract, byte(vm.SWAP1))
|
||||||
|
}
|
||||||
|
return contract
|
||||||
|
}
|
||||||
|
|
||||||
|
state, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||||
|
contractAddr := common.BytesToAddress([]byte("contract"))
|
||||||
|
|
||||||
|
b.Run("10k", func(b *testing.B) {
|
||||||
|
contractCode := swapContract(10_000)
|
||||||
|
state.SetCode(contractAddr, contractCode)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _, err := Call(contractAddr, []byte{}, &Config{State: state})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEVM_RETURN(b *testing.B) {
|
||||||
|
// returns a contract that returns a zero-byte slice of len size
|
||||||
|
returnContract := func(size uint64) []byte {
|
||||||
|
contract := []byte{
|
||||||
|
byte(vm.PUSH8), 0, 0, 0, 0, 0, 0, 0, 0, // PUSH8 0xXXXXXXXXXXXXXXXX
|
||||||
|
byte(vm.PUSH0), // PUSH0
|
||||||
|
byte(vm.RETURN), // RETURN
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint64(contract[1:], size)
|
||||||
|
return contract
|
||||||
|
}
|
||||||
|
|
||||||
|
state, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||||
|
contractAddr := common.BytesToAddress([]byte("contract"))
|
||||||
|
|
||||||
|
for _, n := range []uint64{1_000, 10_000, 100_000, 1_000_000} {
|
||||||
|
b.Run(strconv.FormatUint(n, 10), func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
contractCode := returnContract(n)
|
||||||
|
state.SetCode(contractAddr, contractCode)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ret, _, err := Call(contractAddr, []byte{}, &Config{State: state})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if uint64(len(ret)) != n {
|
||||||
|
b.Fatalf("expected return size %d, got %d", n, len(ret))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func fakeHeader(n uint64, parentHash common.Hash) *types.Header {
|
func fakeHeader(n uint64, parentHash common.Hash) *types.Header {
|
||||||
header := types.Header{
|
header := types.Header{
|
||||||
Coinbase: common.HexToAddress("0x00000000000000000000000000000000deadbeef"),
|
Coinbase: common.HexToAddress("0x00000000000000000000000000000000deadbeef"),
|
||||||
|
|
@ -339,11 +404,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
||||||
Tracer: tracer.Hooks,
|
Tracer: tracer.Hooks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var (
|
destination := common.BytesToAddress([]byte("contract"))
|
||||||
destination = common.BytesToAddress([]byte("contract"))
|
|
||||||
vmenv = NewEnv(cfg)
|
|
||||||
sender = vm.AccountRef(cfg.Origin)
|
|
||||||
)
|
|
||||||
cfg.State.CreateAccount(destination)
|
cfg.State.CreateAccount(destination)
|
||||||
eoa := common.HexToAddress("E0")
|
eoa := common.HexToAddress("E0")
|
||||||
{
|
{
|
||||||
|
|
@ -363,12 +424,12 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
||||||
//cfg.State.CreateAccount(cfg.Origin)
|
//cfg.State.CreateAccount(cfg.Origin)
|
||||||
// set the receiver's (the executing contract) code for execution.
|
// set the receiver's (the executing contract) code for execution.
|
||||||
cfg.State.SetCode(destination, code)
|
cfg.State.SetCode(destination, code)
|
||||||
vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value))
|
Call(destination, nil, cfg)
|
||||||
|
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value))
|
Call(destination, nil, cfg)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ var stackPool = sync.Pool{
|
||||||
|
|
||||||
// Stack is an object for basic stack operations. Items popped to the stack are
|
// Stack is an object for basic stack operations. Items popped to the stack are
|
||||||
// expected to be changed and modified. stack does not take care of adding newly
|
// expected to be changed and modified. stack does not take care of adding newly
|
||||||
// initialised objects.
|
// initialized objects.
|
||||||
type Stack struct {
|
type Stack struct {
|
||||||
data []uint256.Int
|
data []uint256.Int
|
||||||
}
|
}
|
||||||
|
|
@ -64,8 +64,53 @@ func (st *Stack) len() int {
|
||||||
return len(st.data)
|
return len(st.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) swap(n int) {
|
func (st *Stack) swap1() {
|
||||||
st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
|
st.data[st.len()-2], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-2]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap2() {
|
||||||
|
st.data[st.len()-3], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-3]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap3() {
|
||||||
|
st.data[st.len()-4], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-4]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap4() {
|
||||||
|
st.data[st.len()-5], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-5]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap5() {
|
||||||
|
st.data[st.len()-6], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-6]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap6() {
|
||||||
|
st.data[st.len()-7], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-7]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap7() {
|
||||||
|
st.data[st.len()-8], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-8]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap8() {
|
||||||
|
st.data[st.len()-9], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-9]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap9() {
|
||||||
|
st.data[st.len()-10], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-10]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap10() {
|
||||||
|
st.data[st.len()-11], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-11]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap11() {
|
||||||
|
st.data[st.len()-12], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-12]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap12() {
|
||||||
|
st.data[st.len()-13], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-13]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap13() {
|
||||||
|
st.data[st.len()-14], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-14]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap14() {
|
||||||
|
st.data[st.len()-15], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-15]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap15() {
|
||||||
|
st.data[st.len()-16], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-16]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap16() {
|
||||||
|
st.data[st.len()-17], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-17]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) dup(n int) {
|
func (st *Stack) dup(n int) {
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ Since we had merged the removal of `ETH65`, if the entire network were to upgrad
|
||||||
|
|
||||||
- Announce an upcoming security release on Tuesday (August 24th), via Twitter and official channels, plus reach out to downstream projects.
|
- Announce an upcoming security release on Tuesday (August 24th), via Twitter and official channels, plus reach out to downstream projects.
|
||||||
- Temporarily revert the `ETH65`-removal.
|
- Temporarily revert the `ETH65`-removal.
|
||||||
- Place the fix into the PR optimizing the jumpdest analysis [233381](https://github.com/ethereum/go-ethereum/pull/23381).
|
- Place the fix into the PR optimizing the jumpdest analysis [23381](https://github.com/ethereum/go-ethereum/pull/23381).
|
||||||
- After 4-8 weeks, release details about the vulnerability.
|
- After 4-8 weeks, release details about the vulnerability.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,15 +64,13 @@ type Config = ethconfig.Config
|
||||||
|
|
||||||
// Ethereum implements the Ethereum full node service.
|
// Ethereum implements the Ethereum full node service.
|
||||||
type Ethereum struct {
|
type Ethereum struct {
|
||||||
|
// core protocol objects
|
||||||
config *ethconfig.Config
|
config *ethconfig.Config
|
||||||
|
|
||||||
// Handlers
|
|
||||||
txPool *txpool.TxPool
|
txPool *txpool.TxPool
|
||||||
|
|
||||||
blockchain *core.BlockChain
|
blockchain *core.BlockChain
|
||||||
|
|
||||||
handler *handler
|
handler *handler
|
||||||
ethDialCandidates enode.Iterator
|
discmix *enode.FairMix
|
||||||
snapDialCandidates enode.Iterator
|
|
||||||
|
|
||||||
// DB interfaces
|
// DB interfaces
|
||||||
chainDb ethdb.Database // Block chain database
|
chainDb ethdb.Database // Block chain database
|
||||||
|
|
@ -162,6 +160,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
bloomRequests: make(chan chan *bloombits.Retrieval),
|
bloomRequests: make(chan chan *bloombits.Retrieval),
|
||||||
bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms),
|
bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms),
|
||||||
p2pServer: stack.Server(),
|
p2pServer: stack.Server(),
|
||||||
|
discmix: enode.NewFairMix(0),
|
||||||
shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb),
|
shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb),
|
||||||
}
|
}
|
||||||
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
|
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
|
||||||
|
|
@ -266,17 +265,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
}
|
}
|
||||||
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice)
|
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice)
|
||||||
|
|
||||||
// Setup DNS discovery iterators.
|
|
||||||
dnsclient := dnsdisc.NewClient(dnsdisc.Config{})
|
|
||||||
eth.ethDialCandidates, err = dnsclient.NewIterator(eth.config.EthDiscoveryURLs...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
eth.snapDialCandidates, err = dnsclient.NewIterator(eth.config.SnapDiscoveryURLs...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the RPC service
|
// Start the RPC service
|
||||||
eth.netRPCService = ethapi.NewNetAPI(eth.p2pServer, networkID)
|
eth.netRPCService = ethapi.NewNetAPI(eth.p2pServer, networkID)
|
||||||
|
|
||||||
|
|
@ -359,9 +347,9 @@ func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer }
|
||||||
// Protocols returns all the currently configured
|
// Protocols returns all the currently configured
|
||||||
// network protocols to start.
|
// network protocols to start.
|
||||||
func (s *Ethereum) Protocols() []p2p.Protocol {
|
func (s *Ethereum) Protocols() []p2p.Protocol {
|
||||||
protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.ethDialCandidates)
|
protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.discmix)
|
||||||
if s.config.SnapshotCache > 0 {
|
if s.config.SnapshotCache > 0 {
|
||||||
protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...)
|
protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler))...)
|
||||||
}
|
}
|
||||||
return protos
|
return protos
|
||||||
}
|
}
|
||||||
|
|
@ -369,7 +357,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
|
||||||
// Start implements node.Lifecycle, starting all internal goroutines needed by the
|
// Start implements node.Lifecycle, starting all internal goroutines needed by the
|
||||||
// Ethereum protocol implementation.
|
// Ethereum protocol implementation.
|
||||||
func (s *Ethereum) Start() error {
|
func (s *Ethereum) Start() error {
|
||||||
eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode())
|
s.setupDiscovery()
|
||||||
|
|
||||||
// Start the bloom bits servicing goroutines
|
// Start the bloom bits servicing goroutines
|
||||||
s.startBloomHandlers(params.BloomBitsBlocks)
|
s.startBloomHandlers(params.BloomBitsBlocks)
|
||||||
|
|
@ -377,16 +365,40 @@ func (s *Ethereum) Start() error {
|
||||||
// Regularly update shutdown marker
|
// Regularly update shutdown marker
|
||||||
s.shutdownTracker.Start()
|
s.shutdownTracker.Start()
|
||||||
|
|
||||||
// Figure out a max peers count based on the server limits
|
// Start the networking layer
|
||||||
maxPeers := s.p2pServer.MaxPeers
|
s.handler.Start(s.p2pServer.MaxPeers)
|
||||||
if s.config.LightServ > 0 {
|
return nil
|
||||||
if s.config.LightPeers >= s.p2pServer.MaxPeers {
|
|
||||||
return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.MaxPeers)
|
|
||||||
}
|
}
|
||||||
maxPeers -= s.config.LightPeers
|
|
||||||
|
func (s *Ethereum) setupDiscovery() error {
|
||||||
|
eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode())
|
||||||
|
|
||||||
|
// Add eth nodes from DNS.
|
||||||
|
dnsclient := dnsdisc.NewClient(dnsdisc.Config{})
|
||||||
|
if len(s.config.EthDiscoveryURLs) > 0 {
|
||||||
|
iter, err := dnsclient.NewIterator(s.config.EthDiscoveryURLs...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
// Start the networking layer and the light server if requested
|
s.discmix.AddSource(iter)
|
||||||
s.handler.Start(maxPeers)
|
}
|
||||||
|
|
||||||
|
// Add snap nodes from DNS.
|
||||||
|
if len(s.config.SnapDiscoveryURLs) > 0 {
|
||||||
|
iter, err := dnsclient.NewIterator(s.config.SnapDiscoveryURLs...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.discmix.AddSource(iter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add DHT nodes from discv5.
|
||||||
|
if s.p2pServer.DiscoveryV5() != nil {
|
||||||
|
filter := eth.NewNodeFilter(s.blockchain)
|
||||||
|
iter := enode.Filter(s.p2pServer.DiscoveryV5().RandomNodes(), filter)
|
||||||
|
s.discmix.AddSource(iter)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -394,8 +406,7 @@ func (s *Ethereum) Start() error {
|
||||||
// Ethereum protocol.
|
// Ethereum protocol.
|
||||||
func (s *Ethereum) Stop() error {
|
func (s *Ethereum) Stop() error {
|
||||||
// Stop all the peer-related stuff first.
|
// Stop all the peer-related stuff first.
|
||||||
s.ethDialCandidates.Close()
|
s.discmix.Close()
|
||||||
s.snapDialCandidates.Close()
|
|
||||||
s.handler.Stop()
|
s.handler.Stop()
|
||||||
|
|
||||||
// Then stop everything else.
|
// Then stop everything else.
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, pa
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai"))
|
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, false)
|
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload
|
// ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload
|
||||||
|
|
@ -207,7 +207,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, pa
|
||||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV2 must only be called with paris and shanghai payloads"))
|
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV2 must only be called with paris and shanghai payloads"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, params, engine.PayloadV2, false)
|
return api.forkchoiceUpdated(update, params, engine.PayloadV2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForkchoiceUpdatedV3 is equivalent to V2 with the addition of parent beacon block root
|
// ForkchoiceUpdatedV3 is equivalent to V2 with the addition of parent beacon block root
|
||||||
|
|
@ -228,10 +228,10 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa
|
||||||
// hash, even if params are wrong. To do this we need to split up
|
// hash, even if params are wrong. To do this we need to split up
|
||||||
// forkchoiceUpdate into a function that only updates the head and then a
|
// forkchoiceUpdate into a function that only updates the head and then a
|
||||||
// function that kicks off block construction.
|
// function that kicks off block construction.
|
||||||
return api.forkchoiceUpdated(update, params, engine.PayloadV3, false)
|
return api.forkchoiceUpdated(update, params, engine.PayloadV3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes, payloadVersion engine.PayloadVersion, simulatorMode bool) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes, payloadVersion engine.PayloadVersion) (engine.ForkChoiceResponse, error) {
|
||||||
api.forkchoiceLock.Lock()
|
api.forkchoiceLock.Lock()
|
||||||
defer api.forkchoiceLock.Unlock()
|
defer api.forkchoiceLock.Unlock()
|
||||||
|
|
||||||
|
|
@ -374,19 +374,6 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
if api.localBlocks.has(id) {
|
if api.localBlocks.has(id) {
|
||||||
return valid(&id), nil
|
return valid(&id), nil
|
||||||
}
|
}
|
||||||
// If the beacon chain is ran by a simulator, then transaction insertion,
|
|
||||||
// block insertion and block production will happen without any timing
|
|
||||||
// delay between them. This will cause flaky simulator executions due to
|
|
||||||
// the transaction pool running its internal reset operation on a back-
|
|
||||||
// ground thread. To avoid the racey behavior - in simulator mode - the
|
|
||||||
// pool will be explicitly blocked on its reset before continuing to the
|
|
||||||
// block production below.
|
|
||||||
if simulatorMode {
|
|
||||||
if err := api.eth.TxPool().Sync(); err != nil {
|
|
||||||
log.Error("Failed to sync transaction pool", "err", err)
|
|
||||||
return valid(nil), engine.InvalidPayloadAttributes.With(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
payload, err := api.eth.Miner().BuildPayload(args)
|
payload, err := api.eth.Miner().BuildPayload(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to build payload", "err", err)
|
log.Error("Failed to build payload", "err", err)
|
||||||
|
|
@ -546,7 +533,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
|
||||||
bgu = strconv.Itoa(int(*params.BlobGasUsed))
|
bgu = strconv.Itoa(int(*params.BlobGasUsed))
|
||||||
}
|
}
|
||||||
ebg := "nil"
|
ebg := "nil"
|
||||||
if params.BlobGasUsed != nil {
|
if params.ExcessBlobGas != nil {
|
||||||
ebg = strconv.Itoa(int(*params.ExcessBlobGas))
|
ebg = strconv.Itoa(int(*params.ExcessBlobGas))
|
||||||
}
|
}
|
||||||
log.Warn("Invalid NewPayload params",
|
log.Warn("Invalid NewPayload params",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -30,6 +31,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -41,36 +43,46 @@ const devEpochLength = 32
|
||||||
// withdrawalQueue implements a FIFO queue which holds withdrawals that are
|
// withdrawalQueue implements a FIFO queue which holds withdrawals that are
|
||||||
// pending inclusion.
|
// pending inclusion.
|
||||||
type withdrawalQueue struct {
|
type withdrawalQueue struct {
|
||||||
pending chan *types.Withdrawal
|
pending types.Withdrawals
|
||||||
|
mu sync.Mutex
|
||||||
|
feed event.Feed
|
||||||
|
subs event.SubscriptionScope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type newWithdrawalsEvent struct{ Withdrawals types.Withdrawals }
|
||||||
|
|
||||||
// add queues a withdrawal for future inclusion.
|
// add queues a withdrawal for future inclusion.
|
||||||
func (w *withdrawalQueue) add(withdrawal *types.Withdrawal) error {
|
func (w *withdrawalQueue) add(withdrawal *types.Withdrawal) error {
|
||||||
select {
|
w.mu.Lock()
|
||||||
case w.pending <- withdrawal:
|
w.pending = append(w.pending, withdrawal)
|
||||||
break
|
w.mu.Unlock()
|
||||||
default:
|
|
||||||
return errors.New("withdrawal queue full")
|
w.feed.Send(newWithdrawalsEvent{types.Withdrawals{withdrawal}})
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// gatherPending returns a number of queued withdrawals up to a maximum count.
|
// pop dequeues the specified number of withdrawals from the queue.
|
||||||
func (w *withdrawalQueue) gatherPending(maxCount int) []*types.Withdrawal {
|
func (w *withdrawalQueue) pop(count int) types.Withdrawals {
|
||||||
withdrawals := []*types.Withdrawal{}
|
w.mu.Lock()
|
||||||
for {
|
defer w.mu.Unlock()
|
||||||
select {
|
|
||||||
case withdrawal := <-w.pending:
|
count = min(count, len(w.pending))
|
||||||
withdrawals = append(withdrawals, withdrawal)
|
popped := w.pending[0:count]
|
||||||
if len(withdrawals) == maxCount {
|
w.pending = w.pending[count:]
|
||||||
return withdrawals
|
|
||||||
}
|
return popped
|
||||||
default:
|
|
||||||
return withdrawals
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// subscribe allows a listener to be updated when new withdrawals are added to
|
||||||
|
// the queue.
|
||||||
|
func (w *withdrawalQueue) subscribe(ch chan<- newWithdrawalsEvent) event.Subscription {
|
||||||
|
sub := w.feed.Subscribe(ch)
|
||||||
|
return w.subs.Track(sub)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimulatedBeacon drives an Ethereum instance as if it were a real beacon
|
||||||
|
// client. It can run in period mode where it mines a new block every period
|
||||||
|
// (seconds) or on every transaction via Commit, Fork and AdjustTime.
|
||||||
type SimulatedBeacon struct {
|
type SimulatedBeacon struct {
|
||||||
shutdownCh chan struct{}
|
shutdownCh chan struct{}
|
||||||
eth *eth.Ethereum
|
eth *eth.Ethereum
|
||||||
|
|
@ -86,10 +98,6 @@ type SimulatedBeacon struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimulatedBeacon constructs a new simulated beacon chain.
|
// NewSimulatedBeacon constructs a new simulated beacon chain.
|
||||||
// Period sets the period in which blocks should be produced.
|
|
||||||
//
|
|
||||||
// - If period is set to 0, a block is produced on every transaction.
|
|
||||||
// via Commit, Fork and AdjustTime.
|
|
||||||
func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, error) {
|
func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, error) {
|
||||||
block := eth.BlockChain().CurrentBlock()
|
block := eth.BlockChain().CurrentBlock()
|
||||||
current := engine.ForkchoiceStateV1{
|
current := engine.ForkchoiceStateV1{
|
||||||
|
|
@ -112,7 +120,6 @@ func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, err
|
||||||
engineAPI: engineAPI,
|
engineAPI: engineAPI,
|
||||||
lastBlockTime: block.Time,
|
lastBlockTime: block.Time,
|
||||||
curForkchoiceState: current,
|
curForkchoiceState: current,
|
||||||
withdrawals: withdrawalQueue{make(chan *types.Withdrawal, 20)},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,6 +163,16 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
|
||||||
c.setCurrentState(header.Hash(), *finalizedHash)
|
c.setCurrentState(header.Hash(), *finalizedHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Because transaction insertion, block insertion, and block production will
|
||||||
|
// happen without any timing delay between them in simulator mode and the
|
||||||
|
// transaction pool will be running its internal reset operation on a
|
||||||
|
// background thread, flaky executions can happen. To avoid the racey
|
||||||
|
// behavior, the pool will be explicitly blocked on its reset before
|
||||||
|
// continuing to the block production below.
|
||||||
|
if err := c.eth.APIBackend.TxPool().Sync(); err != nil {
|
||||||
|
return fmt.Errorf("failed to sync txpool: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var random [32]byte
|
var random [32]byte
|
||||||
rand.Read(random[:])
|
rand.Read(random[:])
|
||||||
fcResponse, err := c.engineAPI.forkchoiceUpdated(c.curForkchoiceState, &engine.PayloadAttributes{
|
fcResponse, err := c.engineAPI.forkchoiceUpdated(c.curForkchoiceState, &engine.PayloadAttributes{
|
||||||
|
|
@ -164,13 +181,14 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
|
||||||
Withdrawals: withdrawals,
|
Withdrawals: withdrawals,
|
||||||
Random: random,
|
Random: random,
|
||||||
BeaconRoot: &common.Hash{},
|
BeaconRoot: &common.Hash{},
|
||||||
}, engine.PayloadV3, true)
|
}, engine.PayloadV3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if fcResponse == engine.STATUS_SYNCING {
|
if fcResponse == engine.STATUS_SYNCING {
|
||||||
return errors.New("chain rewind prevented invocation of payload creation")
|
return errors.New("chain rewind prevented invocation of payload creation")
|
||||||
}
|
}
|
||||||
|
|
||||||
envelope, err := c.engineAPI.getPayload(*fcResponse.PayloadID, true)
|
envelope, err := c.engineAPI.getPayload(*fcResponse.PayloadID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -223,8 +241,7 @@ func (c *SimulatedBeacon) loop() {
|
||||||
case <-c.shutdownCh:
|
case <-c.shutdownCh:
|
||||||
return
|
return
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
withdrawals := c.withdrawals.gatherPending(10)
|
if err := c.sealBlock(c.withdrawals.pop(10), uint64(time.Now().Unix())); err != nil {
|
||||||
if err := c.sealBlock(withdrawals, uint64(time.Now().Unix())); err != nil {
|
|
||||||
log.Warn("Error performing sealing work", "err", err)
|
log.Warn("Error performing sealing work", "err", err)
|
||||||
} else {
|
} else {
|
||||||
timer.Reset(time.Second * time.Duration(c.period))
|
timer.Reset(time.Second * time.Duration(c.period))
|
||||||
|
|
@ -260,7 +277,7 @@ func (c *SimulatedBeacon) setCurrentState(headHash, finalizedHash common.Hash) {
|
||||||
|
|
||||||
// Commit seals a block on demand.
|
// Commit seals a block on demand.
|
||||||
func (c *SimulatedBeacon) Commit() common.Hash {
|
func (c *SimulatedBeacon) Commit() common.Hash {
|
||||||
withdrawals := c.withdrawals.gatherPending(10)
|
withdrawals := c.withdrawals.pop(10)
|
||||||
if err := c.sealBlock(withdrawals, uint64(time.Now().Unix())); err != nil {
|
if err := c.sealBlock(withdrawals, uint64(time.Now().Unix())); err != nil {
|
||||||
log.Warn("Error performing sealing work", "err", err)
|
log.Warn("Error performing sealing work", "err", err)
|
||||||
}
|
}
|
||||||
|
|
@ -301,16 +318,14 @@ func (c *SimulatedBeacon) AdjustTime(adjustment time.Duration) error {
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return errors.New("parent not found")
|
return errors.New("parent not found")
|
||||||
}
|
}
|
||||||
withdrawals := c.withdrawals.gatherPending(10)
|
withdrawals := c.withdrawals.pop(10)
|
||||||
return c.sealBlock(withdrawals, parent.Time+uint64(adjustment/time.Second))
|
return c.sealBlock(withdrawals, parent.Time+uint64(adjustment/time.Second))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterSimulatedBeaconAPIs registers the simulated beacon's API with the
|
||||||
|
// stack.
|
||||||
func RegisterSimulatedBeaconAPIs(stack *node.Node, sim *SimulatedBeacon) {
|
func RegisterSimulatedBeaconAPIs(stack *node.Node, sim *SimulatedBeacon) {
|
||||||
api := &api{sim}
|
api := newSimulatedBeaconAPI(sim)
|
||||||
if sim.period == 0 {
|
|
||||||
// mine on demand if period is set to 0
|
|
||||||
go api.loop()
|
|
||||||
}
|
|
||||||
stack.RegisterAPIs([]rpc.API{
|
stack.RegisterAPIs([]rpc.API{
|
||||||
{
|
{
|
||||||
Namespace: "dev",
|
Namespace: "dev",
|
||||||
|
|
|
||||||
|
|
@ -18,44 +18,88 @@ package catalyst
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type api struct {
|
// simulatedBeaconAPI provides a RPC API for SimulatedBeacon.
|
||||||
|
type simulatedBeaconAPI struct {
|
||||||
sim *SimulatedBeacon
|
sim *SimulatedBeacon
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) loop() {
|
// newSimulatedBeaconAPI returns an instance of simulatedBeaconAPI with a
|
||||||
|
// buffered commit channel. If period is zero, it starts a goroutine to handle
|
||||||
|
// new tx events.
|
||||||
|
func newSimulatedBeaconAPI(sim *SimulatedBeacon) *simulatedBeaconAPI {
|
||||||
|
api := &simulatedBeaconAPI{sim: sim}
|
||||||
|
if sim.period == 0 {
|
||||||
|
// mine on demand if period is set to 0
|
||||||
|
go api.loop()
|
||||||
|
}
|
||||||
|
return api
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop is the main loop for the API when it's running in period = 0 mode. It
|
||||||
|
// ensures that block production is triggered as soon as a new withdrawal or
|
||||||
|
// transaction is received.
|
||||||
|
func (a *simulatedBeaconAPI) loop() {
|
||||||
var (
|
var (
|
||||||
newTxs = make(chan core.NewTxsEvent)
|
newTxs = make(chan core.NewTxsEvent)
|
||||||
sub = a.sim.eth.TxPool().SubscribeTransactions(newTxs, true)
|
newWxs = make(chan newWithdrawalsEvent)
|
||||||
|
newTxsSub = a.sim.eth.TxPool().SubscribeTransactions(newTxs, true)
|
||||||
|
newWxsSub = a.sim.withdrawals.subscribe(newWxs)
|
||||||
|
doCommit = make(chan struct{}, 1)
|
||||||
)
|
)
|
||||||
defer sub.Unsubscribe()
|
defer newTxsSub.Unsubscribe()
|
||||||
|
defer newWxsSub.Unsubscribe()
|
||||||
|
|
||||||
|
// A background thread which signals to the simulator when to commit
|
||||||
|
// based on messages over doCommit.
|
||||||
|
go func() {
|
||||||
|
for range doCommit {
|
||||||
|
a.sim.Commit()
|
||||||
|
a.sim.eth.TxPool().Sync()
|
||||||
|
|
||||||
|
// It's worth noting that in case a tx ends up in the pool listed as
|
||||||
|
// "executable", but for whatever reason the miner does not include it in
|
||||||
|
// a block -- maybe the miner is enforcing a higher tip than the pool --
|
||||||
|
// this code will spinloop.
|
||||||
|
for {
|
||||||
|
if executable, _ := a.sim.eth.TxPool().Stats(); executable == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
a.sim.Commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-a.sim.shutdownCh:
|
case <-a.sim.shutdownCh:
|
||||||
|
close(doCommit)
|
||||||
return
|
return
|
||||||
case w := <-a.sim.withdrawals.pending:
|
case <-newWxs:
|
||||||
withdrawals := append(a.sim.withdrawals.gatherPending(9), w)
|
select {
|
||||||
if err := a.sim.sealBlock(withdrawals, uint64(time.Now().Unix())); err != nil {
|
case doCommit <- struct{}{}:
|
||||||
log.Warn("Error performing sealing work", "err", err)
|
default:
|
||||||
}
|
}
|
||||||
case <-newTxs:
|
case <-newTxs:
|
||||||
a.sim.Commit()
|
select {
|
||||||
|
case doCommit <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) AddWithdrawal(ctx context.Context, withdrawal *types.Withdrawal) error {
|
// AddWithdrawal adds a withdrawal to the pending queue.
|
||||||
|
func (a *simulatedBeaconAPI) AddWithdrawal(ctx context.Context, withdrawal *types.Withdrawal) error {
|
||||||
return a.sim.withdrawals.add(withdrawal)
|
return a.sim.withdrawals.add(withdrawal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) SetFeeRecipient(ctx context.Context, feeRecipient common.Address) {
|
// SetFeeRecipient sets the fee recipient for block building purposes.
|
||||||
|
func (a *simulatedBeaconAPI) SetFeeRecipient(ctx context.Context, feeRecipient common.Address) {
|
||||||
a.sim.setFeeRecipient(feeRecipient)
|
a.sim.setFeeRecipient(feeRecipient)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func startSimulatedBeaconEthService(t *testing.T, genesis *core.Genesis) (*node.Node, *eth.Ethereum, *SimulatedBeacon) {
|
func startSimulatedBeaconEthService(t *testing.T, genesis *core.Genesis, period uint64) (*node.Node, *eth.Ethereum, *SimulatedBeacon) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
n, err := node.New(&node.Config{
|
n, err := node.New(&node.Config{
|
||||||
|
|
@ -55,7 +55,7 @@ func startSimulatedBeaconEthService(t *testing.T, genesis *core.Genesis) (*node.
|
||||||
t.Fatal("can't create eth service:", err)
|
t.Fatal("can't create eth service:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
simBeacon, err := NewSimulatedBeacon(1, ethservice)
|
simBeacon, err := NewSimulatedBeacon(period, ethservice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("can't create simulated beacon:", err)
|
t.Fatal("can't create simulated beacon:", err)
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +87,7 @@ func TestSimulatedBeaconSendWithdrawals(t *testing.T) {
|
||||||
// short period (1 second) for testing purposes
|
// short period (1 second) for testing purposes
|
||||||
var gasLimit uint64 = 10_000_000
|
var gasLimit uint64 = 10_000_000
|
||||||
genesis := core.DeveloperGenesisBlock(gasLimit, &testAddr)
|
genesis := core.DeveloperGenesisBlock(gasLimit, &testAddr)
|
||||||
node, ethService, mock := startSimulatedBeaconEthService(t, genesis)
|
node, ethService, mock := startSimulatedBeaconEthService(t, genesis, 1)
|
||||||
_ = mock
|
_ = mock
|
||||||
defer node.Close()
|
defer node.Close()
|
||||||
|
|
||||||
|
|
@ -140,3 +140,65 @@ func TestSimulatedBeaconSendWithdrawals(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that zero-period dev mode can handle a lot of simultaneous
|
||||||
|
// transactions/withdrawals
|
||||||
|
func TestOnDemandSpam(t *testing.T) {
|
||||||
|
var (
|
||||||
|
withdrawals []types.Withdrawal
|
||||||
|
txs = make(map[common.Hash]*types.Transaction)
|
||||||
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
gasLimit uint64 = 10_000_000
|
||||||
|
genesis = core.DeveloperGenesisBlock(gasLimit, &testAddr)
|
||||||
|
node, eth, mock = startSimulatedBeaconEthService(t, genesis, 0)
|
||||||
|
_ = newSimulatedBeaconAPI(mock)
|
||||||
|
signer = types.LatestSigner(eth.BlockChain().Config())
|
||||||
|
chainHeadCh = make(chan core.ChainHeadEvent, 100)
|
||||||
|
sub = eth.BlockChain().SubscribeChainHeadEvent(chainHeadCh)
|
||||||
|
)
|
||||||
|
defer node.Close()
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
|
// generate some withdrawals
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
withdrawals = append(withdrawals, types.Withdrawal{Index: uint64(i)})
|
||||||
|
if err := mock.withdrawals.add(&withdrawals[i]); err != nil {
|
||||||
|
t.Fatal("addWithdrawal failed", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a bunch of transactions
|
||||||
|
for i := 0; i < 20000; i++ {
|
||||||
|
tx, err := types.SignTx(types.NewTransaction(uint64(i), common.Address{byte(i), byte(1)}, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), signer, testKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("error signing transaction", err)
|
||||||
|
}
|
||||||
|
txs[tx.Hash()] = tx
|
||||||
|
if err := eth.APIBackend.SendTx(context.Background(), tx); err != nil {
|
||||||
|
t.Fatal("error adding txs to pool", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
includedTxs = make(map[common.Hash]struct{})
|
||||||
|
includedWxs []uint64
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case evt := <-chainHeadCh:
|
||||||
|
for _, itx := range evt.Block.Transactions() {
|
||||||
|
includedTxs[itx.Hash()] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, iwx := range evt.Block.Withdrawals() {
|
||||||
|
includedWxs = append(includedWxs, iwx.Index)
|
||||||
|
}
|
||||||
|
// ensure all withdrawals/txs included. this will take two blocks b/c number of withdrawals > 10
|
||||||
|
if len(includedTxs) == len(txs) && len(includedWxs) == len(withdrawals) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
t.Fatalf("timed out without including all withdrawals/txs: have txs %d, want %d, have wxs %d, want %d", len(includedTxs), len(txs), len(includedWxs), len(withdrawals))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,8 @@ func (b *beaconBackfiller) resume() {
|
||||||
func (b *beaconBackfiller) setMode(mode SyncMode) {
|
func (b *beaconBackfiller) setMode(mode SyncMode) {
|
||||||
// Update the old sync mode and track if it was changed
|
// Update the old sync mode and track if it was changed
|
||||||
b.lock.Lock()
|
b.lock.Lock()
|
||||||
updated := b.syncMode != mode
|
oldMode := b.syncMode
|
||||||
|
updated := oldMode != mode
|
||||||
filling := b.filling
|
filling := b.filling
|
||||||
b.syncMode = mode
|
b.syncMode = mode
|
||||||
b.lock.Unlock()
|
b.lock.Unlock()
|
||||||
|
|
@ -133,7 +134,7 @@ func (b *beaconBackfiller) setMode(mode SyncMode) {
|
||||||
if !updated || !filling {
|
if !updated || !filling {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Error("Downloader sync mode changed mid-run", "old", mode.String(), "new", mode.String())
|
log.Error("Downloader sync mode changed mid-run", "old", oldMode.String(), "new", mode.String())
|
||||||
b.suspend()
|
b.suspend()
|
||||||
b.resume()
|
b.resume()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -385,6 +385,7 @@ func (q *queue) Results(block bool) []*fetchResult {
|
||||||
for _, tx := range result.Transactions {
|
for _, tx := range result.Transactions {
|
||||||
size += common.StorageSize(tx.Size())
|
size += common.StorageSize(tx.Size())
|
||||||
}
|
}
|
||||||
|
size += common.StorageSize(result.Withdrawals.Size())
|
||||||
q.resultSize = common.StorageSize(blockCacheSizeWeight)*size +
|
q.resultSize = common.StorageSize(blockCacheSizeWeight)*size +
|
||||||
(1-common.StorageSize(blockCacheSizeWeight))*q.resultSize
|
(1-common.StorageSize(blockCacheSizeWeight))*q.resultSize
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ var Defaults = Config{
|
||||||
TxLookupLimit: 2350000,
|
TxLookupLimit: 2350000,
|
||||||
TransactionHistory: 2350000,
|
TransactionHistory: 2350000,
|
||||||
StateHistory: params.FullImmutabilityThreshold,
|
StateHistory: params.FullImmutabilityThreshold,
|
||||||
LightPeers: 100,
|
|
||||||
DatabaseCache: 512,
|
DatabaseCache: 512,
|
||||||
TrieCleanCache: 154,
|
TrieCleanCache: 154,
|
||||||
TrieDirtyCache: 256,
|
TrieDirtyCache: 256,
|
||||||
|
|
@ -87,11 +86,13 @@ type Config struct {
|
||||||
EthDiscoveryURLs []string
|
EthDiscoveryURLs []string
|
||||||
SnapDiscoveryURLs []string
|
SnapDiscoveryURLs []string
|
||||||
|
|
||||||
|
// State options.
|
||||||
NoPruning bool // Whether to disable pruning and flush everything to disk
|
NoPruning bool // Whether to disable pruning and flush everything to disk
|
||||||
NoPrefetch bool // Whether to disable prefetching and only load state on demand
|
NoPrefetch bool // Whether to disable prefetching and only load state on demand
|
||||||
|
|
||||||
// Deprecated, use 'TransactionHistory' instead.
|
// Deprecated: use 'TransactionHistory' instead.
|
||||||
TxLookupLimit uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
|
TxLookupLimit uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
|
||||||
|
|
||||||
TransactionHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
|
TransactionHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
|
||||||
StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved.
|
StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved.
|
||||||
|
|
||||||
|
|
@ -105,14 +106,6 @@ type Config struct {
|
||||||
// presence of these blocks for every new peer connection.
|
// presence of these blocks for every new peer connection.
|
||||||
RequiredBlocks map[uint64]common.Hash `toml:"-"`
|
RequiredBlocks map[uint64]common.Hash `toml:"-"`
|
||||||
|
|
||||||
// Light client options
|
|
||||||
LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
|
|
||||||
LightIngress int `toml:",omitempty"` // Incoming bandwidth limit for light servers
|
|
||||||
LightEgress int `toml:",omitempty"` // Outgoing bandwidth limit for light servers
|
|
||||||
LightPeers int `toml:",omitempty"` // Maximum number of LES client peers
|
|
||||||
LightNoPrune bool `toml:",omitempty"` // Whether to disable light chain pruning
|
|
||||||
LightNoSyncServe bool `toml:",omitempty"` // Whether to serve light clients before syncing
|
|
||||||
|
|
||||||
// Database options
|
// Database options
|
||||||
SkipBcVersionCheck bool `toml:"-"`
|
SkipBcVersionCheck bool `toml:"-"`
|
||||||
DatabaseHandles int `toml:"-"`
|
DatabaseHandles int `toml:"-"`
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,6 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
StateHistory uint64 `toml:",omitempty"`
|
StateHistory uint64 `toml:",omitempty"`
|
||||||
StateScheme string `toml:",omitempty"`
|
StateScheme string `toml:",omitempty"`
|
||||||
RequiredBlocks map[uint64]common.Hash `toml:"-"`
|
RequiredBlocks map[uint64]common.Hash `toml:"-"`
|
||||||
LightServ int `toml:",omitempty"`
|
|
||||||
LightIngress int `toml:",omitempty"`
|
|
||||||
LightEgress int `toml:",omitempty"`
|
|
||||||
LightPeers int `toml:",omitempty"`
|
|
||||||
LightNoPrune bool `toml:",omitempty"`
|
|
||||||
LightNoSyncServe bool `toml:",omitempty"`
|
|
||||||
SkipBcVersionCheck bool `toml:"-"`
|
SkipBcVersionCheck bool `toml:"-"`
|
||||||
DatabaseHandles int `toml:"-"`
|
DatabaseHandles int `toml:"-"`
|
||||||
DatabaseCache int
|
DatabaseCache int
|
||||||
|
|
@ -73,12 +67,6 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
enc.StateHistory = c.StateHistory
|
enc.StateHistory = c.StateHistory
|
||||||
enc.StateScheme = c.StateScheme
|
enc.StateScheme = c.StateScheme
|
||||||
enc.RequiredBlocks = c.RequiredBlocks
|
enc.RequiredBlocks = c.RequiredBlocks
|
||||||
enc.LightServ = c.LightServ
|
|
||||||
enc.LightIngress = c.LightIngress
|
|
||||||
enc.LightEgress = c.LightEgress
|
|
||||||
enc.LightPeers = c.LightPeers
|
|
||||||
enc.LightNoPrune = c.LightNoPrune
|
|
||||||
enc.LightNoSyncServe = c.LightNoSyncServe
|
|
||||||
enc.SkipBcVersionCheck = c.SkipBcVersionCheck
|
enc.SkipBcVersionCheck = c.SkipBcVersionCheck
|
||||||
enc.DatabaseHandles = c.DatabaseHandles
|
enc.DatabaseHandles = c.DatabaseHandles
|
||||||
enc.DatabaseCache = c.DatabaseCache
|
enc.DatabaseCache = c.DatabaseCache
|
||||||
|
|
@ -121,12 +109,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
StateHistory *uint64 `toml:",omitempty"`
|
StateHistory *uint64 `toml:",omitempty"`
|
||||||
StateScheme *string `toml:",omitempty"`
|
StateScheme *string `toml:",omitempty"`
|
||||||
RequiredBlocks map[uint64]common.Hash `toml:"-"`
|
RequiredBlocks map[uint64]common.Hash `toml:"-"`
|
||||||
LightServ *int `toml:",omitempty"`
|
|
||||||
LightIngress *int `toml:",omitempty"`
|
|
||||||
LightEgress *int `toml:",omitempty"`
|
|
||||||
LightPeers *int `toml:",omitempty"`
|
|
||||||
LightNoPrune *bool `toml:",omitempty"`
|
|
||||||
LightNoSyncServe *bool `toml:",omitempty"`
|
|
||||||
SkipBcVersionCheck *bool `toml:"-"`
|
SkipBcVersionCheck *bool `toml:"-"`
|
||||||
DatabaseHandles *int `toml:"-"`
|
DatabaseHandles *int `toml:"-"`
|
||||||
DatabaseCache *int
|
DatabaseCache *int
|
||||||
|
|
@ -192,24 +174,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
if dec.RequiredBlocks != nil {
|
if dec.RequiredBlocks != nil {
|
||||||
c.RequiredBlocks = dec.RequiredBlocks
|
c.RequiredBlocks = dec.RequiredBlocks
|
||||||
}
|
}
|
||||||
if dec.LightServ != nil {
|
|
||||||
c.LightServ = *dec.LightServ
|
|
||||||
}
|
|
||||||
if dec.LightIngress != nil {
|
|
||||||
c.LightIngress = *dec.LightIngress
|
|
||||||
}
|
|
||||||
if dec.LightEgress != nil {
|
|
||||||
c.LightEgress = *dec.LightEgress
|
|
||||||
}
|
|
||||||
if dec.LightPeers != nil {
|
|
||||||
c.LightPeers = *dec.LightPeers
|
|
||||||
}
|
|
||||||
if dec.LightNoPrune != nil {
|
|
||||||
c.LightNoPrune = *dec.LightNoPrune
|
|
||||||
}
|
|
||||||
if dec.LightNoSyncServe != nil {
|
|
||||||
c.LightNoSyncServe = *dec.LightNoSyncServe
|
|
||||||
}
|
|
||||||
if dec.SkipBcVersionCheck != nil {
|
if dec.SkipBcVersionCheck != nil {
|
||||||
c.SkipBcVersionCheck = *dec.SkipBcVersionCheck
|
c.SkipBcVersionCheck = *dec.SkipBcVersionCheck
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ var errTerminated = errors.New("terminated")
|
||||||
type txAnnounce struct {
|
type txAnnounce struct {
|
||||||
origin string // Identifier of the peer originating the notification
|
origin string // Identifier of the peer originating the notification
|
||||||
hashes []common.Hash // Batch of transaction hashes being announced
|
hashes []common.Hash // Batch of transaction hashes being announced
|
||||||
metas []*txMetadata // Batch of metadatas associated with the hashes (nil before eth/68)
|
metas []*txMetadata // Batch of metadata associated with the hashes
|
||||||
}
|
}
|
||||||
|
|
||||||
// txMetadata is a set of extra data transmitted along the announcement for better
|
// txMetadata is a set of extra data transmitted along the announcement for better
|
||||||
|
|
@ -137,7 +137,7 @@ type txRequest struct {
|
||||||
type txDelivery struct {
|
type txDelivery struct {
|
||||||
origin string // Identifier of the peer originating the notification
|
origin string // Identifier of the peer originating the notification
|
||||||
hashes []common.Hash // Batch of transaction hashes having been delivered
|
hashes []common.Hash // Batch of transaction hashes having been delivered
|
||||||
metas []txMetadata // Batch of metadatas associated with the delivered hashes
|
metas []txMetadata // Batch of metadata associated with the delivered hashes
|
||||||
direct bool // Whether this is a direct reply or a broadcast
|
direct bool // Whether this is a direct reply or a broadcast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,13 +260,13 @@ func (f *TxFetcher) Notify(peer string, types []byte, sizes []uint32, hashes []c
|
||||||
underpriced++
|
underpriced++
|
||||||
default:
|
default:
|
||||||
unknownHashes = append(unknownHashes, hash)
|
unknownHashes = append(unknownHashes, hash)
|
||||||
if types == nil {
|
|
||||||
unknownMetas = append(unknownMetas, nil)
|
// Transaction metadata has been available since eth68, and all
|
||||||
} else {
|
// legacy eth protocols (prior to eth68) have been deprecated.
|
||||||
|
// Therefore, metadata is always expected in the announcement.
|
||||||
unknownMetas = append(unknownMetas, &txMetadata{kind: types[i], size: sizes[i]})
|
unknownMetas = append(unknownMetas, &txMetadata{kind: types[i], size: sizes[i]})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
txAnnounceKnownMeter.Mark(duplicate)
|
txAnnounceKnownMeter.Mark(duplicate)
|
||||||
txAnnounceUnderpricedMeter.Mark(underpriced)
|
txAnnounceUnderpricedMeter.Mark(underpriced)
|
||||||
|
|
||||||
|
|
@ -892,13 +892,8 @@ func (f *TxFetcher) scheduleFetches(timer *mclock.Timer, timeout chan struct{},
|
||||||
if len(hashes) >= maxTxRetrievals {
|
if len(hashes) >= maxTxRetrievals {
|
||||||
return false // break in the for-each
|
return false // break in the for-each
|
||||||
}
|
}
|
||||||
if meta != nil { // Only set eth/68 and upwards
|
|
||||||
bytes += uint64(meta.size)
|
bytes += uint64(meta.size)
|
||||||
if bytes >= maxTxRetrievalSize {
|
return bytes < maxTxRetrievalSize
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true // scheduled, try to add more
|
|
||||||
})
|
})
|
||||||
// If any hashes were allocated, request them from the peer
|
// If any hashes were allocated, request them from the peer
|
||||||
if len(hashes) > 0 {
|
if len(hashes) > 0 {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -64,3 +64,17 @@ func currentENREntry(chain *core.BlockChain) *enrEntry {
|
||||||
ForkID: forkid.NewID(chain.Config(), chain.Genesis(), head.Number.Uint64(), head.Time),
|
ForkID: forkid.NewID(chain.Config(), chain.Genesis(), head.Number.Uint64(), head.Time),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewNodeFilter returns a filtering function that returns whether the provided
|
||||||
|
// enode advertises a forkid compatible with the current chain.
|
||||||
|
func NewNodeFilter(chain *core.BlockChain) func(*enode.Node) bool {
|
||||||
|
filter := forkid.NewFilter(chain)
|
||||||
|
return func(n *enode.Node) bool {
|
||||||
|
var entry enrEntry
|
||||||
|
if err := n.Load(entry); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
err := filter(entry.ForkID)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,6 @@ func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2
|
||||||
return backend.PeerInfo(id)
|
return backend.PeerInfo(id)
|
||||||
},
|
},
|
||||||
Attributes: []enr.Entry{currentENREntry(backend.Chain())},
|
Attributes: []enr.Entry{currentENREntry(backend.Chain())},
|
||||||
DialCandidates: dnsdisc,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return protocols
|
return protocols
|
||||||
|
|
@ -123,7 +122,7 @@ func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2
|
||||||
// NodeInfo represents a short summary of the `eth` sub-protocol metadata
|
// NodeInfo represents a short summary of the `eth` sub-protocol metadata
|
||||||
// known about the host peer.
|
// known about the host peer.
|
||||||
type NodeInfo struct {
|
type NodeInfo struct {
|
||||||
Network uint64 `json:"network"` // Ethereum network ID (1=Mainnet, Goerli=5)
|
Network uint64 `json:"network"` // Ethereum network ID (1=Mainnet, Holesky=17000)
|
||||||
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
|
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
|
||||||
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
|
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
|
||||||
Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules
|
Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ func handleGetBlockHeaders(backend Backend, msg Decoder, peer *Peer) error {
|
||||||
// ServiceGetBlockHeadersQuery assembles the response to a header query. It is
|
// ServiceGetBlockHeadersQuery assembles the response to a header query. It is
|
||||||
// exposed to allow external packages to test protocol behavior.
|
// exposed to allow external packages to test protocol behavior.
|
||||||
func ServiceGetBlockHeadersQuery(chain *core.BlockChain, query *GetBlockHeadersRequest, peer *Peer) []rlp.RawValue {
|
func ServiceGetBlockHeadersQuery(chain *core.BlockChain, query *GetBlockHeadersRequest, peer *Peer) []rlp.RawValue {
|
||||||
|
if query.Amount == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if query.Skip == 0 {
|
if query.Skip == 0 {
|
||||||
// The fast path: when the request is for a contiguous segment of headers.
|
// The fast path: when the request is for a contiguous segment of headers.
|
||||||
return serviceContiguousBlockHeaderQuery(chain, query)
|
return serviceContiguousBlockHeaderQuery(chain, query)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ type genTrie interface {
|
||||||
// update inserts the state item into generator trie.
|
// update inserts the state item into generator trie.
|
||||||
update(key, value []byte) error
|
update(key, value []byte) error
|
||||||
|
|
||||||
|
// delete removes the state item from the generator trie.
|
||||||
|
delete(key []byte) error
|
||||||
|
|
||||||
// commit flushes the right boundary nodes if complete flag is true. This
|
// commit flushes the right boundary nodes if complete flag is true. This
|
||||||
// function must be called before flushing the associated database batch.
|
// function must be called before flushing the associated database batch.
|
||||||
commit(complete bool) common.Hash
|
commit(complete bool) common.Hash
|
||||||
|
|
@ -113,7 +116,7 @@ func (t *pathTrie) onTrieNode(path []byte, hash common.Hash, blob []byte) {
|
||||||
// removed because it's a sibling of the nodes we want to commit, not
|
// removed because it's a sibling of the nodes we want to commit, not
|
||||||
// the parent or ancestor.
|
// the parent or ancestor.
|
||||||
for i := 0; i < len(path); i++ {
|
for i := 0; i < len(path); i++ {
|
||||||
t.delete(path[:i], false)
|
t.deleteNode(path[:i], false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -136,7 +139,7 @@ func (t *pathTrie) onTrieNode(path []byte, hash common.Hash, blob []byte) {
|
||||||
// byte key. In either case, no gaps will be left in the path.
|
// byte key. In either case, no gaps will be left in the path.
|
||||||
if t.last != nil && bytes.HasPrefix(t.last, path) && len(t.last)-len(path) > 1 {
|
if t.last != nil && bytes.HasPrefix(t.last, path) && len(t.last)-len(path) > 1 {
|
||||||
for i := len(path) + 1; i < len(t.last); i++ {
|
for i := len(path) + 1; i < len(t.last); i++ {
|
||||||
t.delete(t.last[:i], true)
|
t.deleteNode(t.last[:i], true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.write(path, blob)
|
t.write(path, blob)
|
||||||
|
|
@ -192,8 +195,8 @@ func (t *pathTrie) deleteStorageNode(path []byte, inner bool) {
|
||||||
rawdb.DeleteStorageTrieNode(t.batch, t.owner, path)
|
rawdb.DeleteStorageTrieNode(t.batch, t.owner, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete commits the node deletion to provided database batch in path mode.
|
// deleteNode commits the node deletion to provided database batch in path mode.
|
||||||
func (t *pathTrie) delete(path []byte, inner bool) {
|
func (t *pathTrie) deleteNode(path []byte, inner bool) {
|
||||||
if t.owner == (common.Hash{}) {
|
if t.owner == (common.Hash{}) {
|
||||||
t.deleteAccountNode(path, inner)
|
t.deleteAccountNode(path, inner)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -207,6 +210,34 @@ func (t *pathTrie) update(key, value []byte) error {
|
||||||
return t.tr.Update(key, value)
|
return t.tr.Update(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete implements genTrie interface, deleting the item from the stack trie.
|
||||||
|
func (t *pathTrie) delete(key []byte) error {
|
||||||
|
// Commit the trie since the right boundary is incomplete because
|
||||||
|
// of the deleted item. This will implicitly discard the last inserted
|
||||||
|
// item and clean some ancestor trie nodes of the last committed
|
||||||
|
// item in the database.
|
||||||
|
t.commit(false)
|
||||||
|
|
||||||
|
// Reset the trie and all the internal trackers
|
||||||
|
t.first = nil
|
||||||
|
t.last = nil
|
||||||
|
t.tr.Reset()
|
||||||
|
|
||||||
|
// Explicitly mark the left boundary as incomplete, as the left-side
|
||||||
|
// item of the next one has been deleted. Be aware that the next item
|
||||||
|
// to be inserted will be ignored from committing as well as it's on
|
||||||
|
// the left boundary.
|
||||||
|
t.skipLeftBoundary = true
|
||||||
|
|
||||||
|
// Explicitly delete the potential leftover nodes on the specific
|
||||||
|
// path from the database.
|
||||||
|
tkey := t.tr.TrieKey(key)
|
||||||
|
for i := 0; i <= len(tkey); i++ {
|
||||||
|
t.deleteNode(tkey[:i], false)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// commit implements genTrie interface, flushing the right boundary if it's
|
// commit implements genTrie interface, flushing the right boundary if it's
|
||||||
// considered as complete. Otherwise, the nodes on the right boundary are
|
// considered as complete. Otherwise, the nodes on the right boundary are
|
||||||
// discarded and cleaned up.
|
// discarded and cleaned up.
|
||||||
|
|
@ -255,7 +286,7 @@ func (t *pathTrie) commit(complete bool) common.Hash {
|
||||||
// with no issues as they are actually complete. Also, from a database
|
// with no issues as they are actually complete. Also, from a database
|
||||||
// perspective, first deleting and then rewriting is a valid data update.
|
// perspective, first deleting and then rewriting is a valid data update.
|
||||||
for i := 0; i < len(t.last); i++ {
|
for i := 0; i < len(t.last); i++ {
|
||||||
t.delete(t.last[:i], false)
|
t.deleteNode(t.last[:i], false)
|
||||||
}
|
}
|
||||||
return common.Hash{} // the hash is meaningless for incomplete commit
|
return common.Hash{} // the hash is meaningless for incomplete commit
|
||||||
}
|
}
|
||||||
|
|
@ -278,6 +309,9 @@ func (t *hashTrie) update(key, value []byte) error {
|
||||||
return t.tr.Update(key, value)
|
return t.tr.Update(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete implements genTrie interface, ignoring the state item for deleting.
|
||||||
|
func (t *hashTrie) delete(key []byte) error { return nil }
|
||||||
|
|
||||||
// commit implements genTrie interface, committing the nodes on right boundary.
|
// commit implements genTrie interface, committing the nodes on right boundary.
|
||||||
func (t *hashTrie) commit(complete bool) common.Hash {
|
func (t *hashTrie) commit(complete bool) common.Hash {
|
||||||
if !complete {
|
if !complete {
|
||||||
|
|
|
||||||
|
|
@ -551,3 +551,145 @@ func TestTinyPartialTree(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrieDelete(t *testing.T) {
|
||||||
|
var entries []*kv
|
||||||
|
for i := 0; i < 1024; i++ {
|
||||||
|
entries = append(entries, &kv{
|
||||||
|
k: testrand.Bytes(32),
|
||||||
|
v: testrand.Bytes(32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
slices.SortFunc(entries, (*kv).cmp)
|
||||||
|
|
||||||
|
nodes := make(map[string]common.Hash)
|
||||||
|
tr := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
|
nodes[string(path)] = hash
|
||||||
|
})
|
||||||
|
for i := 0; i < len(entries); i++ {
|
||||||
|
tr.Update(entries[i].k, entries[i].v)
|
||||||
|
}
|
||||||
|
tr.Hash()
|
||||||
|
|
||||||
|
check := func(index []int) {
|
||||||
|
var (
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
batch = db.NewBatch()
|
||||||
|
marks = map[int]struct{}{}
|
||||||
|
neighbors = map[int]struct{}{}
|
||||||
|
)
|
||||||
|
for _, n := range index {
|
||||||
|
marks[n] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, n := range index {
|
||||||
|
if n != 0 {
|
||||||
|
if _, ok := marks[n-1]; !ok {
|
||||||
|
neighbors[n-1] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n != len(entries)-1 {
|
||||||
|
if _, ok := neighbors[n+1]; !ok {
|
||||||
|
neighbors[n+1] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Write the junk nodes as the dangling
|
||||||
|
var injects []string
|
||||||
|
for _, n := range index {
|
||||||
|
nibbles := byteToHex(entries[n].k)
|
||||||
|
for i := 0; i <= len(nibbles); i++ {
|
||||||
|
injects = append(injects, string(nibbles[:i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, path := range injects {
|
||||||
|
rawdb.WriteAccountTrieNode(db, []byte(path), testrand.Bytes(32))
|
||||||
|
}
|
||||||
|
tr := newPathTrie(common.Hash{}, false, db, batch)
|
||||||
|
for i := 0; i < len(entries); i++ {
|
||||||
|
if _, ok := marks[i]; ok {
|
||||||
|
tr.delete(entries[i].k)
|
||||||
|
} else {
|
||||||
|
tr.update(entries[i].k, entries[i].v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr.commit(true)
|
||||||
|
|
||||||
|
r := newBatchReplay()
|
||||||
|
batch.Replay(r)
|
||||||
|
batch.Write()
|
||||||
|
|
||||||
|
for _, path := range injects {
|
||||||
|
if rawdb.HasAccountTrieNode(db, []byte(path)) {
|
||||||
|
t.Fatalf("Unexpected leftover node %v", []byte(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure all the written nodes match with the complete tree
|
||||||
|
set := make(map[string]common.Hash)
|
||||||
|
for path, hash := range r.modifies() {
|
||||||
|
if hash == (common.Hash{}) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n, ok := nodes[path]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Unexpected trie node: %v", []byte(path))
|
||||||
|
}
|
||||||
|
if n != hash {
|
||||||
|
t.Fatalf("Unexpected trie node content: %v, want: %x, got: %x", []byte(path), n, hash)
|
||||||
|
}
|
||||||
|
set[path] = hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure all the missing nodes either on the deleted path, or
|
||||||
|
// on the neighbor paths.
|
||||||
|
isMissing := func(path []byte) bool {
|
||||||
|
for n := range marks {
|
||||||
|
key := byteToHex(entries[n].k)
|
||||||
|
if bytes.HasPrefix(key, path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for n := range neighbors {
|
||||||
|
key := byteToHex(entries[n].k)
|
||||||
|
if bytes.HasPrefix(key, path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for path := range nodes {
|
||||||
|
if _, ok := set[path]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !isMissing([]byte(path)) {
|
||||||
|
t.Fatalf("Missing node %v", []byte(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var cases = []struct {
|
||||||
|
index []int
|
||||||
|
}{
|
||||||
|
// delete the first
|
||||||
|
{[]int{0}},
|
||||||
|
|
||||||
|
// delete the last
|
||||||
|
{[]int{len(entries) - 1}},
|
||||||
|
|
||||||
|
// delete the first two
|
||||||
|
{[]int{0, 1}},
|
||||||
|
|
||||||
|
// delete the last two
|
||||||
|
{[]int{len(entries) - 2, len(entries) - 1}},
|
||||||
|
|
||||||
|
{[]int{
|
||||||
|
0, 2, 4, 6,
|
||||||
|
len(entries) - 1,
|
||||||
|
len(entries) - 3,
|
||||||
|
len(entries) - 5,
|
||||||
|
len(entries) - 7,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
check(c.index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,13 +82,7 @@ type Backend interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeProtocols constructs the P2P protocol definitions for `snap`.
|
// MakeProtocols constructs the P2P protocol definitions for `snap`.
|
||||||
func MakeProtocols(backend Backend, dnsdisc enode.Iterator) []p2p.Protocol {
|
func MakeProtocols(backend Backend) []p2p.Protocol {
|
||||||
// Filter the discovery iterator for nodes advertising snap support.
|
|
||||||
dnsdisc = enode.Filter(dnsdisc, func(n *enode.Node) bool {
|
|
||||||
var snap enrEntry
|
|
||||||
return n.Load(&snap) == nil
|
|
||||||
})
|
|
||||||
|
|
||||||
protocols := make([]p2p.Protocol, len(ProtocolVersions))
|
protocols := make([]p2p.Protocol, len(ProtocolVersions))
|
||||||
for i, version := range ProtocolVersions {
|
for i, version := range ProtocolVersions {
|
||||||
version := version // Closure
|
version := version // Closure
|
||||||
|
|
@ -109,7 +103,6 @@ func MakeProtocols(backend Backend, dnsdisc enode.Iterator) []p2p.Protocol {
|
||||||
return backend.PeerInfo(id)
|
return backend.PeerInfo(id)
|
||||||
},
|
},
|
||||||
Attributes: []enr.Entry{&enrEntry{}},
|
Attributes: []enr.Entry{&enrEntry{}},
|
||||||
DialCandidates: dnsdisc,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return protocols
|
return protocols
|
||||||
|
|
|
||||||
|
|
@ -2424,14 +2424,21 @@ func (s *Syncer) forwardAccountTask(task *accountTask) {
|
||||||
slim := types.SlimAccountRLP(*res.accounts[i])
|
slim := types.SlimAccountRLP(*res.accounts[i])
|
||||||
rawdb.WriteAccountSnapshot(batch, hash, slim)
|
rawdb.WriteAccountSnapshot(batch, hash, slim)
|
||||||
|
|
||||||
// If the task is complete, drop it into the stack trie to generate
|
|
||||||
// account trie nodes for it
|
|
||||||
if !task.needHeal[i] {
|
if !task.needHeal[i] {
|
||||||
|
// If the storage task is complete, drop it into the stack trie
|
||||||
|
// to generate account trie nodes for it
|
||||||
full, err := types.FullAccountRLP(slim) // TODO(karalabe): Slim parsing can be omitted
|
full, err := types.FullAccountRLP(slim) // TODO(karalabe): Slim parsing can be omitted
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // Really shouldn't ever happen
|
panic(err) // Really shouldn't ever happen
|
||||||
}
|
}
|
||||||
task.genTrie.update(hash[:], full)
|
task.genTrie.update(hash[:], full)
|
||||||
|
} else {
|
||||||
|
// If the storage task is incomplete, explicitly delete the corresponding
|
||||||
|
// account item from the account trie to ensure that all nodes along the
|
||||||
|
// path to the incomplete storage trie are cleaned up.
|
||||||
|
if err := task.genTrie.delete(hash[:]); err != nil {
|
||||||
|
panic(err) // Really shouldn't ever happen
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Flush anything written just now and update the stats
|
// Flush anything written just now and update the stats
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,12 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
|
||||||
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, eth.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, eth.blockchain.Config(), vm.Config{})
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
||||||
}
|
}
|
||||||
|
// If prague hardfork, insert parent block hash in the state as per EIP-2935.
|
||||||
|
if eth.blockchain.Config().IsPrague(block.Number(), block.Time()) {
|
||||||
|
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
|
||||||
|
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, eth.blockchain.Config(), vm.Config{})
|
||||||
|
core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb)
|
||||||
|
}
|
||||||
if txIndex == 0 && len(block.Transactions()) == 0 {
|
if txIndex == 0 && len(block.Transactions()) == 0 {
|
||||||
return nil, vm.BlockContext{}, statedb, release, nil
|
return nil, vm.BlockContext{}, statedb, release, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -383,6 +383,12 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
|
||||||
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{})
|
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{})
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
||||||
}
|
}
|
||||||
|
// Insert parent hash in history contract.
|
||||||
|
if api.backend.ChainConfig().IsPrague(next.Number(), next.Time()) {
|
||||||
|
context := core.NewEVMBlockContext(next.Header(), api.chainContext(ctx), nil)
|
||||||
|
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{})
|
||||||
|
core.ProcessParentBlockHash(next.ParentHash(), vmenv, statedb)
|
||||||
|
}
|
||||||
// Clean out any pending release functions of trace state. Note this
|
// Clean out any pending release functions of trace state. Note this
|
||||||
// step must be done after constructing tracing state, because the
|
// step must be done after constructing tracing state, because the
|
||||||
// tracing state of block next depends on the parent state and construction
|
// tracing state of block next depends on the parent state and construction
|
||||||
|
|
@ -535,6 +541,9 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
|
||||||
vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
||||||
}
|
}
|
||||||
|
if chainConfig.IsPrague(block.Number(), block.Time()) {
|
||||||
|
core.ProcessParentBlockHash(block.ParentHash(), vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{}), statedb)
|
||||||
|
}
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -614,6 +623,10 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
vmenv := vm.NewEVM(blockCtx, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{})
|
vmenv := vm.NewEVM(blockCtx, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{})
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
||||||
}
|
}
|
||||||
|
if api.backend.ChainConfig().IsPrague(block.Number(), block.Time()) {
|
||||||
|
vmenv := vm.NewEVM(blockCtx, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{})
|
||||||
|
core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb)
|
||||||
|
}
|
||||||
for i, tx := range txs {
|
for i, tx := range txs {
|
||||||
// Generate the next state snapshot fast without tracing
|
// Generate the next state snapshot fast without tracing
|
||||||
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
||||||
|
|
@ -772,6 +785,10 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
||||||
}
|
}
|
||||||
|
if chainConfig.IsPrague(block.Number(), block.Time()) {
|
||||||
|
vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
||||||
|
core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb)
|
||||||
|
}
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
// Prepare the transaction for un-traced execution
|
// Prepare the transaction for un-traced execution
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue