mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Merge branch 'master' into devmode-random
This commit is contained in:
commit
6ebedcbb4b
171 changed files with 3092 additions and 1591 deletions
42
.travis.yml
42
.travis.yml
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
- stage: lint
|
||||
os: linux
|
||||
dist: bionic
|
||||
go: 1.20.x
|
||||
go: 1.21.x
|
||||
env:
|
||||
- lint
|
||||
git:
|
||||
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
os: linux
|
||||
arch: amd64
|
||||
dist: bionic
|
||||
go: 1.20.x
|
||||
go: 1.21.x
|
||||
env:
|
||||
- docker
|
||||
services:
|
||||
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
os: linux
|
||||
arch: arm64
|
||||
dist: bionic
|
||||
go: 1.20.x
|
||||
go: 1.21.x
|
||||
env:
|
||||
- docker
|
||||
services:
|
||||
|
|
@ -63,10 +63,9 @@ jobs:
|
|||
os: linux
|
||||
dist: bionic
|
||||
sudo: required
|
||||
go: 1.20.x
|
||||
go: 1.21.x
|
||||
env:
|
||||
- azure-linux
|
||||
- GO111MODULE=on
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
addons:
|
||||
|
|
@ -97,10 +96,9 @@ jobs:
|
|||
- stage: build
|
||||
if: type = push
|
||||
os: osx
|
||||
go: 1.20.x
|
||||
go: 1.21.x
|
||||
env:
|
||||
- azure-osx
|
||||
- GO111MODULE=on
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
script:
|
||||
|
|
@ -112,41 +110,34 @@ jobs:
|
|||
os: linux
|
||||
arch: amd64
|
||||
dist: bionic
|
||||
go: 1.20.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
go: 1.21.x
|
||||
script:
|
||||
- go run build/ci.go test $TEST_PACKAGES
|
||||
- travis_wait 30 go run build/ci.go test $TEST_PACKAGES
|
||||
|
||||
- stage: build
|
||||
if: type = pull_request
|
||||
os: linux
|
||||
arch: arm64
|
||||
dist: bionic
|
||||
go: 1.19.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
go: 1.20.x
|
||||
script:
|
||||
- go run build/ci.go test $TEST_PACKAGES
|
||||
- travis_wait 30 go run build/ci.go test $TEST_PACKAGES
|
||||
|
||||
- stage: build
|
||||
os: linux
|
||||
dist: bionic
|
||||
go: 1.19.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
go: 1.20.x
|
||||
script:
|
||||
- go run build/ci.go test $TEST_PACKAGES
|
||||
- travis_wait 30 go run build/ci.go test $TEST_PACKAGES
|
||||
|
||||
# This builder does the Ubuntu PPA nightly uploads
|
||||
- stage: build
|
||||
if: type = cron || (type = push && tag ~= /^v[0-9]/)
|
||||
os: linux
|
||||
dist: bionic
|
||||
go: 1.20.x
|
||||
go: 1.21.x
|
||||
env:
|
||||
- ubuntu-ppa
|
||||
- GO111MODULE=on
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
addons:
|
||||
|
|
@ -167,10 +158,9 @@ jobs:
|
|||
if: type = cron
|
||||
os: linux
|
||||
dist: bionic
|
||||
go: 1.20.x
|
||||
go: 1.21.x
|
||||
env:
|
||||
- azure-purge
|
||||
- GO111MODULE=on
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
script:
|
||||
|
|
@ -181,9 +171,7 @@ jobs:
|
|||
if: type = cron
|
||||
os: linux
|
||||
dist: bionic
|
||||
go: 1.20.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
go: 1.21.x
|
||||
script:
|
||||
- go run build/ci.go test -race $TEST_PACKAGES
|
||||
- travis_wait 30 go run build/ci.go test -race $TEST_PACKAGES
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ ARG VERSION=""
|
|||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.20-alpine as builder
|
||||
FROM golang:1.21-alpine as builder
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
|
||||
|
|
|
|||
4
Makefile
4
Makefile
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
GOBIN = ./build/bin
|
||||
GO ?= latest
|
||||
GORUN = env GO111MODULE=on go run
|
||||
GORUN = go run
|
||||
|
||||
geth:
|
||||
$(GORUN) build/ci.go install ./cmd/geth
|
||||
|
|
@ -23,7 +23,7 @@ lint: ## Run linters.
|
|||
$(GORUN) build/ci.go lint
|
||||
|
||||
clean:
|
||||
env GO111MODULE=on go clean -cache
|
||||
go clean -cache
|
||||
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
||||
|
||||
# The devtools target installs tools required for 'go generate'.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
|
@ -246,17 +247,37 @@ func (abi *ABI) HasReceive() bool {
|
|||
// revertSelector is a special function selector for revert reason unpacking.
|
||||
var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
|
||||
|
||||
// panicSelector is a special function selector for panic reason unpacking.
|
||||
var panicSelector = crypto.Keccak256([]byte("Panic(uint256)"))[:4]
|
||||
|
||||
// panicReasons map is for readable panic codes
|
||||
// see this linkage for the deails
|
||||
// https://docs.soliditylang.org/en/v0.8.21/control-structures.html#panic-via-assert-and-error-via-require
|
||||
// the reason string list is copied from ether.js
|
||||
// https://github.com/ethers-io/ethers.js/blob/fa3a883ff7c88611ce766f58bdd4b8ac90814470/src.ts/abi/interface.ts#L207-L218
|
||||
var panicReasons = map[uint64]string{
|
||||
0x00: "generic panic",
|
||||
0x01: "assert(false)",
|
||||
0x11: "arithmetic underflow or overflow",
|
||||
0x12: "division or modulo by zero",
|
||||
0x21: "enum overflow",
|
||||
0x22: "invalid encoded storage byte array accessed",
|
||||
0x31: "out-of-bounds array access; popping on an empty array",
|
||||
0x32: "out-of-bounds access of an array or bytesN",
|
||||
0x41: "out of memory",
|
||||
0x51: "uninitialized function",
|
||||
}
|
||||
|
||||
// UnpackRevert resolves the abi-encoded revert reason. According to the solidity
|
||||
// spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
|
||||
// the provided revert reason is abi-encoded as if it were a call to a function
|
||||
// `Error(string)`. So it's a special tool for it.
|
||||
// the provided revert reason is abi-encoded as if it were a call to function
|
||||
// `Error(string)` or `Panic(uint256)`. So it's a special tool for it.
|
||||
func UnpackRevert(data []byte) (string, error) {
|
||||
if len(data) < 4 {
|
||||
return "", errors.New("invalid data for unpacking")
|
||||
}
|
||||
if !bytes.Equal(data[:4], revertSelector) {
|
||||
return "", errors.New("invalid data for unpacking")
|
||||
}
|
||||
switch {
|
||||
case bytes.Equal(data[:4], revertSelector):
|
||||
typ, err := NewType("string", "", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
@ -266,4 +287,25 @@ func UnpackRevert(data []byte) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
return unpacked[0].(string), nil
|
||||
case bytes.Equal(data[:4], panicSelector):
|
||||
typ, err := NewType("uint256", "", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pCode := unpacked[0].(*big.Int)
|
||||
// uint64 safety check for future
|
||||
// but the code is not bigger than MAX(uint64) now
|
||||
if pCode.IsUint64() {
|
||||
if reason, ok := panicReasons[pCode.Uint64()]; ok {
|
||||
return reason, nil
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("unknown panic code: %#x", pCode), nil
|
||||
default:
|
||||
return "", errors.New("invalid data for unpacking")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1173,6 +1173,8 @@ func TestUnpackRevert(t *testing.T) {
|
|||
{"", "", errors.New("invalid data for unpacking")},
|
||||
{"08c379a1", "", errors.New("invalid data for unpacking")},
|
||||
{"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
|
||||
{"4e487b710000000000000000000000000000000000000000000000000000000000000000", "generic panic", nil},
|
||||
{"4e487b7100000000000000000000000000000000000000000000000000000000000000ff", "unknown panic code: 0xff", nil},
|
||||
}
|
||||
for index, c := range cases {
|
||||
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -127,11 +127,12 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
|
|||
state = state + " "
|
||||
}
|
||||
identity := fmt.Sprintf("function %v", rawName)
|
||||
if funType == Fallback {
|
||||
switch funType {
|
||||
case Fallback:
|
||||
identity = "fallback"
|
||||
} else if funType == Receive {
|
||||
case Receive:
|
||||
identity = "receive"
|
||||
} else if funType == Constructor {
|
||||
case Constructor:
|
||||
identity = "constructor"
|
||||
}
|
||||
str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
|
||||
|
|
|
|||
|
|
@ -84,11 +84,12 @@ func TestMethodString(t *testing.T) {
|
|||
|
||||
for _, test := range table {
|
||||
var got string
|
||||
if test.method == "fallback" {
|
||||
switch test.method {
|
||||
case "fallback":
|
||||
got = abi.Fallback.String()
|
||||
} else if test.method == "receive" {
|
||||
case "receive":
|
||||
got = abi.Receive.String()
|
||||
} else {
|
||||
default:
|
||||
got = abi.Methods[test.method].String()
|
||||
}
|
||||
if got != test.expectation {
|
||||
|
|
|
|||
|
|
@ -160,13 +160,14 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
|||
// this value will become our slice or our array, depending on the type
|
||||
var refSlice reflect.Value
|
||||
|
||||
if t.T == SliceTy {
|
||||
switch t.T {
|
||||
case SliceTy:
|
||||
// declare our slice
|
||||
refSlice = reflect.MakeSlice(t.GetType(), size, size)
|
||||
} else if t.T == ArrayTy {
|
||||
case ArrayTy:
|
||||
// declare our array
|
||||
refSlice = reflect.New(t.GetType()).Elem()
|
||||
} else {
|
||||
default:
|
||||
return nil, errors.New("abi: invalid type in array/slice unpacking stage")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ var (
|
|||
InvalidPayloadAttributes = &EngineAPIError{code: -38003, msg: "Invalid payload attributes"}
|
||||
TooLargeRequest = &EngineAPIError{code: -38004, msg: "Too large request"}
|
||||
InvalidParams = &EngineAPIError{code: -32602, msg: "Invalid parameters"}
|
||||
UnsupportedFork = &EngineAPIError{code: -38005, msg: "Unsupported fork"}
|
||||
|
||||
STATUS_INVALID = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: INVALID}, PayloadID: nil}
|
||||
STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,14 @@ func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
|
|||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
}
|
||||
var enc PayloadAttributes
|
||||
enc.Timestamp = hexutil.Uint64(p.Timestamp)
|
||||
enc.Random = p.Random
|
||||
enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient
|
||||
enc.Withdrawals = p.Withdrawals
|
||||
enc.BeaconRoot = p.BeaconRoot
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
|
@ -36,6 +38,7 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
|
|||
Random *common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
}
|
||||
var dec PayloadAttributes
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -56,5 +59,8 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
|
|||
if dec.Withdrawals != nil {
|
||||
p.Withdrawals = dec.Withdrawals
|
||||
}
|
||||
if dec.BeaconRoot != nil {
|
||||
p.BeaconRoot = dec.BeaconRoot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
|
|
@ -36,6 +35,7 @@ type PayloadAttributes struct {
|
|||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
}
|
||||
|
||||
// JSON type overrides for PayloadAttributes.
|
||||
|
|
@ -172,7 +172,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
|||
// and that the blockhash of the constructed block matches the parameters. Nil
|
||||
// Withdrawals value will propagate through the returned block. Empty
|
||||
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
||||
func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash) (*types.Block, error) {
|
||||
func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
||||
txs, err := decodeTransactions(params.Transactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -226,6 +226,7 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash)
|
|||
WithdrawalsHash: withdrawalsRoot,
|
||||
ExcessBlobGas: params.ExcessBlobGas,
|
||||
BlobGasUsed: params.BlobGasUsed,
|
||||
ParentBeaconRoot: beaconRoot,
|
||||
}
|
||||
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
|
||||
if block.Hash() != params.BlockHash {
|
||||
|
|
@ -236,7 +237,7 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash)
|
|||
|
||||
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||
// fields from the given block. It assumes the given block is post-merge block.
|
||||
func BlockToExecutableData(block *types.Block, fees *big.Int, blobs []kzg4844.Blob, commitments []kzg4844.Commitment, proofs []kzg4844.Proof) *ExecutionPayloadEnvelope {
|
||||
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar) *ExecutionPayloadEnvelope {
|
||||
data := &ExecutableData{
|
||||
BlockHash: block.Hash(),
|
||||
ParentHash: block.ParentHash(),
|
||||
|
|
@ -256,17 +257,19 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, blobs []kzg4844.Bl
|
|||
BlobGasUsed: block.BlobGasUsed(),
|
||||
ExcessBlobGas: block.ExcessBlobGas(),
|
||||
}
|
||||
blobsBundle := BlobsBundleV1{
|
||||
bundle := BlobsBundleV1{
|
||||
Commitments: make([]hexutil.Bytes, 0),
|
||||
Blobs: make([]hexutil.Bytes, 0),
|
||||
Proofs: make([]hexutil.Bytes, 0),
|
||||
}
|
||||
for i := range blobs {
|
||||
blobsBundle.Blobs = append(blobsBundle.Blobs, hexutil.Bytes(blobs[i][:]))
|
||||
blobsBundle.Commitments = append(blobsBundle.Commitments, hexutil.Bytes(commitments[i][:]))
|
||||
blobsBundle.Proofs = append(blobsBundle.Proofs, hexutil.Bytes(proofs[i][:]))
|
||||
for _, sidecar := range sidecars {
|
||||
for j := range sidecar.Blobs {
|
||||
bundle.Blobs = append(bundle.Blobs, hexutil.Bytes(sidecar.Blobs[j][:]))
|
||||
bundle.Commitments = append(bundle.Commitments, hexutil.Bytes(sidecar.Commitments[j][:]))
|
||||
bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(sidecar.Proofs[j][:]))
|
||||
}
|
||||
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &blobsBundle}
|
||||
}
|
||||
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &bundle}
|
||||
}
|
||||
|
||||
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1
|
||||
|
|
|
|||
|
|
@ -1,20 +1,25 @@
|
|||
# This file contains sha256 checksums of optional build dependencies.
|
||||
|
||||
2c5ee9c9ec1e733b0dbbc2bdfed3f62306e51d8172bf38f4f4e542b27520f597 go1.20.7.src.tar.gz
|
||||
785170eab380a8985d53896808b0a71336d0ea60e0a26099b4ccec77798b1cf4 go1.20.7.darwin-amd64.tar.gz
|
||||
eea1e7e4c2f75c72629050e6a6c7c46c446d64056732a7787fb3ba16ace1982e go1.20.7.darwin-arm64.tar.gz
|
||||
d8cff0357ac24eb06f3f280535397eeaacf95611d29f9b2abc3060f3d6dce3b4 go1.20.7.freebsd-386.tar.gz
|
||||
26918dcebf474a9e81ccf9f648cdf36968dfb76b481518cf615d78455dda4416 go1.20.7.freebsd-amd64.tar.gz
|
||||
ddb48145f05bda2f4617a22c979d4e94b22802cdb1a1fde1b1974e733b26f091 go1.20.7.linux-386.tar.gz
|
||||
f0a87f1bcae91c4b69f8dc2bc6d7e6bfcd7524fceec130af525058c0c17b1b44 go1.20.7.linux-amd64.tar.gz
|
||||
44781ae3b153c3b07651d93b6bc554e835a36e2d72a696281c1e4dad9efffe43 go1.20.7.linux-arm64.tar.gz
|
||||
7cc231b415b94f2f7065870a73f67dd2b0ec12b5a98052b7ee0121c42bc04f8d go1.20.7.linux-armv6l.tar.gz
|
||||
6318a1db307c12b8afe68808bd6fae4fba1e558a85b958216096869ed506dcb3 go1.20.7.linux-ppc64le.tar.gz
|
||||
26aea2ede8722ceecbd9db883328a8d963136fd96c11dacc356c44c4c19c6515 go1.20.7.linux-s390x.tar.gz
|
||||
5b0ef6f58d3e04d6cc003aa98e9172f41ba9e091b1c98e7339b41c4c87fb78a1 go1.20.7.windows-386.zip
|
||||
736dc6c7fcab1c96b682c8c93e38d7e371e62a17d34cb2c37d451a1147f66af9 go1.20.7.windows-amd64.zip
|
||||
fc6f79c1e1ed9e506c65f2112ac4e387479916f1accb0d046a6a19ff6938baa5 go1.20.7.windows-arm64.zip
|
||||
# https://github.com/ethereum/execution-spec-tests/releases
|
||||
24bac679f3a2d8240d8e08e7f6a70b70c2dabf673317d924cf1d1887b9fe1f81 fixtures.tar.gz
|
||||
|
||||
# https://go.dev/dl/
|
||||
818d46ede85682dd551ad378ef37a4d247006f12ec59b5b755601d2ce114369a go1.21.0.src.tar.gz
|
||||
b314de9f704ab122c077d2ec8e67e3670affe8865479d1f01991e7ac55d65e70 go1.21.0.darwin-amd64.tar.gz
|
||||
3aca44de55c5e098de2f406e98aba328898b05d509a2e2a356416faacf2c4566 go1.21.0.darwin-arm64.tar.gz
|
||||
312a0065714a50862af714e7a5b3fbbd70fe68f905ffb9bcc56d42eadf6bffab go1.21.0.freebsd-386.tar.gz
|
||||
b8eaa36654625df799654f77f4af0ea7bd9e5e760ebe86e68fe7c484748ae995 go1.21.0.freebsd-amd64.tar.gz
|
||||
0e6f378d9b072fab0a3d9ff4d5e990d98487d47252dba8160015a61e6bd0bcba go1.21.0.linux-386.tar.gz
|
||||
d0398903a16ba2232b389fb31032ddf57cac34efda306a0eebac34f0965a0742 go1.21.0.linux-amd64.tar.gz
|
||||
f3d4548edf9b22f26bbd49720350bbfe59d75b7090a1a2bff1afad8214febaf3 go1.21.0.linux-arm64.tar.gz
|
||||
e377a0004957c8c560a3ff99601bce612330a3d95ba3b0a2ae144165fc87deb1 go1.21.0.linux-armv6l.tar.gz
|
||||
e938ffc81d8ebe5efc179240960ba22da6a841ff05d5cab7ce2547112b14a47f go1.21.0.linux-ppc64le.tar.gz
|
||||
be7338df8e5d5472dfa307b0df2b446d85d001b0a2a3cdb1a14048d751b70481 go1.21.0.linux-s390x.tar.gz
|
||||
af920fbb74fc3d173118dc3cc35f02a709c1de642700e92a91a7d16981df3fec go1.21.0.windows-386.zip
|
||||
732121e64e0ecb07c77fdf6cc1bc5ce7b242c2d40d4ac29021ad4c64a08731f6 go1.21.0.windows-amd64.zip
|
||||
41342f5a0f8c083b14c68bde738ddcd313a4f53a5854bfdfab47f0e88247de12 go1.21.0.windows-arm64.zip
|
||||
|
||||
# https://github.com/golangci/golangci-lint/releases
|
||||
fba08acc4027f69f07cef48fbff70b8a7ecdfaa1c2aba9ad3fb31d60d9f5d4bc golangci-lint-1.51.1-darwin-amd64.tar.gz
|
||||
75b8f0ff3a4e68147156be4161a49d4576f1be37a0b506473f8c482140c1e7f2 golangci-lint-1.51.1-darwin-arm64.tar.gz
|
||||
e06b3459aaed356e1667580be00b05f41f3b2e29685d12cdee571c23e1edb414 golangci-lint-1.51.1-freebsd-386.tar.gz
|
||||
|
|
|
|||
30
build/ci.go
30
build/ci.go
|
|
@ -139,7 +139,7 @@ var (
|
|||
// This is the version of Go that will be downloaded by
|
||||
//
|
||||
// go run ci.go install -dlgo
|
||||
dlgoVersion = "1.20.7"
|
||||
dlgoVersion = "1.21.0"
|
||||
|
||||
// This is the version of Go that will be used to bootstrap the PPA builder.
|
||||
//
|
||||
|
|
@ -148,6 +148,13 @@ var (
|
|||
// we need to switch over to a recursive builder to jumpt across supported
|
||||
// versions.
|
||||
gobootVersion = "1.19.6"
|
||||
|
||||
// This is the version of execution-spec-tests that we are using.
|
||||
// When updating, you must also update build/checksums.txt.
|
||||
executionSpecTestsVersion = "1.0.2"
|
||||
|
||||
// This is where the tests should be unpacked.
|
||||
executionSpecTestsDir = "tests/spec-tests"
|
||||
)
|
||||
|
||||
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
||||
|
|
@ -294,13 +301,17 @@ func doTest(cmdline []string) {
|
|||
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
|
||||
verbose = flag.Bool("v", false, "Whether to log verbosely")
|
||||
race = flag.Bool("race", false, "Execute the race detector")
|
||||
cachedir = flag.String("cachedir", "./build/cache", "directory for caching downloads")
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
|
||||
// Get test fixtures.
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
downloadSpecTestFixtures(csdb, *cachedir)
|
||||
|
||||
// Configure the toolchain.
|
||||
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
||||
if *dlgo {
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
tc.Root = build.DownloadGo(csdb, dlgoVersion)
|
||||
}
|
||||
gotest := tc.Go("test")
|
||||
|
|
@ -332,6 +343,21 @@ func doTest(cmdline []string) {
|
|||
build.MustRun(gotest)
|
||||
}
|
||||
|
||||
// downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures.
|
||||
func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
|
||||
ext := ".tar.gz"
|
||||
base := "fixtures" // TODO(MariusVanDerWijden) rename once the version becomes part of the filename
|
||||
url := fmt.Sprintf("https://github.com/ethereum/execution-spec-tests/releases/download/v%s/%s%s", executionSpecTestsVersion, base, ext)
|
||||
archivePath := filepath.Join(cachedir, base+ext)
|
||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return filepath.Join(cachedir, base)
|
||||
}
|
||||
|
||||
// doLint runs golangci-lint on requested packages.
|
||||
func doLint(cmdline []string) {
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ set to standard output. The following filters are supported:
|
|||
- `-limit <N>` limits the output set to N entries, taking the top N nodes by score
|
||||
- `-ip <CIDR>` filters nodes by IP subnet
|
||||
- `-min-age <duration>` filters nodes by 'first seen' time
|
||||
- `-eth-network <mainnet/goerli/sepolia>` filters nodes by "eth" ENR entry
|
||||
- `-eth-network <mainnet/goerli/sepolia/holesky>` filters nodes by "eth" ENR entry
|
||||
- `-les-server` filters nodes by LES server support
|
||||
- `-snap` filters nodes by snap protocol support
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ func (c *Chain) RootAt(height int) common.Hash {
|
|||
|
||||
// ForkID gets the fork id of the chain.
|
||||
func (c *Chain) ForkID() forkid.ID {
|
||||
return forkid.NewID(c.chainConfig, c.blocks[0].Hash(), uint64(c.Len()), c.blocks[0].Time())
|
||||
return forkid.NewID(c.chainConfig, c.blocks[0], uint64(c.Len()), c.blocks[0].Time())
|
||||
}
|
||||
|
||||
// Shorten returns a copy chain of a desired height from the imported
|
||||
|
|
|
|||
|
|
@ -233,6 +233,8 @@ func ethFilter(args []string) (nodeFilter, error) {
|
|||
filter = forkid.NewStaticFilter(params.GoerliChainConfig, params.GoerliGenesisHash)
|
||||
case "sepolia":
|
||||
filter = forkid.NewStaticFilter(params.SepoliaChainConfig, params.SepoliaGenesisHash)
|
||||
case "holesky":
|
||||
filter = forkid.NewStaticFilter(params.HoleskyChainConfig, params.HoleskyGenesisHash)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown network %q", args[0])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ type stEnv struct {
|
|||
ExcessBlobGas *uint64 `json:"excessBlobGas,omitempty"`
|
||||
ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"`
|
||||
ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"`
|
||||
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
}
|
||||
|
||||
type stEnvMarshaling struct {
|
||||
|
|
@ -182,6 +183,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
|
||||
misc.ApplyDAOHardFork(statedb)
|
||||
}
|
||||
if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil {
|
||||
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
|
||||
}
|
||||
var blobGasUsed uint64
|
||||
for i, tx := range txs {
|
||||
if tx.Type() == types.BlobTxType && vmContext.ExcessBlobGas == nil {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
|||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"`
|
||||
ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"`
|
||||
ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"`
|
||||
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
}
|
||||
var enc stEnv
|
||||
enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
|
||||
|
|
@ -57,6 +58,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
|||
enc.ExcessBlobGas = (*math.HexOrDecimal64)(s.ExcessBlobGas)
|
||||
enc.ParentExcessBlobGas = (*math.HexOrDecimal64)(s.ParentExcessBlobGas)
|
||||
enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed)
|
||||
enc.ParentBeaconBlockRoot = s.ParentBeaconBlockRoot
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +84,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
|||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"`
|
||||
ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"`
|
||||
ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"`
|
||||
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
}
|
||||
var dec stEnv
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -148,5 +151,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
|||
if dec.ParentBlobGasUsed != nil {
|
||||
s.ParentBlobGasUsed = (*uint64)(dec.ParentBlobGasUsed)
|
||||
}
|
||||
if dec.ParentBeaconBlockRoot != nil {
|
||||
s.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,105 +192,20 @@ func Transition(ctx *cli.Context) error {
|
|||
// Set the chain id
|
||||
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
|
||||
|
||||
var txsWithKeys []*txWithKey
|
||||
if txStr != stdinSelector {
|
||||
inFile, err := os.Open(txStr)
|
||||
if err != nil {
|
||||
return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
|
||||
}
|
||||
defer inFile.Close()
|
||||
decoder := json.NewDecoder(inFile)
|
||||
if strings.HasSuffix(txStr, ".rlp") {
|
||||
var body hexutil.Bytes
|
||||
if err := decoder.Decode(&body); err != nil {
|
||||
if txs, err = loadTransactions(txStr, inputData, prestate.Env, chainConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
var txs types.Transactions
|
||||
if err := rlp.DecodeBytes(body, &txs); err != nil {
|
||||
if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, tx := range txs {
|
||||
txsWithKeys = append(txsWithKeys, &txWithKey{
|
||||
key: nil,
|
||||
tx: tx,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if err := decoder.Decode(&txsWithKeys); err != nil {
|
||||
return NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if len(inputData.TxRlp) > 0 {
|
||||
// Decode the body of already signed transactions
|
||||
body := common.FromHex(inputData.TxRlp)
|
||||
var txs types.Transactions
|
||||
if err := rlp.DecodeBytes(body, &txs); err != nil {
|
||||
if err := applyShanghaiChecks(&prestate.Env, chainConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, tx := range txs {
|
||||
txsWithKeys = append(txsWithKeys, &txWithKey{
|
||||
key: nil,
|
||||
tx: tx,
|
||||
})
|
||||
if err := applyMergeChecks(&prestate.Env, chainConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// JSON encoded transactions
|
||||
txsWithKeys = inputData.Txs
|
||||
}
|
||||
}
|
||||
// We may have to sign the transactions.
|
||||
signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp)
|
||||
|
||||
if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil {
|
||||
return NewError(ErrorJson, fmt.Errorf("failed signing transactions: %v", err))
|
||||
}
|
||||
// Sanity check, to not `panic` in state_transition
|
||||
if chainConfig.IsLondon(big.NewInt(int64(prestate.Env.Number))) {
|
||||
if prestate.Env.BaseFee != nil {
|
||||
// Already set, base fee has precedent over parent base fee.
|
||||
} else if prestate.Env.ParentBaseFee != nil && prestate.Env.Number != 0 {
|
||||
parent := &types.Header{
|
||||
Number: new(big.Int).SetUint64(prestate.Env.Number - 1),
|
||||
BaseFee: prestate.Env.ParentBaseFee,
|
||||
GasUsed: prestate.Env.ParentGasUsed,
|
||||
GasLimit: prestate.Env.ParentGasLimit,
|
||||
}
|
||||
prestate.Env.BaseFee = eip1559.CalcBaseFee(chainConfig, parent)
|
||||
} else {
|
||||
return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
|
||||
}
|
||||
}
|
||||
if chainConfig.IsShanghai(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp) && prestate.Env.Withdrawals == nil {
|
||||
return NewError(ErrorConfig, errors.New("Shanghai config but missing 'withdrawals' in env section"))
|
||||
}
|
||||
isMerged := chainConfig.TerminalTotalDifficulty != nil && chainConfig.TerminalTotalDifficulty.BitLen() == 0
|
||||
env := prestate.Env
|
||||
if isMerged {
|
||||
// post-merge:
|
||||
// - random must be supplied
|
||||
// - difficulty must be zero
|
||||
switch {
|
||||
case env.Random == nil:
|
||||
return NewError(ErrorConfig, errors.New("post-merge requires currentRandom to be defined in env"))
|
||||
case env.Difficulty != nil && env.Difficulty.BitLen() != 0:
|
||||
return NewError(ErrorConfig, errors.New("post-merge difficulty must be zero (or omitted) in env"))
|
||||
}
|
||||
prestate.Env.Difficulty = nil
|
||||
} else if env.Difficulty == nil {
|
||||
// pre-merge:
|
||||
// If difficulty was not provided by caller, we need to calculate it.
|
||||
switch {
|
||||
case env.ParentDifficulty == nil:
|
||||
return NewError(ErrorConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty"))
|
||||
case env.Number == 0:
|
||||
return NewError(ErrorConfig, errors.New("currentDifficulty needs to be provided for block number 0"))
|
||||
case env.Timestamp <= env.ParentTimestamp:
|
||||
return NewError(ErrorConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)",
|
||||
env.Timestamp, env.ParentTimestamp))
|
||||
}
|
||||
prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp,
|
||||
env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash)
|
||||
if err := applyCancunChecks(&prestate.Env, chainConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
// Run the test and aggregate the result
|
||||
s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
|
||||
|
|
@ -358,33 +273,149 @@ func (t *txWithKey) UnmarshalJSON(input []byte) error {
|
|||
// and secondly to read them with the standard tx json format
|
||||
func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) {
|
||||
var signedTxs []*types.Transaction
|
||||
for i, txWithKey := range txs {
|
||||
tx := txWithKey.tx
|
||||
key := txWithKey.key
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
if key != nil && v.BitLen()+r.BitLen()+s.BitLen() == 0 {
|
||||
// This transaction needs to be signed
|
||||
for i, tx := range txs {
|
||||
var (
|
||||
v, r, s = tx.tx.RawSignatureValues()
|
||||
signed *types.Transaction
|
||||
err error
|
||||
)
|
||||
if txWithKey.protected {
|
||||
signed, err = types.SignTx(tx, signer, key)
|
||||
if tx.key == nil || v.BitLen()+r.BitLen()+s.BitLen() != 0 {
|
||||
// Already signed
|
||||
signedTxs = append(signedTxs, tx.tx)
|
||||
continue
|
||||
}
|
||||
// This transaction needs to be signed
|
||||
if tx.protected {
|
||||
signed, err = types.SignTx(tx.tx, signer, tx.key)
|
||||
} else {
|
||||
signed, err = types.SignTx(tx, types.FrontierSigner{}, key)
|
||||
signed, err = types.SignTx(tx.tx, types.FrontierSigner{}, tx.key)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err))
|
||||
}
|
||||
signedTxs = append(signedTxs, signed)
|
||||
} else {
|
||||
// Already signed
|
||||
signedTxs = append(signedTxs, tx)
|
||||
}
|
||||
}
|
||||
return signedTxs, nil
|
||||
}
|
||||
|
||||
func loadTransactions(txStr string, inputData *input, env stEnv, chainConfig *params.ChainConfig) (types.Transactions, error) {
|
||||
var txsWithKeys []*txWithKey
|
||||
var signed types.Transactions
|
||||
if txStr != stdinSelector {
|
||||
data, err := os.ReadFile(txStr)
|
||||
if err != nil {
|
||||
return nil, NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
|
||||
}
|
||||
if strings.HasSuffix(txStr, ".rlp") { // A file containing an rlp list
|
||||
var body hexutil.Bytes
|
||||
if err := json.Unmarshal(data, &body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Already signed transactions
|
||||
if err := rlp.DecodeBytes(body, &signed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signed, nil
|
||||
}
|
||||
if err := json.Unmarshal(data, &txsWithKeys); err != nil {
|
||||
return nil, NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
|
||||
}
|
||||
} else {
|
||||
if len(inputData.TxRlp) > 0 {
|
||||
// Decode the body of already signed transactions
|
||||
body := common.FromHex(inputData.TxRlp)
|
||||
// Already signed transactions
|
||||
if err := rlp.DecodeBytes(body, &signed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signed, nil
|
||||
}
|
||||
// JSON encoded transactions
|
||||
txsWithKeys = inputData.Txs
|
||||
}
|
||||
// We may have to sign the transactions.
|
||||
signer := types.MakeSigner(chainConfig, big.NewInt(int64(env.Number)), env.Timestamp)
|
||||
return signUnsignedTransactions(txsWithKeys, signer)
|
||||
}
|
||||
|
||||
func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
||||
if !chainConfig.IsLondon(big.NewInt(int64(env.Number))) {
|
||||
return nil
|
||||
}
|
||||
// Sanity check, to not `panic` in state_transition
|
||||
if env.BaseFee != nil {
|
||||
// Already set, base fee has precedent over parent base fee.
|
||||
return nil
|
||||
}
|
||||
if env.ParentBaseFee == nil || env.Number == 0 {
|
||||
return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
|
||||
}
|
||||
env.BaseFee = eip1559.CalcBaseFee(chainConfig, &types.Header{
|
||||
Number: new(big.Int).SetUint64(env.Number - 1),
|
||||
BaseFee: env.ParentBaseFee,
|
||||
GasUsed: env.ParentGasUsed,
|
||||
GasLimit: env.ParentGasLimit,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyShanghaiChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
||||
if !chainConfig.IsShanghai(big.NewInt(int64(env.Number)), env.Timestamp) {
|
||||
return nil
|
||||
}
|
||||
if env.Withdrawals == nil {
|
||||
return NewError(ErrorConfig, errors.New("Shanghai config but missing 'withdrawals' in env section"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyMergeChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
||||
isMerged := chainConfig.TerminalTotalDifficulty != nil && chainConfig.TerminalTotalDifficulty.BitLen() == 0
|
||||
if !isMerged {
|
||||
// pre-merge: If difficulty was not provided by caller, we need to calculate it.
|
||||
if env.Difficulty != nil {
|
||||
// already set
|
||||
return nil
|
||||
}
|
||||
switch {
|
||||
case env.ParentDifficulty == nil:
|
||||
return NewError(ErrorConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty"))
|
||||
case env.Number == 0:
|
||||
return NewError(ErrorConfig, errors.New("currentDifficulty needs to be provided for block number 0"))
|
||||
case env.Timestamp <= env.ParentTimestamp:
|
||||
return NewError(ErrorConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)",
|
||||
env.Timestamp, env.ParentTimestamp))
|
||||
}
|
||||
env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp,
|
||||
env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash)
|
||||
return nil
|
||||
}
|
||||
// post-merge:
|
||||
// - random must be supplied
|
||||
// - difficulty must be zero
|
||||
switch {
|
||||
case env.Random == nil:
|
||||
return NewError(ErrorConfig, errors.New("post-merge requires currentRandom to be defined in env"))
|
||||
case env.Difficulty != nil && env.Difficulty.BitLen() != 0:
|
||||
return NewError(ErrorConfig, errors.New("post-merge difficulty must be zero (or omitted) in env"))
|
||||
}
|
||||
env.Difficulty = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyCancunChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
||||
if !chainConfig.IsCancun(big.NewInt(int64(env.Number)), env.Timestamp) {
|
||||
env.ParentBeaconBlockRoot = nil // un-set it if it has been set too early
|
||||
return nil
|
||||
}
|
||||
// Post-cancun
|
||||
// We require EIP-4788 beacon root to be set in the env
|
||||
if env.ParentBeaconBlockRoot == nil {
|
||||
return NewError(ErrorConfig, errors.New("post-cancun env requires parentBeaconBlockRoot to be set"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Alloc map[common.Address]core.GenesisAccount
|
||||
|
||||
func (g Alloc) OnRoot(common.Hash) {}
|
||||
|
|
|
|||
|
|
@ -107,6 +107,13 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
|
|||
// Run the test and aggregate the result
|
||||
result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true}
|
||||
test.Run(st, cfg, false, rawdb.HashScheme, func(err error, snaps *snapshot.Tree, state *state.StateDB) {
|
||||
if state != nil {
|
||||
root := state.IntermediateRoot(false)
|
||||
result.Root = &root
|
||||
if jsonOut {
|
||||
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// Test failed, mark as so and dump any state to aid debugging
|
||||
result.Pass, result.Error = false, err.Error()
|
||||
|
|
@ -114,12 +121,6 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
|
|||
dump := state.RawDump(nil)
|
||||
result.State = &dump
|
||||
}
|
||||
} else {
|
||||
root := state.IntermediateRoot(false)
|
||||
result.Root = &root
|
||||
if jsonOut {
|
||||
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
|
||||
}
|
||||
}
|
||||
})
|
||||
results = append(results, *result)
|
||||
|
|
|
|||
|
|
@ -267,6 +267,14 @@ func TestT8n(t *testing.T) {
|
|||
output: t8nOutput{alloc: true, result: true},
|
||||
expOut: "exp.json",
|
||||
},
|
||||
{ // More cancun tests
|
||||
base: "./testdata/29",
|
||||
input: t8nInput{
|
||||
"alloc.json", "txs.json", "env.json", "Cancun", "",
|
||||
},
|
||||
output: t8nOutput{alloc: true, result: true},
|
||||
expOut: "exp.json",
|
||||
},
|
||||
} {
|
||||
args := []string{"t8n"}
|
||||
args = append(args, tc.output.get()...)
|
||||
|
|
|
|||
3
cmd/evm/testdata/28/env.json
vendored
3
cmd/evm/testdata/28/env.json
vendored
|
|
@ -18,5 +18,6 @@
|
|||
"parentBlobGasUsed" : "0x00",
|
||||
"blockHashes" : {
|
||||
"0" : "0x3a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6"
|
||||
}
|
||||
},
|
||||
"parentBeaconBlockRoot": "0x0000beac00beac00beac00beac00beac00beac00beac00beac00beac00beac00"
|
||||
}
|
||||
16
cmd/evm/testdata/29/alloc.json
vendored
Normal file
16
cmd/evm/testdata/29/alloc.json
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||
"balance" : "0x016345785d8a0000",
|
||||
"code" : "0x",
|
||||
"nonce" : "0x00",
|
||||
"storage" : {
|
||||
}
|
||||
},
|
||||
"0xbEac00dDB15f3B6d645C48263dC93862413A222D" : {
|
||||
"balance" : "0x1",
|
||||
"code" : "0x3373fffffffffffffffffffffffffffffffffffffffe14604457602036146024575f5ffd5b620180005f350680545f35146037575f5ffd5b6201800001545f5260205ff35b6201800042064281555f359062018000015500",
|
||||
"nonce" : "0x00",
|
||||
"storage" : {
|
||||
}
|
||||
}
|
||||
}
|
||||
20
cmd/evm/testdata/29/env.json
vendored
Normal file
20
cmd/evm/testdata/29/env.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||
"currentNumber" : "0x01",
|
||||
"currentTimestamp" : "0x079e",
|
||||
"currentGasLimit" : "0x7fffffffffffffff",
|
||||
"previousHash" : "0x3a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6",
|
||||
"currentBlobGasUsed" : "0x00",
|
||||
"parentTimestamp" : "0x03b6",
|
||||
"parentDifficulty" : "0x00",
|
||||
"parentUncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"currentRandom" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"withdrawals" : [
|
||||
],
|
||||
"parentBaseFee" : "0x0a",
|
||||
"parentGasUsed" : "0x00",
|
||||
"parentGasLimit" : "0x7fffffffffffffff",
|
||||
"parentExcessBlobGas" : "0x00",
|
||||
"parentBlobGasUsed" : "0x00",
|
||||
"parentBeaconBlockRoot": "0x0000beac00beac00beac00beac00beac00beac00beac00beac00beac00beac00"
|
||||
}
|
||||
45
cmd/evm/testdata/29/exp.json
vendored
Normal file
45
cmd/evm/testdata/29/exp.json
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"alloc": {
|
||||
"0xbeac00ddb15f3b6d645c48263dc93862413a222d": {
|
||||
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604457602036146024575f5ffd5b620180005f350680545f35146037575f5ffd5b6201800001545f5260205ff35b6201800042064281555f359062018000015500",
|
||||
"storage": {
|
||||
"0x000000000000000000000000000000000000000000000000000000000000079e": "0x000000000000000000000000000000000000000000000000000000000000079e",
|
||||
"0x000000000000000000000000000000000000000000000000000000000001879e": "0x0000beac00beac00beac00beac00beac00beac00beac00beac00beac00beac00"
|
||||
},
|
||||
"balance": "0x1"
|
||||
},
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||
"balance": "0x16345785d871db8",
|
||||
"nonce": "0x1"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"stateRoot": "0x2db9f6bc233e8fd0af2d8023404493a19b37d9d69ace71f4e73158851fced574",
|
||||
"txRoot": "0x248074fabe112f7d93917f292b64932394f835bb98da91f21501574d58ec92ab",
|
||||
"receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa",
|
||||
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receipts": [
|
||||
{
|
||||
"type": "0x2",
|
||||
"root": "0x",
|
||||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"transactionHash": "0x84f70aba406a55628a0620f26d260f90aeb6ccc55fed6ec2ac13dd4f727032ed",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x5208",
|
||||
"effectiveGasPrice": null,
|
||||
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"transactionIndex": "0x0"
|
||||
}
|
||||
],
|
||||
"currentDifficulty": null,
|
||||
"gasUsed": "0x5208",
|
||||
"currentBaseFee": "0x9",
|
||||
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"currentExcessBlobGas": "0x0",
|
||||
"currentBlobGasUsed": "0x0"
|
||||
}
|
||||
}
|
||||
29
cmd/evm/testdata/29/readme.md
vendored
Normal file
29
cmd/evm/testdata/29/readme.md
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
## EIP 4788
|
||||
|
||||
This test contains testcases for EIP-4788. The 4788-contract is
|
||||
located at address `0xbeac00ddb15f3b6d645c48263dc93862413a222d`, and this test executes a simple transaction. It also
|
||||
implicitly invokes the system tx, which sets calls the contract and sets the
|
||||
storage values
|
||||
```
|
||||
$ dir=./testdata/29/ && go run . t8n --state.fork=Cancun --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout
|
||||
INFO [08-15|20:07:56.335] Trie dumping started root=ecde45..2af8a7
|
||||
INFO [08-15|20:07:56.335] Trie dumping complete accounts=2 elapsed="225.848µs"
|
||||
INFO [08-15|20:07:56.335] Wrote file file=result.json
|
||||
{
|
||||
"alloc": {
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||
"balance": "0x16345785d871db8",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"0xbeac00541d49391ed88abf392bfc1f4dea8c4143": {
|
||||
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604457602036146024575f5ffd5b620180005f350680545f35146037575f5ffd5b6201800001545f5260205ff35b6201800042064281555f359062018000015500",
|
||||
"storage": {
|
||||
"0x000000000000000000000000000000000000000000000000000000000000079e": "0x000000000000000000000000000000000000000000000000000000000000079e",
|
||||
"0x000000000000000000000000000000000000000000000000000000000001879e": "0x0000beac00beac00beac00beac00beac00beac00beac00beac00beac00beac00"
|
||||
},
|
||||
"balance": "0x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
19
cmd/evm/testdata/29/txs.json
vendored
Normal file
19
cmd/evm/testdata/29/txs.json
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[
|
||||
{
|
||||
"input" : "0x",
|
||||
"gas" : "0x10000000",
|
||||
"nonce" : "0x0",
|
||||
"to" : "0x1111111111111111111111111111111111111111",
|
||||
"value" : "0x0",
|
||||
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||
"chainId" : "0x1",
|
||||
"type" : "0x2",
|
||||
"v": "0x0",
|
||||
"r": "0x0",
|
||||
"s": "0x0",
|
||||
"maxFeePerGas" : "0xfa0",
|
||||
"maxPriorityFeePerGas" : "0x0",
|
||||
"accessList" : [
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -275,6 +275,9 @@ func prepare(ctx *cli.Context) {
|
|||
case ctx.IsSet(utils.SepoliaFlag.Name):
|
||||
log.Info("Starting Geth on Sepolia testnet...")
|
||||
|
||||
case ctx.IsSet(utils.HoleskyFlag.Name):
|
||||
log.Info("Starting Geth on Holesky testnet...")
|
||||
|
||||
case ctx.IsSet(utils.DeveloperFlag.Name):
|
||||
log.Info("Starting Geth in ephemeral dev mode...")
|
||||
log.Warn(`You are running Geth in --dev mode. Please note the following:
|
||||
|
|
@ -299,7 +302,8 @@ func prepare(ctx *cli.Context) {
|
|||
// If we're a full node on mainnet without --cache specified, bump default cache allowance
|
||||
if ctx.String(utils.SyncModeFlag.Name) != "light" && !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
|
||||
// Make sure we're not on any supported preconfigured testnet either
|
||||
if !ctx.IsSet(utils.SepoliaFlag.Name) &&
|
||||
if !ctx.IsSet(utils.HoleskyFlag.Name) &&
|
||||
!ctx.IsSet(utils.SepoliaFlag.Name) &&
|
||||
!ctx.IsSet(utils.GoerliFlag.Name) &&
|
||||
!ctx.IsSet(utils.DeveloperFlag.Name) {
|
||||
// Nope, we're really on mainnet. Bump that cache up!
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ var (
|
|||
}
|
||||
NetworkIdFlag = &cli.Uint64Flag{
|
||||
Name: "networkid",
|
||||
Usage: "Explicitly set network id (integer)(For testnets: use --goerli, --sepolia instead)",
|
||||
Usage: "Explicitly set network id (integer)(For testnets: use --goerli, --sepolia, --holesky instead)",
|
||||
Value: ethconfig.Defaults.NetworkId,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
|
|
@ -155,7 +155,11 @@ var (
|
|||
Usage: "Sepolia network: pre-configured proof-of-work test network",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
|
||||
HoleskyFlag = &cli.BoolFlag{
|
||||
Name: "holesky",
|
||||
Usage: "Holesky network: pre-configured proof-of-stake test network",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
// Dev mode
|
||||
DeveloperFlag = &cli.BoolFlag{
|
||||
Name: "dev",
|
||||
|
|
@ -952,6 +956,7 @@ var (
|
|||
TestnetFlags = []cli.Flag{
|
||||
GoerliFlag,
|
||||
SepoliaFlag,
|
||||
HoleskyFlag,
|
||||
}
|
||||
// NetworkFlags is the flag group of all built-in supported networks.
|
||||
NetworkFlags = append([]cli.Flag{MainnetFlag}, TestnetFlags...)
|
||||
|
|
@ -982,6 +987,9 @@ func MakeDataDir(ctx *cli.Context) string {
|
|||
if ctx.Bool(SepoliaFlag.Name) {
|
||||
return filepath.Join(path, "sepolia")
|
||||
}
|
||||
if ctx.Bool(HoleskyFlag.Name) {
|
||||
return filepath.Join(path, "holesky")
|
||||
}
|
||||
return path
|
||||
}
|
||||
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
|
||||
|
|
@ -1028,6 +1036,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
|||
switch {
|
||||
case ctx.IsSet(BootnodesFlag.Name):
|
||||
urls = SplitAndTrim(ctx.String(BootnodesFlag.Name))
|
||||
case ctx.Bool(HoleskyFlag.Name):
|
||||
urls = params.HoleskyBootnodes
|
||||
case ctx.Bool(SepoliaFlag.Name):
|
||||
urls = params.SepoliaBootnodes
|
||||
case ctx.Bool(GoerliFlag.Name):
|
||||
|
|
@ -1480,6 +1490,8 @@ func SetDataDir(ctx *cli.Context, cfg *node.Config) {
|
|||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
|
||||
case ctx.Bool(SepoliaFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia")
|
||||
case ctx.Bool(HoleskyFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "holesky")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1636,7 +1648,7 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
|
|||
// SetEthConfig applies eth-related command line flags to the config.
|
||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
// Avoid conflicting network flags
|
||||
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag)
|
||||
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag, HoleskyFlag)
|
||||
CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light")
|
||||
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||
|
||||
|
|
@ -1789,6 +1801,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
}
|
||||
cfg.Genesis = core.DefaultGenesisBlock()
|
||||
SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash)
|
||||
case ctx.Bool(HoleskyFlag.Name):
|
||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 17000
|
||||
}
|
||||
cfg.Genesis = core.DefaultHoleskyGenesisBlock()
|
||||
SetDNSDiscoveryDefaults(cfg, params.HoleskyGenesisHash)
|
||||
case ctx.Bool(SepoliaFlag.Name):
|
||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 11155111
|
||||
|
|
@ -2073,10 +2091,10 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
|
|||
// tryMakeReadOnlyDatabase try to open the chain database in read-only mode,
|
||||
// or fallback to write mode if the database is not initialized.
|
||||
func tryMakeReadOnlyDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||
// If datadir doesn't exist we need to open db in write-mode
|
||||
// so database engine can create files.
|
||||
// If the database doesn't exist we need to open it in write-mode to allow
|
||||
// the engine to create files.
|
||||
readonly := true
|
||||
if !common.FileExist(stack.ResolvePath("chaindata")) {
|
||||
if rawdb.PreexistingDatabase(stack.ResolvePath("chaindata")) == "" {
|
||||
readonly = false
|
||||
}
|
||||
return MakeChainDatabase(ctx, stack, readonly)
|
||||
|
|
@ -2121,6 +2139,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
|||
switch {
|
||||
case ctx.Bool(MainnetFlag.Name):
|
||||
genesis = core.DefaultGenesisBlock()
|
||||
case ctx.Bool(HoleskyFlag.Name):
|
||||
genesis = core.DefaultHoleskyGenesisBlock()
|
||||
case ctx.Bool(SepoliaFlag.Name):
|
||||
genesis = core.DefaultSepoliaGenesisBlock()
|
||||
case ctx.Bool(GoerliFlag.Name):
|
||||
|
|
|
|||
|
|
@ -269,15 +269,21 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
|
|||
if !shanghai && header.WithdrawalsHash != nil {
|
||||
return fmt.Errorf("invalid withdrawalsHash: have %x, expected nil", header.WithdrawalsHash)
|
||||
}
|
||||
// Verify the existence / non-existence of excessBlobGas
|
||||
// Verify the existence / non-existence of cancun-specific header fields
|
||||
cancun := chain.Config().IsCancun(header.Number, header.Time)
|
||||
if !cancun && header.ExcessBlobGas != nil {
|
||||
if !cancun {
|
||||
switch {
|
||||
case header.ExcessBlobGas != nil:
|
||||
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
|
||||
}
|
||||
if !cancun && header.BlobGasUsed != nil {
|
||||
case header.BlobGasUsed != nil:
|
||||
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
|
||||
case header.ParentBeaconRoot != nil:
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
}
|
||||
} else {
|
||||
if header.ParentBeaconRoot == nil {
|
||||
return errors.New("header is missing beaconRoot")
|
||||
}
|
||||
if cancun {
|
||||
if err := eip4844.VerifyEIP4844Header(parent, header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ func VerifyEIP4844Header(parent, header *types.Header) error {
|
|||
return errors.New("header is missing blobGasUsed")
|
||||
}
|
||||
// Verify that the blob gas used remains within reasonable limits.
|
||||
if *header.BlobGasUsed > params.BlobTxMaxBlobGasPerBlock {
|
||||
return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, params.BlobTxMaxBlobGasPerBlock)
|
||||
if *header.BlobGasUsed > params.MaxBlobGasPerBlock {
|
||||
return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, params.MaxBlobGasPerBlock)
|
||||
}
|
||||
if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 {
|
||||
return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, params.BlobTxBlobGasPerBlob)
|
||||
|
|
|
|||
|
|
@ -1021,7 +1021,7 @@ func (bc *BlockChain) Stop() {
|
|||
for !bc.triegc.Empty() {
|
||||
triedb.Dereference(bc.triegc.PopItem())
|
||||
}
|
||||
if size, _ := triedb.Size(); size != 0 {
|
||||
if _, nodes, _ := triedb.Size(); nodes != 0 { // all memory is contained within the nodes return for hashdb
|
||||
log.Error("Dangling trie nodes after full cleanup")
|
||||
}
|
||||
}
|
||||
|
|
@ -1429,7 +1429,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
}
|
||||
// If we exceeded our memory allowance, flush matured singleton nodes to disk
|
||||
var (
|
||||
nodes, imgs = bc.triedb.Size()
|
||||
_, nodes, imgs = bc.triedb.Size() // all memory is contained within the nodes return for hashdb
|
||||
limit = common.StorageSize(bc.cacheConfig.TrieDirtyLimit) * 1024 * 1024
|
||||
)
|
||||
if nodes > limit || imgs > 4*1024*1024 {
|
||||
|
|
@ -1866,8 +1866,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
|||
stats.processed++
|
||||
stats.usedGas += usedGas
|
||||
|
||||
dirty, _ := bc.triedb.Size()
|
||||
stats.report(chain, it.index, dirty, setHead)
|
||||
var snapDiffItems, snapBufItems common.StorageSize
|
||||
if bc.snaps != nil {
|
||||
snapDiffItems, snapBufItems = bc.snaps.Size()
|
||||
}
|
||||
trieDiffNodes, trieBufNodes, _ := bc.triedb.Size()
|
||||
stats.report(chain, it.index, snapDiffItems, snapBufItems, trieDiffNodes, trieBufNodes, setHead)
|
||||
|
||||
if !setHead {
|
||||
// After merge we expect few side chains. Simply count
|
||||
|
|
@ -2421,6 +2425,12 @@ func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
|
|||
func (bc *BlockChain) indexBlocks(tail *uint64, head uint64, done chan struct{}) {
|
||||
defer func() { close(done) }()
|
||||
|
||||
// If head is 0, it means the chain is just initialized and no blocks are inserted,
|
||||
// so don't need to indexing anything.
|
||||
if head == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// The tail flag is not existent, it means the node is just initialized
|
||||
// and all blocks(may from ancient store) are not indexed yet.
|
||||
if tail == nil {
|
||||
|
|
@ -2480,6 +2490,14 @@ func (bc *BlockChain) maintainTxIndex() {
|
|||
defer sub.Unsubscribe()
|
||||
log.Info("Initialized transaction indexer", "limit", bc.TxLookupLimit())
|
||||
|
||||
// Launch the initial processing if chain is not empty. This step is
|
||||
// useful in these scenarios that chain has no progress and indexer
|
||||
// is never triggered.
|
||||
if head := rawdb.ReadHeadBlock(bc.db); head != nil {
|
||||
done = make(chan struct{})
|
||||
go bc.indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.NumberU64(), done)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case head := <-headCh:
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ const statsReportLimit = 8 * time.Second
|
|||
|
||||
// report prints statistics if some number of blocks have been processed
|
||||
// or more than a few seconds have passed since the last message.
|
||||
func (st *insertStats) report(chain []*types.Block, index int, dirty common.StorageSize, setHead bool) {
|
||||
func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, snapBufItems, trieDiffNodes, triebufNodes common.StorageSize, setHead bool) {
|
||||
// Fetch the timings for the batch
|
||||
var (
|
||||
now = mclock.Now()
|
||||
|
|
@ -63,7 +63,16 @@ func (st *insertStats) report(chain []*types.Block, index int, dirty common.Stor
|
|||
if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute {
|
||||
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
|
||||
}
|
||||
context = append(context, []interface{}{"dirty", dirty}...)
|
||||
if snapDiffItems != 0 || snapBufItems != 0 { // snapshots enabled
|
||||
context = append(context, []interface{}{"snapdiffs", snapDiffItems}...)
|
||||
if snapBufItems != 0 { // future snapshot refactor
|
||||
context = append(context, []interface{}{"snapdirty", snapBufItems}...)
|
||||
}
|
||||
}
|
||||
if trieDiffNodes != 0 { // pathdb
|
||||
context = append(context, []interface{}{"triediffs", trieDiffNodes}...)
|
||||
}
|
||||
context = append(context, []interface{}{"triedirty", triebufNodes}...)
|
||||
|
||||
if st.queued > 0 {
|
||||
context = append(context, []interface{}{"queued", st.queued}...)
|
||||
|
|
|
|||
|
|
@ -1767,6 +1767,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
|||
db, err := rawdb.Open(rawdb.OpenOptions{
|
||||
Directory: datadir,
|
||||
AncientsDirectory: ancient,
|
||||
Ephemeral: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create persistent database: %v", err)
|
||||
|
|
@ -1847,6 +1848,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
|||
db, err = rawdb.Open(rawdb.OpenOptions{
|
||||
Directory: datadir,
|
||||
AncientsDirectory: ancient,
|
||||
Ephemeral: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to reopen persistent database: %v", err)
|
||||
|
|
@ -1968,6 +1970,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
|||
db, err = rawdb.Open(rawdb.OpenOptions{
|
||||
Directory: datadir,
|
||||
AncientsDirectory: ancient,
|
||||
Ephemeral: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to reopen persistent database: %v", err)
|
||||
|
|
|
|||
|
|
@ -1971,6 +1971,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
|||
db, err := rawdb.Open(rawdb.OpenOptions{
|
||||
Directory: datadir,
|
||||
AncientsDirectory: ancient,
|
||||
Ephemeral: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create persistent database: %v", err)
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
|
|||
db, err := rawdb.Open(rawdb.OpenOptions{
|
||||
Directory: datadir,
|
||||
AncientsDirectory: ancient,
|
||||
Ephemeral: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create persistent database: %v", err)
|
||||
|
|
@ -258,6 +259,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
|||
newdb, err := rawdb.Open(rawdb.OpenOptions{
|
||||
Directory: snaptest.datadir,
|
||||
AncientsDirectory: snaptest.ancient,
|
||||
Ephemeral: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to reopen persistent database: %v", err)
|
||||
|
|
|
|||
|
|
@ -1844,7 +1844,7 @@ func TestTrieForkGC(t *testing.T) {
|
|||
chain.TrieDB().Dereference(blocks[len(blocks)-1-i].Root())
|
||||
chain.TrieDB().Dereference(forks[len(blocks)-1-i].Root())
|
||||
}
|
||||
if nodes, _ := chain.TrieDB().Size(); nodes > 0 {
|
||||
if _, nodes, _ := chain.TrieDB().Size(); nodes > 0 { // all memory is returned in the nodes return for hashdb
|
||||
t.Fatalf("stale tries still alive after garbase collection")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -87,6 +88,11 @@ func (b *BlockGen) SetPoS() {
|
|||
b.header.Difficulty = new(big.Int)
|
||||
}
|
||||
|
||||
// SetBlobGas sets the data gas used by the blob in the generated block.
|
||||
func (b *BlockGen) SetBlobGas(blobGasUsed uint64) {
|
||||
b.header.BlobGasUsed = &blobGasUsed
|
||||
}
|
||||
|
||||
// addTx adds a transaction to the generated block. If no coinbase has
|
||||
// been set, the block's coinbase is set to the zero address.
|
||||
//
|
||||
|
|
@ -393,6 +399,20 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
|
|||
header.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit)
|
||||
}
|
||||
}
|
||||
if chain.Config().IsCancun(header.Number, header.Time) {
|
||||
var (
|
||||
parentExcessBlobGas uint64
|
||||
parentBlobGasUsed uint64
|
||||
)
|
||||
if parent.ExcessBlobGas() != nil {
|
||||
parentExcessBlobGas = *parent.ExcessBlobGas()
|
||||
parentBlobGasUsed = *parent.BlobGasUsed()
|
||||
}
|
||||
excessBlobGas := eip4844.CalcExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed)
|
||||
header.ExcessBlobGas = &excessBlobGas
|
||||
header.BlobGasUsed = new(uint64)
|
||||
header.ParentBeaconRoot = new(common.Hash)
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@ type ID struct {
|
|||
type Filter func(id ID) error
|
||||
|
||||
// NewID calculates the Ethereum fork ID from the chain config, genesis hash, head and time.
|
||||
func NewID(config *params.ChainConfig, genesis common.Hash, head, time uint64) ID {
|
||||
func NewID(config *params.ChainConfig, genesis *types.Block, head, time uint64) ID {
|
||||
// Calculate the starting checksum from the genesis hash
|
||||
hash := crc32.ChecksumIEEE(genesis[:])
|
||||
hash := crc32.ChecksumIEEE(genesis.Hash().Bytes())
|
||||
|
||||
// Calculate the current fork checksum and the next fork block
|
||||
forksByBlock, forksByTime := gatherForks(config)
|
||||
|
|
@ -88,6 +88,10 @@ func NewID(config *params.ChainConfig, genesis common.Hash, head, time uint64) I
|
|||
return ID{Hash: checksumToBytes(hash), Next: fork}
|
||||
}
|
||||
for _, fork := range forksByTime {
|
||||
if fork <= genesis.Time() {
|
||||
// Fork active in genesis, skip in forkid calculation
|
||||
continue
|
||||
}
|
||||
if fork <= time {
|
||||
// Fork already passed, checksum the previous hash and fork timestamp
|
||||
hash = checksumUpdate(hash, fork)
|
||||
|
|
@ -104,7 +108,7 @@ func NewIDWithChain(chain Blockchain) ID {
|
|||
|
||||
return NewID(
|
||||
chain.Config(),
|
||||
chain.Genesis().Hash(),
|
||||
chain.Genesis(),
|
||||
head.Number.Uint64(),
|
||||
head.Time,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,10 +18,14 @@ package forkid
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"hash/crc32"
|
||||
"math"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
|
@ -36,13 +40,13 @@ func TestCreation(t *testing.T) {
|
|||
}
|
||||
tests := []struct {
|
||||
config *params.ChainConfig
|
||||
genesis common.Hash
|
||||
genesis *types.Block
|
||||
cases []testcase
|
||||
}{
|
||||
// Mainnet test cases
|
||||
{
|
||||
params.MainnetChainConfig,
|
||||
params.MainnetGenesisHash,
|
||||
core.DefaultGenesisBlock().ToBlock(),
|
||||
[]testcase{
|
||||
{0, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced
|
||||
{1149999, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block
|
||||
|
|
@ -77,7 +81,7 @@ func TestCreation(t *testing.T) {
|
|||
// Goerli test cases
|
||||
{
|
||||
params.GoerliChainConfig,
|
||||
params.GoerliGenesisHash,
|
||||
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
|
||||
|
|
@ -94,7 +98,7 @@ func TestCreation(t *testing.T) {
|
|||
// Sepolia test cases
|
||||
{
|
||||
params.SepoliaChainConfig,
|
||||
params.SepoliaGenesisHash,
|
||||
core.DefaultSepoliaGenesisBlock().ToBlock(),
|
||||
[]testcase{
|
||||
{0, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin and first London block
|
||||
{1735370, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Last London block
|
||||
|
|
@ -382,3 +386,55 @@ func TestEncoding(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that time-based forks which are active at genesis are not included in
|
||||
// forkid hash.
|
||||
func TestTimeBasedForkInGenesis(t *testing.T) {
|
||||
var (
|
||||
time = uint64(1690475657)
|
||||
genesis = types.NewBlockWithHeader(&types.Header{Time: time})
|
||||
forkidHash = checksumToBytes(crc32.ChecksumIEEE(genesis.Hash().Bytes()))
|
||||
config = func(shanghai, cancun uint64) *params.ChainConfig {
|
||||
return ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(1337),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
MuirGlacierBlock: big.NewInt(0),
|
||||
BerlinBlock: big.NewInt(0),
|
||||
LondonBlock: big.NewInt(0),
|
||||
TerminalTotalDifficulty: big.NewInt(0),
|
||||
TerminalTotalDifficultyPassed: true,
|
||||
MergeNetsplitBlock: big.NewInt(0),
|
||||
ShanghaiTime: &shanghai,
|
||||
CancunTime: &cancun,
|
||||
Ethash: new(params.EthashConfig),
|
||||
}
|
||||
}
|
||||
)
|
||||
tests := []struct {
|
||||
config *params.ChainConfig
|
||||
want ID
|
||||
}{
|
||||
// Shanghai active before genesis, skip
|
||||
{config(time-1, time+1), ID{Hash: forkidHash, Next: time + 1}},
|
||||
|
||||
// Shanghai active at genesis, skip
|
||||
{config(time, time+1), ID{Hash: forkidHash, Next: time + 1}},
|
||||
|
||||
// Shanghai not active, skip
|
||||
{config(time+1, time+2), ID{Hash: forkidHash, Next: time + 1}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if have := NewID(tt.config, genesis, 0, time); have != tt.want {
|
||||
t.Fatalf("incorrect forkid hash: have %x, want %x", have, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,9 @@ func (ga *GenesisAlloc) deriveHash() (common.Hash, error) {
|
|||
return common.Hash{}, err
|
||||
}
|
||||
for addr, account := range *ga {
|
||||
if account.Balance != nil {
|
||||
statedb.AddBalance(addr, account.Balance)
|
||||
}
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
for key, value := range account.Storage {
|
||||
|
|
@ -149,7 +151,9 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
|
|||
return err
|
||||
}
|
||||
for addr, account := range *ga {
|
||||
if account.Balance != nil {
|
||||
statedb.AddBalance(addr, account.Balance)
|
||||
}
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
for key, value := range account.Storage {
|
||||
|
|
@ -479,6 +483,11 @@ func (g *Genesis) ToBlock() *types.Block {
|
|||
withdrawals = make([]*types.Withdrawal, 0)
|
||||
}
|
||||
if conf.IsCancun(num, g.Timestamp) {
|
||||
// EIP-4788: The parentBeaconBlockRoot of the genesis block is always
|
||||
// the zero hash. This is because the genesis block does not have a parent
|
||||
// by definition.
|
||||
head.ParentBeaconRoot = new(common.Hash)
|
||||
// EIP-4844 fields
|
||||
head.ExcessBlobGas = g.ExcessBlobGas
|
||||
head.BlobGasUsed = g.BlobGasUsed
|
||||
if head.ExcessBlobGas == nil {
|
||||
|
|
@ -573,6 +582,19 @@ func DefaultSepoliaGenesisBlock() *Genesis {
|
|||
}
|
||||
}
|
||||
|
||||
// DefaultHoleskyGenesisBlock returns the Holesky network genesis block.
|
||||
func DefaultHoleskyGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
Config: params.HoleskyChainConfig,
|
||||
Nonce: 0x1234,
|
||||
ExtraData: hexutil.MustDecode("0x686f77206d7563682069732074686520666973683f"),
|
||||
GasLimit: 0x17d7840,
|
||||
Difficulty: big.NewInt(0x01),
|
||||
Timestamp: 1694786100,
|
||||
Alloc: decodePrealloc(holeskyAllocData),
|
||||
}
|
||||
}
|
||||
|
||||
// DeveloperGenesisBlock returns the 'geth --dev' genesis block.
|
||||
func DeveloperGenesisBlock(gasLimit uint64, faucet common.Address) *Genesis {
|
||||
// Override the default period to the user requested one
|
||||
|
|
@ -600,13 +622,34 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet common.Address) *Genesis {
|
|||
}
|
||||
|
||||
func decodePrealloc(data string) GenesisAlloc {
|
||||
var p []struct{ Addr, Balance *big.Int }
|
||||
var p []struct {
|
||||
Addr *big.Int
|
||||
Balance *big.Int
|
||||
Misc *struct {
|
||||
Nonce uint64
|
||||
Code []byte
|
||||
Slots []struct {
|
||||
Key common.Hash
|
||||
Val common.Hash
|
||||
}
|
||||
} `rlp:"optional"`
|
||||
}
|
||||
if err := rlp.NewStream(strings.NewReader(data), 0).Decode(&p); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ga := make(GenesisAlloc, len(p))
|
||||
for _, account := range p {
|
||||
ga[common.BigToAddress(account.Addr)] = GenesisAccount{Balance: account.Balance}
|
||||
acc := GenesisAccount{Balance: account.Balance}
|
||||
if account.Misc != nil {
|
||||
acc.Nonce = account.Misc.Nonce
|
||||
acc.Code = account.Misc.Code
|
||||
|
||||
acc.Storage = make(map[common.Hash]common.Hash)
|
||||
for _, slot := range account.Misc.Slots {
|
||||
acc.Storage[slot.Key] = slot.Val
|
||||
}
|
||||
}
|
||||
ga[common.BigToAddress(account.Addr)] = acc
|
||||
}
|
||||
return ga
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -32,24 +32,51 @@ import (
|
|||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type allocItem struct{ Addr, Balance *big.Int }
|
||||
type allocItem struct {
|
||||
Addr *big.Int
|
||||
Balance *big.Int
|
||||
Misc *allocItemMisc `rlp:"optional"`
|
||||
}
|
||||
|
||||
type allocItemMisc struct {
|
||||
Nonce uint64
|
||||
Code []byte
|
||||
Slots []allocItemStorageItem
|
||||
}
|
||||
|
||||
type allocItemStorageItem struct {
|
||||
Key common.Hash
|
||||
Val common.Hash
|
||||
}
|
||||
|
||||
func makelist(g *core.Genesis) []allocItem {
|
||||
items := make([]allocItem, 0, len(g.Alloc))
|
||||
for addr, account := range g.Alloc {
|
||||
var misc *allocItemMisc
|
||||
if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 {
|
||||
panic(fmt.Sprintf("can't encode account %x", addr))
|
||||
misc = &allocItemMisc{
|
||||
Nonce: account.Nonce,
|
||||
Code: account.Code,
|
||||
Slots: make([]allocItemStorageItem, 0, len(account.Storage)),
|
||||
}
|
||||
for key, val := range account.Storage {
|
||||
misc.Slots = append(misc.Slots, allocItemStorageItem{key, val})
|
||||
}
|
||||
slices.SortFunc(misc.Slots, func(a, b allocItemStorageItem) int {
|
||||
return a.Key.Cmp(b.Key)
|
||||
})
|
||||
}
|
||||
bigAddr := new(big.Int).SetBytes(addr.Bytes())
|
||||
items = append(items, allocItem{bigAddr, account.Balance})
|
||||
items = append(items, allocItem{bigAddr, account.Balance, misc})
|
||||
}
|
||||
slices.SortFunc(items, func(a, b allocItem) bool {
|
||||
return a.Addr.Cmp(b.Addr) < 0
|
||||
slices.SortFunc(items, func(a, b allocItem) int {
|
||||
return a.Addr.Cmp(b.Addr)
|
||||
})
|
||||
return items
|
||||
}
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header
|
|||
return nil
|
||||
}
|
||||
header := new(types.Header)
|
||||
if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
|
||||
if err := rlp.DecodeBytes(data, header); err != nil {
|
||||
log.Error("Invalid block header RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -498,7 +498,7 @@ func ReadBody(db ethdb.Reader, hash common.Hash, number uint64) *types.Body {
|
|||
return nil
|
||||
}
|
||||
body := new(types.Body)
|
||||
if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
|
||||
if err := rlp.DecodeBytes(data, body); err != nil {
|
||||
log.Error("Invalid block body RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -544,7 +544,7 @@ func ReadTd(db ethdb.Reader, hash common.Hash, number uint64) *big.Int {
|
|||
return nil
|
||||
}
|
||||
td := new(big.Int)
|
||||
if err := rlp.Decode(bytes.NewReader(data), td); err != nil {
|
||||
if err := rlp.DecodeBytes(data, td); err != nil {
|
||||
log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
package rawdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
|
@ -53,7 +51,7 @@ func ReadSkeletonHeader(db ethdb.KeyValueReader, number uint64) *types.Header {
|
|||
return nil
|
||||
}
|
||||
header := new(types.Header)
|
||||
if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
|
||||
if err := rlp.DecodeBytes(data, header); err != nil {
|
||||
log.Error("Invalid skeleton header RLP", "number", number, "err", err)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -326,10 +326,10 @@ const (
|
|||
dbLeveldb = "leveldb"
|
||||
)
|
||||
|
||||
// hasPreexistingDb checks the given data directory whether a database is already
|
||||
// PreexistingDatabase checks the given data directory whether a database is already
|
||||
// instantiated at that location, and if so, returns the type of database (or the
|
||||
// empty string).
|
||||
func hasPreexistingDb(path string) string {
|
||||
func PreexistingDatabase(path string) string {
|
||||
if _, err := os.Stat(filepath.Join(path, "CURRENT")); err != nil {
|
||||
return "" // No pre-existing db
|
||||
}
|
||||
|
|
@ -352,6 +352,9 @@ type OpenOptions struct {
|
|||
Cache int // the capacity(in megabytes) of the data caching
|
||||
Handles int // number of files to be open simultaneously
|
||||
ReadOnly bool
|
||||
// Ephemeral means that filesystem sync operations should be avoided: data integrity in the face of
|
||||
// a crash is not important. This option should typically be used in tests.
|
||||
Ephemeral bool
|
||||
}
|
||||
|
||||
// openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble.
|
||||
|
|
@ -367,14 +370,14 @@ func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) {
|
|||
}
|
||||
// Retrieve any pre-existing database's type and use that or the requested one
|
||||
// as long as there's no conflict between the two types
|
||||
existingDb := hasPreexistingDb(o.Directory)
|
||||
existingDb := PreexistingDatabase(o.Directory)
|
||||
if len(existingDb) != 0 && len(o.Type) != 0 && o.Type != existingDb {
|
||||
return nil, fmt.Errorf("db.engine choice was %v but found pre-existing %v database in specified data directory", o.Type, existingDb)
|
||||
}
|
||||
if o.Type == dbPebble || existingDb == dbPebble {
|
||||
if PebbleEnabled {
|
||||
log.Info("Using pebble as the backing database")
|
||||
return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
||||
return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
|
||||
} else {
|
||||
return nil, errors.New("db.engine 'pebble' not supported on this platform")
|
||||
}
|
||||
|
|
@ -387,7 +390,7 @@ func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) {
|
|||
// on supported platforms and LevelDB on anything else.
|
||||
if PebbleEnabled {
|
||||
log.Info("Defaulting to pebble as the backing database")
|
||||
return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
||||
return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
|
||||
} else {
|
||||
log.Info("Defaulting to leveldb as the backing database")
|
||||
return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ const PebbleEnabled = true
|
|||
|
||||
// NewPebbleDBDatabase creates a persistent key-value database without a freezer
|
||||
// moving immutable chain segments into cold storage.
|
||||
func NewPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
|
||||
db, err := pebble.New(file, cache, handles, namespace, readonly)
|
||||
func NewPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly, ephemeral bool) (ethdb.Database, error) {
|
||||
db, err := pebble.New(file, cache, handles, namespace, readonly, ephemeral)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,6 @@ const PebbleEnabled = false
|
|||
|
||||
// NewPebbleDBDatabase creates a persistent key-value database without a freezer
|
||||
// moving immutable chain segments into cold storage.
|
||||
func NewPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
|
||||
func NewPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly, ephemeral bool) (ethdb.Database, error) {
|
||||
return nil, errors.New("pebble is not supported on this platform")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,7 +195,11 @@ func accountSnapshotKey(hash common.Hash) []byte {
|
|||
|
||||
// storageSnapshotKey = SnapshotStoragePrefix + account hash + storage hash
|
||||
func storageSnapshotKey(accountHash, storageHash common.Hash) []byte {
|
||||
return append(append(SnapshotStoragePrefix, accountHash.Bytes()...), storageHash.Bytes()...)
|
||||
buf := make([]byte, len(SnapshotStoragePrefix)+common.HashLength+common.HashLength)
|
||||
n := copy(buf, SnapshotStoragePrefix)
|
||||
n += copy(buf[n:], accountHash.Bytes())
|
||||
copy(buf[n:], storageHash.Bytes())
|
||||
return buf
|
||||
}
|
||||
|
||||
// storageSnapshotsKey = SnapshotStoragePrefix + account hash + storage hash
|
||||
|
|
@ -259,7 +263,11 @@ func accountTrieNodeKey(path []byte) []byte {
|
|||
|
||||
// storageTrieNodeKey = trieNodeStoragePrefix + accountHash + nodePath.
|
||||
func storageTrieNodeKey(accountHash common.Hash, path []byte) []byte {
|
||||
return append(append(trieNodeStoragePrefix, accountHash.Bytes()...), path...)
|
||||
buf := make([]byte, len(trieNodeStoragePrefix)+common.HashLength+len(path))
|
||||
n := copy(buf, trieNodeStoragePrefix)
|
||||
n += copy(buf[n:], accountHash.Bytes())
|
||||
copy(buf[n:], path)
|
||||
return buf
|
||||
}
|
||||
|
||||
// IsLegacyTrieNode reports whether a provided database entry is a legacy trie
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ func (it *nodeIterator) step() error {
|
|||
}
|
||||
// Otherwise we've reached an account node, initiate data iteration
|
||||
var account types.StateAccount
|
||||
if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), &account); err != nil {
|
||||
if err := rlp.DecodeBytes(it.stateIt.LeafBlob(), &account); err != nil {
|
||||
return err
|
||||
}
|
||||
// Lookup the preimage of account hash
|
||||
|
|
|
|||
|
|
@ -564,7 +564,7 @@ func diffToDisk(bottom *diffLayer) *diskLayer {
|
|||
// Ensure we don't delete too much data blindly (contract can be
|
||||
// huge). It's ok to flush, the root will go missing in case of a
|
||||
// crash and we'll detect and regenerate the snapshot.
|
||||
if batch.ValueSize() > ethdb.IdealBatchSize {
|
||||
if batch.ValueSize() > 64*1024*1024 {
|
||||
if err := batch.Write(); err != nil {
|
||||
log.Crit("Failed to write storage deletions", "err", err)
|
||||
}
|
||||
|
|
@ -590,7 +590,7 @@ func diffToDisk(bottom *diffLayer) *diskLayer {
|
|||
// Ensure we don't write too much data blindly. It's ok to flush, the
|
||||
// root will go missing in case of a crash and we'll detect and regen
|
||||
// the snapshot.
|
||||
if batch.ValueSize() > ethdb.IdealBatchSize {
|
||||
if batch.ValueSize() > 64*1024*1024 {
|
||||
if err := batch.Write(); err != nil {
|
||||
log.Crit("Failed to write storage deletions", "err", err)
|
||||
}
|
||||
|
|
@ -852,3 +852,21 @@ func (t *Tree) DiskRoot() common.Hash {
|
|||
|
||||
return t.diskRoot()
|
||||
}
|
||||
|
||||
// Size returns the memory usage of the diff layers above the disk layer and the
|
||||
// dirty nodes buffered in the disk layer. Currently, the implementation uses a
|
||||
// special diff layer (the first) as an aggregator simulating a dirty buffer, so
|
||||
// the second return will always be 0. However, this will be made consistent with
|
||||
// the pathdb, which will require a second return.
|
||||
func (t *Tree) Size() (diffs common.StorageSize, buf common.StorageSize) {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
var size common.StorageSize
|
||||
for _, layer := range t.layers {
|
||||
if layer, ok := layer.(*diffLayer); ok {
|
||||
size += common.StorageSize(layer.memory)
|
||||
}
|
||||
}
|
||||
return size, 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,12 +32,17 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"github.com/ethereum/go-ethereum/trie/triestate"
|
||||
)
|
||||
|
||||
const (
|
||||
// storageDeleteLimit denotes the highest permissible memory allocation
|
||||
// employed for contract storage deletion.
|
||||
storageDeleteLimit = 512 * 1024 * 1024
|
||||
)
|
||||
|
||||
type revision struct {
|
||||
id int
|
||||
journalIndex int
|
||||
|
|
@ -722,43 +727,6 @@ func (s *StateDB) CreateAccount(addr common.Address) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
|
||||
so := s.getStateObject(addr)
|
||||
if so == nil {
|
||||
return nil
|
||||
}
|
||||
tr, err := so.getTrie()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trieIt, err := tr.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it := trie.NewIterator(trieIt)
|
||||
|
||||
for it.Next() {
|
||||
key := common.BytesToHash(s.trie.GetKey(it.Key))
|
||||
if value, dirty := so.dirtyStorage[key]; dirty {
|
||||
if !cb(key, value) {
|
||||
return nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if len(it.Value) > 0 {
|
||||
_, content, _, err := rlp.Split(it.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cb(key, common.BytesToHash(content)) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy creates a deep, independent copy of the state.
|
||||
// Snapshots of the copied state cannot be applied to the copy.
|
||||
func (s *StateDB) Copy() *StateDB {
|
||||
|
|
@ -1022,59 +990,130 @@ func (s *StateDB) clearJournalAndRefund() {
|
|||
s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entries
|
||||
}
|
||||
|
||||
// deleteStorage iterates the storage trie belongs to the account and mark all
|
||||
// slots inside as deleted.
|
||||
func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (bool, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||
start := time.Now()
|
||||
// fastDeleteStorage is the function that efficiently deletes the storage trie
|
||||
// of a specific account. It leverages the associated state snapshot for fast
|
||||
// storage iteration and constructs trie node deletion markers by creating
|
||||
// stack trie with iterated slots.
|
||||
func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash) (bool, common.StorageSize, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||
iter, err := s.snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{})
|
||||
if err != nil {
|
||||
return false, 0, nil, nil, err
|
||||
}
|
||||
defer iter.Release()
|
||||
|
||||
var (
|
||||
size common.StorageSize
|
||||
nodes = trienode.NewNodeSet(addrHash)
|
||||
slots = make(map[common.Hash][]byte)
|
||||
)
|
||||
stack := trie.NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, blob []byte) {
|
||||
nodes.AddNode(path, trienode.NewDeleted())
|
||||
size += common.StorageSize(len(path))
|
||||
})
|
||||
for iter.Next() {
|
||||
if size > storageDeleteLimit {
|
||||
return true, size, nil, nil, nil
|
||||
}
|
||||
slot := common.CopyBytes(iter.Slot())
|
||||
if iter.Error() != nil { // error might occur after Slot function
|
||||
return false, 0, nil, nil, err
|
||||
}
|
||||
size += common.StorageSize(common.HashLength + len(slot))
|
||||
slots[iter.Hash()] = slot
|
||||
|
||||
if err := stack.Update(iter.Hash().Bytes(), slot); err != nil {
|
||||
return false, 0, nil, nil, err
|
||||
}
|
||||
}
|
||||
if iter.Error() != nil { // error might occur during iteration
|
||||
return false, 0, nil, nil, err
|
||||
}
|
||||
if stack.Hash() != root {
|
||||
return false, 0, nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash())
|
||||
}
|
||||
return false, size, slots, nodes, nil
|
||||
}
|
||||
|
||||
// slowDeleteStorage serves as a less-efficient alternative to "fastDeleteStorage,"
|
||||
// employed when the associated state snapshot is not available. It iterates the
|
||||
// storage slots along with all internal trie nodes via trie directly.
|
||||
func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (bool, common.StorageSize, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||
tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root)
|
||||
if err != nil {
|
||||
return false, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
|
||||
return false, 0, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
|
||||
}
|
||||
it, err := tr.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return false, nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err)
|
||||
return false, 0, nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err)
|
||||
}
|
||||
var (
|
||||
set = trienode.NewNodeSet(addrHash)
|
||||
size common.StorageSize
|
||||
nodes = trienode.NewNodeSet(addrHash)
|
||||
slots = make(map[common.Hash][]byte)
|
||||
stateSize common.StorageSize
|
||||
nodeSize common.StorageSize
|
||||
)
|
||||
for it.Next(true) {
|
||||
// arbitrary stateSize limit, make it configurable
|
||||
if stateSize+nodeSize > 512*1024*1024 {
|
||||
log.Info("Skip large storage deletion", "address", addr.Hex(), "states", stateSize, "nodes", nodeSize)
|
||||
if metrics.EnabledExpensive {
|
||||
slotDeletionSkip.Inc(1)
|
||||
}
|
||||
return true, nil, nil, nil
|
||||
if size > storageDeleteLimit {
|
||||
return true, size, nil, nil, nil
|
||||
}
|
||||
if it.Leaf() {
|
||||
slots[common.BytesToHash(it.LeafKey())] = common.CopyBytes(it.LeafBlob())
|
||||
stateSize += common.StorageSize(common.HashLength + len(it.LeafBlob()))
|
||||
size += common.StorageSize(common.HashLength + len(it.LeafBlob()))
|
||||
continue
|
||||
}
|
||||
if it.Hash() == (common.Hash{}) {
|
||||
continue
|
||||
}
|
||||
nodeSize += common.StorageSize(len(it.Path()))
|
||||
set.AddNode(it.Path(), trienode.NewDeleted())
|
||||
size += common.StorageSize(len(it.Path()))
|
||||
nodes.AddNode(it.Path(), trienode.NewDeleted())
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
return false, 0, nil, nil, err
|
||||
}
|
||||
return false, size, slots, nodes, nil
|
||||
}
|
||||
|
||||
// deleteStorage is designed to delete the storage trie of a designated account.
|
||||
// It could potentially be terminated if the storage size is excessively large,
|
||||
// potentially leading to an out-of-memory panic. The function will make an attempt
|
||||
// to utilize an efficient strategy if the associated state snapshot is reachable;
|
||||
// otherwise, it will resort to a less-efficient approach.
|
||||
func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (bool, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||
var (
|
||||
start = time.Now()
|
||||
err error
|
||||
aborted bool
|
||||
size common.StorageSize
|
||||
slots map[common.Hash][]byte
|
||||
nodes *trienode.NodeSet
|
||||
)
|
||||
// The fast approach can be failed if the snapshot is not fully
|
||||
// generated, or it's internally corrupted. Fallback to the slow
|
||||
// one just in case.
|
||||
if s.snap != nil {
|
||||
aborted, size, slots, nodes, err = s.fastDeleteStorage(addrHash, root)
|
||||
}
|
||||
if s.snap == nil || err != nil {
|
||||
aborted, size, slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
|
||||
}
|
||||
if err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
if metrics.EnabledExpensive {
|
||||
if int64(len(slots)) > slotDeletionMaxCount.Value() {
|
||||
slotDeletionMaxCount.Update(int64(len(slots)))
|
||||
if aborted {
|
||||
slotDeletionSkip.Inc(1)
|
||||
}
|
||||
if int64(stateSize+nodeSize) > slotDeletionMaxSize.Value() {
|
||||
slotDeletionMaxSize.Update(int64(stateSize + nodeSize))
|
||||
n := int64(len(slots))
|
||||
if n > slotDeletionMaxCount.Value() {
|
||||
slotDeletionMaxCount.Update(n)
|
||||
}
|
||||
if int64(size) > slotDeletionMaxSize.Value() {
|
||||
slotDeletionMaxSize.Update(int64(size))
|
||||
}
|
||||
slotDeletionTimer.UpdateSince(start)
|
||||
slotDeletionCount.Mark(int64(len(slots)))
|
||||
slotDeletionSize.Mark(int64(stateSize + nodeSize))
|
||||
slotDeletionCount.Mark(n)
|
||||
slotDeletionSize.Mark(int64(size))
|
||||
}
|
||||
return false, slots, set, nil
|
||||
return aborted, slots, nodes, nil
|
||||
}
|
||||
|
||||
// handleDestruction processes all destruction markers and deletes the account
|
||||
|
|
@ -1102,7 +1141,13 @@ func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root
|
|||
// In case (d), **original** account along with its storages should be deleted,
|
||||
// with their values be tracked as original value.
|
||||
func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.Address]struct{}, error) {
|
||||
// Short circuit if geth is running with hash mode. This procedure can consume
|
||||
// considerable time and storage deletion isn't supported in hash mode, thus
|
||||
// preemptively avoiding unnecessary expenses.
|
||||
incomplete := make(map[common.Address]struct{})
|
||||
if s.db.TrieDB().Scheme() == rawdb.HashScheme {
|
||||
return incomplete, nil
|
||||
}
|
||||
for addr, prev := range s.stateObjectsDestruct {
|
||||
// The original account was non-existing, and it's marked as destructed
|
||||
// in the scope of block. It can be case (a) or (b).
|
||||
|
|
|
|||
|
|
@ -31,10 +31,12 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
||||
"github.com/ethereum/go-ethereum/trie/triestate"
|
||||
)
|
||||
|
||||
|
|
@ -179,16 +181,28 @@ func (test *stateTest) run() bool {
|
|||
storageList = append(storageList, copy2DSet(states.Storages))
|
||||
}
|
||||
disk = rawdb.NewMemoryDatabase()
|
||||
tdb = trie.NewDatabase(disk, &trie.Config{OnCommit: onCommit})
|
||||
tdb = trie.NewDatabase(disk, &trie.Config{OnCommit: onCommit, PathDB: pathdb.Defaults})
|
||||
sdb = NewDatabaseWithNodeDB(disk, tdb)
|
||||
byzantium = rand.Intn(2) == 0
|
||||
)
|
||||
defer disk.Close()
|
||||
defer tdb.Close()
|
||||
|
||||
var snaps *snapshot.Tree
|
||||
if rand.Intn(3) == 0 {
|
||||
snaps, _ = snapshot.New(snapshot.Config{
|
||||
CacheSize: 1,
|
||||
Recovery: false,
|
||||
NoBuild: false,
|
||||
AsyncBuild: false,
|
||||
}, disk, tdb, types.EmptyRootHash)
|
||||
}
|
||||
for i, actions := range test.actions {
|
||||
root := types.EmptyRootHash
|
||||
if i != 0 {
|
||||
root = roots[len(roots)-1]
|
||||
}
|
||||
state, err := New(root, sdb, nil)
|
||||
state, err := New(root, sdb, snaps)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,12 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// Tests that updating a state trie does not leak any database writes prior to
|
||||
|
|
@ -447,6 +450,43 @@ func (test *snapshotTest) run() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func forEachStorage(s *StateDB, addr common.Address, cb func(key, value common.Hash) bool) error {
|
||||
so := s.getStateObject(addr)
|
||||
if so == nil {
|
||||
return nil
|
||||
}
|
||||
tr, err := so.getTrie()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trieIt, err := tr.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it := trie.NewIterator(trieIt)
|
||||
|
||||
for it.Next() {
|
||||
key := common.BytesToHash(s.trie.GetKey(it.Key))
|
||||
if value, dirty := so.dirtyStorage[key]; dirty {
|
||||
if !cb(key, value) {
|
||||
return nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if len(it.Value) > 0 {
|
||||
_, content, _, err := rlp.Split(it.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cb(key, common.BytesToHash(content)) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkEqual checks that methods of state and checkstate return the same values.
|
||||
func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||
for _, addr := range test.addrs {
|
||||
|
|
@ -468,10 +508,10 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
|||
checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
|
||||
// Check storage.
|
||||
if obj := state.getStateObject(addr); obj != nil {
|
||||
state.ForEachStorage(addr, func(key, value common.Hash) bool {
|
||||
forEachStorage(state, addr, func(key, value common.Hash) bool {
|
||||
return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
|
||||
})
|
||||
checkstate.ForEachStorage(addr, func(key, value common.Hash) bool {
|
||||
forEachStorage(checkstate, addr, func(key, value common.Hash) bool {
|
||||
return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
|
||||
})
|
||||
}
|
||||
|
|
@ -1097,3 +1137,57 @@ func TestResetObject(t *testing.T) {
|
|||
t.Fatalf("Unexpected storage slot value %v", slot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteStorage(t *testing.T) {
|
||||
var (
|
||||
disk = rawdb.NewMemoryDatabase()
|
||||
tdb = trie.NewDatabase(disk, nil)
|
||||
db = NewDatabaseWithNodeDB(disk, tdb)
|
||||
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash)
|
||||
state, _ = New(types.EmptyRootHash, db, snaps)
|
||||
addr = common.HexToAddress("0x1")
|
||||
)
|
||||
// Initialize account and populate storage
|
||||
state.SetBalance(addr, big.NewInt(1))
|
||||
state.CreateAccount(addr)
|
||||
for i := 0; i < 1000; i++ {
|
||||
slot := common.Hash(uint256.NewInt(uint64(i)).Bytes32())
|
||||
value := common.Hash(uint256.NewInt(uint64(10 * i)).Bytes32())
|
||||
state.SetState(addr, slot, value)
|
||||
}
|
||||
root, _ := state.Commit(0, true)
|
||||
// Init phase done, create two states, one with snap and one without
|
||||
fastState, _ := New(root, db, snaps)
|
||||
slowState, _ := New(root, db, nil)
|
||||
|
||||
obj := fastState.GetOrNewStateObject(addr)
|
||||
storageRoot := obj.data.Root
|
||||
|
||||
_, _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, _, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
check := func(set *trienode.NodeSet) string {
|
||||
var a []string
|
||||
set.ForEachWithOrder(func(path string, n *trienode.Node) {
|
||||
if n.Hash != (common.Hash{}) {
|
||||
t.Fatal("delete should have empty hashes")
|
||||
}
|
||||
if len(n.Blob) != 0 {
|
||||
t.Fatal("delete should have have empty blobs")
|
||||
}
|
||||
a = append(a, fmt.Sprintf("%x", path))
|
||||
})
|
||||
return strings.Join(a, ",")
|
||||
}
|
||||
slowRes := check(slowNodes)
|
||||
fastRes := check(fastNodes)
|
||||
if slowRes != fastRes {
|
||||
t.Fatalf("difference found:\nfast: %v\nslow: %v\n", fastRes, slowRes)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
|
|
@ -45,7 +43,7 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, onLeaf func(k
|
|||
}
|
||||
}
|
||||
var obj types.StateAccount
|
||||
if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil {
|
||||
if err := rlp.DecodeBytes(leaf, &obj); err != nil {
|
||||
return err
|
||||
}
|
||||
syncer.AddSubTrie(obj.Root, path, parent, parentPath, onSlot)
|
||||
|
|
|
|||
|
|
@ -76,6 +76,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||
vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
|
||||
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||
)
|
||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
||||
}
|
||||
// Iterate over and process the individual transactions
|
||||
for i, tx := range block.Transactions() {
|
||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||
|
|
@ -132,6 +135,9 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
|
|||
receipt.TxHash = tx.Hash()
|
||||
receipt.GasUsed = result.UsedGas
|
||||
|
||||
receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob)
|
||||
receipt.BlobGasPrice = tx.BlobGasFeeCap()
|
||||
|
||||
// If the transaction created a contract, store the creation address in the receipt.
|
||||
if msg.To == nil {
|
||||
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
||||
|
|
@ -160,3 +166,23 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
|||
vmenv := vm.NewEVM(blockContext, vm.TxContext{BlobHashes: tx.BlobHashes()}, statedb, config, cfg)
|
||||
return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
|
||||
}
|
||||
|
||||
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
||||
// contract. This method is exported to be used in tests.
|
||||
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
|
||||
// If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with
|
||||
// the new root
|
||||
msg := &Message{
|
||||
From: params.SystemAddress,
|
||||
GasLimit: 30_000_000,
|
||||
GasPrice: common.Big0,
|
||||
GasFeeCap: common.Big0,
|
||||
GasTipCap: common.Big0,
|
||||
To: ¶ms.BeaconRootsStorageAddress,
|
||||
Data: beaconRoot[:],
|
||||
}
|
||||
vmenv.Reset(NewEVMTxContext(msg), statedb)
|
||||
statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress)
|
||||
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.Big0)
|
||||
statedb.Finalise(true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -410,6 +410,9 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
|
|||
used := uint64(nBlobs * params.BlobTxBlobGasPerBlob)
|
||||
header.ExcessBlobGas = &excess
|
||||
header.BlobGasUsed = &used
|
||||
|
||||
beaconRoot := common.HexToHash("0xbeac00")
|
||||
header.ParentBeaconRoot = &beaconRoot
|
||||
}
|
||||
// Assemble and return the final block for sealing
|
||||
if config.IsShanghai(header.Number, header.Time) {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ const (
|
|||
// maxBlobsPerTransaction is the maximum number of blobs a single transaction
|
||||
// is allowed to contain. Whilst the spec states it's unlimited, the block
|
||||
// data slots are protocol bound, which implicitly also limit this.
|
||||
maxBlobsPerTransaction = params.BlobTxMaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob
|
||||
maxBlobsPerTransaction = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob
|
||||
|
||||
// txAvgSize is an approximate byte size of a transaction metadata to avoid
|
||||
// tiny overflows causing all txs to move a shelf higher, wasting disk space.
|
||||
|
|
@ -714,7 +714,7 @@ func (p *BlobPool) offload(addr common.Address, nonce uint64, id uint64, inclusi
|
|||
return
|
||||
}
|
||||
var tx types.Transaction
|
||||
if err = rlp.DecodeBytes(data, tx); err != nil {
|
||||
if err = rlp.DecodeBytes(data, &tx); err != nil {
|
||||
log.Error("Blobs corrupted for included transaction", "from", addr, "nonce", nonce, "id", id, "err", err)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ type sortedMap struct {
|
|||
items map[uint64]*types.Transaction // Hash map storing the transaction data
|
||||
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
|
||||
cache types.Transactions // Cache of the transactions already sorted
|
||||
cacheMu sync.Mutex // Mutex covering the cache
|
||||
}
|
||||
|
||||
// newSortedMap creates a new nonce-sorted transaction map.
|
||||
|
|
@ -78,7 +79,9 @@ func (m *sortedMap) Put(tx *types.Transaction) {
|
|||
if m.items[nonce] == nil {
|
||||
heap.Push(m.index, nonce)
|
||||
}
|
||||
m.cacheMu.Lock()
|
||||
m.items[nonce], m.cache = tx, nil
|
||||
m.cacheMu.Unlock()
|
||||
}
|
||||
|
||||
// Forward removes all transactions from the map with a nonce lower than the
|
||||
|
|
@ -94,9 +97,11 @@ func (m *sortedMap) Forward(threshold uint64) types.Transactions {
|
|||
delete(m.items, nonce)
|
||||
}
|
||||
// If we had a cached order, shift the front
|
||||
m.cacheMu.Lock()
|
||||
if m.cache != nil {
|
||||
m.cache = m.cache[len(removed):]
|
||||
}
|
||||
m.cacheMu.Unlock()
|
||||
return removed
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +125,9 @@ func (m *sortedMap) reheap() {
|
|||
*m.index = append(*m.index, nonce)
|
||||
}
|
||||
heap.Init(m.index)
|
||||
m.cacheMu.Lock()
|
||||
m.cache = nil
|
||||
m.cacheMu.Unlock()
|
||||
}
|
||||
|
||||
// filter is identical to Filter, but **does not** regenerate the heap. This method
|
||||
|
|
@ -136,7 +143,9 @@ func (m *sortedMap) filter(filter func(*types.Transaction) bool) types.Transacti
|
|||
}
|
||||
}
|
||||
if len(removed) > 0 {
|
||||
m.cacheMu.Lock()
|
||||
m.cache = nil
|
||||
m.cacheMu.Unlock()
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
|
@ -160,9 +169,11 @@ func (m *sortedMap) Cap(threshold int) types.Transactions {
|
|||
heap.Init(m.index)
|
||||
|
||||
// If we had a cache, shift the back
|
||||
m.cacheMu.Lock()
|
||||
if m.cache != nil {
|
||||
m.cache = m.cache[:len(m.cache)-len(drops)]
|
||||
}
|
||||
m.cacheMu.Unlock()
|
||||
return drops
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +193,9 @@ func (m *sortedMap) Remove(nonce uint64) bool {
|
|||
}
|
||||
}
|
||||
delete(m.items, nonce)
|
||||
m.cacheMu.Lock()
|
||||
m.cache = nil
|
||||
m.cacheMu.Unlock()
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
@ -206,7 +219,9 @@ func (m *sortedMap) Ready(start uint64) types.Transactions {
|
|||
delete(m.items, next)
|
||||
heap.Pop(m.index)
|
||||
}
|
||||
m.cacheMu.Lock()
|
||||
m.cache = nil
|
||||
m.cacheMu.Unlock()
|
||||
|
||||
return ready
|
||||
}
|
||||
|
|
@ -217,6 +232,8 @@ func (m *sortedMap) Len() int {
|
|||
}
|
||||
|
||||
func (m *sortedMap) flatten() types.Transactions {
|
||||
m.cacheMu.Lock()
|
||||
defer m.cacheMu.Unlock()
|
||||
// If the sorting was not cached yet, create and cache it
|
||||
if m.cache == nil {
|
||||
m.cache = make(types.Transactions, 0, len(m.items))
|
||||
|
|
@ -232,8 +249,8 @@ func (m *sortedMap) flatten() types.Transactions {
|
|||
// sorted internal representation. The result of the sorting is cached in case
|
||||
// it's requested again before any modifications are made to the contents.
|
||||
func (m *sortedMap) Flatten() types.Transactions {
|
||||
// Copy the cache to prevent accidental modifications
|
||||
cache := m.flatten()
|
||||
// Copy the cache to prevent accidental modification
|
||||
txs := make(types.Transactions, len(cache))
|
||||
copy(txs, cache)
|
||||
return txs
|
||||
|
|
|
|||
|
|
@ -120,8 +120,8 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
|||
if len(hashes) == 0 {
|
||||
return fmt.Errorf("blobless blob transaction")
|
||||
}
|
||||
if len(hashes) > params.BlobTxMaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob {
|
||||
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.BlobTxMaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)
|
||||
if len(hashes) > params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob {
|
||||
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)
|
||||
}
|
||||
if err := validateBlobSidecar(hashes, sidecar); err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -90,6 +90,9 @@ type Header struct {
|
|||
|
||||
// ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers.
|
||||
ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||
|
||||
// ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers.
|
||||
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||
}
|
||||
|
||||
// field type overrides for gencodec
|
||||
|
|
@ -297,6 +300,10 @@ func CopyHeader(h *Header) *Header {
|
|||
cpy.BlobGasUsed = new(uint64)
|
||||
*cpy.BlobGasUsed = *h.BlobGasUsed
|
||||
}
|
||||
if h.ParentBeaconRoot != nil {
|
||||
cpy.ParentBeaconRoot = new(common.Hash)
|
||||
*cpy.ParentBeaconRoot = *h.ParentBeaconRoot
|
||||
}
|
||||
return &cpy
|
||||
}
|
||||
|
||||
|
|
@ -376,6 +383,8 @@ func (b *Block) BaseFee() *big.Int {
|
|||
return new(big.Int).Set(b.header.BaseFee)
|
||||
}
|
||||
|
||||
func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot }
|
||||
|
||||
func (b *Block) ExcessBlobGas() *uint64 {
|
||||
var excessBlobGas *uint64
|
||||
if b.header.ExcessBlobGas != nil {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
|||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
}
|
||||
var enc Header
|
||||
|
|
@ -57,6 +58,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
|||
enc.WithdrawalsHash = h.WithdrawalsHash
|
||||
enc.BlobGasUsed = (*hexutil.Uint64)(h.BlobGasUsed)
|
||||
enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas)
|
||||
enc.ParentBeaconRoot = h.ParentBeaconRoot
|
||||
enc.Hash = h.Hash()
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
|
@ -83,6 +85,7 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
|||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||
}
|
||||
var dec Header
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -157,5 +160,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
|||
if dec.ExcessBlobGas != nil {
|
||||
h.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
||||
}
|
||||
if dec.ParentBeaconRoot != nil {
|
||||
h.ParentBeaconRoot = dec.ParentBeaconRoot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
|||
_tmp2 := obj.WithdrawalsHash != nil
|
||||
_tmp3 := obj.BlobGasUsed != nil
|
||||
_tmp4 := obj.ExcessBlobGas != nil
|
||||
if _tmp1 || _tmp2 || _tmp3 || _tmp4 {
|
||||
_tmp5 := obj.ParentBeaconRoot != nil
|
||||
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 {
|
||||
if obj.BaseFee == nil {
|
||||
w.Write(rlp.EmptyString)
|
||||
} else {
|
||||
|
|
@ -54,27 +55,34 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
|||
w.WriteBigInt(obj.BaseFee)
|
||||
}
|
||||
}
|
||||
if _tmp2 || _tmp3 || _tmp4 {
|
||||
if _tmp2 || _tmp3 || _tmp4 || _tmp5 {
|
||||
if obj.WithdrawalsHash == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteBytes(obj.WithdrawalsHash[:])
|
||||
}
|
||||
}
|
||||
if _tmp3 || _tmp4 {
|
||||
if _tmp3 || _tmp4 || _tmp5 {
|
||||
if obj.BlobGasUsed == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteUint64((*obj.BlobGasUsed))
|
||||
}
|
||||
}
|
||||
if _tmp4 {
|
||||
if _tmp4 || _tmp5 {
|
||||
if obj.ExcessBlobGas == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteUint64((*obj.ExcessBlobGas))
|
||||
}
|
||||
}
|
||||
if _tmp5 {
|
||||
if obj.ParentBeaconRoot == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteBytes(obj.ParentBeaconRoot[:])
|
||||
}
|
||||
}
|
||||
w.ListEnd(_tmp0)
|
||||
return w.Flush()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ func (l Log) MarshalJSON() ([]byte, error) {
|
|||
Address common.Address `json:"address" gencodec:"required"`
|
||||
Topics []common.Hash `json:"topics" gencodec:"required"`
|
||||
Data hexutil.Bytes `json:"data" gencodec:"required"`
|
||||
BlockNumber hexutil.Uint64 `json:"blockNumber"`
|
||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
TxIndex hexutil.Uint `json:"transactionIndex"`
|
||||
BlockHash common.Hash `json:"blockHash"`
|
||||
Index hexutil.Uint `json:"logIndex"`
|
||||
Removed bool `json:"removed"`
|
||||
BlockNumber hexutil.Uint64 `json:"blockNumber" rlp:"-"`
|
||||
TxHash common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
|
||||
TxIndex hexutil.Uint `json:"transactionIndex" rlp:"-"`
|
||||
BlockHash common.Hash `json:"blockHash" rlp:"-"`
|
||||
Index hexutil.Uint `json:"logIndex" rlp:"-"`
|
||||
Removed bool `json:"removed" rlp:"-"`
|
||||
}
|
||||
var enc Log
|
||||
enc.Address = l.Address
|
||||
|
|
@ -44,12 +44,12 @@ func (l *Log) UnmarshalJSON(input []byte) error {
|
|||
Address *common.Address `json:"address" gencodec:"required"`
|
||||
Topics []common.Hash `json:"topics" gencodec:"required"`
|
||||
Data *hexutil.Bytes `json:"data" gencodec:"required"`
|
||||
BlockNumber *hexutil.Uint64 `json:"blockNumber"`
|
||||
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
TxIndex *hexutil.Uint `json:"transactionIndex"`
|
||||
BlockHash *common.Hash `json:"blockHash"`
|
||||
Index *hexutil.Uint `json:"logIndex"`
|
||||
Removed *bool `json:"removed"`
|
||||
BlockNumber *hexutil.Uint64 `json:"blockNumber" rlp:"-"`
|
||||
TxHash *common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
|
||||
TxIndex *hexutil.Uint `json:"transactionIndex" rlp:"-"`
|
||||
BlockHash *common.Hash `json:"blockHash" rlp:"-"`
|
||||
Index *hexutil.Uint `json:"logIndex" rlp:"-"`
|
||||
Removed *bool `json:"removed" rlp:"-"`
|
||||
}
|
||||
var dec Log
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ package types
|
|||
import "github.com/ethereum/go-ethereum/rlp"
|
||||
import "io"
|
||||
|
||||
func (obj *rlpLog) EncodeRLP(_w io.Writer) error {
|
||||
func (obj *Log) EncodeRLP(_w io.Writer) error {
|
||||
w := rlp.NewEncoderBuffer(_w)
|
||||
_tmp0 := w.List()
|
||||
w.WriteBytes(obj.Address[:])
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package types
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -36,6 +38,22 @@ var encodeBufferPool = sync.Pool{
|
|||
New: func() interface{} { return new(bytes.Buffer) },
|
||||
}
|
||||
|
||||
// getPooledBuffer retrieves a buffer from the pool and creates a byte slice of the
|
||||
// requested size from it.
|
||||
//
|
||||
// The caller should return the *bytes.Buffer object back into encodeBufferPool after use!
|
||||
// The returned byte slice must not be used after returning the buffer.
|
||||
func getPooledBuffer(size uint64) ([]byte, *bytes.Buffer, error) {
|
||||
if size > math.MaxInt {
|
||||
return nil, nil, fmt.Errorf("can't get buffer of size %d", size)
|
||||
}
|
||||
buf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
buf.Grow(int(size))
|
||||
b := buf.Bytes()[:int(size)]
|
||||
return b, buf, nil
|
||||
}
|
||||
|
||||
// rlpHash encodes x and hashes the encoded bytes.
|
||||
func rlpHash(x interface{}) (h common.Hash) {
|
||||
sha := hasherPool.Get().(crypto.KeccakState)
|
||||
|
|
|
|||
|
|
@ -17,13 +17,11 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
//go:generate go run ../../rlp/rlpgen -type Log -out gen_log_rlp.go
|
||||
//go:generate go run github.com/fjl/gencodec -type Log -field-override logMarshaling -out gen_log_json.go
|
||||
|
||||
// Log represents a contract log event. These events are generated by the LOG opcode and
|
||||
|
|
@ -40,19 +38,19 @@ type Log struct {
|
|||
// Derived fields. These fields are filled in by the node
|
||||
// but not secured by consensus.
|
||||
// block in which the transaction was included
|
||||
BlockNumber uint64 `json:"blockNumber"`
|
||||
BlockNumber uint64 `json:"blockNumber" rlp:"-"`
|
||||
// hash of the transaction
|
||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
TxHash common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
|
||||
// index of the transaction in the block
|
||||
TxIndex uint `json:"transactionIndex"`
|
||||
TxIndex uint `json:"transactionIndex" rlp:"-"`
|
||||
// hash of the block in which the transaction was included
|
||||
BlockHash common.Hash `json:"blockHash"`
|
||||
BlockHash common.Hash `json:"blockHash" rlp:"-"`
|
||||
// index of the log in the block
|
||||
Index uint `json:"logIndex"`
|
||||
Index uint `json:"logIndex" rlp:"-"`
|
||||
|
||||
// The Removed field is true if this log was reverted due to a chain reorganisation.
|
||||
// You must pay attention to this field if you receive logs through a filter query.
|
||||
Removed bool `json:"removed"`
|
||||
Removed bool `json:"removed" rlp:"-"`
|
||||
}
|
||||
|
||||
type logMarshaling struct {
|
||||
|
|
@ -61,28 +59,3 @@ type logMarshaling struct {
|
|||
TxIndex hexutil.Uint
|
||||
Index hexutil.Uint
|
||||
}
|
||||
|
||||
//go:generate go run ../../rlp/rlpgen -type rlpLog -out gen_log_rlp.go
|
||||
|
||||
// rlpLog is used to RLP-encode both the consensus and storage formats.
|
||||
type rlpLog struct {
|
||||
Address common.Address
|
||||
Topics []common.Hash
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (l *Log) EncodeRLP(w io.Writer) error {
|
||||
rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
|
||||
return rlp.Encode(w, &rl)
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
func (l *Log) DecodeRLP(s *rlp.Stream) error {
|
||||
var dec rlpLog
|
||||
err := s.Decode(&dec)
|
||||
if err == nil {
|
||||
l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ func (r *Receipt) MarshalBinary() ([]byte, error) {
|
|||
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
|
||||
// from an RLP stream.
|
||||
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
||||
kind, _, err := s.Kind()
|
||||
kind, size, err := s.Kind()
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
|
|
@ -165,12 +165,18 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
|||
}
|
||||
r.Type = LegacyTxType
|
||||
return r.setFromRLP(dec)
|
||||
case kind == rlp.Byte:
|
||||
return errShortTypedReceipt
|
||||
default:
|
||||
// It's an EIP-2718 typed tx receipt.
|
||||
b, err := s.Bytes()
|
||||
b, buf, err := getPooledBuffer(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer encodeBufferPool.Put(buf)
|
||||
if err := s.ReadBytes(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.decodeTyped(b)
|
||||
}
|
||||
}
|
||||
|
|
@ -264,7 +270,7 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
|
|||
w.WriteUint64(r.CumulativeGasUsed)
|
||||
logList := w.List()
|
||||
for _, log := range r.Logs {
|
||||
if err := rlp.Encode(w, log); err != nil {
|
||||
if err := log.EncodeRLP(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,15 +145,23 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
|||
tx.setDecoded(&inner, rlp.ListSize(size))
|
||||
}
|
||||
return err
|
||||
case kind == rlp.Byte:
|
||||
return errShortTypedTx
|
||||
default:
|
||||
// It's an EIP-2718 typed TX envelope.
|
||||
var b []byte
|
||||
if b, err = s.Bytes(); err != nil {
|
||||
// First read the tx payload bytes into a temporary buffer.
|
||||
b, buf, err := getPooledBuffer(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer encodeBufferPool.Put(buf)
|
||||
if err := s.ReadBytes(b); err != nil {
|
||||
return err
|
||||
}
|
||||
// Now decode the inner transaction.
|
||||
inner, err := tx.decodeTyped(b)
|
||||
if err == nil {
|
||||
tx.setDecoded(inner, uint64(len(b)))
|
||||
tx.setDecoded(inner, size)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -373,20 +373,20 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
|||
itx.BlobHashes = dec.BlobVersionedHashes
|
||||
|
||||
// signature R
|
||||
var ok bool
|
||||
var overflow bool
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' in transaction")
|
||||
}
|
||||
itx.R, ok = uint256.FromBig((*big.Int)(dec.R))
|
||||
if !ok {
|
||||
itx.R, overflow = uint256.FromBig((*big.Int)(dec.R))
|
||||
if overflow {
|
||||
return errors.New("'r' value overflows uint256")
|
||||
}
|
||||
// signature S
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' in transaction")
|
||||
}
|
||||
itx.S, ok = uint256.FromBig((*big.Int)(dec.S))
|
||||
if !ok {
|
||||
itx.S, overflow = uint256.FromBig((*big.Int)(dec.S))
|
||||
if overflow {
|
||||
return errors.New("'s' value overflows uint256")
|
||||
}
|
||||
// signature V
|
||||
|
|
@ -394,8 +394,8 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
itx.V, ok = uint256.FromBig(vbig)
|
||||
if !ok {
|
||||
itx.V, overflow = uint256.FromBig(vbig)
|
||||
if overflow {
|
||||
return errors.New("'v' value overflows uint256")
|
||||
}
|
||||
if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 {
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ func TestEIP2718TransactionEncode(t *testing.T) {
|
|||
|
||||
func decodeTx(data []byte) (*Transaction, error) {
|
||||
var tx Transaction
|
||||
t, err := &tx, rlp.Decode(bytes.NewReader(data), &tx)
|
||||
t, err := &tx, rlp.DecodeBytes(data, &tx)
|
||||
return t, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ func NewEnv(cfg *Config) *vm.EVM {
|
|||
Difficulty: cfg.Difficulty,
|
||||
GasLimit: cfg.GasLimit,
|
||||
BaseFee: cfg.BaseFee,
|
||||
Random: cfg.Random,
|
||||
}
|
||||
|
||||
return vm.NewEVM(blockContext, txContext, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ type Config struct {
|
|||
EVMConfig vm.Config
|
||||
BaseFee *big.Int
|
||||
BlobHashes []common.Hash
|
||||
Random *common.Hash
|
||||
|
||||
State *state.StateDB
|
||||
GetHashFn func(n uint64) common.Hash
|
||||
|
|
|
|||
|
|
@ -199,9 +199,8 @@ func miller(q *twistPoint, p *curvePoint) *gfP12 {
|
|||
r = newR
|
||||
|
||||
r2.Square(&minusQ2.y)
|
||||
a, b, c, newR = lineFunctionAdd(r, minusQ2, bAffine, r2)
|
||||
a, b, c, _ = lineFunctionAdd(r, minusQ2, bAffine, r2)
|
||||
mulLine(ret, a, b, c)
|
||||
r = newR
|
||||
|
||||
return ret
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,10 @@ func (api *AdminAPI) ImportChain(file string) (bool, error) {
|
|||
} else if err != nil {
|
||||
return false, fmt.Errorf("block %d: failed to parse: %v", index, err)
|
||||
}
|
||||
// ignore the genesis block when importing blocks
|
||||
if block.NumberU64() == 0 {
|
||||
continue
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
index++
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,25 +78,19 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
|
|||
return b.eth.blockchain.CurrentBlock(), nil
|
||||
}
|
||||
if number == rpc.FinalizedBlockNumber {
|
||||
if !b.eth.Merger().TDDReached() {
|
||||
return nil, errors.New("'finalized' tag not supported on pre-merge network")
|
||||
}
|
||||
block := b.eth.blockchain.CurrentFinalBlock()
|
||||
if block != nil {
|
||||
return block, nil
|
||||
}
|
||||
if block == nil {
|
||||
return nil, errors.New("finalized block not found")
|
||||
}
|
||||
if number == rpc.SafeBlockNumber {
|
||||
if !b.eth.Merger().TDDReached() {
|
||||
return nil, errors.New("'safe' tag not supported on pre-merge network")
|
||||
}
|
||||
block := b.eth.blockchain.CurrentSafeBlock()
|
||||
if block != nil {
|
||||
return block, nil
|
||||
}
|
||||
if number == rpc.SafeBlockNumber {
|
||||
block := b.eth.blockchain.CurrentSafeBlock()
|
||||
if block == nil {
|
||||
return nil, errors.New("safe block not found")
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
|
||||
}
|
||||
|
||||
|
|
@ -136,9 +130,6 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
|
|||
return b.eth.blockchain.GetBlock(header.Hash(), header.Number.Uint64()), nil
|
||||
}
|
||||
if number == rpc.FinalizedBlockNumber {
|
||||
if !b.eth.Merger().TDDReached() {
|
||||
return nil, errors.New("'finalized' tag not supported on pre-merge network")
|
||||
}
|
||||
header := b.eth.blockchain.CurrentFinalBlock()
|
||||
if header == nil {
|
||||
return nil, errors.New("finalized block not found")
|
||||
|
|
@ -146,9 +137,6 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
|
|||
return b.eth.blockchain.GetBlock(header.Hash(), header.Number.Uint64()), nil
|
||||
}
|
||||
if number == rpc.SafeBlockNumber {
|
||||
if !b.eth.Merger().TDDReached() {
|
||||
return nil, errors.New("'safe' tag not supported on pre-merge network")
|
||||
}
|
||||
header := b.eth.blockchain.CurrentSafeBlock()
|
||||
if header == nil {
|
||||
return nil, errors.New("safe block not found")
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ const (
|
|||
var caps = []string{
|
||||
"engine_forkchoiceUpdatedV1",
|
||||
"engine_forkchoiceUpdatedV2",
|
||||
"engine_forkchoiceUpdatedV3",
|
||||
"engine_exchangeTransitionConfigurationV1",
|
||||
"engine_getPayloadV1",
|
||||
"engine_getPayloadV2",
|
||||
|
|
@ -135,6 +136,13 @@ type ConsensusAPI struct {
|
|||
// NewConsensusAPI creates a new consensus api for the given backend.
|
||||
// The underlying blockchain needs to have a valid terminal total difficulty set.
|
||||
func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI {
|
||||
api := newConsensusAPIWithoutHeartbeat(eth)
|
||||
go api.heartbeat()
|
||||
return api
|
||||
}
|
||||
|
||||
// newConsensusAPIWithoutHeartbeat creates a new consensus api for the SimulatedBeacon Node.
|
||||
func newConsensusAPIWithoutHeartbeat(eth *eth.Ethereum) *ConsensusAPI {
|
||||
if eth.BlockChain().Config().TerminalTotalDifficulty == nil {
|
||||
log.Warn("Engine API started but chain not configured for merge yet")
|
||||
}
|
||||
|
|
@ -146,8 +154,6 @@ func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI {
|
|||
invalidTipsets: make(map[common.Hash]*types.Header),
|
||||
}
|
||||
eth.Downloader().SetBadBlockCallback(api.setInvalidAncestor)
|
||||
go api.heartbeat()
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
|
|
@ -187,17 +193,36 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, pa
|
|||
return api.forkchoiceUpdated(update, payloadAttributes)
|
||||
}
|
||||
|
||||
// ForkchoiceUpdatedV3 is equivalent to V2 with the addition of parent beacon block root in the payload attributes.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if payloadAttributes != nil {
|
||||
if err := api.verifyPayloadAttributes(payloadAttributes); err != nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidParams.With(err)
|
||||
}
|
||||
}
|
||||
return api.forkchoiceUpdated(update, payloadAttributes)
|
||||
}
|
||||
|
||||
func (api *ConsensusAPI) verifyPayloadAttributes(attr *engine.PayloadAttributes) error {
|
||||
if !api.eth.BlockChain().Config().IsShanghai(api.eth.BlockChain().Config().LondonBlock, attr.Timestamp) {
|
||||
// Reject payload attributes with withdrawals before shanghai
|
||||
if attr.Withdrawals != nil {
|
||||
return errors.New("withdrawals before shanghai")
|
||||
c := api.eth.BlockChain().Config()
|
||||
|
||||
// Verify withdrawals attribute for Shanghai.
|
||||
if err := checkAttribute(c.IsShanghai, attr.Withdrawals != nil, attr.Timestamp); err != nil {
|
||||
return fmt.Errorf("invalid withdrawals: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Reject payload attributes with nil withdrawals after shanghai
|
||||
if attr.Withdrawals == nil {
|
||||
return errors.New("missing withdrawals list")
|
||||
// Verify beacon root attribute for Cancun.
|
||||
if err := checkAttribute(c.IsCancun, attr.BeaconRoot != nil, attr.Timestamp); err != nil {
|
||||
return fmt.Errorf("invalid parent beacon block root: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkAttribute(active func(*big.Int, uint64) bool, exists bool, time uint64) error {
|
||||
if active(common.Big0, time) && !exists {
|
||||
return errors.New("fork active, missing expected attribute")
|
||||
}
|
||||
if !active(common.Big0, time) && exists {
|
||||
return errors.New("fork inactive, unexpected attribute set")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -345,6 +370,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
|||
FeeRecipient: payloadAttributes.SuggestedFeeRecipient,
|
||||
Random: payloadAttributes.Random,
|
||||
Withdrawals: payloadAttributes.Withdrawals,
|
||||
BeaconRoot: payloadAttributes.BeaconRoot,
|
||||
}
|
||||
id := args.Id()
|
||||
// If we already are busy generating this work, then we do not need
|
||||
|
|
@ -395,7 +421,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit
|
|||
|
||||
// GetPayloadV1 returns a cached payload by id.
|
||||
func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.ExecutableData, error) {
|
||||
data, err := api.getPayload(payloadID)
|
||||
data, err := api.getPayload(payloadID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -404,17 +430,17 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.Execu
|
|||
|
||||
// GetPayloadV2 returns a cached payload by id.
|
||||
func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
return api.getPayload(payloadID)
|
||||
return api.getPayload(payloadID, false)
|
||||
}
|
||||
|
||||
// GetPayloadV3 returns a cached payload by id.
|
||||
func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
return api.getPayload(payloadID)
|
||||
return api.getPayload(payloadID, false)
|
||||
}
|
||||
|
||||
func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
|
||||
data := api.localBlocks.get(payloadID, true)
|
||||
data := api.localBlocks.get(payloadID, full)
|
||||
if data == nil {
|
||||
return nil, engine.UnknownPayload
|
||||
}
|
||||
|
|
@ -426,7 +452,7 @@ func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.Payl
|
|||
if params.Withdrawals != nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1"))
|
||||
}
|
||||
return api.newPayload(params, nil)
|
||||
return api.newPayload(params, nil, nil)
|
||||
}
|
||||
|
||||
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||
|
|
@ -441,26 +467,32 @@ func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.Payl
|
|||
if api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("newPayloadV2 called post-cancun"))
|
||||
}
|
||||
return api.newPayload(params, nil)
|
||||
return api.newPayload(params, nil, nil)
|
||||
}
|
||||
|
||||
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes *[]common.Hash) (engine.PayloadStatusV1, error) {
|
||||
if !api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("newPayloadV3 called pre-cancun"))
|
||||
}
|
||||
|
||||
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||
if params.ExcessBlobGas == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
||||
}
|
||||
var hashes []common.Hash
|
||||
if versionedHashes != nil {
|
||||
hashes = *versionedHashes
|
||||
if params.BlobGasUsed == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil params.BlobGasUsed post-cancun"))
|
||||
}
|
||||
return api.newPayload(params, hashes)
|
||||
if versionedHashes == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
||||
}
|
||||
if beaconRoot == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil parentBeaconBlockRoot post-cancun"))
|
||||
}
|
||||
|
||||
if !api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 called pre-cancun"))
|
||||
}
|
||||
|
||||
return api.newPayload(params, versionedHashes, beaconRoot)
|
||||
}
|
||||
|
||||
func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash) (engine.PayloadStatusV1, error) {
|
||||
func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||
// The locking here is, strictly, not required. Without these locks, this can happen:
|
||||
//
|
||||
// 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to
|
||||
|
|
@ -478,7 +510,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
|
|||
defer api.newPayloadLock.Unlock()
|
||||
|
||||
log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash)
|
||||
block, err := engine.ExecutableDataToBlock(params, versionedHashes)
|
||||
block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot)
|
||||
if err != nil {
|
||||
log.Warn("Invalid NewPayload params", "params", params, "error", err)
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, nil
|
||||
|
|
|
|||
|
|
@ -41,12 +41,14 @@ import (
|
|||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/mattn/go-colorable"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -69,7 +71,10 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
|
|||
}
|
||||
genesis := &core.Genesis{
|
||||
Config: &config,
|
||||
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
|
||||
Alloc: core.GenesisAlloc{
|
||||
testAddr: {Balance: testBalance},
|
||||
params.BeaconRootsStorageAddress: {Balance: common.Big0, Code: common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604457602036146024575f5ffd5b620180005f350680545f35146037575f5ffd5b6201800001545f5260205ff35b6201800042064281555f359062018000015500")},
|
||||
},
|
||||
ExtraData: []byte("test genesis"),
|
||||
Timestamp: 9000,
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
|
|
@ -204,6 +209,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
|
|||
Timestamp: blockParams.Timestamp,
|
||||
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
||||
Random: blockParams.Random,
|
||||
BeaconRoot: blockParams.BeaconRoot,
|
||||
}).Id()
|
||||
execData, err := api.GetPayloadV1(payloadID)
|
||||
if err != nil {
|
||||
|
|
@ -314,7 +320,7 @@ func TestEth2NewBlock(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Failed to create the executable data %v", err)
|
||||
}
|
||||
block, err := engine.ExecutableDataToBlock(*execData, nil)
|
||||
block, err := engine.ExecutableDataToBlock(*execData, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||
}
|
||||
|
|
@ -356,7 +362,7 @@ func TestEth2NewBlock(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Failed to create the executable data %v", err)
|
||||
}
|
||||
block, err := engine.ExecutableDataToBlock(*execData, nil)
|
||||
block, err := engine.ExecutableDataToBlock(*execData, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||
}
|
||||
|
|
@ -667,6 +673,7 @@ func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *engine.Pay
|
|||
FeeRecipient: params.SuggestedFeeRecipient,
|
||||
Random: params.Random,
|
||||
Withdrawals: params.Withdrawals,
|
||||
BeaconRoot: params.BeaconRoot,
|
||||
}
|
||||
payload, err := api.eth.Miner().BuildPayload(args)
|
||||
if err != nil {
|
||||
|
|
@ -988,7 +995,7 @@ func TestSimultaneousNewBlock(t *testing.T) {
|
|||
t.Fatal(testErr)
|
||||
}
|
||||
}
|
||||
block, err := engine.ExecutableDataToBlock(*execData, nil)
|
||||
block, err := engine.ExecutableDataToBlock(*execData, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||
}
|
||||
|
|
@ -1068,6 +1075,7 @@ func TestWithdrawals(t *testing.T) {
|
|||
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
||||
Random: blockParams.Random,
|
||||
Withdrawals: blockParams.Withdrawals,
|
||||
BeaconRoot: blockParams.BeaconRoot,
|
||||
}).Id()
|
||||
execData, err := api.GetPayloadV2(payloadID)
|
||||
if err != nil {
|
||||
|
|
@ -1115,6 +1123,7 @@ func TestWithdrawals(t *testing.T) {
|
|||
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
||||
Random: blockParams.Random,
|
||||
Withdrawals: blockParams.Withdrawals,
|
||||
BeaconRoot: blockParams.BeaconRoot,
|
||||
}).Id()
|
||||
execData, err = api.GetPayloadV2(payloadID)
|
||||
if err != nil {
|
||||
|
|
@ -1245,6 +1254,7 @@ func TestNilWithdrawals(t *testing.T) {
|
|||
Timestamp: test.blockParams.Timestamp,
|
||||
FeeRecipient: test.blockParams.SuggestedFeeRecipient,
|
||||
Random: test.blockParams.Random,
|
||||
BeaconRoot: test.blockParams.BeaconRoot,
|
||||
}).Id()
|
||||
execData, err := api.GetPayloadV2(payloadID)
|
||||
if err != nil {
|
||||
|
|
@ -1521,13 +1531,16 @@ func TestBlockToPayloadWithBlobs(t *testing.T) {
|
|||
}
|
||||
|
||||
txs = append(txs, types.NewTx(&inner))
|
||||
|
||||
blobs := make([]kzg4844.Blob, 1)
|
||||
commitments := make([]kzg4844.Commitment, 1)
|
||||
proofs := make([]kzg4844.Proof, 1)
|
||||
sidecars := []*types.BlobTxSidecar{
|
||||
{
|
||||
Blobs: make([]kzg4844.Blob, 1),
|
||||
Commitments: make([]kzg4844.Commitment, 1),
|
||||
Proofs: make([]kzg4844.Proof, 1),
|
||||
},
|
||||
}
|
||||
|
||||
block := types.NewBlock(&header, txs, nil, nil, trie.NewStackTrie(nil))
|
||||
envelope := engine.BlockToExecutableData(block, nil, blobs, commitments, proofs)
|
||||
envelope := engine.BlockToExecutableData(block, nil, sidecars)
|
||||
var want int
|
||||
for _, tx := range txs {
|
||||
want += len(tx.BlobHashes())
|
||||
|
|
@ -1541,8 +1554,91 @@ func TestBlockToPayloadWithBlobs(t *testing.T) {
|
|||
if got := len(envelope.BlobsBundle.Blobs); got != want {
|
||||
t.Fatalf("invalid number of blobs: got %v, want %v", got, want)
|
||||
}
|
||||
_, err := engine.ExecutableDataToBlock(*envelope.ExecutionPayload, make([]common.Hash, 1))
|
||||
_, err := engine.ExecutableDataToBlock(*envelope.ExecutionPayload, make([]common.Hash, 1), nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// This checks that beaconRoot is applied to the state from the engine API.
|
||||
func TestParentBeaconBlockRoot(t *testing.T) {
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
|
||||
|
||||
genesis, blocks := generateMergeChain(10, true)
|
||||
|
||||
// Set cancun time to last block + 5 seconds
|
||||
time := blocks[len(blocks)-1].Time() + 5
|
||||
genesis.Config.ShanghaiTime = &time
|
||||
genesis.Config.CancunTime = &time
|
||||
|
||||
n, ethservice := startEthService(t, genesis, blocks)
|
||||
ethservice.Merger().ReachTTD()
|
||||
defer n.Close()
|
||||
|
||||
api := NewConsensusAPI(ethservice)
|
||||
|
||||
// 11: Build Shanghai block with no withdrawals.
|
||||
parent := ethservice.BlockChain().CurrentHeader()
|
||||
blockParams := engine.PayloadAttributes{
|
||||
Timestamp: parent.Time + 5,
|
||||
Withdrawals: make([]*types.Withdrawal, 0),
|
||||
BeaconRoot: &common.Hash{42},
|
||||
}
|
||||
fcState := engine.ForkchoiceStateV1{
|
||||
HeadBlockHash: parent.Hash(),
|
||||
}
|
||||
resp, err := api.ForkchoiceUpdatedV2(fcState, &blockParams)
|
||||
if err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err.(*engine.EngineAPIError).ErrorData())
|
||||
}
|
||||
if resp.PayloadStatus.Status != engine.VALID {
|
||||
t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID)
|
||||
}
|
||||
|
||||
// 11: verify state root is the same as parent
|
||||
payloadID := (&miner.BuildPayloadArgs{
|
||||
Parent: fcState.HeadBlockHash,
|
||||
Timestamp: blockParams.Timestamp,
|
||||
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
||||
Random: blockParams.Random,
|
||||
Withdrawals: blockParams.Withdrawals,
|
||||
BeaconRoot: blockParams.BeaconRoot,
|
||||
}).Id()
|
||||
execData, err := api.GetPayloadV3(payloadID)
|
||||
if err != nil {
|
||||
t.Fatalf("error getting payload, err=%v", err)
|
||||
}
|
||||
|
||||
// 11: verify locally built block
|
||||
if status, err := api.NewPayloadV3(*execData.ExecutionPayload, []common.Hash{}, &common.Hash{42}); err != nil {
|
||||
t.Fatalf("error validating payload: %v", err)
|
||||
} else if status.Status != engine.VALID {
|
||||
t.Fatalf("invalid payload")
|
||||
}
|
||||
|
||||
fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
|
||||
resp, err = api.ForkchoiceUpdatedV3(fcState, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err.(*engine.EngineAPIError).ErrorData())
|
||||
}
|
||||
if resp.PayloadStatus.Status != engine.VALID {
|
||||
t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID)
|
||||
}
|
||||
|
||||
// 11: verify beacon root was processed.
|
||||
db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to load db: %v", err)
|
||||
}
|
||||
var (
|
||||
timeIdx = common.BigToHash(big.NewInt(int64(execData.ExecutionPayload.Timestamp % 98304)))
|
||||
rootIdx = common.BigToHash(big.NewInt(int64((execData.ExecutionPayload.Timestamp % 98304) + 98304)))
|
||||
)
|
||||
|
||||
if num := db.GetState(params.BeaconRootsStorageAddress, timeIdx); num != timeIdx {
|
||||
t.Fatalf("incorrect number stored: want %s, got %s", timeIdx, num)
|
||||
}
|
||||
if root := db.GetState(params.BeaconRootsStorageAddress, rootIdx); root != *blockParams.BeaconRoot {
|
||||
t.Fatalf("incorrect root stored: want %s, got %s", *blockParams.BeaconRoot, root)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package catalyst
|
|||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -33,6 +32,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
const devEpochLength = 32
|
||||
|
||||
// withdrawalQueue implements a FIFO queue which holds withdrawals that are
|
||||
// pending inclusion.
|
||||
type withdrawalQueue struct {
|
||||
|
|
@ -91,7 +92,7 @@ func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, err
|
|||
SafeBlockHash: block.Hash(),
|
||||
FinalizedBlockHash: block.Hash(),
|
||||
}
|
||||
engineAPI := NewConsensusAPI(eth)
|
||||
engineAPI := newConsensusAPIWithoutHeartbeat(eth)
|
||||
|
||||
// if genesis block, send forkchoiceUpdated to trigger transition to PoS
|
||||
if block.Number.Sign() == 0 {
|
||||
|
|
@ -143,9 +144,14 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal) error {
|
|||
feeRecipient := c.feeRecipient
|
||||
c.feeRecipientLock.Unlock()
|
||||
|
||||
// Reset to CurrentBlock in case of the chain was rewound
|
||||
if header := c.eth.BlockChain().CurrentBlock(); c.curForkchoiceState.HeadBlockHash != header.Hash() {
|
||||
finalizedHash := c.finalizedBlockHash(header.Number.Uint64())
|
||||
c.setCurrentState(header.Hash(), *finalizedHash)
|
||||
}
|
||||
|
||||
var random [32]byte
|
||||
rand.Read(random[:])
|
||||
|
||||
fcResponse, err := c.engineAPI.ForkchoiceUpdatedV2(c.curForkchoiceState, &engine.PayloadAttributes{
|
||||
Timestamp: tstamp,
|
||||
SuggestedFeeRecipient: feeRecipient,
|
||||
|
|
@ -153,27 +159,37 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal) error {
|
|||
Random: random,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error calling forkchoice update: %v", err)
|
||||
return err
|
||||
}
|
||||
if fcResponse == engine.STATUS_SYNCING {
|
||||
return errors.New("chain rewind prevented invocation of payload creation")
|
||||
}
|
||||
|
||||
envelope, err := c.engineAPI.getPayload(*fcResponse.PayloadID)
|
||||
envelope, err := c.engineAPI.getPayload(*fcResponse.PayloadID, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error retrieving payload: %v", err)
|
||||
return err
|
||||
}
|
||||
payload := envelope.ExecutionPayload
|
||||
|
||||
// mark the payload as canon
|
||||
var finalizedHash common.Hash
|
||||
if payload.Number%devEpochLength == 0 {
|
||||
finalizedHash = payload.BlockHash
|
||||
} else {
|
||||
if fh := c.finalizedBlockHash(payload.Number); fh == nil {
|
||||
return errors.New("chain rewind interrupted calculation of finalized block hash")
|
||||
} else {
|
||||
finalizedHash = *fh
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the payload as canon
|
||||
if _, err = c.engineAPI.NewPayloadV2(*payload); err != nil {
|
||||
return fmt.Errorf("failed to mark payload as canonical: %v", err)
|
||||
return err
|
||||
}
|
||||
c.curForkchoiceState = engine.ForkchoiceStateV1{
|
||||
HeadBlockHash: payload.BlockHash,
|
||||
SafeBlockHash: payload.BlockHash,
|
||||
FinalizedBlockHash: payload.BlockHash,
|
||||
}
|
||||
// mark the block containing the payload as canonical
|
||||
c.setCurrentState(payload.BlockHash, finalizedHash)
|
||||
// Mark the block containing the payload as canonical
|
||||
if _, err = c.engineAPI.ForkchoiceUpdatedV2(c.curForkchoiceState, nil); err != nil {
|
||||
return fmt.Errorf("failed to mark block as canonical: %v", err)
|
||||
return err
|
||||
}
|
||||
c.lastBlockTime = payload.Timestamp
|
||||
return nil
|
||||
|
|
@ -194,20 +210,18 @@ func (c *SimulatedBeacon) loopOnDemand() {
|
|||
case w := <-c.withdrawals.pending:
|
||||
withdrawals := append(c.withdrawals.gatherPending(9), w)
|
||||
if err := c.sealBlock(withdrawals); err != nil {
|
||||
log.Error("Error performing sealing-work", "err", err)
|
||||
return
|
||||
log.Warn("Error performing sealing work", "err", err)
|
||||
}
|
||||
case <-newTxs:
|
||||
withdrawals := c.withdrawals.gatherPending(10)
|
||||
if err := c.sealBlock(withdrawals); err != nil {
|
||||
log.Error("Error performing sealing-work", "err", err)
|
||||
return
|
||||
log.Warn("Error performing sealing work", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loopOnDemand runs the block production loop for non-zero period configuration
|
||||
// loop runs the block production loop for non-zero period configuration
|
||||
func (c *SimulatedBeacon) loop() {
|
||||
timer := time.NewTimer(0)
|
||||
for {
|
||||
|
|
@ -217,12 +231,38 @@ func (c *SimulatedBeacon) loop() {
|
|||
case <-timer.C:
|
||||
withdrawals := c.withdrawals.gatherPending(10)
|
||||
if err := c.sealBlock(withdrawals); err != nil {
|
||||
log.Error("Error performing sealing-work", "err", err)
|
||||
return
|
||||
}
|
||||
log.Warn("Error performing sealing work", "err", err)
|
||||
} else {
|
||||
timer.Reset(time.Second * time.Duration(c.period))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finalizedBlockHash returns the block hash of the finalized block corresponding to the given number
|
||||
// or nil if doesn't exist in the chain.
|
||||
func (c *SimulatedBeacon) finalizedBlockHash(number uint64) *common.Hash {
|
||||
var finalizedNumber uint64
|
||||
if number%devEpochLength == 0 {
|
||||
finalizedNumber = number
|
||||
} else {
|
||||
finalizedNumber = (number - 1) / devEpochLength * devEpochLength
|
||||
}
|
||||
|
||||
if finalizedBlock := c.eth.BlockChain().GetBlockByNumber(finalizedNumber); finalizedBlock != nil {
|
||||
fh := finalizedBlock.Hash()
|
||||
return &fh
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setCurrentState sets the current forkchoice state
|
||||
func (c *SimulatedBeacon) setCurrentState(headHash, finalizedHash common.Hash) {
|
||||
c.curForkchoiceState = engine.ForkchoiceStateV1{
|
||||
HeadBlockHash: headHash,
|
||||
SafeBlockHash: headHash,
|
||||
FinalizedBlockHash: finalizedHash,
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterSimulatedBeaconAPIs(stack *node.Node, sim *SimulatedBeacon) {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ type FullSyncTester struct {
|
|||
// stack for launching and stopping the service controlled by node.
|
||||
func RegisterFullSyncTester(stack *node.Node, backend *eth.Ethereum, block *types.Block) (*FullSyncTester, error) {
|
||||
cl := &FullSyncTester{
|
||||
api: NewConsensusAPI(backend),
|
||||
api: newConsensusAPIWithoutHeartbeat(backend),
|
||||
block: block,
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ func (tester *FullSyncTester) Start() error {
|
|||
}
|
||||
// Trigger beacon sync with the provided block header as
|
||||
// trusted chain head.
|
||||
err := tester.api.eth.Downloader().BeaconSync(downloader.FullSync, tester.block.Header(), nil)
|
||||
err := tester.api.eth.Downloader().BeaconSync(downloader.FullSync, tester.block.Header(), tester.block.Header())
|
||||
if err != nil {
|
||||
log.Info("Failed to beacon sync", "err", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -648,7 +648,7 @@ func (s *skeleton) processNewHead(head *types.Header, final *types.Header, force
|
|||
}
|
||||
if parent := rawdb.ReadSkeletonHeader(s.db, number-1); parent.Hash() != head.ParentHash {
|
||||
if force {
|
||||
log.Warn("Beacon chain forked", "ancestor", parent.Number, "hash", parent.Hash(), "want", head.ParentHash)
|
||||
log.Warn("Beacon chain forked", "ancestor", number-1, "hash", parent.Hash(), "want", head.ParentHash)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
|
|||
number = head.Number.Uint64()
|
||||
td = h.chain.GetTd(hash, number)
|
||||
)
|
||||
forkID := forkid.NewID(h.chain.Config(), genesis.Hash(), number, head.Time)
|
||||
forkID := forkid.NewID(h.chain.Config(), genesis, number, head.Time)
|
||||
if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil {
|
||||
peer.Log().Debug("Ethereum handshake failed", "err", err)
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -61,6 +61,6 @@ func StartENRUpdater(chain *core.BlockChain, ln *enode.LocalNode) {
|
|||
func currentENREntry(chain *core.BlockChain) *enrEntry {
|
||||
head := chain.CurrentHeader()
|
||||
return &enrEntry{
|
||||
ForkID: forkid.NewID(chain.Config(), chain.Genesis().Hash(), head.Number.Uint64(), head.Time),
|
||||
ForkID: forkid.NewID(chain.Config(), chain.Genesis(), head.Number.Uint64(), head.Time),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
|
|
@ -95,11 +96,15 @@ type TxPool interface {
|
|||
|
||||
// MakeProtocols constructs the P2P protocol definitions for `eth`.
|
||||
func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2p.Protocol {
|
||||
protocols := make([]p2p.Protocol, len(ProtocolVersions))
|
||||
for i, version := range ProtocolVersions {
|
||||
protocols := make([]p2p.Protocol, 0, len(ProtocolVersions))
|
||||
for _, version := range ProtocolVersions {
|
||||
version := version // Closure
|
||||
|
||||
protocols[i] = p2p.Protocol{
|
||||
// Path scheme does not support GetNodeData, don't advertise eth66 on it
|
||||
if version <= ETH66 && backend.Chain().TrieDB().Scheme() == rawdb.PathScheme {
|
||||
continue
|
||||
}
|
||||
protocols = append(protocols, p2p.Protocol{
|
||||
Name: ProtocolName,
|
||||
Version: version,
|
||||
Length: protocolLengths[version],
|
||||
|
|
@ -119,7 +124,7 @@ func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2
|
|||
},
|
||||
Attributes: []enr.Entry{currentENREntry(backend.Chain())},
|
||||
DialCandidates: dnsdisc,
|
||||
}
|
||||
})
|
||||
}
|
||||
return protocols
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func testHandshake(t *testing.T, protocol uint) {
|
|||
genesis = backend.chain.Genesis()
|
||||
head = backend.chain.CurrentBlock()
|
||||
td = backend.chain.GetTd(head.Hash(), head.Number.Uint64())
|
||||
forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis().Hash(), backend.chain.CurrentHeader().Number.Uint64(), backend.chain.CurrentHeader().Time)
|
||||
forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis(), backend.chain.CurrentHeader().Number.Uint64(), backend.chain.CurrentHeader().Time)
|
||||
)
|
||||
tests := []struct {
|
||||
code uint64
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u
|
|||
parent = root
|
||||
}
|
||||
if report {
|
||||
nodes, imgs := triedb.Size()
|
||||
_, nodes, imgs := triedb.Size() // all memory is contained within the nodes return in hashdb
|
||||
log.Info("Historical state regenerated", "block", current.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs)
|
||||
}
|
||||
return statedb, func() { triedb.Dereference(block.Root()) }, nil
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package tracers
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
|
@ -369,8 +368,8 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
|
|||
// if the relevant state is available in disk.
|
||||
var preferDisk bool
|
||||
if statedb != nil {
|
||||
s1, s2 := statedb.Database().TrieDB().Size()
|
||||
preferDisk = s1+s2 > defaultTracechainMemLimit
|
||||
s1, s2, s3 := statedb.Database().TrieDB().Size()
|
||||
preferDisk = s1+s2+s3 > defaultTracechainMemLimit
|
||||
}
|
||||
statedb, release, err = api.backend.StateAtBlock(ctx, block, reexec, statedb, false, preferDisk)
|
||||
if err != nil {
|
||||
|
|
@ -453,7 +452,7 @@ func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *
|
|||
// and returns them as a JSON object.
|
||||
func (api *API) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) {
|
||||
block := new(types.Block)
|
||||
if err := rlp.Decode(bytes.NewReader(blob), block); err != nil {
|
||||
if err := rlp.DecodeBytes(blob, block); err != nil {
|
||||
return nil, fmt.Errorf("could not decode block: %v", err)
|
||||
}
|
||||
return api.traceBlock(ctx, block, config)
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ type Database struct {
|
|||
writeDelayStartTime time.Time // The start time of the latest write stall
|
||||
writeDelayCount atomic.Int64 // Total number of write stall counts
|
||||
writeDelayTime atomic.Int64 // Total time spent in write stalls
|
||||
|
||||
writeOptions *pebble.WriteOptions
|
||||
}
|
||||
|
||||
func (d *Database) onCompactionBegin(info pebble.CompactionInfo) {
|
||||
|
|
@ -118,7 +120,7 @@ func (d *Database) onWriteStallEnd() {
|
|||
|
||||
// New returns a wrapped pebble DB object. The namespace is the prefix that the
|
||||
// metrics reporting should use for surfacing internal stats.
|
||||
func New(file string, cache int, handles int, namespace string, readonly bool) (*Database, error) {
|
||||
func New(file string, cache int, handles int, namespace string, readonly bool, ephemeral bool) (*Database, error) {
|
||||
// Ensure we have some minimal caching and file guarantees
|
||||
if cache < minCache {
|
||||
cache = minCache
|
||||
|
|
@ -145,6 +147,7 @@ func New(file string, cache int, handles int, namespace string, readonly bool) (
|
|||
fn: file,
|
||||
log: logger,
|
||||
quitChan: make(chan chan error),
|
||||
writeOptions: &pebble.WriteOptions{Sync: !ephemeral},
|
||||
}
|
||||
opt := &pebble.Options{
|
||||
// Pebble has a single combined cache area and the write
|
||||
|
|
@ -279,7 +282,7 @@ func (d *Database) Put(key []byte, value []byte) error {
|
|||
if d.closed {
|
||||
return pebble.ErrClosed
|
||||
}
|
||||
return d.db.Set(key, value, pebble.Sync)
|
||||
return d.db.Set(key, value, d.writeOptions)
|
||||
}
|
||||
|
||||
// Delete removes the key from the key-value store.
|
||||
|
|
@ -535,7 +538,7 @@ func (b *batch) Write() error {
|
|||
if b.db.closed {
|
||||
return pebble.ErrClosed
|
||||
}
|
||||
return b.b.Commit(pebble.Sync)
|
||||
return b.b.Commit(b.db.writeOptions)
|
||||
}
|
||||
|
||||
// Reset resets the batch for reuse.
|
||||
|
|
|
|||
9
go.mod
9
go.mod
|
|
@ -1,9 +1,10 @@
|
|||
module github.com/ethereum/go-ethereum
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0
|
||||
github.com/Microsoft/go-winio v0.6.1
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.2.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.1.1
|
||||
|
|
@ -12,12 +13,12 @@ require (
|
|||
github.com/btcsuite/btcd/btcec/v2 v2.2.0
|
||||
github.com/cespare/cp v0.1.0
|
||||
github.com/cloudflare/cloudflare-go v0.14.0
|
||||
github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811
|
||||
github.com/cockroachdb/pebble v0.0.0-20230821143352-55b44ac08de8
|
||||
github.com/consensys/gnark-crypto v0.10.0
|
||||
github.com/crate-crypto/go-kzg-4844 v0.3.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/deckarep/golang-set/v2 v2.1.0
|
||||
github.com/docker/docker v1.6.2
|
||||
github.com/docker/docker v24.0.5+incompatible
|
||||
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127
|
||||
github.com/ethereum/c-kzg-4844 v0.3.1
|
||||
github.com/fatih/color v1.7.0
|
||||
|
|
@ -70,7 +71,6 @@ require (
|
|||
golang.org/x/time v0.3.0
|
||||
golang.org/x/tools v0.9.1
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
|
|
@ -130,5 +130,6 @@ require (
|
|||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gotest.tools/v3 v3.5.0 // indirect
|
||||
rsc.io/tmplfunc v0.0.3 // indirect
|
||||
)
|
||||
|
|
|
|||
16
go.sum
16
go.sum
|
|
@ -13,6 +13,8 @@ github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMd
|
|||
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
|
||||
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
|
|
@ -61,15 +63,15 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
|||
github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA=
|
||||
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
|
||||
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877 h1:1MLK4YpFtIEo3ZtMA5C795Wtv5VuUnrXX7mQG+aHg6o=
|
||||
github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8=
|
||||
github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk=
|
||||
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
|
||||
github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk=
|
||||
github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM=
|
||||
github.com/cockroachdb/pebble v0.0.0-20230821143352-55b44ac08de8 h1:2fNOyQqrHSjFdqnKqSLj9W7eGTcqKujJPA8vAuT1zN8=
|
||||
github.com/cockroachdb/pebble v0.0.0-20230821143352-55b44ac08de8/go.mod h1:bynZ3gvVyhlvjLI7PT6dmZ7g76xzJ7HpxfjgkzCGz6s=
|
||||
github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
|
||||
github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
||||
|
|
@ -111,8 +113,8 @@ github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnm
|
|||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/docker/docker v1.6.2 h1:HlFGsy+9/xrgMmhmN+NGhCc5SHGJ7I+kHosRR1xc/aI=
|
||||
github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
|
||||
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5RVoRccG8a5DhOdWdQ4zN62zzo=
|
||||
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||
|
|
@ -653,8 +655,6 @@ gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
@ -670,6 +670,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
||||
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
|
||||
|
|
|
|||
|
|
@ -88,7 +88,9 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
response := h.Schema.Exec(ctx, params.Query, params.OperationName, params.Variables)
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
}
|
||||
responded.Do(func() {
|
||||
responseJSON, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -1162,12 +1162,30 @@ func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrO
|
|||
return result.Return(), result.Err
|
||||
}
|
||||
|
||||
// executeEstimate is a helper that executes the transaction under a given gas limit and returns
|
||||
// true if the transaction fails for a reason that might be related to not enough gas. A non-nil
|
||||
// error means execution failed due to reasons unrelated to the gas limit.
|
||||
func executeEstimate(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, gasCap uint64, gasLimit uint64) (bool, *core.ExecutionResult, error) {
|
||||
args.Gas = (*hexutil.Uint64)(&gasLimit)
|
||||
result, err := doCall(ctx, b, args, state, header, nil, nil, 0, gasCap)
|
||||
if err != nil {
|
||||
if errors.Is(err, core.ErrIntrinsicGas) {
|
||||
return true, nil, nil // Special case, raise gas limit
|
||||
}
|
||||
return true, nil, err // Bail out
|
||||
}
|
||||
return result.Failed(), result, nil
|
||||
}
|
||||
|
||||
// DoEstimateGas returns the lowest possible gas limit that allows the transaction to run
|
||||
// successfully at block `blockNrOrHash`. It returns error if the transaction would revert, or if
|
||||
// there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil &
|
||||
// non-zero) and `gasCap` (if non-zero).
|
||||
func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) {
|
||||
// Binary search the gas requirement, as it may be higher than the amount used
|
||||
// Binary search the gas limit, as it may need to be higher than the amount used
|
||||
var (
|
||||
lo uint64 = params.TxGas - 1
|
||||
hi uint64
|
||||
cap uint64
|
||||
lo uint64 // lowest-known gas limit where tx execution fails
|
||||
hi uint64 // lowest-known gas limit where tx execution succeeds
|
||||
)
|
||||
// Use zero address if sender unspecified.
|
||||
if args.From == nil {
|
||||
|
|
@ -1198,16 +1216,17 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
|||
} else {
|
||||
feeCap = common.Big0
|
||||
}
|
||||
|
||||
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||
if state == nil || err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := overrides.Apply(state); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Recap the highest gas limit with account's available balance.
|
||||
if feeCap.BitLen() != 0 {
|
||||
state, _, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = overrides.Apply(state)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
balance := state.GetBalance(*args.From) // from can't be nil
|
||||
available := new(big.Int).Set(balance)
|
||||
if args.Value != nil {
|
||||
|
|
@ -1234,50 +1253,10 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
|||
log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
|
||||
hi = gasCap
|
||||
}
|
||||
cap = hi
|
||||
|
||||
// Create a helper to check if a gas allowance results in an executable transaction
|
||||
executable := func(gas uint64, state *state.StateDB, header *types.Header) (bool, *core.ExecutionResult, error) {
|
||||
args.Gas = (*hexutil.Uint64)(&gas)
|
||||
|
||||
result, err := doCall(ctx, b, args, state, header, nil, nil, 0, gasCap)
|
||||
if err != nil {
|
||||
if errors.Is(err, core.ErrIntrinsicGas) {
|
||||
return true, nil, nil // Special case, raise gas limit
|
||||
}
|
||||
return true, nil, err // Bail out
|
||||
}
|
||||
return result.Failed(), result, nil
|
||||
}
|
||||
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||
if state == nil || err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = overrides.Apply(state)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Execute the binary search and hone in on an executable gas limit
|
||||
for lo+1 < hi {
|
||||
s := state.Copy()
|
||||
mid := (hi + lo) / 2
|
||||
failed, _, err := executable(mid, s, header)
|
||||
|
||||
// If the error is not nil(consensus error), it means the provided message
|
||||
// call or transaction will never be accepted no matter how much gas it is
|
||||
// assigned. Return the error directly, don't struggle any more.
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if failed {
|
||||
lo = mid
|
||||
} else {
|
||||
hi = mid
|
||||
}
|
||||
}
|
||||
// Reject the transaction as invalid if it still fails at the highest allowance
|
||||
if hi == cap {
|
||||
failed, result, err := executable(hi, state, header)
|
||||
// We first execute the transaction at the highest allowable gas limit, since if this fails we
|
||||
// can return error immediately.
|
||||
failed, result, err := executeEstimate(ctx, b, args, state.Copy(), header, gasCap, hi)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
@ -1288,15 +1267,44 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
|||
}
|
||||
return 0, result.Err
|
||||
}
|
||||
// Otherwise, the specified gas cap is too low
|
||||
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
||||
return 0, fmt.Errorf("gas required exceeds allowance (%d)", hi)
|
||||
}
|
||||
// For almost any transaction, the gas consumed by the unconstrained execution above
|
||||
// lower-bounds the gas limit required for it to succeed. One exception is those txs that
|
||||
// explicitly check gas remaining in order to successfully execute within a given limit, but we
|
||||
// probably don't want to return a lowest possible gas limit for these cases anyway.
|
||||
lo = result.UsedGas - 1
|
||||
|
||||
// Binary search for the smallest gas limit that allows the tx to execute successfully.
|
||||
for lo+1 < hi {
|
||||
mid := (hi + lo) / 2
|
||||
if mid > lo*2 {
|
||||
// Most txs don't need much higher gas limit than their gas used, and most txs don't
|
||||
// require near the full block limit of gas, so the selection of where to bisect the
|
||||
// range here is skewed to favor the low side.
|
||||
mid = lo * 2
|
||||
}
|
||||
failed, _, err = executeEstimate(ctx, b, args, state.Copy(), header, gasCap, mid)
|
||||
if err != nil {
|
||||
// This should not happen under normal conditions since if we make it this far the
|
||||
// transaction had run without error at least once before.
|
||||
log.Error("execution error in estimate gas", "err", err)
|
||||
return 0, err
|
||||
}
|
||||
if failed {
|
||||
lo = mid
|
||||
} else {
|
||||
hi = mid
|
||||
}
|
||||
}
|
||||
return hexutil.Uint64(hi), nil
|
||||
}
|
||||
|
||||
// EstimateGas returns an estimate of the amount of gas needed to execute the
|
||||
// given transaction against the current pending block.
|
||||
// EstimateGas returns the lowest possible gas limit that allows the transaction to run
|
||||
// successfully at block `blockNrOrHash`, or the latest block if `blockNrOrHash` is unspecified. It
|
||||
// returns error if the transaction would revert or if there are unexpected failures. The returned
|
||||
// value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap
|
||||
// configuration (if non-zero).
|
||||
func (s *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Uint64, error) {
|
||||
bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||
if blockNrOrHash != nil {
|
||||
|
|
@ -1325,15 +1333,21 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
|
|||
"transactionsRoot": head.TxHash,
|
||||
"receiptsRoot": head.ReceiptHash,
|
||||
}
|
||||
|
||||
if head.BaseFee != nil {
|
||||
result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee)
|
||||
}
|
||||
|
||||
if head.WithdrawalsHash != nil {
|
||||
result["withdrawalsRoot"] = head.WithdrawalsHash
|
||||
}
|
||||
|
||||
if head.BlobGasUsed != nil {
|
||||
result["blobGasUsed"] = hexutil.Uint64(*head.BlobGasUsed)
|
||||
}
|
||||
if head.ExcessBlobGas != nil {
|
||||
result["excessBlobGas"] = hexutil.Uint64(*head.ExcessBlobGas)
|
||||
}
|
||||
if head.ParentBeaconRoot != nil {
|
||||
result["parentBeaconBlockRoot"] = head.ParentBeaconRoot
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -1399,6 +1413,7 @@ type RPCTransaction struct {
|
|||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"`
|
||||
GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"`
|
||||
MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
Input hexutil.Bytes `json:"input"`
|
||||
Nonce hexutil.Uint64 `json:"nonce"`
|
||||
|
|
@ -1408,6 +1423,7 @@ type RPCTransaction struct {
|
|||
Type hexutil.Uint64 `json:"type"`
|
||||
Accesses *types.AccessList `json:"accessList,omitempty"`
|
||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
|
|
@ -1465,15 +1481,43 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
|
|||
// if the transaction has been mined, compute the effective gas price
|
||||
if baseFee != nil && blockHash != (common.Hash{}) {
|
||||
// price = min(gasTipCap + baseFee, gasFeeCap)
|
||||
price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
|
||||
result.GasPrice = (*hexutil.Big)(price)
|
||||
result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee))
|
||||
} else {
|
||||
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
|
||||
}
|
||||
|
||||
case types.BlobTxType:
|
||||
al := tx.AccessList()
|
||||
yparity := hexutil.Uint64(v.Sign())
|
||||
result.Accesses = &al
|
||||
result.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||
result.YParity = &yparity
|
||||
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
|
||||
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
|
||||
// if the transaction has been mined, compute the effective gas price
|
||||
if baseFee != nil && blockHash != (common.Hash{}) {
|
||||
result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee))
|
||||
} else {
|
||||
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
|
||||
}
|
||||
result.MaxFeePerBlobGas = (*hexutil.Big)(tx.BlobGasFeeCap())
|
||||
result.BlobVersionedHashes = tx.BlobHashes()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// effectiveGasPrice computes the transaction gas fee, based on the given basefee value.
|
||||
//
|
||||
// price = min(gasTipCap + baseFee, gasFeeCap)
|
||||
func effectiveGasPrice(tx *types.Transaction, baseFee *big.Int) *big.Int {
|
||||
fee := tx.GasTipCap()
|
||||
fee = fee.Add(fee, baseFee)
|
||||
if tx.GasTipCapIntCmp(fee) < 0 {
|
||||
return tx.GasTipCap()
|
||||
}
|
||||
return fee
|
||||
}
|
||||
|
||||
// NewRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
|
||||
func NewRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction {
|
||||
var (
|
||||
|
|
@ -1778,6 +1822,11 @@ func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber u
|
|||
fields["logs"] = []*types.Log{}
|
||||
}
|
||||
|
||||
if tx.Type() == types.BlobTxType {
|
||||
fields["blobGasUsed"] = hexutil.Uint64(receipt.BlobGasUsed)
|
||||
fields["blobGasPrice"] = (*hexutil.Big)(receipt.BlobGasPrice)
|
||||
}
|
||||
|
||||
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
|
||||
if receipt.ContractAddress != (common.Address{}) {
|
||||
fields["contractAddress"] = receipt.ContractAddress
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
25
internal/ethapi/testdata/eth_getBlockByHash-hash-1.json
vendored
Normal file
25
internal/ethapi/testdata/eth_getBlockByHash-hash-1.json
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"baseFeePerGas": "0x342770c0",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x1",
|
||||
"parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
|
||||
"timestamp": "0xa",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
"0x644a31c354391520d00e95b9affbbb010fc79ac268144ab8e28207f4cf51097e"
|
||||
],
|
||||
"transactionsRoot": "0xca0ebcce920d2cdfbf9e1dbe90ed3441a1a576f344bd80e60508da814916f4e7",
|
||||
"uncles": []
|
||||
}
|
||||
1
internal/ethapi/testdata/eth_getBlockByHash-hash-empty-fullTx.json
vendored
Normal file
1
internal/ethapi/testdata/eth_getBlockByHash-hash-empty-fullTx.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
null
|
||||
23
internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json
vendored
Normal file
23
internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"baseFeePerGas": "0x3b9aca00",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x0",
|
||||
"hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x200",
|
||||
"stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
|
||||
"timestamp": "0x0",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [],
|
||||
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"uncles": []
|
||||
}
|
||||
41
internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json
vendored
Normal file
41
internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"baseFeePerGas": "0x121a9cca",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x9",
|
||||
"parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
|
||||
"timestamp": "0x5a",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
{
|
||||
"blockHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"blockNumber": "0x9",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gas": "0x5208",
|
||||
"gasPrice": "0x121a9cca",
|
||||
"hash": "0xecd155a61a5734b3efab75924e3ae34026c7c4133d8c2a46122bd03d7d199725",
|
||||
"input": "0x",
|
||||
"nonce": "0x8",
|
||||
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
|
||||
"transactionIndex": "0x0",
|
||||
"value": "0x3e8",
|
||||
"type": "0x0",
|
||||
"v": "0x1b",
|
||||
"r": "0xc6028b8e983d62fa8542f8a7633fb23cc941be2c897134352d95a7d9b19feafd",
|
||||
"s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d"
|
||||
}
|
||||
],
|
||||
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5",
|
||||
"uncles": []
|
||||
}
|
||||
25
internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json
vendored
Normal file
25
internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"baseFeePerGas": "0xfdc7303",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0xa",
|
||||
"parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
|
||||
"timestamp": "0x64",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
"0x3ee4094ca1e0b07a66dd616a057e081e53144ca7e9685a126fd4dda9ca042644"
|
||||
],
|
||||
"transactionsRoot": "0xb0893d21a4a44dc26a962a6e91abae66df87fb61ac9c60e936aee89c76331445",
|
||||
"uncles": []
|
||||
}
|
||||
1
internal/ethapi/testdata/eth_getBlockByHash-hash-pending-fullTx.json
vendored
Normal file
1
internal/ethapi/testdata/eth_getBlockByHash-hash-pending-fullTx.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
null
|
||||
1
internal/ethapi/testdata/eth_getBlockByHash-hash-pending.json
vendored
Normal file
1
internal/ethapi/testdata/eth_getBlockByHash-hash-pending.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
null
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue