resolve merge conflicts

This commit is contained in:
Sina Mahmoodi 2023-09-13 16:17:36 +02:00
commit 6ee4fb8eac
398 changed files with 13412 additions and 3872 deletions

View file

@ -13,7 +13,7 @@ jobs:
- stage: lint - stage: lint
os: linux os: linux
dist: bionic dist: bionic
go: 1.20.x go: 1.21.x
env: env:
- lint - lint
git: git:
@ -28,7 +28,7 @@ jobs:
os: linux os: linux
arch: amd64 arch: amd64
dist: bionic dist: bionic
go: 1.20.x go: 1.21.x
env: env:
- docker - docker
services: services:
@ -45,7 +45,7 @@ jobs:
os: linux os: linux
arch: arm64 arch: arm64
dist: bionic dist: bionic
go: 1.20.x go: 1.21.x
env: env:
- docker - docker
services: services:
@ -63,10 +63,9 @@ jobs:
os: linux os: linux
dist: bionic dist: bionic
sudo: required sudo: required
go: 1.20.x go: 1.21.x
env: env:
- azure-linux - azure-linux
- GO111MODULE=on
git: git:
submodules: false # avoid cloning ethereum/tests submodules: false # avoid cloning ethereum/tests
addons: addons:
@ -97,10 +96,9 @@ jobs:
- stage: build - stage: build
if: type = push if: type = push
os: osx os: osx
go: 1.20.x go: 1.21.x
env: env:
- azure-osx - azure-osx
- GO111MODULE=on
git: git:
submodules: false # avoid cloning ethereum/tests submodules: false # avoid cloning ethereum/tests
script: script:
@ -112,41 +110,34 @@ jobs:
os: linux os: linux
arch: amd64 arch: amd64
dist: bionic dist: bionic
go: 1.20.x go: 1.21.x
env:
- GO111MODULE=on
script: script:
- go run build/ci.go test $TEST_PACKAGES - travis_wait 30 go run build/ci.go test $TEST_PACKAGES
- stage: build - stage: build
if: type = pull_request if: type = pull_request
os: linux os: linux
arch: arm64 arch: arm64
dist: bionic dist: bionic
go: 1.19.x go: 1.20.x
env:
- GO111MODULE=on
script: script:
- go run build/ci.go test $TEST_PACKAGES - travis_wait 30 go run build/ci.go test $TEST_PACKAGES
- stage: build - stage: build
os: linux os: linux
dist: bionic dist: bionic
go: 1.19.x go: 1.20.x
env:
- GO111MODULE=on
script: 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 # This builder does the Ubuntu PPA nightly uploads
- stage: build - stage: build
if: type = cron || (type = push && tag ~= /^v[0-9]/) if: type = cron || (type = push && tag ~= /^v[0-9]/)
os: linux os: linux
dist: bionic dist: bionic
go: 1.20.x go: 1.21.x
env: env:
- ubuntu-ppa - ubuntu-ppa
- GO111MODULE=on
git: git:
submodules: false # avoid cloning ethereum/tests submodules: false # avoid cloning ethereum/tests
addons: addons:
@ -167,10 +158,9 @@ jobs:
if: type = cron if: type = cron
os: linux os: linux
dist: bionic dist: bionic
go: 1.20.x go: 1.21.x
env: env:
- azure-purge - azure-purge
- GO111MODULE=on
git: git:
submodules: false # avoid cloning ethereum/tests submodules: false # avoid cloning ethereum/tests
script: script:
@ -181,9 +171,7 @@ jobs:
if: type = cron if: type = cron
os: linux os: linux
dist: bionic dist: bionic
go: 1.20.x go: 1.21.x
env:
- GO111MODULE=on
script: script:
- go run build/ci.go test -race $TEST_PACKAGES - travis_wait 30 go run build/ci.go test -race $TEST_PACKAGES

View file

@ -4,7 +4,7 @@ ARG VERSION=""
ARG BUILDNUM="" ARG BUILDNUM=""
# Build Geth in a stock Go builder container # Build Geth in a stock Go builder container
FROM golang:1.20-alpine as builder FROM golang:1.21-alpine as builder
RUN apk add --no-cache gcc musl-dev linux-headers git RUN apk add --no-cache gcc musl-dev linux-headers git

View file

@ -6,7 +6,7 @@
GOBIN = ./build/bin GOBIN = ./build/bin
GO ?= latest GO ?= latest
GORUN = env GO111MODULE=on go run GORUN = go run
geth: geth:
$(GORUN) build/ci.go install ./cmd/geth $(GORUN) build/ci.go install ./cmd/geth
@ -23,7 +23,7 @@ lint: ## Run linters.
$(GORUN) build/ci.go lint $(GORUN) build/ci.go lint
clean: clean:
env GO111MODULE=on go clean -cache go clean -cache
rm -fr build/_workspace/pkg/ $(GOBIN)/* rm -fr build/_workspace/pkg/ $(GOBIN)/*
# The devtools target installs tools required for 'go generate'. # The devtools target installs tools required for 'go generate'.

View file

@ -22,6 +22,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -246,24 +247,65 @@ func (abi *ABI) HasReceive() bool {
// revertSelector is a special function selector for revert reason unpacking. // revertSelector is a special function selector for revert reason unpacking.
var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4] 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 // UnpackRevert resolves the abi-encoded revert reason. According to the solidity
// spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert, // 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 // the provided revert reason is abi-encoded as if it were a call to function
// `Error(string)`. So it's a special tool for it. // `Error(string)` or `Panic(uint256)`. So it's a special tool for it.
func UnpackRevert(data []byte) (string, error) { func UnpackRevert(data []byte) (string, error) {
if len(data) < 4 { if len(data) < 4 {
return "", errors.New("invalid data for unpacking") return "", errors.New("invalid data for unpacking")
} }
if !bytes.Equal(data[:4], revertSelector) { switch {
case bytes.Equal(data[:4], revertSelector):
typ, err := NewType("string", "", nil)
if err != nil {
return "", err
}
unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
if err != nil {
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") return "", errors.New("invalid data for unpacking")
} }
typ, err := NewType("string", "", nil)
if err != nil {
return "", err
}
unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
if err != nil {
return "", err
}
return unpacked[0].(string), nil
} }

View file

@ -1173,6 +1173,8 @@ func TestUnpackRevert(t *testing.T) {
{"", "", errors.New("invalid data for unpacking")}, {"", "", errors.New("invalid data for unpacking")},
{"08c379a1", "", errors.New("invalid data for unpacking")}, {"08c379a1", "", errors.New("invalid data for unpacking")},
{"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil}, {"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
{"4e487b710000000000000000000000000000000000000000000000000000000000000000", "generic panic", nil},
{"4e487b7100000000000000000000000000000000000000000000000000000000000000ff", "unknown panic code: 0xff", nil},
} }
for index, c := range cases { for index, c := range cases {
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) { t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {

View file

@ -892,7 +892,7 @@ func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (typ
} }
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) { func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
logs := rawdb.ReadLogs(fb.db, hash, number, fb.bc.Config()) logs := rawdb.ReadLogs(fb.db, hash, number)
return logs, nil return logs, nil
} }

View file

@ -325,7 +325,7 @@ var (
if err != nil { if err != nil {
return *outstruct, err return *outstruct, err
} }
{{range $i, $t := .Normalized.Outputs}} {{range $i, $t := .Normalized.Outputs}}
outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}} outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
return *outstruct, err return *outstruct, err
@ -335,7 +335,7 @@ var (
} }
{{range $i, $t := .Normalized.Outputs}} {{range $i, $t := .Normalized.Outputs}}
out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}} out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err
{{end}} {{end}}
} }
@ -378,7 +378,7 @@ var (
} }
{{end}} {{end}}
{{if .Fallback}} {{if .Fallback}}
// Fallback is a paid mutator transaction binding the contract fallback function. // Fallback is a paid mutator transaction binding the contract fallback function.
// //
// Solidity: {{.Fallback.Original.String}} // Solidity: {{.Fallback.Original.String}}
@ -392,16 +392,16 @@ var (
func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) { func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata) return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
} }
// Fallback is a paid mutator transaction binding the contract fallback function. // Fallback is a paid mutator transaction binding the contract fallback function.
// //
// Solidity: {{.Fallback.Original.String}} // Solidity: {{.Fallback.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata) return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
} }
{{end}} {{end}}
{{if .Receive}} {{if .Receive}}
// Receive is a paid mutator transaction binding the contract receive function. // Receive is a paid mutator transaction binding the contract receive function.
// //
// Solidity: {{.Receive.Original.String}} // Solidity: {{.Receive.Original.String}}
@ -415,9 +415,9 @@ var (
func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) { func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts) return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
} }
// Receive is a paid mutator transaction binding the contract receive function. // Receive is a paid mutator transaction binding the contract receive function.
// //
// Solidity: {{.Receive.Original.String}} // Solidity: {{.Receive.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) { func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts) return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)

View file

@ -32,7 +32,7 @@ type Error struct {
str string str string
// Sig contains the string signature according to the ABI spec. // Sig contains the string signature according to the ABI spec.
// e.g. error foo(uint32 a, int b) = "foo(uint32,int256)" // e.g. error foo(uint32 a, int b) = "foo(uint32,int256)"
// Please note that "int" is substitute for its canonical representation "int256" // Please note that "int" is substitute for its canonical representation "int256"
Sig string Sig string

View file

@ -127,11 +127,12 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
state = state + " " state = state + " "
} }
identity := fmt.Sprintf("function %v", rawName) identity := fmt.Sprintf("function %v", rawName)
if funType == Fallback { switch funType {
case Fallback:
identity = "fallback" identity = "fallback"
} else if funType == Receive { case Receive:
identity = "receive" identity = "receive"
} else if funType == Constructor { case Constructor:
identity = "constructor" identity = "constructor"
} }
str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", ")) str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))

View file

@ -84,11 +84,12 @@ func TestMethodString(t *testing.T) {
for _, test := range table { for _, test := range table {
var got string var got string
if test.method == "fallback" { switch test.method {
case "fallback":
got = abi.Fallback.String() got = abi.Fallback.String()
} else if test.method == "receive" { case "receive":
got = abi.Receive.String() got = abi.Receive.String()
} else { default:
got = abi.Methods[test.method].String() got = abi.Methods[test.method].String()
} }
if got != test.expectation { if got != test.expectation {

View file

@ -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 // this value will become our slice or our array, depending on the type
var refSlice reflect.Value var refSlice reflect.Value
if t.T == SliceTy { switch t.T {
case SliceTy:
// declare our slice // declare our slice
refSlice = reflect.MakeSlice(t.GetType(), size, size) refSlice = reflect.MakeSlice(t.GetType(), size, size)
} else if t.T == ArrayTy { case ArrayTy:
// declare our array // declare our array
refSlice = reflect.New(t.GetType()).Elem() refSlice = reflect.New(t.GetType()).Elem()
} else { default:
return nil, errors.New("abi: invalid type in array/slice unpacking stage") return nil, errors.New("abi: invalid type in array/slice unpacking stage")
} }

View file

@ -40,8 +40,8 @@ import (
const minReloadInterval = 2 * time.Second const minReloadInterval = 2 * time.Second
// byURL defines the sorting order for accounts. // byURL defines the sorting order for accounts.
func byURL(a, b accounts.Account) bool { func byURL(a, b accounts.Account) int {
return a.URL.Cmp(b.URL) < 0 return a.URL.Cmp(b.URL)
} }
// AmbiguousAddrError is returned when attempting to unlock // AmbiguousAddrError is returned when attempting to unlock

View file

@ -397,19 +397,19 @@ func TestImportRace(t *testing.T) {
t.Fatalf("failed to export account: %v", acc) t.Fatalf("failed to export account: %v", acc)
} }
_, ks2 := tmpKeyStore(t, true) _, ks2 := tmpKeyStore(t, true)
var atom uint32 var atom atomic.Uint32
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(2) wg.Add(2)
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
go func() { go func() {
defer wg.Done() defer wg.Done()
if _, err := ks2.Import(json, "new", "new"); err != nil { if _, err := ks2.Import(json, "new", "new"); err != nil {
atomic.AddUint32(&atom, 1) atom.Add(1)
} }
}() }()
} }
wg.Wait() wg.Wait()
if atom != 1 { if atom.Load() != 1 {
t.Errorf("Import is racy") t.Errorf("Import is racy")
} }
} }

View file

@ -63,9 +63,9 @@ type Hub struct {
stateLock sync.RWMutex // Protects the internals of the hub from racey access stateLock sync.RWMutex // Protects the internals of the hub from racey access
// TODO(karalabe): remove if hotplug lands on Windows // TODO(karalabe): remove if hotplug lands on Windows
commsPend int // Number of operations blocking enumeration commsPend int // Number of operations blocking enumeration
commsLock sync.Mutex // Lock protecting the pending counter and enumeration commsLock sync.Mutex // Lock protecting the pending counter and enumeration
enumFails uint32 // Number of times enumeration has failed enumFails atomic.Uint32 // Number of times enumeration has failed
} }
// NewLedgerHub creates a new hardware wallet manager for Ledger devices. // NewLedgerHub creates a new hardware wallet manager for Ledger devices.
@ -151,7 +151,7 @@ func (hub *Hub) refreshWallets() {
return return
} }
// If USB enumeration is continually failing, don't keep trying indefinitely // If USB enumeration is continually failing, don't keep trying indefinitely
if atomic.LoadUint32(&hub.enumFails) > 2 { if hub.enumFails.Load() > 2 {
return return
} }
// Retrieve the current list of USB wallet devices // Retrieve the current list of USB wallet devices
@ -172,7 +172,7 @@ func (hub *Hub) refreshWallets() {
} }
infos, err := usb.Enumerate(hub.vendorID, 0) infos, err := usb.Enumerate(hub.vendorID, 0)
if err != nil { if err != nil {
failcount := atomic.AddUint32(&hub.enumFails, 1) failcount := hub.enumFails.Add(1)
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
// See rationale before the enumeration why this is needed and only on Linux. // See rationale before the enumeration why this is needed and only on Linux.
hub.commsLock.Unlock() hub.commsLock.Unlock()
@ -181,7 +181,7 @@ func (hub *Hub) refreshWallets() {
"vendor", hub.vendorID, "failcount", failcount, "err", err) "vendor", hub.vendorID, "failcount", failcount, "err", err)
return return
} }
atomic.StoreUint32(&hub.enumFails, 0) hub.enumFails.Store(0)
for _, info := range infos { for _, info := range infos {
for _, id := range hub.productIDs { for _, id := range hub.productIDs {

View file

@ -80,6 +80,7 @@ var (
InvalidPayloadAttributes = &EngineAPIError{code: -38003, msg: "Invalid payload attributes"} InvalidPayloadAttributes = &EngineAPIError{code: -38003, msg: "Invalid payload attributes"}
TooLargeRequest = &EngineAPIError{code: -38004, msg: "Too large request"} TooLargeRequest = &EngineAPIError{code: -38004, msg: "Too large request"}
InvalidParams = &EngineAPIError{code: -32602, msg: "Invalid parameters"} InvalidParams = &EngineAPIError{code: -32602, msg: "Invalid parameters"}
UnsupportedFork = &EngineAPIError{code: -38005, msg: "Unsupported fork"}
STATUS_INVALID = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: INVALID}, PayloadID: nil} STATUS_INVALID = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: INVALID}, PayloadID: nil}
STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil} STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil}

View file

@ -20,12 +20,14 @@ func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
Random common.Hash `json:"prevRandao" gencodec:"required"` Random common.Hash `json:"prevRandao" gencodec:"required"`
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
} }
var enc PayloadAttributes var enc PayloadAttributes
enc.Timestamp = hexutil.Uint64(p.Timestamp) enc.Timestamp = hexutil.Uint64(p.Timestamp)
enc.Random = p.Random enc.Random = p.Random
enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient
enc.Withdrawals = p.Withdrawals enc.Withdrawals = p.Withdrawals
enc.BeaconRoot = p.BeaconRoot
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -36,6 +38,7 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
Random *common.Hash `json:"prevRandao" gencodec:"required"` Random *common.Hash `json:"prevRandao" gencodec:"required"`
SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
} }
var dec PayloadAttributes var dec PayloadAttributes
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -56,5 +59,8 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
if dec.Withdrawals != nil { if dec.Withdrawals != nil {
p.Withdrawals = dec.Withdrawals p.Withdrawals = dec.Withdrawals
} }
if dec.BeaconRoot != nil {
p.BeaconRoot = dec.BeaconRoot
}
return nil return nil
} }

View file

@ -32,8 +32,8 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
BlockHash common.Hash `json:"blockHash" gencodec:"required"` BlockHash common.Hash `json:"blockHash" gencodec:"required"`
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
DataGasUsed *hexutil.Uint64 `json:"dataGasUsed"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
ExcessDataGas *hexutil.Uint64 `json:"excessDataGas"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
} }
var enc ExecutableData var enc ExecutableData
enc.ParentHash = e.ParentHash enc.ParentHash = e.ParentHash
@ -56,8 +56,8 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
} }
} }
enc.Withdrawals = e.Withdrawals enc.Withdrawals = e.Withdrawals
enc.DataGasUsed = (*hexutil.Uint64)(e.DataGasUsed) enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
enc.ExcessDataGas = (*hexutil.Uint64)(e.ExcessDataGas) enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -79,8 +79,8 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
BlockHash *common.Hash `json:"blockHash" gencodec:"required"` BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
DataGasUsed *hexutil.Uint64 `json:"dataGasUsed"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
ExcessDataGas *hexutil.Uint64 `json:"excessDataGas"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
} }
var dec ExecutableData var dec ExecutableData
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -148,11 +148,11 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
if dec.Withdrawals != nil { if dec.Withdrawals != nil {
e.Withdrawals = dec.Withdrawals e.Withdrawals = dec.Withdrawals
} }
if dec.DataGasUsed != nil { if dec.BlobGasUsed != nil {
e.DataGasUsed = (*uint64)(dec.DataGasUsed) e.BlobGasUsed = (*uint64)(dec.BlobGasUsed)
} }
if dec.ExcessDataGas != nil { if dec.ExcessBlobGas != nil {
e.ExcessDataGas = (*uint64)(dec.ExcessDataGas) e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
} }
return nil return nil
} }

View file

@ -18,11 +18,13 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) {
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
Override bool `json:"shouldOverrideBuilder"`
} }
var enc ExecutionPayloadEnvelope var enc ExecutionPayloadEnvelope
enc.ExecutionPayload = e.ExecutionPayload enc.ExecutionPayload = e.ExecutionPayload
enc.BlockValue = (*hexutil.Big)(e.BlockValue) enc.BlockValue = (*hexutil.Big)(e.BlockValue)
enc.BlobsBundle = e.BlobsBundle enc.BlobsBundle = e.BlobsBundle
enc.Override = e.Override
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -32,6 +34,7 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
Override *bool `json:"shouldOverrideBuilder"`
} }
var dec ExecutionPayloadEnvelope var dec ExecutionPayloadEnvelope
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -48,5 +51,8 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
if dec.BlobsBundle != nil { if dec.BlobsBundle != nil {
e.BlobsBundle = dec.BlobsBundle e.BlobsBundle = dec.BlobsBundle
} }
if dec.Override != nil {
e.Override = *dec.Override
}
return nil return nil
} }

View file

@ -23,7 +23,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
@ -36,6 +35,7 @@ type PayloadAttributes struct {
Random common.Hash `json:"prevRandao" gencodec:"required"` Random common.Hash `json:"prevRandao" gencodec:"required"`
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
} }
// JSON type overrides for PayloadAttributes. // JSON type overrides for PayloadAttributes.
@ -62,8 +62,8 @@ type ExecutableData struct {
BlockHash common.Hash `json:"blockHash" gencodec:"required"` BlockHash common.Hash `json:"blockHash" gencodec:"required"`
Transactions [][]byte `json:"transactions" gencodec:"required"` Transactions [][]byte `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
DataGasUsed *uint64 `json:"dataGasUsed"` BlobGasUsed *uint64 `json:"blobGasUsed"`
ExcessDataGas *uint64 `json:"excessDataGas"` ExcessBlobGas *uint64 `json:"excessBlobGas"`
} }
// JSON type overrides for executableData. // JSON type overrides for executableData.
@ -76,8 +76,8 @@ type executableDataMarshaling struct {
ExtraData hexutil.Bytes ExtraData hexutil.Bytes
LogsBloom hexutil.Bytes LogsBloom hexutil.Bytes
Transactions []hexutil.Bytes Transactions []hexutil.Bytes
DataGasUsed *hexutil.Uint64 BlobGasUsed *hexutil.Uint64
ExcessDataGas *hexutil.Uint64 ExcessBlobGas *hexutil.Uint64
} }
//go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go //go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go
@ -86,6 +86,7 @@ type ExecutionPayloadEnvelope struct {
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
BlockValue *big.Int `json:"blockValue" gencodec:"required"` BlockValue *big.Int `json:"blockValue" gencodec:"required"`
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
Override bool `json:"shouldOverrideBuilder"`
} }
type BlobsBundleV1 struct { type BlobsBundleV1 struct {
@ -172,7 +173,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
// and that the blockhash of the constructed block matches the parameters. Nil // and that the blockhash of the constructed block matches the parameters. Nil
// Withdrawals value will propagate through the returned block. Empty // Withdrawals value will propagate through the returned block. Empty
// Withdrawals value must be passed via non-nil, length 0 value in params. // Withdrawals value must be passed via non-nil, length 0 value in 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) txs, err := decodeTransactions(params.Transactions)
if err != nil { if err != nil {
return nil, err return nil, err
@ -208,24 +209,25 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash)
withdrawalsRoot = &h withdrawalsRoot = &h
} }
header := &types.Header{ header := &types.Header{
ParentHash: params.ParentHash, ParentHash: params.ParentHash,
UncleHash: types.EmptyUncleHash, UncleHash: types.EmptyUncleHash,
Coinbase: params.FeeRecipient, Coinbase: params.FeeRecipient,
Root: params.StateRoot, Root: params.StateRoot,
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
ReceiptHash: params.ReceiptsRoot, ReceiptHash: params.ReceiptsRoot,
Bloom: types.BytesToBloom(params.LogsBloom), Bloom: types.BytesToBloom(params.LogsBloom),
Difficulty: common.Big0, Difficulty: common.Big0,
Number: new(big.Int).SetUint64(params.Number), Number: new(big.Int).SetUint64(params.Number),
GasLimit: params.GasLimit, GasLimit: params.GasLimit,
GasUsed: params.GasUsed, GasUsed: params.GasUsed,
Time: params.Timestamp, Time: params.Timestamp,
BaseFee: params.BaseFeePerGas, BaseFee: params.BaseFeePerGas,
Extra: params.ExtraData, Extra: params.ExtraData,
MixDigest: params.Random, MixDigest: params.Random,
WithdrawalsHash: withdrawalsRoot, WithdrawalsHash: withdrawalsRoot,
ExcessDataGas: params.ExcessDataGas, ExcessBlobGas: params.ExcessBlobGas,
DataGasUsed: params.DataGasUsed, BlobGasUsed: params.BlobGasUsed,
ParentBeaconRoot: beaconRoot,
} }
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals) block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
if block.Hash() != params.BlockHash { if block.Hash() != params.BlockHash {
@ -236,7 +238,7 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash)
// BlockToExecutableData constructs the ExecutableData structure by filling the // BlockToExecutableData constructs the ExecutableData structure by filling the
// fields from the given block. It assumes the given block is post-merge block. // 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{ data := &ExecutableData{
BlockHash: block.Hash(), BlockHash: block.Hash(),
ParentHash: block.ParentHash(), ParentHash: block.ParentHash(),
@ -253,20 +255,22 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, blobs []kzg4844.Bl
Random: block.MixDigest(), Random: block.MixDigest(),
ExtraData: block.Extra(), ExtraData: block.Extra(),
Withdrawals: block.Withdrawals(), Withdrawals: block.Withdrawals(),
DataGasUsed: block.DataGasUsed(), BlobGasUsed: block.BlobGasUsed(),
ExcessDataGas: block.ExcessDataGas(), ExcessBlobGas: block.ExcessBlobGas(),
} }
blobsBundle := BlobsBundleV1{ bundle := BlobsBundleV1{
Commitments: make([]hexutil.Bytes, 0), Commitments: make([]hexutil.Bytes, 0),
Blobs: make([]hexutil.Bytes, 0), Blobs: make([]hexutil.Bytes, 0),
Proofs: make([]hexutil.Bytes, 0), Proofs: make([]hexutil.Bytes, 0),
} }
for i := range blobs { for _, sidecar := range sidecars {
blobsBundle.Blobs = append(blobsBundle.Blobs, hexutil.Bytes(blobs[i][:])) for j := range sidecar.Blobs {
blobsBundle.Commitments = append(blobsBundle.Commitments, hexutil.Bytes(commitments[i][:])) bundle.Blobs = append(bundle.Blobs, hexutil.Bytes(sidecar.Blobs[j][:]))
blobsBundle.Proofs = append(blobsBundle.Proofs, hexutil.Bytes(proofs[i][:])) 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, Override: false}
} }
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1 // ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1

View file

@ -1,20 +1,25 @@
# This file contains sha256 checksums of optional build dependencies. # This file contains sha256 checksums of optional build dependencies.
62ee5bc6fb55b8bae8f705e0cb8df86d6453626b4ecf93279e2867092e0b7f70 go1.20.6.src.tar.gz # https://github.com/ethereum/execution-spec-tests/releases
98a09c085b4c385abae7d35b9155195d5e584d14988347ac7f18e4cbe3b5ef3d go1.20.6.darwin-amd64.tar.gz 24bac679f3a2d8240d8e08e7f6a70b70c2dabf673317d924cf1d1887b9fe1f81 fixtures.tar.gz
1163be1998835a13f00dfc869a8e3cdebf86984ad41ff2fff43e35ac2a0d8344 go1.20.6.darwin-arm64.tar.gz
3e6801d33a52a599af9c5258e8626e368d6c7ba958e1b6a468a9a3eecad25d8b go1.20.6.freebsd-386.tar.gz
e0d35bb22fa792448b675368189519ca636dedd7af7f8a2fafca344e497eb70e go1.20.6.freebsd-amd64.tar.gz
2e27c9db1defbf4d58e907f9843bf60a1ce229688f8463bf24d6a0a19dc949de go1.20.6.linux-386.tar.gz
b945ae2bb5db01a0fb4786afde64e6fbab50b67f6fa0eb6cfa4924f16a7ff1eb go1.20.6.linux-amd64.tar.gz
4e15ab37556e979181a1a1cc60f6d796932223a0f5351d7c83768b356f84429b go1.20.6.linux-arm64.tar.gz
669902f5c8efefbd5d5fd078db01e34355af3693e48659b89593da7db367c488 go1.20.6.linux-armv6l.tar.gz
a1b91a42a40bba54bfd5c96c23d72250e0c424038d0d2b5c7950b828b4905822 go1.20.6.linux-ppc64le.tar.gz
c5ec315cc57edd646f66d7079b51d3717db5bbeae4c83a771b709761db73688d go1.20.6.linux-s390x.tar.gz
315c49723f93295bfaff0c15179e2d6936fb1ffc6f92837321d0d608fe1b6b51 go1.20.6.windows-386.zip
b67dd7f2b4589701e53c98e348e1b4d9a7c3536dc316941172b2f0b60ae4ce5f go1.20.6.windows-amd64.zip
9027e52be386e779ef1a0c938994ee2361689496ac832100407238f5ed0fd82a go1.20.6.windows-arm64.zip
# 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 fba08acc4027f69f07cef48fbff70b8a7ecdfaa1c2aba9ad3fb31d60d9f5d4bc golangci-lint-1.51.1-darwin-amd64.tar.gz
75b8f0ff3a4e68147156be4161a49d4576f1be37a0b506473f8c482140c1e7f2 golangci-lint-1.51.1-darwin-arm64.tar.gz 75b8f0ff3a4e68147156be4161a49d4576f1be37a0b506473f8c482140c1e7f2 golangci-lint-1.51.1-darwin-arm64.tar.gz
e06b3459aaed356e1667580be00b05f41f3b2e29685d12cdee571c23e1edb414 golangci-lint-1.51.1-freebsd-386.tar.gz e06b3459aaed356e1667580be00b05f41f3b2e29685d12cdee571c23e1edb414 golangci-lint-1.51.1-freebsd-386.tar.gz

View file

@ -120,15 +120,15 @@ var (
// Distros for which packages are created. // Distros for which packages are created.
// Note: vivid is unsupported because there is no golang-1.6 package for it. // Note: vivid is unsupported because there is no golang-1.6 package for it.
// Note: the following Ubuntu releases have been officially deprecated on Launchpad: // Note: the following Ubuntu releases have been officially deprecated on Launchpad:
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish // wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish,
// kinetic
debDistroGoBoots = map[string]string{ debDistroGoBoots = map[string]string{
"trusty": "golang-1.11", // EOL: 04/2024 "trusty": "golang-1.11", // EOL: 04/2024
"xenial": "golang-go", // EOL: 04/2026 "xenial": "golang-go", // EOL: 04/2026
"bionic": "golang-go", // EOL: 04/2028 "bionic": "golang-go", // EOL: 04/2028
"focal": "golang-go", // EOL: 04/2030 "focal": "golang-go", // EOL: 04/2030
"jammy": "golang-go", // EOL: 04/2032 "jammy": "golang-go", // EOL: 04/2032
"kinetic": "golang-go", // EOL: 07/2023 "lunar": "golang-go", // EOL: 01/2024
"lunar": "golang-go", // EOL: 01/2024
} }
debGoBootPaths = map[string]string{ debGoBootPaths = map[string]string{
@ -139,7 +139,7 @@ var (
// This is the version of Go that will be downloaded by // This is the version of Go that will be downloaded by
// //
// go run ci.go install -dlgo // go run ci.go install -dlgo
dlgoVersion = "1.20.6" dlgoVersion = "1.21.0"
// This is the version of Go that will be used to bootstrap the PPA builder. // 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 // we need to switch over to a recursive builder to jumpt across supported
// versions. // versions.
gobootVersion = "1.19.6" 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")) var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
@ -200,6 +207,7 @@ func doInstall(cmdline []string) {
staticlink = flag.Bool("static", false, "Create statically-linked executable") staticlink = flag.Bool("static", false, "Create statically-linked executable")
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
env := build.Env()
// Configure the toolchain. // Configure the toolchain.
tc := build.GoToolchain{GOARCH: *arch, CC: *cc} tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
@ -207,12 +215,16 @@ func doInstall(cmdline []string) {
csdb := build.MustLoadChecksums("build/checksums.txt") csdb := build.MustLoadChecksums("build/checksums.txt")
tc.Root = build.DownloadGo(csdb, dlgoVersion) tc.Root = build.DownloadGo(csdb, dlgoVersion)
} }
// Disable CLI markdown doc generation in release builds and enable linking
// the CKZG library since we can make it portable here. // Disable CLI markdown doc generation in release builds.
buildTags := []string{"urfave_cli_no_docs", "ckzg"} buildTags := []string{"urfave_cli_no_docs"}
// Enable linking the CKZG library since we can make it work with additional flags.
if env.UbuntuVersion != "trusty" {
buildTags = append(buildTags, "ckzg")
}
// Configure the build. // Configure the build.
env := build.Env()
gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...) gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...)
// arm64 CI builders are memory-constrained and can't handle concurrent builds, // arm64 CI builders are memory-constrained and can't handle concurrent builds,
@ -289,16 +301,26 @@ func doTest(cmdline []string) {
coverage = flag.Bool("coverage", false, "Whether to record code coverage") coverage = flag.Bool("coverage", false, "Whether to record code coverage")
verbose = flag.Bool("v", false, "Whether to log verbosely") verbose = flag.Bool("v", false, "Whether to log verbosely")
race = flag.Bool("race", false, "Execute the race detector") race = flag.Bool("race", false, "Execute the race detector")
cachedir = flag.String("cachedir", "./build/cache", "directory for caching downloads")
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
// Get test fixtures.
csdb := build.MustLoadChecksums("build/checksums.txt")
downloadSpecTestFixtures(csdb, *cachedir)
// Configure the toolchain. // Configure the toolchain.
tc := build.GoToolchain{GOARCH: *arch, CC: *cc} tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
if *dlgo { if *dlgo {
csdb := build.MustLoadChecksums("build/checksums.txt")
tc.Root = build.DownloadGo(csdb, dlgoVersion) tc.Root = build.DownloadGo(csdb, dlgoVersion)
} }
gotest := tc.Go("test", "-tags=ckzg") gotest := tc.Go("test")
// CI needs a bit more time for the statetests (default 10m).
gotest.Args = append(gotest.Args, "-timeout=20m")
// Enable CKZG backend in CI.
gotest.Args = append(gotest.Args, "-tags=ckzg")
// Test a single package at a time. CI builders are slow // Test a single package at a time. CI builders are slow
// and some tests run into timeouts under load. // and some tests run into timeouts under load.
@ -321,6 +343,21 @@ func doTest(cmdline []string) {
build.MustRun(gotest) 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. // doLint runs golangci-lint on requested packages.
func doLint(cmdline []string) { func doLint(cmdline []string) {
var ( var (

View file

@ -28,7 +28,7 @@ override_dh_auto_build:
mv .mod $(GOPATH)/pkg/mod mv .mod $(GOPATH)/pkg/mod
# A fresh Go was built, all dependency downloads faked, hope build works now # A fresh Go was built, all dependency downloads faked, hope build works now
../.go/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}} ../.go/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}} -ubuntu {{.Distro}}
override_dh_auto_test: override_dh_auto_test:

View file

@ -108,6 +108,7 @@ func main() {
if err != nil { if err != nil {
utils.Fatalf("-ListenUDP: %v", err) utils.Fatalf("-ListenUDP: %v", err)
} }
defer conn.Close()
db, _ := enode.OpenDB("") db, _ := enode.OpenDB("")
ln := enode.NewLocalNode(db, nodeKey) ln := enode.NewLocalNode(db, nodeKey)
@ -196,6 +197,7 @@ func doPortMapping(natm nat.Interface, ln *enode.LocalNode, addr *net.UDPAddr) *
// Refresh the mapping periodically. // Refresh the mapping periodically.
go func() { go func() {
refresh := time.NewTimer(mapTimeout) refresh := time.NewTimer(mapTimeout)
defer refresh.Stop()
for range refresh.C { for range refresh.C {
addMapping() addMapping()
refresh.Reset(mapTimeout) refresh.Reset(mapTimeout)

View file

@ -44,7 +44,7 @@ set to standard output. The following filters are supported:
- `-limit <N>` limits the output set to N entries, taking the top N nodes by score - `-limit <N>` limits the output set to N entries, taking the top N nodes by score
- `-ip <CIDR>` filters nodes by IP subnet - `-ip <CIDR>` filters nodes by IP subnet
- `-min-age <duration>` filters nodes by 'first seen' time - `-min-age <duration>` filters nodes by 'first seen' time
- `-eth-network <mainnet/goerli/sepolia>` 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 - `-les-server` filters nodes by LES server support
- `-snap` filters nodes by snap protocol support - `-snap` filters nodes by snap protocol support

View file

@ -288,11 +288,17 @@ func makeDeletionChanges(records map[string]recordSet, keep map[string]string) [
// sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order. // sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order.
func sortChanges(changes []types.Change) { func sortChanges(changes []types.Change) {
score := map[string]int{"CREATE": 1, "UPSERT": 2, "DELETE": 3} score := map[string]int{"CREATE": 1, "UPSERT": 2, "DELETE": 3}
slices.SortFunc(changes, func(a, b types.Change) bool { slices.SortFunc(changes, func(a, b types.Change) int {
if a.Action == b.Action { if a.Action == b.Action {
return *a.ResourceRecordSet.Name < *b.ResourceRecordSet.Name return strings.Compare(*a.ResourceRecordSet.Name, *b.ResourceRecordSet.Name)
} }
return score[string(a.Action)] < score[string(b.Action)] if score[string(a.Action)] < score[string(b.Action)] {
return -1
}
if score[string(a.Action)] > score[string(b.Action)] {
return 1
}
return 0
}) })
} }

View file

@ -77,7 +77,7 @@ func (c *Chain) RootAt(height int) common.Hash {
// ForkID gets the fork id of the chain. // ForkID gets the fork id of the chain.
func (c *Chain) ForkID() forkid.ID { 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 // Shorten returns a copy chain of a desired height from the imported

View file

@ -77,8 +77,8 @@ func (ns nodeSet) nodes() []*enode.Node {
result = append(result, n.N) result = append(result, n.N)
} }
// Sort by ID. // Sort by ID.
slices.SortFunc(result, func(a, b *enode.Node) bool { slices.SortFunc(result, func(a, b *enode.Node) int {
return bytes.Compare(a.ID().Bytes(), b.ID().Bytes()) < 0 return bytes.Compare(a.ID().Bytes(), b.ID().Bytes())
}) })
return result return result
} }
@ -103,8 +103,14 @@ func (ns nodeSet) topN(n int) nodeSet {
for _, v := range ns { for _, v := range ns {
byscore = append(byscore, v) byscore = append(byscore, v)
} }
slices.SortFunc(byscore, func(a, b nodeJSON) bool { slices.SortFunc(byscore, func(a, b nodeJSON) int {
return a.Score >= b.Score if a.Score > b.Score {
return -1
}
if a.Score < b.Score {
return 1
}
return 0
}) })
result := make(nodeSet, n) result := make(nodeSet, n)
for _, v := range byscore[:n] { for _, v := range byscore[:n] {

View file

@ -25,6 +25,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -228,11 +229,13 @@ func ethFilter(args []string) (nodeFilter, error) {
var filter forkid.Filter var filter forkid.Filter
switch args[0] { switch args[0] {
case "mainnet": case "mainnet":
filter = forkid.NewStaticFilter(params.MainnetChainConfig, params.MainnetGenesisHash) filter = forkid.NewStaticFilter(params.MainnetChainConfig, core.DefaultGenesisBlock().ToBlock())
case "goerli": case "goerli":
filter = forkid.NewStaticFilter(params.GoerliChainConfig, params.GoerliGenesisHash) filter = forkid.NewStaticFilter(params.GoerliChainConfig, core.DefaultGoerliGenesisBlock().ToBlock())
case "sepolia": case "sepolia":
filter = forkid.NewStaticFilter(params.SepoliaChainConfig, params.SepoliaGenesisHash) filter = forkid.NewStaticFilter(params.SepoliaChainConfig, core.DefaultSepoliaGenesisBlock().ToBlock())
case "holesky":
filter = forkid.NewStaticFilter(params.HoleskyChainConfig, core.DefaultHoleskyGenesisBlock().ToBlock())
default: default:
return nil, fmt.Errorf("unknown network %q", args[0]) return nil, fmt.Errorf("unknown network %q", args[0])
} }

View file

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -64,7 +65,7 @@ func blockTestCmd(ctx *cli.Context) error {
return err return err
} }
for i, test := range tests { for i, test := range tests {
if err := test.Run(false, tracer); err != nil { if err := test.Run(false, rawdb.HashScheme, tracer); err != nil {
return fmt.Errorf("test %v: %w", i, err) return fmt.Errorf("test %v: %w", i, err)
} }
} }

View file

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
@ -46,17 +47,19 @@ type Prestate struct {
// ExecutionResult contains the execution status after running a state test, any // ExecutionResult contains the execution status after running a state test, any
// error that might have occurred and a dump of the final state if requested. // error that might have occurred and a dump of the final state if requested.
type ExecutionResult struct { type ExecutionResult struct {
StateRoot common.Hash `json:"stateRoot"` StateRoot common.Hash `json:"stateRoot"`
TxRoot common.Hash `json:"txRoot"` TxRoot common.Hash `json:"txRoot"`
ReceiptRoot common.Hash `json:"receiptsRoot"` ReceiptRoot common.Hash `json:"receiptsRoot"`
LogsHash common.Hash `json:"logsHash"` LogsHash common.Hash `json:"logsHash"`
Bloom types.Bloom `json:"logsBloom" gencodec:"required"` Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
Receipts types.Receipts `json:"receipts"` Receipts types.Receipts `json:"receipts"`
Rejected []*rejectedTx `json:"rejected,omitempty"` Rejected []*rejectedTx `json:"rejected,omitempty"`
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"` GasUsed math.HexOrDecimal64 `json:"gasUsed"`
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"` WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"`
CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"`
CurrentBlobGasUsed *math.HexOrDecimal64 `json:"currentBlobGasUsed,omitempty"`
} }
type ommer struct { type ommer struct {
@ -66,37 +69,44 @@ type ommer struct {
//go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go //go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
type stEnv struct { type stEnv struct {
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
Difficulty *big.Int `json:"currentDifficulty"` Difficulty *big.Int `json:"currentDifficulty"`
Random *big.Int `json:"currentRandom"` Random *big.Int `json:"currentRandom"`
ParentDifficulty *big.Int `json:"parentDifficulty"` ParentDifficulty *big.Int `json:"parentDifficulty"`
ParentBaseFee *big.Int `json:"parentBaseFee,omitempty"` ParentBaseFee *big.Int `json:"parentBaseFee,omitempty"`
ParentGasUsed uint64 `json:"parentGasUsed,omitempty"` ParentGasUsed uint64 `json:"parentGasUsed,omitempty"`
ParentGasLimit uint64 `json:"parentGasLimit,omitempty"` ParentGasLimit uint64 `json:"parentGasLimit,omitempty"`
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
Number uint64 `json:"currentNumber" gencodec:"required"` Number uint64 `json:"currentNumber" gencodec:"required"`
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
ParentTimestamp uint64 `json:"parentTimestamp,omitempty"` ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
Ommers []ommer `json:"ommers,omitempty"` Ommers []ommer `json:"ommers,omitempty"`
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
BaseFee *big.Int `json:"currentBaseFee,omitempty"` BaseFee *big.Int `json:"currentBaseFee,omitempty"`
ParentUncleHash common.Hash `json:"parentUncleHash"` ParentUncleHash common.Hash `json:"parentUncleHash"`
ExcessBlobGas *uint64 `json:"excessBlobGas,omitempty"`
ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"`
ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
} }
type stEnvMarshaling struct { type stEnvMarshaling struct {
Coinbase common.UnprefixedAddress Coinbase common.UnprefixedAddress
Difficulty *math.HexOrDecimal256 Difficulty *math.HexOrDecimal256
Random *math.HexOrDecimal256 Random *math.HexOrDecimal256
ParentDifficulty *math.HexOrDecimal256 ParentDifficulty *math.HexOrDecimal256
ParentBaseFee *math.HexOrDecimal256 ParentBaseFee *math.HexOrDecimal256
ParentGasUsed math.HexOrDecimal64 ParentGasUsed math.HexOrDecimal64
ParentGasLimit math.HexOrDecimal64 ParentGasLimit math.HexOrDecimal64
GasLimit math.HexOrDecimal64 GasLimit math.HexOrDecimal64
Number math.HexOrDecimal64 Number math.HexOrDecimal64
Timestamp math.HexOrDecimal64 Timestamp math.HexOrDecimal64
ParentTimestamp math.HexOrDecimal64 ParentTimestamp math.HexOrDecimal64
BaseFee *math.HexOrDecimal256 BaseFee *math.HexOrDecimal256
ExcessBlobGas *math.HexOrDecimal64
ParentExcessBlobGas *math.HexOrDecimal64
ParentBlobGasUsed *math.HexOrDecimal64
} }
type rejectedTx struct { type rejectedTx struct {
@ -153,6 +163,19 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
rnd := common.BigToHash(pre.Env.Random) rnd := common.BigToHash(pre.Env.Random)
vmContext.Random = &rnd vmContext.Random = &rnd
} }
// If excessBlobGas is defined, add it to the vmContext.
if pre.Env.ExcessBlobGas != nil {
vmContext.ExcessBlobGas = pre.Env.ExcessBlobGas
} else {
// If it is not explicitly defined, but we have the parent values, we try
// to calculate it ourselves.
parentExcessBlobGas := pre.Env.ParentExcessBlobGas
parentBlobGasUsed := pre.Env.ParentBlobGasUsed
if parentExcessBlobGas != nil && parentBlobGasUsed != nil {
excessBlobGas := eip4844.CalcExcessBlobGas(*parentExcessBlobGas, *parentBlobGasUsed)
vmContext.ExcessBlobGas = &excessBlobGas
}
}
// If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's // If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's
// done in StateProcessor.Process(block, ...), right before transactions are applied. // done in StateProcessor.Process(block, ...), right before transactions are applied.
if chainConfig.DAOForkSupport && if chainConfig.DAOForkSupport &&
@ -160,8 +183,18 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 { chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
misc.ApplyDAOHardFork(statedb) 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 { for i, tx := range txs {
if tx.Type() == types.BlobTxType && vmContext.ExcessBlobGas == nil {
errMsg := "blob tx used but field env.ExcessBlobGas missing"
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", errMsg)
rejectedTxs = append(rejectedTxs, &rejectedTx{i, errMsg})
continue
}
msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee) msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee)
if err != nil { if err != nil {
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err) log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err)
@ -193,6 +226,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
tracer.CaptureTxEnd(nil, err) tracer.CaptureTxEnd(nil, err)
continue continue
} }
if tx.Type() == types.BlobTxType {
blobGasUsed += params.BlobTxBlobGasPerBlob
}
includedTxs = append(includedTxs, tx) includedTxs = append(includedTxs, tx)
if hashError != nil { if hashError != nil {
return nil, nil, NewError(ErrorMissingBlockhash, hashError) return nil, nil, NewError(ErrorMissingBlockhash, hashError)
@ -289,6 +325,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil)) h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil))
execRs.WithdrawalsRoot = &h execRs.WithdrawalsRoot = &h
} }
if vmContext.ExcessBlobGas != nil {
execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(vmContext.ExcessBlobGas)
execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed)
}
// Re-create statedb instance with new root upon the updated database // Re-create statedb instance with new root upon the updated database
// for accessing latest states. // for accessing latest states.
statedb, err = state.New(root, statedb.Database(), nil) statedb, err = state.New(root, statedb.Database(), nil)

View file

@ -17,22 +17,26 @@ var _ = (*stEnvMarshaling)(nil)
// MarshalJSON marshals as JSON. // MarshalJSON marshals as JSON.
func (s stEnv) MarshalJSON() ([]byte, error) { func (s stEnv) MarshalJSON() ([]byte, error) {
type stEnv struct { type stEnv struct {
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
Random *math.HexOrDecimal256 `json:"currentRandom"` Random *math.HexOrDecimal256 `json:"currentRandom"`
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"`
ParentGasUsed math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` ParentGasUsed math.HexOrDecimal64 `json:"parentGasUsed,omitempty"`
ParentGasLimit math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` ParentGasLimit math.HexOrDecimal64 `json:"parentGasLimit,omitempty"`
GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
Ommers []ommer `json:"ommers,omitempty"` Ommers []ommer `json:"ommers,omitempty"`
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
ParentUncleHash common.Hash `json:"parentUncleHash"` ParentUncleHash common.Hash `json:"parentUncleHash"`
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 var enc stEnv
enc.Coinbase = common.UnprefixedAddress(s.Coinbase) enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
@ -51,28 +55,36 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
enc.Withdrawals = s.Withdrawals enc.Withdrawals = s.Withdrawals
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee) enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
enc.ParentUncleHash = s.ParentUncleHash enc.ParentUncleHash = s.ParentUncleHash
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) return json.Marshal(&enc)
} }
// UnmarshalJSON unmarshals from JSON. // UnmarshalJSON unmarshals from JSON.
func (s *stEnv) UnmarshalJSON(input []byte) error { func (s *stEnv) UnmarshalJSON(input []byte) error {
type stEnv struct { type stEnv struct {
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
Random *math.HexOrDecimal256 `json:"currentRandom"` Random *math.HexOrDecimal256 `json:"currentRandom"`
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"`
ParentGasUsed *math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` ParentGasUsed *math.HexOrDecimal64 `json:"parentGasUsed,omitempty"`
ParentGasLimit *math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` ParentGasLimit *math.HexOrDecimal64 `json:"parentGasLimit,omitempty"`
GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
Ommers []ommer `json:"ommers,omitempty"` Ommers []ommer `json:"ommers,omitempty"`
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
ParentUncleHash *common.Hash `json:"parentUncleHash"` ParentUncleHash *common.Hash `json:"parentUncleHash"`
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 var dec stEnv
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -130,5 +142,17 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
if dec.ParentUncleHash != nil { if dec.ParentUncleHash != nil {
s.ParentUncleHash = *dec.ParentUncleHash s.ParentUncleHash = *dec.ParentUncleHash
} }
if dec.ExcessBlobGas != nil {
s.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
}
if dec.ParentExcessBlobGas != nil {
s.ParentExcessBlobGas = (*uint64)(dec.ParentExcessBlobGas)
}
if dec.ParentBlobGasUsed != nil {
s.ParentBlobGasUsed = (*uint64)(dec.ParentBlobGasUsed)
}
if dec.ParentBeaconBlockRoot != nil {
s.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot
}
return nil return nil
} }

View file

@ -28,7 +28,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -192,105 +192,20 @@ func Transition(ctx *cli.Context) error {
// Set the chain id // Set the chain id
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name)) chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
var txsWithKeys []*txWithKey if txs, err = loadTransactions(txStr, inputData, prestate.Env, chainConfig); err != nil {
if txStr != stdinSelector { return err
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 {
return err
}
var txs types.Transactions
if err := rlp.DecodeBytes(body, &txs); 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 {
return err
}
for _, tx := range txs {
txsWithKeys = append(txsWithKeys, &txWithKey{
key: nil,
tx: tx,
})
}
} else {
// JSON encoded transactions
txsWithKeys = inputData.Txs
}
} }
// We may have to sign the transactions. if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil {
signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp) return err
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 err := applyShanghaiChecks(&prestate.Env, chainConfig); err != nil {
if chainConfig.IsLondon(big.NewInt(int64(prestate.Env.Number))) { return err
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 = misc.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 { if err := applyMergeChecks(&prestate.Env, chainConfig); err != nil {
return NewError(ErrorConfig, errors.New("Shanghai config but missing 'withdrawals' in env section")) return err
} }
isMerged := chainConfig.TerminalTotalDifficulty != nil && chainConfig.TerminalTotalDifficulty.BitLen() == 0 if err := applyCancunChecks(&prestate.Env, chainConfig); err != nil {
env := prestate.Env return err
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)
} }
// Run the test and aggregate the result // Run the test and aggregate the result
s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer) 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 // and secondly to read them with the standard tx json format
func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) { func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) {
var signedTxs []*types.Transaction var signedTxs []*types.Transaction
for i, txWithKey := range txs { for i, tx := range txs {
tx := txWithKey.tx var (
key := txWithKey.key v, r, s = tx.tx.RawSignatureValues()
v, r, s := tx.RawSignatureValues() signed *types.Transaction
if key != nil && v.BitLen()+r.BitLen()+s.BitLen() == 0 { err error
// This transaction needs to be signed )
var ( if tx.key == nil || v.BitLen()+r.BitLen()+s.BitLen() != 0 {
signed *types.Transaction
err error
)
if txWithKey.protected {
signed, err = types.SignTx(tx, signer, key)
} else {
signed, err = types.SignTx(tx, types.FrontierSigner{}, 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 // Already signed
signedTxs = append(signedTxs, tx) 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.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)
} }
return signedTxs, nil 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 type Alloc map[common.Address]core.GenesisAccount
func (g Alloc) OnRoot(common.Hash) {} func (g Alloc) OnRoot(common.Hash) {}

View file

@ -42,6 +42,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -142,12 +143,23 @@ func runCmd(ctx *cli.Context) error {
gen := readGenesis(ctx.String(GenesisFlag.Name)) gen := readGenesis(ctx.String(GenesisFlag.Name))
genesisConfig = gen genesisConfig = gen
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
genesis := gen.MustCommit(db) triedb := trie.NewDatabase(db, &trie.Config{
sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: preimages}) Preimages: preimages,
HashDB: hashdb.Defaults,
})
defer triedb.Close()
genesis := gen.MustCommit(db, triedb)
sdb := state.NewDatabaseWithNodeDB(db, triedb)
statedb, _ = state.New(genesis.Root(), sdb, nil) statedb, _ = state.New(genesis.Root(), sdb, nil)
chainConfig = gen.Config chainConfig = gen.Config
} else { } else {
sdb := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: preimages}) db := rawdb.NewMemoryDatabase()
triedb := trie.NewDatabase(db, &trie.Config{
Preimages: preimages,
HashDB: hashdb.Defaults,
})
defer triedb.Close()
sdb := state.NewDatabaseWithNodeDB(db, triedb)
statedb, _ = state.New(types.EmptyRootHash, sdb, nil) statedb, _ = state.New(types.EmptyRootHash, sdb, nil)
genesisConfig = new(core.Genesis) genesisConfig = new(core.Genesis)
} }

View file

@ -23,7 +23,9 @@ import (
"os" "os"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -104,25 +106,23 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
for _, st := range test.Subtests() { for _, st := range test.Subtests() {
// Run the test and aggregate the result // Run the test and aggregate the result
result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true} result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true}
_, s, err := test.Run(st, cfg, false) test.Run(st, cfg, false, rawdb.HashScheme, func(err error, snaps *snapshot.Tree, state *state.StateDB) {
// print state root for evmlab tracing if state != nil {
if s != nil { root := state.IntermediateRoot(false)
root := s.IntermediateRoot(false) result.Root = &root
result.Root = &root if jsonOut {
if jsonOut { fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root) }
} }
} if err != nil {
if err != nil { // Test failed, mark as so and dump any state to aid debugging
// Test failed, mark as so and dump any state to aid debugging result.Pass, result.Error = false, err.Error()
result.Pass, result.Error = false, err.Error() if dump {
if dump && s != nil { dump := state.RawDump(nil)
s, _ = state.New(*result.Root, s.Database(), nil) result.State = &dump
dump := s.RawDump(nil) }
result.State = &dump
} }
} })
results = append(results, *result) results = append(results, *result)
} }
} }

View file

@ -259,6 +259,22 @@ func TestT8n(t *testing.T) {
output: t8nOutput{alloc: true, result: true}, output: t8nOutput{alloc: true, result: true},
expOut: "exp.json", expOut: "exp.json",
}, },
{ // Cancun tests
base: "./testdata/28",
input: t8nInput{
"alloc.json", "txs.rlp", "env.json", "Cancun", "",
},
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 := []string{"t8n"}
args = append(args, tc.output.get()...) args = append(args, tc.output.get()...)

16
cmd/evm/testdata/28/alloc.json vendored Normal file
View file

@ -0,0 +1,16 @@
{
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x016345785d8a0000",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x016345785d8a0000",
"code" : "0x60004960015500",
"nonce" : "0x00",
"storage" : {
}
}
}

23
cmd/evm/testdata/28/env.json vendored Normal file
View file

@ -0,0 +1,23 @@
{
"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",
"blockHashes" : {
"0" : "0x3a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6"
},
"parentBeaconBlockRoot": "0x0000beac00beac00beac00beac00beac00beac00beac00beac00beac00beac00"
}

47
cmd/evm/testdata/28/exp.json vendored Normal file
View file

@ -0,0 +1,47 @@
{
"alloc": {
"0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": {
"balance": "0x150ca"
},
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0x16345785d80c3a9",
"nonce": "0x1"
},
"0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"code": "0x60004960015500",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x01a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
},
"balance": "0x16345785d8a0000"
}
},
"result": {
"stateRoot": "0xa40cb3fab01848e922a48bd24191815df9f721ad4b60376edac75161517663e8",
"txRoot": "0x4409cc4b699384ba5f8248d92b784713610c5ff9c1de51e9239da0dac76de9ce",
"receiptsRoot": "0xbff643da765981266133094092d98c81d2ac8e9a83a7bbda46c3d736f1f874ac",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receipts": [
{
"type": "0x3",
"root": "0x",
"status": "0x1",
"cumulativeGasUsed": "0xa865",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"logs": null,
"transactionHash": "0x7508d7139d002a4b3a26a4f12dec0d87cb46075c78bf77a38b569a133b509262",
"contractAddress": "0x0000000000000000000000000000000000000000",
"gasUsed": "0xa865",
"effectiveGasPrice": null,
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"transactionIndex": "0x0"
}
],
"currentDifficulty": null,
"gasUsed": "0xa865",
"currentBaseFee": "0x9",
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"currentExcessBlobGas": "0x0",
"currentBlobGasUsed": "0x20000"
}
}

1
cmd/evm/testdata/28/txs.rlp vendored Normal file
View file

@ -0,0 +1 @@
"0xf88bb88903f8860180026483061a8094b94f5374fce5edbc8e2a8697c15331677e6ebf0b8080c00ae1a001a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d801a025e16bb498552165016751911c3608d79000ab89dc3100776e729e6ea13091c7a03acacff7fc0cff6eda8a927dec93ca17765e1ee6cbc06c5954ce102e097c01d2"

16
cmd/evm/testdata/29/alloc.json vendored Normal file
View 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
View 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
View 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
View 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
View 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" : [
]
}
]

View file

@ -39,7 +39,6 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/trie"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -49,7 +48,10 @@ var (
Name: "init", Name: "init",
Usage: "Bootstrap and initialize a new genesis block", Usage: "Bootstrap and initialize a new genesis block",
ArgsUsage: "<genesisPath>", ArgsUsage: "<genesisPath>",
Flags: flags.Merge([]cli.Flag{utils.CachePreimagesFlag}, utils.DatabasePathFlags), Flags: flags.Merge([]cli.Flag{
utils.CachePreimagesFlag,
utils.StateSchemeFlag,
}, utils.DatabasePathFlags),
Description: ` Description: `
The init command initializes a new genesis block and definition for the network. The init command initializes a new genesis block and definition for the network.
This is a destructive action and changes the network in which you will be This is a destructive action and changes the network in which you will be
@ -95,6 +97,9 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
utils.MetricsInfluxDBOrganizationFlag, utils.MetricsInfluxDBOrganizationFlag,
utils.TxLookupLimitFlag, utils.TxLookupLimitFlag,
utils.VMTraceFlag, utils.VMTraceFlag,
utils.TransactionHistoryFlag,
utils.StateSchemeFlag,
utils.StateHistoryFlag,
}, utils.DatabasePathFlags), }, utils.DatabasePathFlags),
Description: ` Description: `
The import command imports blocks from an RLP-encoded form. The form can be one file The import command imports blocks from an RLP-encoded form. The form can be one file
@ -111,6 +116,7 @@ processing will proceed even if an individual RLP-file import failure occurs.`,
Flags: flags.Merge([]cli.Flag{ Flags: flags.Merge([]cli.Flag{
utils.CacheFlag, utils.CacheFlag,
utils.SyncModeFlag, utils.SyncModeFlag,
utils.StateSchemeFlag,
}, utils.DatabasePathFlags), }, utils.DatabasePathFlags),
Description: ` Description: `
Requires a first argument of the file to write to. Requires a first argument of the file to write to.
@ -160,6 +166,7 @@ It's deprecated, please use "geth db export" instead.
utils.IncludeIncompletesFlag, utils.IncludeIncompletesFlag,
utils.StartKeyFlag, utils.StartKeyFlag,
utils.DumpLimitFlag, utils.DumpLimitFlag,
utils.StateSchemeFlag,
}, utils.DatabasePathFlags), }, utils.DatabasePathFlags),
Description: ` Description: `
This command dumps out the state for a given block (or latest, if none provided). This command dumps out the state for a given block (or latest, if none provided).
@ -196,14 +203,15 @@ func initGenesis(ctx *cli.Context) error {
if err != nil { if err != nil {
utils.Fatalf("Failed to open database: %v", err) utils.Fatalf("Failed to open database: %v", err)
} }
triedb := trie.NewDatabaseWithConfig(chaindb, &trie.Config{ defer chaindb.Close()
Preimages: ctx.Bool(utils.CachePreimagesFlag.Name),
}) triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false)
defer triedb.Close()
_, hash, err := core.SetupGenesisBlock(chaindb, triedb, genesis) _, hash, err := core.SetupGenesisBlock(chaindb, triedb, genesis)
if err != nil { if err != nil {
utils.Fatalf("Failed to write genesis block: %v", err) utils.Fatalf("Failed to write genesis block: %v", err)
} }
chaindb.Close()
log.Info("Successfully wrote genesis state", "database", name, "hash", hash) log.Info("Successfully wrote genesis state", "database", name, "hash", hash)
} }
return nil return nil
@ -242,7 +250,7 @@ func dumpGenesis(ctx *cli.Context) error {
if ctx.IsSet(utils.DataDirFlag.Name) { if ctx.IsSet(utils.DataDirFlag.Name) {
utils.Fatalf("no existing datadir at %s", stack.Config().DataDir) utils.Fatalf("no existing datadir at %s", stack.Config().DataDir)
} }
utils.Fatalf("no network preset provided. no exisiting genesis in the default datadir") utils.Fatalf("no network preset provided, no existing genesis in the default datadir")
return nil return nil
} }
@ -466,10 +474,10 @@ func dump(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
config := &trie.Config{ triedb := utils.MakeTrieDatabase(ctx, db, true, false) // always enable preimage lookup
Preimages: true, // always enable preimage lookup defer triedb.Close()
}
state, err := state.New(root, state.NewDatabaseWithConfig(db, config), nil) state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)
if err != nil { if err != nil {
return err return err
} }

View file

@ -22,10 +22,10 @@ import (
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
"runtime"
"strings"
"unicode" "unicode"
"github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/external" "github.com/ethereum/go-ethereum/accounts/external"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
@ -33,7 +33,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/usbwallet" "github.com/ethereum/go-ethereum/accounts/usbwallet"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/catalyst"
ethcatalyst "github.com/ethereum/go-ethereum/eth/catalyst"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers/directory"
@ -45,6 +44,7 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/naoina/toml" "github.com/naoina/toml"
"github.com/urfave/cli/v2"
) )
var ( var (
@ -191,6 +191,20 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
backend, eth := utils.RegisterEthService(stack, &cfg.Eth) backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
// Create gauge with geth system and build information
if eth != nil { // The 'eth' backend may be nil in light mode
var protos []string
for _, p := range eth.Protocols() {
protos = append(protos, fmt.Sprintf("%v/%d", p.Name, p.Version))
}
metrics.NewRegisteredGaugeInfo("geth/info", nil).Update(metrics.GaugeInfoValue{
"arch": runtime.GOARCH,
"os": runtime.GOOS,
"version": cfg.Node.Version,
"eth_protocols": strings.Join(protos, ","),
})
}
// Configure log filter RPC API. // Configure log filter RPC API.
filterSystem := utils.RegisterFilterAPI(stack, backend, &cfg.Eth) filterSystem := utils.RegisterFilterAPI(stack, backend, &cfg.Eth)
@ -219,7 +233,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon) catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon)
stack.RegisterLifecycle(simBeacon) stack.RegisterLifecycle(simBeacon)
} else if cfg.Eth.SyncMode != downloader.LightSync { } else if cfg.Eth.SyncMode != downloader.LightSync {
err := ethcatalyst.Register(stack, eth) err := catalyst.Register(stack, eth)
if err != nil { if err != nil {
utils.Fatalf("failed to register catalyst service: %v", err) utils.Fatalf("failed to register catalyst service: %v", err)
} }

View file

@ -151,6 +151,7 @@ WARNING: This is a low-level operation which may cause database corruption!`,
ArgsUsage: "<hex-encoded state root> <hex-encoded account hash> <hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>", ArgsUsage: "<hex-encoded state root> <hex-encoded account hash> <hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>",
Flags: flags.Merge([]cli.Flag{ Flags: flags.Merge([]cli.Flag{
utils.SyncModeFlag, utils.SyncModeFlag,
utils.StateSchemeFlag,
}, utils.NetworkFlags, utils.DatabasePathFlags), }, utils.NetworkFlags, utils.DatabasePathFlags),
Description: "This command looks up the specified database key from the database.", Description: "This command looks up the specified database key from the database.",
} }
@ -482,6 +483,9 @@ func dbDumpTrie(ctx *cli.Context) error {
db := utils.MakeChainDatabase(ctx, stack, true) db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close() defer db.Close()
triedb := utils.MakeTrieDatabase(ctx, db, false, true)
defer triedb.Close()
var ( var (
state []byte state []byte
storage []byte storage []byte
@ -515,7 +519,7 @@ func dbDumpTrie(ctx *cli.Context) error {
} }
} }
id := trie.StorageTrieID(common.BytesToHash(state), common.BytesToHash(account), common.BytesToHash(storage)) id := trie.StorageTrieID(common.BytesToHash(state), common.BytesToHash(account), common.BytesToHash(storage))
theTrie, err := trie.New(id, trie.NewDatabase(db)) theTrie, err := trie.New(id, triedb)
if err != nil { if err != nil {
return err return err
} }

View file

@ -176,12 +176,12 @@ func TestCustomBackend(t *testing.T) {
{ // Can't start pebble on top of leveldb { // Can't start pebble on top of leveldb
initArgs: []string{"--db.engine", "leveldb"}, initArgs: []string{"--db.engine", "leveldb"},
execArgs: []string{"--db.engine", "pebble"}, execArgs: []string{"--db.engine", "pebble"},
execExpect: `Fatal: Failed to register the Ethereum service: db.engine choice was pebble but found pre-existing leveldb database in specified data directory`, execExpect: `Fatal: Could not open database: db.engine choice was pebble but found pre-existing leveldb database in specified data directory`,
}, },
{ // Can't start leveldb on top of pebble { // Can't start leveldb on top of pebble
initArgs: []string{"--db.engine", "pebble"}, initArgs: []string{"--db.engine", "pebble"},
execArgs: []string{"--db.engine", "leveldb"}, execArgs: []string{"--db.engine", "leveldb"},
execExpect: `Fatal: Failed to register the Ethereum service: db.engine choice was leveldb but found pre-existing pebble database in specified data directory`, execExpect: `Fatal: Could not open database: db.engine choice was leveldb but found pre-existing pebble database in specified data directory`,
}, },
{ // Reject invalid backend choice { // Reject invalid backend choice
initArgs: []string{"--db.engine", "mssql"}, initArgs: []string{"--db.engine", "mssql"},

View file

@ -39,14 +39,12 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"go.uber.org/automaxprocs/maxprocs"
// Force-load the tracer engines to trigger registration // Force-load the tracer engines to trigger registration
_ "github.com/ethereum/go-ethereum/eth/tracers/js" _ "github.com/ethereum/go-ethereum/eth/tracers/js"
_ "github.com/ethereum/go-ethereum/eth/tracers/native" _ "github.com/ethereum/go-ethereum/eth/tracers/native"
// Automatically set GOMAXPROCS to match Linux container CPU quota.
_ "go.uber.org/automaxprocs"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -90,6 +88,9 @@ var (
utils.GCModeFlag, utils.GCModeFlag,
utils.SnapshotFlag, utils.SnapshotFlag,
utils.TxLookupLimitFlag, utils.TxLookupLimitFlag,
utils.TransactionHistoryFlag,
utils.StateSchemeFlag,
utils.StateHistoryFlag,
utils.LightServeFlag, utils.LightServeFlag,
utils.LightIngressFlag, utils.LightIngressFlag,
utils.LightEgressFlag, utils.LightEgressFlag,
@ -246,6 +247,7 @@ func init() {
) )
app.Before = func(ctx *cli.Context) error { app.Before = func(ctx *cli.Context) error {
maxprocs.Set() // Automatically set GOMAXPROCS to match Linux container CPU quota.
flags.MigrateGlobalFlags(ctx) flags.MigrateGlobalFlags(ctx)
return debug.Setup(ctx) return debug.Setup(ctx)
} }
@ -274,6 +276,9 @@ func prepare(ctx *cli.Context) {
case ctx.IsSet(utils.SepoliaFlag.Name): case ctx.IsSet(utils.SepoliaFlag.Name):
log.Info("Starting Geth on Sepolia testnet...") log.Info("Starting Geth on Sepolia testnet...")
case ctx.IsSet(utils.HoleskyFlag.Name):
log.Info("Starting Geth on Holesky testnet...")
case ctx.IsSet(utils.DeveloperFlag.Name): case ctx.IsSet(utils.DeveloperFlag.Name):
log.Info("Starting Geth in ephemeral dev mode...") log.Info("Starting Geth in ephemeral dev mode...")
log.Warn(`You are running Geth in --dev mode. Please note the following: log.Warn(`You are running Geth in --dev mode. Please note the following:
@ -298,7 +303,8 @@ func prepare(ctx *cli.Context) {
// If we're a full node on mainnet without --cache specified, bump default cache allowance // 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) { 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 // 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.GoerliFlag.Name) &&
!ctx.IsSet(utils.DeveloperFlag.Name) { !ctx.IsSet(utils.DeveloperFlag.Name) {
// Nope, we're really on mainnet. Bump that cache up! // Nope, we're really on mainnet. Bump that cache up!

View file

@ -61,10 +61,7 @@ two version states are available: genesis and the specific one.
The default pruning target is the HEAD-127 state. The default pruning target is the HEAD-127 state.
WARNING: It's necessary to delete the trie clean cache after the pruning. WARNING: it's only supported in hash mode(--state.scheme=hash)".
If you specify another directory for the trie clean cache via "--cache.trie.journal"
during the use of Geth, please also specify it here for correct deletion. Otherwise
the trie clean cache with default directory will be deleted.
`, `,
}, },
{ {
@ -72,7 +69,9 @@ the trie clean cache with default directory will be deleted.
Usage: "Recalculate state hash based on the snapshot for verification", Usage: "Recalculate state hash based on the snapshot for verification",
ArgsUsage: "<root>", ArgsUsage: "<root>",
Action: verifyState, Action: verifyState,
Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags), Flags: flags.Merge([]cli.Flag{
utils.StateSchemeFlag,
}, utils.NetworkFlags, utils.DatabasePathFlags),
Description: ` Description: `
geth snapshot verify-state <state-root> geth snapshot verify-state <state-root>
will traverse the whole accounts and storages set based on the specified will traverse the whole accounts and storages set based on the specified
@ -107,7 +106,9 @@ information about the specified address.
Usage: "Traverse the state with given root hash and perform quick verification", Usage: "Traverse the state with given root hash and perform quick verification",
ArgsUsage: "<root>", ArgsUsage: "<root>",
Action: traverseState, Action: traverseState,
Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags), Flags: flags.Merge([]cli.Flag{
utils.StateSchemeFlag,
}, utils.NetworkFlags, utils.DatabasePathFlags),
Description: ` Description: `
geth snapshot traverse-state <state-root> geth snapshot traverse-state <state-root>
will traverse the whole state from the given state root and will abort if any will traverse the whole state from the given state root and will abort if any
@ -122,7 +123,9 @@ It's also usable without snapshot enabled.
Usage: "Traverse the state with given root hash and perform detailed verification", Usage: "Traverse the state with given root hash and perform detailed verification",
ArgsUsage: "<root>", ArgsUsage: "<root>",
Action: traverseRawState, Action: traverseRawState,
Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags), Flags: flags.Merge([]cli.Flag{
utils.StateSchemeFlag,
}, utils.NetworkFlags, utils.DatabasePathFlags),
Description: ` Description: `
geth snapshot traverse-rawstate <state-root> geth snapshot traverse-rawstate <state-root>
will traverse the whole state from the given root and will abort if any referenced will traverse the whole state from the given root and will abort if any referenced
@ -143,6 +146,7 @@ It's also usable without snapshot enabled.
utils.ExcludeStorageFlag, utils.ExcludeStorageFlag,
utils.StartKeyFlag, utils.StartKeyFlag,
utils.DumpLimitFlag, utils.DumpLimitFlag,
utils.StateSchemeFlag,
}, utils.NetworkFlags, utils.DatabasePathFlags), }, utils.NetworkFlags, utils.DatabasePathFlags),
Description: ` Description: `
This command is semantically equivalent to 'geth dump', but uses the snapshots This command is semantically equivalent to 'geth dump', but uses the snapshots
@ -165,6 +169,9 @@ func pruneState(ctx *cli.Context) error {
chaindb := utils.MakeChainDatabase(ctx, stack, false) chaindb := utils.MakeChainDatabase(ctx, stack, false)
defer chaindb.Close() defer chaindb.Close()
if rawdb.ReadStateScheme(chaindb) != rawdb.HashScheme {
log.Crit("Offline pruning is not required for path scheme")
}
prunerconfig := pruner.Config{ prunerconfig := pruner.Config{
Datadir: stack.ResolvePath(""), Datadir: stack.ResolvePath(""),
BloomSize: ctx.Uint64(utils.BloomFilterSizeFlag.Name), BloomSize: ctx.Uint64(utils.BloomFilterSizeFlag.Name),
@ -205,13 +212,16 @@ func verifyState(ctx *cli.Context) error {
log.Error("Failed to load head block") log.Error("Failed to load head block")
return errors.New("no head block") return errors.New("no head block")
} }
snapconfig := snapshot.Config{ triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
defer triedb.Close()
snapConfig := snapshot.Config{
CacheSize: 256, CacheSize: 256,
Recovery: false, Recovery: false,
NoBuild: true, NoBuild: true,
AsyncBuild: false, AsyncBuild: false,
} }
snaptree, err := snapshot.New(snapconfig, chaindb, trie.NewDatabase(chaindb), headBlock.Root()) snaptree, err := snapshot.New(snapConfig, chaindb, triedb, headBlock.Root())
if err != nil { if err != nil {
log.Error("Failed to open snapshot tree", "err", err) log.Error("Failed to open snapshot tree", "err", err)
return err return err
@ -253,6 +263,11 @@ func traverseState(ctx *cli.Context) error {
defer stack.Close() defer stack.Close()
chaindb := utils.MakeChainDatabase(ctx, stack, true) chaindb := utils.MakeChainDatabase(ctx, stack, true)
defer chaindb.Close()
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
defer triedb.Close()
headBlock := rawdb.ReadHeadBlock(chaindb) headBlock := rawdb.ReadHeadBlock(chaindb)
if headBlock == nil { if headBlock == nil {
log.Error("Failed to load head block") log.Error("Failed to load head block")
@ -277,7 +292,6 @@ func traverseState(ctx *cli.Context) error {
root = headBlock.Root() root = headBlock.Root()
log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
} }
triedb := trie.NewDatabase(chaindb)
t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb) t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
if err != nil { if err != nil {
log.Error("Failed to open trie", "root", root, "err", err) log.Error("Failed to open trie", "root", root, "err", err)
@ -353,6 +367,11 @@ func traverseRawState(ctx *cli.Context) error {
defer stack.Close() defer stack.Close()
chaindb := utils.MakeChainDatabase(ctx, stack, true) chaindb := utils.MakeChainDatabase(ctx, stack, true)
defer chaindb.Close()
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
defer triedb.Close()
headBlock := rawdb.ReadHeadBlock(chaindb) headBlock := rawdb.ReadHeadBlock(chaindb)
if headBlock == nil { if headBlock == nil {
log.Error("Failed to load head block") log.Error("Failed to load head block")
@ -377,7 +396,6 @@ func traverseRawState(ctx *cli.Context) error {
root = headBlock.Root() root = headBlock.Root()
log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
} }
triedb := trie.NewDatabase(chaindb)
t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb) t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
if err != nil { if err != nil {
log.Error("Failed to open trie", "root", root, "err", err) log.Error("Failed to open trie", "root", root, "err", err)
@ -398,6 +416,11 @@ func traverseRawState(ctx *cli.Context) error {
log.Error("Failed to open iterator", "root", root, "err", err) log.Error("Failed to open iterator", "root", root, "err", err)
return err return err
} }
reader, err := triedb.Reader(root)
if err != nil {
log.Error("State is non-existent", "root", root)
return nil
}
for accIter.Next(true) { for accIter.Next(true) {
nodes += 1 nodes += 1
node := accIter.Hash() node := accIter.Hash()
@ -405,7 +428,7 @@ func traverseRawState(ctx *cli.Context) error {
// Check the present for non-empty hash node(embedded node doesn't // Check the present for non-empty hash node(embedded node doesn't
// have their own hash). // have their own hash).
if node != (common.Hash{}) { if node != (common.Hash{}) {
blob := rawdb.ReadLegacyTrieNode(chaindb, node) blob, _ := reader.Node(common.Hash{}, accIter.Path(), node)
if len(blob) == 0 { if len(blob) == 0 {
log.Error("Missing trie node(account)", "hash", node) log.Error("Missing trie node(account)", "hash", node)
return errors.New("missing account") return errors.New("missing account")
@ -446,7 +469,7 @@ func traverseRawState(ctx *cli.Context) error {
// Check the presence for non-empty hash node(embedded node doesn't // Check the presence for non-empty hash node(embedded node doesn't
// have their own hash). // have their own hash).
if node != (common.Hash{}) { if node != (common.Hash{}) {
blob := rawdb.ReadLegacyTrieNode(chaindb, node) blob, _ := reader.Node(common.BytesToHash(accIter.LeafKey()), storageIter.Path(), node)
if len(blob) == 0 { if len(blob) == 0 {
log.Error("Missing trie node(storage)", "hash", node) log.Error("Missing trie node(storage)", "hash", node)
return errors.New("missing storage") return errors.New("missing storage")
@ -506,13 +529,16 @@ func dumpState(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
triedb := utils.MakeTrieDatabase(ctx, db, false, true)
defer triedb.Close()
snapConfig := snapshot.Config{ snapConfig := snapshot.Config{
CacheSize: 256, CacheSize: 256,
Recovery: false, Recovery: false,
NoBuild: true, NoBuild: true,
AsyncBuild: false, AsyncBuild: false,
} }
snaptree, err := snapshot.New(snapConfig, db, trie.NewDatabase(db), root) snaptree, err := snapshot.New(snapConfig, db, triedb, root)
if err != nil { if err != nil {
return err return err
} }

View file

@ -0,0 +1,4 @@
untrusted comment: signature from minisign secret key
RUQkliYstQBOKLK05Sy5f3bVRMBqJT26ABo6Vbp3BNJAVjejoqYCu4GWE/+7qcDfHBqYIniDCbFIUvYEnOHxV6vZ93wO1xJWDQw=
trusted comment: timestamp:1693986492 file:data.json hashed
6Fdw2H+W1ZXK7QXSF77Z5AWC7+AEFAfDmTSxNGylU5HLT1AuSJQmxslj+VjtUBamYCvOuET7plbXza942AlWDw==

View file

@ -30,17 +30,24 @@ import (
) )
func TestVerification(t *testing.T) { func TestVerification(t *testing.T) {
// Signatures generated with `minisign` // Signatures generated with `minisign`. Legacy format, not pre-hashed file.
t.Run("minisig", func(t *testing.T) { t.Run("minisig-legacy", func(t *testing.T) {
// For this test, the pubkey is in testdata/minisign.pub // For this test, the pubkey is in testdata/vcheck/minisign.pub
// (the privkey is `minisign.sec`, if we want to expand this test. Password 'test' ) // (the privkey is `minisign.sec`, if we want to expand this test. Password 'test' )
pub := "RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp" pub := "RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp"
testVerification(t, pub, "./testdata/vcheck/minisig-sigs/") testVerification(t, pub, "./testdata/vcheck/minisig-sigs/")
}) })
t.Run("minisig-new", func(t *testing.T) {
// For this test, the pubkey is in testdata/vcheck/minisign.pub
// (the privkey is `minisign.sec`, if we want to expand this test. Password 'test' )
// `minisign -S -s ./minisign.sec -m data.json -x ./minisig-sigs-new/data.json.minisig`
pub := "RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp"
testVerification(t, pub, "./testdata/vcheck/minisig-sigs-new/")
})
// Signatures generated with `signify-openbsd` // Signatures generated with `signify-openbsd`
t.Run("signify-openbsd", func(t *testing.T) { t.Run("signify-openbsd", func(t *testing.T) {
t.Skip("This currently fails, minisign expects 4 lines of data, signify provides only 2") t.Skip("This currently fails, minisign expects 4 lines of data, signify provides only 2")
// For this test, the pubkey is in testdata/signifykey.pub // For this test, the pubkey is in testdata/vcheck/signifykey.pub
// (the privkey is `signifykey.sec`, if we want to expand this test. Password 'test' ) // (the privkey is `signifykey.sec`, if we want to expand this test. Password 'test' )
pub := "RWSKLNhZb0KdATtRT7mZC/bybI3t3+Hv/O2i3ye04Dq9fnT9slpZ1a2/" pub := "RWSKLNhZb0KdATtRT7mZC/bybI3t3+Hv/O2i3ye04Dq9fnT9slpZ1a2/"
testVerification(t, pub, "./testdata/vcheck/signify-sigs/") testVerification(t, pub, "./testdata/vcheck/signify-sigs/")
@ -58,6 +65,9 @@ func testVerification(t *testing.T, pubkey, sigdir string) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(files) == 0 {
t.Fatal("Missing tests")
}
for _, f := range files { for _, f := range files {
sig, err := os.ReadFile(filepath.Join(sigdir, f.Name())) sig, err := os.ReadFile(filepath.Join(sigdir, f.Name()))
if err != nil { if err != nil {

View file

@ -48,7 +48,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
ethcatalyst "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/catalyst"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
@ -75,6 +75,9 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
pcsclite "github.com/gballet/go-libpcsclite" pcsclite "github.com/gballet/go-libpcsclite"
gopsutil "github.com/shirou/gopsutil/mem" gopsutil "github.com/shirou/gopsutil/mem"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -134,7 +137,7 @@ var (
} }
NetworkIdFlag = &cli.Uint64Flag{ NetworkIdFlag = &cli.Uint64Flag{
Name: "networkid", 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, Value: ethconfig.Defaults.NetworkId,
Category: flags.EthCategory, Category: flags.EthCategory,
} }
@ -153,7 +156,11 @@ var (
Usage: "Sepolia network: pre-configured proof-of-work test network", Usage: "Sepolia network: pre-configured proof-of-work test network",
Category: flags.EthCategory, Category: flags.EthCategory,
} }
HoleskyFlag = &cli.BoolFlag{
Name: "holesky",
Usage: "Holesky network: pre-configured proof-of-stake test network",
Category: flags.EthCategory,
}
// Dev mode // Dev mode
DeveloperFlag = &cli.BoolFlag{ DeveloperFlag = &cli.BoolFlag{
Name: "dev", Name: "dev",
@ -219,30 +226,12 @@ var (
} }
defaultSyncMode = ethconfig.Defaults.SyncMode defaultSyncMode = ethconfig.Defaults.SyncMode
SyncModeFlag = &flags.TextMarshalerFlag{ SnapshotFlag = &cli.BoolFlag{
Name: "syncmode",
Usage: `Blockchain sync mode ("snap", "full" or "light")`,
Value: &defaultSyncMode,
Category: flags.EthCategory,
}
GCModeFlag = &cli.StringFlag{
Name: "gcmode",
Usage: `Blockchain garbage collection mode ("full", "archive")`,
Value: "full",
Category: flags.EthCategory,
}
SnapshotFlag = &cli.BoolFlag{
Name: "snapshot", Name: "snapshot",
Usage: `Enables snapshot-database mode (default = enable)`, Usage: `Enables snapshot-database mode (default = enable)`,
Value: true, Value: true,
Category: flags.EthCategory, Category: flags.EthCategory,
} }
TxLookupLimitFlag = &cli.Uint64Flag{
Name: "txlookuplimit",
Usage: "Number of recent blocks to maintain transactions index for (default = about one year, 0 = entire chain)",
Value: ethconfig.Defaults.TxLookupLimit,
Category: flags.EthCategory,
}
LightKDFFlag = &cli.BoolFlag{ LightKDFFlag = &cli.BoolFlag{
Name: "lightkdf", Name: "lightkdf",
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
@ -269,6 +258,36 @@ var (
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting", Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",
Category: flags.EthCategory, Category: flags.EthCategory,
} }
SyncModeFlag = &flags.TextMarshalerFlag{
Name: "syncmode",
Usage: `Blockchain sync mode ("snap", "full" or "light")`,
Value: &defaultSyncMode,
Category: flags.StateCategory,
}
GCModeFlag = &cli.StringFlag{
Name: "gcmode",
Usage: `Blockchain garbage collection mode, only relevant in state.scheme=hash ("full", "archive")`,
Value: "full",
Category: flags.StateCategory,
}
StateSchemeFlag = &cli.StringFlag{
Name: "state.scheme",
Usage: "Scheme to use for storing ethereum state ('hash' or 'path')",
Value: rawdb.HashScheme,
Category: flags.StateCategory,
}
StateHistoryFlag = &cli.Uint64Flag{
Name: "history.state",
Usage: "Number of recent blocks to retain state history for (default = 90,000 blocks, 0 = entire chain)",
Value: ethconfig.Defaults.StateHistory,
Category: flags.StateCategory,
}
TransactionHistoryFlag = &cli.Uint64Flag{
Name: "history.transactions",
Usage: "Number of recent blocks to maintain transactions index for (default = about one year, 0 = entire chain)",
Value: ethconfig.Defaults.TransactionHistory,
Category: flags.StateCategory,
}
// Light server and client settings // Light server and client settings
LightServeFlag = &cli.IntFlag{ LightServeFlag = &cli.IntFlag{
Name: "light.serve", Name: "light.serve",
@ -943,6 +962,7 @@ var (
TestnetFlags = []cli.Flag{ TestnetFlags = []cli.Flag{
GoerliFlag, GoerliFlag,
SepoliaFlag, SepoliaFlag,
HoleskyFlag,
} }
// NetworkFlags is the flag group of all built-in supported networks. // NetworkFlags is the flag group of all built-in supported networks.
NetworkFlags = append([]cli.Flag{MainnetFlag}, TestnetFlags...) NetworkFlags = append([]cli.Flag{MainnetFlag}, TestnetFlags...)
@ -973,6 +993,9 @@ func MakeDataDir(ctx *cli.Context) string {
if ctx.Bool(SepoliaFlag.Name) { if ctx.Bool(SepoliaFlag.Name) {
return filepath.Join(path, "sepolia") return filepath.Join(path, "sepolia")
} }
if ctx.Bool(HoleskyFlag.Name) {
return filepath.Join(path, "holesky")
}
return path return path
} }
Fatalf("Cannot determine default data directory, please set manually (--datadir)") Fatalf("Cannot determine default data directory, please set manually (--datadir)")
@ -1019,6 +1042,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
switch { switch {
case ctx.IsSet(BootnodesFlag.Name): case ctx.IsSet(BootnodesFlag.Name):
urls = SplitAndTrim(ctx.String(BootnodesFlag.Name)) urls = SplitAndTrim(ctx.String(BootnodesFlag.Name))
case ctx.Bool(HoleskyFlag.Name):
urls = params.HoleskyBootnodes
case ctx.Bool(SepoliaFlag.Name): case ctx.Bool(SepoliaFlag.Name):
urls = params.SepoliaBootnodes urls = params.SepoliaBootnodes
case ctx.Bool(GoerliFlag.Name): case ctx.Bool(GoerliFlag.Name):
@ -1471,6 +1496,8 @@ func SetDataDir(ctx *cli.Context, cfg *node.Config) {
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli") cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
case ctx.Bool(SepoliaFlag.Name) && cfg.DataDir == node.DefaultDataDir(): case ctx.Bool(SepoliaFlag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia") cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia")
case ctx.Bool(HoleskyFlag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "holesky")
} }
} }
@ -1627,16 +1654,11 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
// SetEthConfig applies eth-related command line flags to the config. // SetEthConfig applies eth-related command line flags to the config.
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// Avoid conflicting network flags // Avoid conflicting network flags
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag) CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag, HoleskyFlag)
CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light") CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light")
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
if ctx.String(GCModeFlag.Name) == "archive" && ctx.Uint64(TxLookupLimitFlag.Name) != 0 {
ctx.Set(TxLookupLimitFlag.Name, "0") // Set configurations from CLI flags
log.Warn("Disable transaction unindexing for archive node")
}
if ctx.IsSet(LightServeFlag.Name) && ctx.Uint64(TxLookupLimitFlag.Name) != 0 {
log.Warn("LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited")
}
setEtherbase(ctx, cfg) setEtherbase(ctx, cfg)
setGPO(ctx, &cfg.GPO, ctx.String(SyncModeFlag.Name) == "light") setGPO(ctx, &cfg.GPO, ctx.String(SyncModeFlag.Name) == "light")
setTxPool(ctx, &cfg.TxPool) setTxPool(ctx, &cfg.TxPool)
@ -1693,8 +1715,36 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
cfg.Preimages = true cfg.Preimages = true
log.Info("Enabling recording of key preimages since archive mode is used") log.Info("Enabling recording of key preimages since archive mode is used")
} }
if ctx.IsSet(TxLookupLimitFlag.Name) { if ctx.IsSet(StateHistoryFlag.Name) {
cfg.TxLookupLimit = ctx.Uint64(TxLookupLimitFlag.Name) cfg.StateHistory = ctx.Uint64(StateHistoryFlag.Name)
}
// Parse state scheme, abort the process if it's not compatible.
chaindb := tryMakeReadOnlyDatabase(ctx, stack)
scheme, err := ParseStateScheme(ctx, chaindb)
chaindb.Close()
if err != nil {
Fatalf("%v", err)
}
cfg.StateScheme = scheme
// Parse transaction history flag, if user is still using legacy config
// file with 'TxLookupLimit' configured, copy the value to 'TransactionHistory'.
if cfg.TransactionHistory == ethconfig.Defaults.TransactionHistory && cfg.TxLookupLimit != ethconfig.Defaults.TxLookupLimit {
log.Warn("The config option 'TxLookupLimit' is deprecated and will be removed, please use 'TransactionHistory'")
cfg.TransactionHistory = cfg.TxLookupLimit
}
if ctx.IsSet(TransactionHistoryFlag.Name) {
cfg.TransactionHistory = ctx.Uint64(TransactionHistoryFlag.Name)
} else if ctx.IsSet(TxLookupLimitFlag.Name) {
log.Warn("The flag --txlookuplimit is deprecated and will be removed, please use --history.transactions")
cfg.TransactionHistory = ctx.Uint64(TxLookupLimitFlag.Name)
}
if ctx.String(GCModeFlag.Name) == "archive" && cfg.TransactionHistory != 0 {
cfg.TransactionHistory = 0
log.Warn("Disabled transaction unindexing for archive node")
}
if ctx.IsSet(LightServeFlag.Name) && cfg.TransactionHistory != 0 {
log.Warn("LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited")
} }
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheTrieFlag.Name) { if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheTrieFlag.Name) {
cfg.TrieCleanCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheTrieFlag.Name) / 100 cfg.TrieCleanCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheTrieFlag.Name) / 100
@ -1757,6 +1807,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
} }
cfg.Genesis = core.DefaultGenesisBlock() cfg.Genesis = core.DefaultGenesisBlock()
SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) 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): case ctx.Bool(SepoliaFlag.Name):
if !ctx.IsSet(NetworkIdFlag.Name) { if !ctx.IsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 11155111 cfg.NetworkId = 11155111
@ -1820,15 +1876,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// Create a new developer genesis block or reuse existing one // Create a new developer genesis block or reuse existing one
cfg.Genesis = core.DeveloperGenesisBlock(ctx.Uint64(DeveloperGasLimitFlag.Name), developer.Address) cfg.Genesis = core.DeveloperGenesisBlock(ctx.Uint64(DeveloperGasLimitFlag.Name), developer.Address)
if ctx.IsSet(DataDirFlag.Name) { if ctx.IsSet(DataDirFlag.Name) {
// If datadir doesn't exist we need to open db in write-mode chaindb := tryMakeReadOnlyDatabase(ctx, stack)
// so leveldb can create files.
readonly := true
if !common.FileExist(stack.ResolvePath("chaindata")) {
readonly = false
}
// Check if we have an already initialized chain and fall back to
// that if so. Otherwise we need to generate a new genesis spec.
chaindb := MakeChainDatabase(ctx, stack, readonly)
if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) { if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) {
cfg.Genesis = nil // fallback to db content cfg.Genesis = nil // fallback to db content
} }
@ -1936,7 +1984,7 @@ func RegisterFullSyncTester(stack *node.Node, eth *eth.Ethereum, path string) {
if err := rlp.DecodeBytes(rlpBlob, &block); err != nil { if err := rlp.DecodeBytes(rlpBlob, &block); err != nil {
Fatalf("Failed to decode block: %v", err) Fatalf("Failed to decode block: %v", err)
} }
ethcatalyst.RegisterFullSyncTester(stack, eth, &block) catalyst.RegisterFullSyncTester(stack, eth, &block)
log.Info("Registered full-sync tester", "number", block.NumberU64(), "hash", block.Hash()) log.Info("Registered full-sync tester", "number", block.NumberU64(), "hash", block.Hash())
} }
@ -2046,6 +2094,18 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
return chainDb return chainDb
} }
// 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 the database doesn't exist we need to open it in write-mode to allow
// the engine to create files.
readonly := true
if rawdb.PreexistingDatabase(stack.ResolvePath("chaindata")) == "" {
readonly = false
}
return MakeChainDatabase(ctx, stack, readonly)
}
func IsNetworkPreset(ctx *cli.Context) bool { func IsNetworkPreset(ctx *cli.Context) bool {
for _, flag := range NetworkFlags { for _, flag := range NetworkFlags {
bFlag, _ := flag.(*cli.BoolFlag) bFlag, _ := flag.(*cli.BoolFlag)
@ -2085,6 +2145,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
switch { switch {
case ctx.Bool(MainnetFlag.Name): case ctx.Bool(MainnetFlag.Name):
genesis = core.DefaultGenesisBlock() genesis = core.DefaultGenesisBlock()
case ctx.Bool(HoleskyFlag.Name):
genesis = core.DefaultHoleskyGenesisBlock()
case ctx.Bool(SepoliaFlag.Name): case ctx.Bool(SepoliaFlag.Name):
genesis = core.DefaultSepoliaGenesisBlock() genesis = core.DefaultSepoliaGenesisBlock()
case ctx.Bool(GoerliFlag.Name): case ctx.Bool(GoerliFlag.Name):
@ -2112,6 +2174,10 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
} }
scheme, err := ParseStateScheme(ctx, chainDb)
if err != nil {
Fatalf("%v", err)
}
cache := &core.CacheConfig{ cache := &core.CacheConfig{
TrieCleanLimit: ethconfig.Defaults.TrieCleanCache, TrieCleanLimit: ethconfig.Defaults.TrieCleanCache,
TrieCleanNoPrefetch: ctx.Bool(CacheNoPrefetchFlag.Name), TrieCleanNoPrefetch: ctx.Bool(CacheNoPrefetchFlag.Name),
@ -2120,6 +2186,8 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
TrieTimeLimit: ethconfig.Defaults.TrieTimeout, TrieTimeLimit: ethconfig.Defaults.TrieTimeout,
SnapshotLimit: ethconfig.Defaults.SnapshotCache, SnapshotLimit: ethconfig.Defaults.SnapshotCache,
Preimages: ctx.Bool(CachePreimagesFlag.Name), Preimages: ctx.Bool(CachePreimagesFlag.Name),
StateScheme: scheme,
StateHistory: ctx.Uint64(StateHistoryFlag.Name),
} }
if cache.TrieDirtyDisabled && !cache.Preimages { if cache.TrieDirtyDisabled && !cache.Preimages {
cache.Preimages = true cache.Preimages = true
@ -2173,3 +2241,62 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
} }
return preloads return preloads
} }
// ParseStateScheme resolves scheme identifier from CLI flag. If the provided
// state scheme is not compatible with the one of persistent scheme, an error
// will be returned.
//
// - none: use the scheme consistent with persistent state, or fallback
// to hash-based scheme if state is empty.
// - hash: use hash-based scheme or error out if not compatible with
// persistent state scheme.
// - path: use path-based scheme or error out if not compatible with
// persistent state scheme.
func ParseStateScheme(ctx *cli.Context, disk ethdb.Database) (string, error) {
// If state scheme is not specified, use the scheme consistent
// with persistent state, or fallback to hash mode if database
// is empty.
stored := rawdb.ReadStateScheme(disk)
if !ctx.IsSet(StateSchemeFlag.Name) {
if stored == "" {
// use default scheme for empty database, flip it when
// path mode is chosen as default
log.Info("State schema set to default", "scheme", "hash")
return rawdb.HashScheme, nil
}
log.Info("State scheme set to already existing", "scheme", stored)
return stored, nil // reuse scheme of persistent scheme
}
// If state scheme is specified, ensure it's compatible with
// persistent state.
scheme := ctx.String(StateSchemeFlag.Name)
if stored == "" || scheme == stored {
log.Info("State scheme set by user", "scheme", scheme)
return scheme, nil
}
return "", fmt.Errorf("incompatible state scheme, stored: %s, provided: %s", stored, scheme)
}
// MakeTrieDatabase constructs a trie database based on the configured scheme.
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool) *trie.Database {
config := &trie.Config{
Preimages: preimage,
}
scheme, err := ParseStateScheme(ctx, disk)
if err != nil {
Fatalf("%v", err)
}
if scheme == rawdb.HashScheme {
// Read-only mode is not implemented in hash mode,
// ignore the parameter silently. TODO(rjl493456442)
// please config it if read mode is implemented.
config.HashDB = hashdb.Defaults
return trie.NewDatabase(disk, config)
}
if readOnly {
config.PathDB = pathdb.ReadOnly
} else {
config.PathDB = pathdb.Defaults
}
return trie.NewDatabase(disk, config)
}

View file

@ -19,6 +19,7 @@ package utils
import ( import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/internal/flags"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -37,6 +38,7 @@ var DeprecatedFlags = []cli.Flag{
CacheTrieJournalFlag, CacheTrieJournalFlag,
CacheTrieRejournalFlag, CacheTrieRejournalFlag,
LegacyDiscoveryV5Flag, LegacyDiscoveryV5Flag,
TxLookupLimitFlag,
} }
var ( var (
@ -68,6 +70,13 @@ var (
Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism (deprecated, use --discv5 instead)", Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism (deprecated, use --discv5 instead)",
Category: flags.DeprecatedCategory, Category: flags.DeprecatedCategory,
} }
// Deprecated August 2023
TxLookupLimitFlag = &cli.Uint64Flag{
Name: "txlookuplimit",
Usage: "Number of recent blocks to maintain transactions index for (default = about one year, 0 = entire chain) (deprecated, use history.transactions instead)",
Value: ethconfig.Defaults.TransactionHistory,
Category: flags.DeprecatedCategory,
}
) )
// showDeprecated displays deprecated flags that will be soon removed from the codebase. // showDeprecated displays deprecated flags that will be soon removed from the codebase.

View file

@ -65,9 +65,9 @@ func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
// If b is larger than len(h), b will be cropped from the left. // If b is larger than len(h), b will be cropped from the left.
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
// Less compares two hashes. // Cmp compares two hashes.
func (h Hash) Less(other Hash) bool { func (h Hash) Cmp(other Hash) int {
return bytes.Compare(h[:], other[:]) < 0 return bytes.Compare(h[:], other[:])
} }
// Bytes gets the byte representation of the underlying hash. // Bytes gets the byte representation of the underlying hash.
@ -231,9 +231,9 @@ func IsHexAddress(s string) bool {
return len(s) == 2*AddressLength && isHex(s) return len(s) == 2*AddressLength && isHex(s)
} }
// Less compares two addresses. // Cmp compares two addresses.
func (a Address) Less(other Address) bool { func (a Address) Cmp(other Address) int {
return bytes.Compare(a[:], other[:]) < 0 return bytes.Compare(a[:], other[:])
} }
// Bytes gets the string representation of the underlying address. // Bytes gets the string representation of the underlying address.

View file

@ -25,6 +25,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"time"
) )
func TestBytesConversion(t *testing.T) { func TestBytesConversion(t *testing.T) {
@ -583,3 +584,14 @@ func TestAddressEIP55(t *testing.T) {
t.Fatal("Unexpected address after unmarshal") t.Fatal("Unexpected address after unmarshal")
} }
} }
func BenchmarkPrettyDuration(b *testing.B) {
var x = PrettyDuration(time.Duration(int64(1203123912312)))
b.Logf("Pre %s", time.Duration(x).String())
var a string
b.ResetTimer()
for i := 0; i < b.N; i++ {
a = x.String()
}
b.Logf("Post %s", a)
}

View file

@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "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/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -258,7 +258,7 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
return consensus.ErrInvalidNumber return consensus.ErrInvalidNumber
} }
// Verify the header's EIP-1559 attributes. // Verify the header's EIP-1559 attributes.
if err := misc.VerifyEIP1559Header(chain.Config(), parent, header); err != nil { if err := eip1559.VerifyEIP1559Header(chain.Config(), parent, header); err != nil {
return err return err
} }
// Verify existence / non-existence of withdrawalsHash. // Verify existence / non-existence of withdrawalsHash.
@ -269,15 +269,21 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
if !shanghai && header.WithdrawalsHash != nil { if !shanghai && header.WithdrawalsHash != nil {
return fmt.Errorf("invalid withdrawalsHash: have %x, expected nil", header.WithdrawalsHash) return fmt.Errorf("invalid withdrawalsHash: have %x, expected nil", header.WithdrawalsHash)
} }
// Verify the existence / non-existence of excessDataGas // Verify the existence / non-existence of cancun-specific header fields
cancun := chain.Config().IsCancun(header.Number, header.Time) cancun := chain.Config().IsCancun(header.Number, header.Time)
if !cancun && header.ExcessDataGas != nil { if !cancun {
return fmt.Errorf("invalid excessDataGas: have %d, expected nil", header.ExcessDataGas) switch {
} case header.ExcessBlobGas != nil:
if !cancun && header.DataGasUsed != nil { return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
return fmt.Errorf("invalid dataGasUsed: have %d, expected nil", header.DataGasUsed) case header.BlobGasUsed != nil:
} return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
if cancun { 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 err := eip4844.VerifyEIP4844Header(parent, header); err != nil { if err := eip4844.VerifyEIP4844Header(parent, header); err != nil {
return err return err
} }

View file

@ -33,6 +33,7 @@ import (
lru "github.com/ethereum/go-ethereum/common/lru" lru "github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -343,7 +344,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header
if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil { if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil {
return err return err
} }
} else if err := misc.VerifyEIP1559Header(chain.Config(), parent, header); err != nil { } else if err := eip1559.VerifyEIP1559Header(chain.Config(), parent, header); err != nil {
// Verify the header's EIP-1559 attributes. // Verify the header's EIP-1559 attributes.
return err return err
} }

View file

@ -308,7 +308,7 @@ func (s *Snapshot) signers() []common.Address {
for sig := range s.Signers { for sig := range s.Signers {
sigs = append(sigs, sig) sigs = append(sigs, sig)
} }
slices.SortFunc(sigs, common.Address.Less) slices.SortFunc(sigs, common.Address.Cmp)
return sigs return sigs
} }

View file

@ -53,7 +53,7 @@ func (ap *testerAccountPool) checkpoint(header *types.Header, signers []string)
for i, signer := range signers { for i, signer := range signers {
auths[i] = ap.address(signer) auths[i] = ap.address(signer)
} }
slices.SortFunc(auths, common.Address.Less) slices.SortFunc(auths, common.Address.Cmp)
for i, auth := range auths { for i, auth := range auths {
copy(header.Extra[extraVanity+i*common.AddressLength:], auth.Bytes()) copy(header.Extra[extraVanity+i*common.AddressLength:], auth.Bytes())
} }

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -254,7 +255,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa
if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil { if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil {
return err return err
} }
} else if err := misc.VerifyEIP1559Header(chain.Config(), parent, header); err != nil { } else if err := eip1559.VerifyEIP1559Header(chain.Config(), parent, header); err != nil {
// Verify the header's EIP-1559 attributes. // Verify the header's EIP-1559 attributes.
return err return err
} }

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package misc package eip1559
import ( import (
"errors" "errors"
@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
@ -36,7 +37,7 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade
if !config.IsLondon(parent.Number) { if !config.IsLondon(parent.Number) {
parentGasLimit = parent.GasLimit * config.ElasticityMultiplier() parentGasLimit = parent.GasLimit * config.ElasticityMultiplier()
} }
if err := VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil { if err := misc.VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil {
return err return err
} }
// Verify the header is not malformed // Verify the header is not malformed

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package misc package eip1559
import ( import (
"math/big" "math/big"

View file

@ -26,58 +26,58 @@ import (
) )
var ( var (
minDataGasPrice = big.NewInt(params.BlobTxMinDataGasprice) minBlobGasPrice = big.NewInt(params.BlobTxMinBlobGasprice)
dataGaspriceUpdateFraction = big.NewInt(params.BlobTxDataGaspriceUpdateFraction) blobGaspriceUpdateFraction = big.NewInt(params.BlobTxBlobGaspriceUpdateFraction)
) )
// VerifyEIP4844Header verifies the presence of the excessDataGas field and that // VerifyEIP4844Header verifies the presence of the excessBlobGas field and that
// if the current block contains no transactions, the excessDataGas is updated // if the current block contains no transactions, the excessBlobGas is updated
// accordingly. // accordingly.
func VerifyEIP4844Header(parent, header *types.Header) error { func VerifyEIP4844Header(parent, header *types.Header) error {
// Verify the header is not malformed // Verify the header is not malformed
if header.ExcessDataGas == nil { if header.ExcessBlobGas == nil {
return errors.New("header is missing excessDataGas") return errors.New("header is missing excessBlobGas")
} }
if header.DataGasUsed == nil { if header.BlobGasUsed == nil {
return errors.New("header is missing dataGasUsed") return errors.New("header is missing blobGasUsed")
} }
// Verify that the data gas used remains within reasonable limits. // Verify that the blob gas used remains within reasonable limits.
if *header.DataGasUsed > params.BlobTxMaxDataGasPerBlock { if *header.BlobGasUsed > params.MaxBlobGasPerBlock {
return fmt.Errorf("data gas used %d exceeds maximum allowance %d", *header.DataGasUsed, params.BlobTxMaxDataGasPerBlock) return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, params.MaxBlobGasPerBlock)
} }
if *header.DataGasUsed%params.BlobTxDataGasPerBlob != 0 { if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 {
return fmt.Errorf("data gas used %d not a multiple of data gas per blob %d", header.DataGasUsed, params.BlobTxDataGasPerBlob) return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, params.BlobTxBlobGasPerBlob)
} }
// Verify the excessDataGas is correct based on the parent header // Verify the excessBlobGas is correct based on the parent header
var ( var (
parentExcessDataGas uint64 parentExcessBlobGas uint64
parentDataGasUsed uint64 parentBlobGasUsed uint64
) )
if parent.ExcessDataGas != nil { if parent.ExcessBlobGas != nil {
parentExcessDataGas = *parent.ExcessDataGas parentExcessBlobGas = *parent.ExcessBlobGas
parentDataGasUsed = *parent.DataGasUsed parentBlobGasUsed = *parent.BlobGasUsed
} }
expectedExcessDataGas := CalcExcessDataGas(parentExcessDataGas, parentDataGasUsed) expectedExcessBlobGas := CalcExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed)
if *header.ExcessDataGas != expectedExcessDataGas { if *header.ExcessBlobGas != expectedExcessBlobGas {
return fmt.Errorf("invalid excessDataGas: have %d, want %d, parent excessDataGas %d, parent blobDataUsed %d", return fmt.Errorf("invalid excessBlobGas: have %d, want %d, parent excessBlobGas %d, parent blobDataUsed %d",
*header.ExcessDataGas, expectedExcessDataGas, parentExcessDataGas, parentDataGasUsed) *header.ExcessBlobGas, expectedExcessBlobGas, parentExcessBlobGas, parentBlobGasUsed)
} }
return nil return nil
} }
// CalcExcessDataGas calculates the excess data gas after applying the set of // CalcExcessBlobGas calculates the excess blob gas after applying the set of
// blobs on top of the excess data gas. // blobs on top of the excess blob gas.
func CalcExcessDataGas(parentExcessDataGas uint64, parentDataGasUsed uint64) uint64 { func CalcExcessBlobGas(parentExcessBlobGas uint64, parentBlobGasUsed uint64) uint64 {
excessDataGas := parentExcessDataGas + parentDataGasUsed excessBlobGas := parentExcessBlobGas + parentBlobGasUsed
if excessDataGas < params.BlobTxTargetDataGasPerBlock { if excessBlobGas < params.BlobTxTargetBlobGasPerBlock {
return 0 return 0
} }
return excessDataGas - params.BlobTxTargetDataGasPerBlock return excessBlobGas - params.BlobTxTargetBlobGasPerBlock
} }
// CalcBlobFee calculates the blobfee from the header's excess data gas field. // CalcBlobFee calculates the blobfee from the header's excess blob gas field.
func CalcBlobFee(excessDataGas uint64) *big.Int { func CalcBlobFee(excessBlobGas uint64) *big.Int {
return fakeExponential(minDataGasPrice, new(big.Int).SetUint64(excessDataGas), dataGaspriceUpdateFraction) return fakeExponential(minBlobGasPrice, new(big.Int).SetUint64(excessBlobGas), blobGaspriceUpdateFraction)
} }
// fakeExponential approximates factor * e ** (numerator / denominator) using // fakeExponential approximates factor * e ** (numerator / denominator) using

View file

@ -24,51 +24,51 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
func TestCalcExcessDataGas(t *testing.T) { func TestCalcExcessBlobGas(t *testing.T) {
var tests = []struct { var tests = []struct {
excess uint64 excess uint64
blobs uint64 blobs uint64
want uint64 want uint64
}{ }{
// The excess data gas should not increase from zero if the used blob // The excess blob gas should not increase from zero if the used blob
// slots are below - or equal - to the target. // slots are below - or equal - to the target.
{0, 0, 0}, {0, 0, 0},
{0, 1, 0}, {0, 1, 0},
{0, params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob, 0}, {0, params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob, 0},
// If the target data gas is exceeded, the excessDataGas should increase // If the target blob gas is exceeded, the excessBlobGas should increase
// by however much it was overshot // by however much it was overshot
{0, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) + 1, params.BlobTxDataGasPerBlob}, {0, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) + 1, params.BlobTxBlobGasPerBlob},
{1, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) + 1, params.BlobTxDataGasPerBlob + 1}, {1, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) + 1, params.BlobTxBlobGasPerBlob + 1},
{1, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) + 2, 2*params.BlobTxDataGasPerBlob + 1}, {1, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) + 2, 2*params.BlobTxBlobGasPerBlob + 1},
// The excess data gas should decrease by however much the target was // The excess blob gas should decrease by however much the target was
// under-shot, capped at zero. // under-shot, capped at zero.
{params.BlobTxTargetDataGasPerBlock, params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob, params.BlobTxTargetDataGasPerBlock}, {params.BlobTxTargetBlobGasPerBlock, params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob, params.BlobTxTargetBlobGasPerBlock},
{params.BlobTxTargetDataGasPerBlock, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) - 1, params.BlobTxDataGasPerBlob}, {params.BlobTxTargetBlobGasPerBlock, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) - 1, params.BlobTxTargetBlobGasPerBlock - params.BlobTxBlobGasPerBlob},
{params.BlobTxTargetDataGasPerBlock, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) - 2, 0}, {params.BlobTxTargetBlobGasPerBlock, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) - 2, params.BlobTxTargetBlobGasPerBlock - (2 * params.BlobTxBlobGasPerBlob)},
{params.BlobTxDataGasPerBlob - 1, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) - 1, 0}, {params.BlobTxBlobGasPerBlob - 1, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) - 1, 0},
} }
for _, tt := range tests { for i, tt := range tests {
result := CalcExcessDataGas(tt.excess, tt.blobs*params.BlobTxDataGasPerBlob) result := CalcExcessBlobGas(tt.excess, tt.blobs*params.BlobTxBlobGasPerBlob)
if result != tt.want { if result != tt.want {
t.Errorf("excess data gas mismatch: have %v, want %v", result, tt.want) t.Errorf("test %d: excess blob gas mismatch: have %v, want %v", i, result, tt.want)
} }
} }
} }
func TestCalcBlobFee(t *testing.T) { func TestCalcBlobFee(t *testing.T) {
tests := []struct { tests := []struct {
excessDataGas uint64 excessBlobGas uint64
blobfee int64 blobfee int64
}{ }{
{0, 1}, {0, 1},
{1542706, 1}, {2314057, 1},
{1542707, 2}, {2314058, 2},
{10 * 1024 * 1024, 111}, {10 * 1024 * 1024, 23},
} }
for i, tt := range tests { for i, tt := range tests {
have := CalcBlobFee(tt.excessDataGas) have := CalcBlobFee(tt.excessBlobGas)
if have.Int64() != tt.blobfee { if have.Int64() != tt.blobfee {
t.Errorf("test %d: blobfee mismatch: have %v want %v", i, have, tt.blobfee) t.Errorf("test %d: blobfee mismatch: have %v want %v", i, have, tt.blobfee)
} }

View file

@ -17,7 +17,6 @@
package misc package misc
import ( import (
"errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -36,7 +35,7 @@ func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error {
return fmt.Errorf("invalid gas limit: have %d, want %d +-= %d", headerGasLimit, parentGasLimit, limit-1) return fmt.Errorf("invalid gas limit: have %d, want %d +-= %d", headerGasLimit, parentGasLimit, limit-1)
} }
if headerGasLimit < params.MinGasLimit { if headerGasLimit < params.MinGasLimit {
return errors.New("invalid gas limit below 5000") return fmt.Errorf("invalid gas limit below %d", params.MinGasLimit)
} }
return nil return nil
} }

View file

@ -68,6 +68,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash { if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
return fmt.Errorf("transaction root hash mismatch (header value %x, calculated %x)", header.TxHash, hash) return fmt.Errorf("transaction root hash mismatch (header value %x, calculated %x)", header.TxHash, hash)
} }
// Withdrawals are present after the Shanghai fork. // Withdrawals are present after the Shanghai fork.
if header.WithdrawalsHash != nil { if header.WithdrawalsHash != nil {
// Withdrawals list must be present in body after Shanghai. // Withdrawals list must be present in body after Shanghai.
@ -81,23 +82,34 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Withdrawals are not allowed prior to Shanghai fork // Withdrawals are not allowed prior to Shanghai fork
return errors.New("withdrawals present in block body") return errors.New("withdrawals present in block body")
} }
// Blob transactions may be present after the Cancun fork. // Blob transactions may be present after the Cancun fork.
var blobs int var blobs int
for _, tx := range block.Transactions() { for i, tx := range block.Transactions() {
// Count the number of blobs to validate against the header's dataGasUsed // Count the number of blobs to validate against the header's blobGasUsed
blobs += len(tx.BlobHashes()) blobs += len(tx.BlobHashes())
// If the tx is a blob tx, it must NOT have a sidecar attached to be valid in a block.
if tx.BlobTxSidecar() != nil {
return fmt.Errorf("unexpected blob sidecar in transaction at index %d", i)
}
// The individual checks for blob validity (version-check + not empty) // The individual checks for blob validity (version-check + not empty)
// happens in the state_transition check. // happens in StateTransition.
} }
if header.DataGasUsed != nil {
if want := *header.DataGasUsed / params.BlobTxDataGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated // Check blob gas usage.
return fmt.Errorf("data gas used mismatch (header %v, calculated %v)", *header.DataGasUsed, blobs*params.BlobTxDataGasPerBlob) if header.BlobGasUsed != nil {
if want := *header.BlobGasUsed / params.BlobTxBlobGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated
return fmt.Errorf("blob gas used mismatch (header %v, calculated %v)", *header.BlobGasUsed, blobs*params.BlobTxBlobGasPerBlob)
} }
} else { } else {
if blobs > 0 { if blobs > 0 {
return errors.New("data blobs present in block body") return errors.New("data blobs present in block body")
} }
} }
// Ancestor block must be known.
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor return consensus.ErrUnknownAncestor

View file

@ -35,6 +35,11 @@ import (
// Tests that simple header verification works, for both good and bad blocks. // Tests that simple header verification works, for both good and bad blocks.
func TestHeaderVerification(t *testing.T) { func TestHeaderVerification(t *testing.T) {
testHeaderVerification(t, rawdb.HashScheme)
testHeaderVerification(t, rawdb.PathScheme)
}
func testHeaderVerification(t *testing.T, scheme string) {
// Create a simple chain to verify // Create a simple chain to verify
var ( var (
gspec = &Genesis{Config: params.TestChainConfig} gspec = &Genesis{Config: params.TestChainConfig}
@ -45,7 +50,7 @@ func TestHeaderVerification(t *testing.T) {
headers[i] = block.Header() headers[i] = block.Header()
} }
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil) chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
defer chain.Stop() defer chain.Stop()
for i := 0; i < len(blocks); i++ { for i := 0; i < len(blocks); i++ {

View file

@ -48,6 +48,8 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
@ -58,6 +60,8 @@ var (
headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil) headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil) headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil)
chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil) accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil) accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil) accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil)
@ -128,7 +132,7 @@ const (
) )
// CacheConfig contains the configuration values for the trie database // CacheConfig contains the configuration values for the trie database
// that's resident in a blockchain. // and state snapshot these are resident in a blockchain.
type CacheConfig struct { type CacheConfig struct {
TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory
TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks
@ -137,11 +141,31 @@ type CacheConfig struct {
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
Preimages bool // Whether to store preimage of trie key to the disk Preimages bool // Whether to store preimage of trie key to the disk
StateHistory uint64 // Number of blocks from head whose state histories are reserved.
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
SnapshotNoBuild bool // Whether the background generation is allowed SnapshotNoBuild bool // Whether the background generation is allowed
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
} }
// triedbConfig derives the configures for trie database.
func (c *CacheConfig) triedbConfig() *trie.Config {
config := &trie.Config{Preimages: c.Preimages}
if c.StateScheme == rawdb.HashScheme {
config.HashDB = &hashdb.Config{
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
}
}
if c.StateScheme == rawdb.PathScheme {
config.PathDB = &pathdb.Config{
StateHistory: c.StateHistory,
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
DirtyCacheSize: c.TrieDirtyLimit * 1024 * 1024,
}
}
return config
}
// defaultCacheConfig are the default caching values if none are specified by the // defaultCacheConfig are the default caching values if none are specified by the
// user (also used during testing). // user (also used during testing).
var defaultCacheConfig = &CacheConfig{ var defaultCacheConfig = &CacheConfig{
@ -150,6 +174,15 @@ var defaultCacheConfig = &CacheConfig{
TrieTimeLimit: 5 * time.Minute, TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256, SnapshotLimit: 256,
SnapshotWait: true, SnapshotWait: true,
StateScheme: rawdb.HashScheme,
}
// DefaultCacheConfigWithScheme returns a deep copied default cache config with
// a provided trie node scheme.
func DefaultCacheConfigWithScheme(scheme string) *CacheConfig {
config := *defaultCacheConfig
config.StateScheme = scheme
return &config
} }
// BlockchainLogger is used to collect traces during chain processing. // BlockchainLogger is used to collect traces during chain processing.
@ -248,10 +281,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
cacheConfig = defaultCacheConfig cacheConfig = defaultCacheConfig
} }
// Open trie database with provided config // Open trie database with provided config
triedb := trie.NewDatabaseWithConfig(db, &trie.Config{ triedb := trie.NewDatabase(db, cacheConfig.triedbConfig())
Cache: cacheConfig.TrieCleanLimit,
Preimages: cacheConfig.Preimages,
})
var logger BlockchainLogger var logger BlockchainLogger
if vmConfig.Tracer != nil { if vmConfig.Tracer != nil {
l, ok := vmConfig.Tracer.(BlockchainLogger) l, ok := vmConfig.Tracer.(BlockchainLogger)
@ -316,6 +346,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
bc.currentFinalBlock.Store(nil) bc.currentFinalBlock.Store(nil)
bc.currentSafeBlock.Store(nil) bc.currentSafeBlock.Store(nil)
// Update chain info data metrics
chainInfoGauge.Update(metrics.GaugeInfoValue{"chain_id": bc.chainConfig.ChainID.String()})
// If Geth is initialized with an external ancient store, re-initialize the // If Geth is initialized with an external ancient store, re-initialize the
// missing chain indexes and chain flags. This procedure can survive crash // missing chain indexes and chain flags. This procedure can survive crash
// and can be resumed in next restart since chain flags are updated in last step. // and can be resumed in next restart since chain flags are updated in last step.
@ -650,6 +683,25 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
pivot := rawdb.ReadLastPivotNumber(bc.db) pivot := rawdb.ReadLastPivotNumber(bc.db)
frozen, _ := bc.db.Ancients() frozen, _ := bc.db.Ancients()
// resetState resets the persistent state to genesis if it's not available.
resetState := func() {
// Short circuit if the genesis state is already present.
if bc.HasState(bc.genesisBlock.Root()) {
return
}
// Reset the state database to empty for committing genesis state.
// Note, it should only happen in path-based scheme and Reset function
// is also only call-able in this mode.
if bc.triedb.Scheme() == rawdb.PathScheme {
if err := bc.triedb.Reset(types.EmptyRootHash); err != nil {
log.Crit("Failed to clean state", "err", err) // Shouldn't happen
}
}
// Write genesis state into database.
if err := CommitGenesisState(bc.db, bc.triedb, bc.genesisBlock.Hash()); err != nil {
log.Crit("Failed to commit genesis state", "err", err)
}
}
updateFn := func(db ethdb.KeyValueWriter, header *types.Header) (*types.Header, bool) { updateFn := func(db ethdb.KeyValueWriter, header *types.Header) (*types.Header, bool) {
// Rewind the blockchain, ensuring we don't end up with a stateless head // Rewind the blockchain, ensuring we don't end up with a stateless head
// block. Note, depth equality is permitted to allow using SetHead as a // block. Note, depth equality is permitted to allow using SetHead as a
@ -659,6 +711,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
if newHeadBlock == nil { if newHeadBlock == nil {
log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash()) log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash())
newHeadBlock = bc.genesisBlock newHeadBlock = bc.genesisBlock
resetState()
} else { } else {
// Block exists, keep rewinding until we find one with state, // Block exists, keep rewinding until we find one with state,
// keeping rewinding until we exceed the optional threshold // keeping rewinding until we exceed the optional threshold
@ -670,7 +723,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
if root != (common.Hash{}) && !beyondRoot && newHeadBlock.Root() == root { if root != (common.Hash{}) && !beyondRoot && newHeadBlock.Root() == root {
beyondRoot, rootNumber = true, newHeadBlock.NumberU64() beyondRoot, rootNumber = true, newHeadBlock.NumberU64()
} }
if !bc.HasState(newHeadBlock.Root()) { if !bc.HasState(newHeadBlock.Root()) && !bc.stateRecoverable(newHeadBlock.Root()) {
log.Trace("Block state missing, rewinding further", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash()) log.Trace("Block state missing, rewinding further", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
if pivot == nil || newHeadBlock.NumberU64() > *pivot { if pivot == nil || newHeadBlock.NumberU64() > *pivot {
parent := bc.GetBlock(newHeadBlock.ParentHash(), newHeadBlock.NumberU64()-1) parent := bc.GetBlock(newHeadBlock.ParentHash(), newHeadBlock.NumberU64()-1)
@ -687,16 +740,12 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
} }
if beyondRoot || newHeadBlock.NumberU64() == 0 { if beyondRoot || newHeadBlock.NumberU64() == 0 {
if newHeadBlock.NumberU64() == 0 { if newHeadBlock.NumberU64() == 0 {
// Recommit the genesis state into disk in case the rewinding destination resetState()
// is genesis block and the relevant state is gone. In the future this } else if !bc.HasState(newHeadBlock.Root()) {
// rewinding destination can be the earliest block stored in the chain // Rewind to a block with recoverable state. If the state is
// if the historical chain pruning is enabled. In that case the logic // missing, run the state recovery here.
// needs to be improved here. if err := bc.triedb.Recover(newHeadBlock.Root()); err != nil {
if !bc.HasState(bc.genesisBlock.Root()) { log.Crit("Failed to rollback state", "err", err) // Shouldn't happen
if err := CommitGenesisState(bc.db, bc.triedb, bc.genesisBlock.Hash()); err != nil {
log.Crit("Failed to commit genesis state", "err", err)
}
log.Debug("Recommitted genesis state to disk")
} }
} }
log.Debug("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash()) log.Debug("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
@ -751,7 +800,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
if num+1 <= frozen { if num+1 <= frozen {
// Truncate all relative data(header, total difficulty, body, receipt // Truncate all relative data(header, total difficulty, body, receipt
// and canonical hash) from ancient store. // and canonical hash) from ancient store.
if err := bc.db.TruncateHead(num); err != nil { if _, err := bc.db.TruncateHead(num); err != nil {
log.Crit("Failed to truncate ancient data", "number", num, "err", err) log.Crit("Failed to truncate ancient data", "number", num, "err", err)
} }
// Remove the hash <-> number mapping from the active store. // Remove the hash <-> number mapping from the active store.
@ -810,7 +859,13 @@ func (bc *BlockChain) SnapSyncCommitHead(hash common.Hash) error {
if block == nil { if block == nil {
return fmt.Errorf("non existent block [%x..]", hash[:4]) return fmt.Errorf("non existent block [%x..]", hash[:4])
} }
// Reset the trie database with the fresh snap synced state.
root := block.Root() root := block.Root()
if bc.triedb.Scheme() == rawdb.PathScheme {
if err := bc.triedb.Reset(root); err != nil {
return err
}
}
if !bc.HasState(root) { if !bc.HasState(root) {
return fmt.Errorf("non existent state [%x..]", root[:4]) return fmt.Errorf("non existent state [%x..]", root[:4])
} }
@ -975,41 +1030,47 @@ func (bc *BlockChain) Stop() {
log.Error("Failed to journal state snapshot", "err", err) log.Error("Failed to journal state snapshot", "err", err)
} }
} }
if bc.triedb.Scheme() == rawdb.PathScheme {
// Ensure that the in-memory trie nodes are journaled to disk properly.
if err := bc.triedb.Journal(bc.CurrentBlock().Root); err != nil {
log.Info("Failed to journal in-memory trie nodes", "err", err)
}
} else {
// Ensure the state of a recent block is also stored to disk before exiting.
// We're writing three different states to catch different restart scenarios:
// - HEAD: So we don't need to reprocess any blocks in the general case
// - HEAD-1: So we don't do large reorgs if our HEAD becomes an uncle
// - HEAD-127: So we have a hard limit on the number of blocks reexecuted
if !bc.cacheConfig.TrieDirtyDisabled {
triedb := bc.triedb
// Ensure the state of a recent block is also stored to disk before exiting. for _, offset := range []uint64{0, 1, TriesInMemory - 1} {
// We're writing three different states to catch different restart scenarios: if number := bc.CurrentBlock().Number.Uint64(); number > offset {
// - HEAD: So we don't need to reprocess any blocks in the general case recent := bc.GetBlockByNumber(number - offset)
// - HEAD-1: So we don't do large reorgs if our HEAD becomes an uncle
// - HEAD-127: So we have a hard limit on the number of blocks reexecuted
if !bc.cacheConfig.TrieDirtyDisabled {
triedb := bc.triedb
for _, offset := range []uint64{0, 1, TriesInMemory - 1} { log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
if number := bc.CurrentBlock().Number.Uint64(); number > offset { if err := triedb.Commit(recent.Root(), true); err != nil {
recent := bc.GetBlockByNumber(number - offset) log.Error("Failed to commit recent state trie", "err", err)
}
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root()) }
if err := triedb.Commit(recent.Root(), true); err != nil { }
if snapBase != (common.Hash{}) {
log.Info("Writing snapshot state to disk", "root", snapBase)
if err := triedb.Commit(snapBase, true); err != nil {
log.Error("Failed to commit recent state trie", "err", err) log.Error("Failed to commit recent state trie", "err", err)
} }
} }
} for !bc.triegc.Empty() {
if snapBase != (common.Hash{}) { triedb.Dereference(bc.triegc.PopItem())
log.Info("Writing snapshot state to disk", "root", snapBase) }
if err := triedb.Commit(snapBase, true); err != nil { if _, nodes, _ := triedb.Size(); nodes != 0 { // all memory is contained within the nodes return for hashdb
log.Error("Failed to commit recent state trie", "err", err) log.Error("Dangling trie nodes after full cleanup")
} }
} }
for !bc.triegc.Empty() {
triedb.Dereference(bc.triegc.PopItem())
}
if size, _ := triedb.Size(); size != 0 {
log.Error("Dangling trie nodes after full cleanup")
}
} }
// Flush the collected preimages to disk // Close the trie database, release all the held resources as the last step.
if err := bc.stateCache.TrieDB().Close(); err != nil { if err := bc.triedb.Close(); err != nil {
log.Error("Failed to close trie db", "err", err) log.Error("Failed to close trie database", "err", err)
} }
log.Info("Blockchain stopped") log.Info("Blockchain stopped")
} }
@ -1034,8 +1095,8 @@ func (bc *BlockChain) procFutureBlocks() {
} }
} }
if len(blocks) > 0 { if len(blocks) > 0 {
slices.SortFunc(blocks, func(a, b *types.Block) bool { slices.SortFunc(blocks, func(a, b *types.Block) int {
return a.NumberU64() < b.NumberU64() return a.Number().Cmp(b.Number())
}) })
// Insert one by one as chain insertion needs contiguous ancestry between blocks // Insert one by one as chain insertion needs contiguous ancestry between blocks
for i := range blocks { for i := range blocks {
@ -1066,19 +1127,30 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
ancientReceipts, liveReceipts []types.Receipts ancientReceipts, liveReceipts []types.Receipts
) )
// Do a sanity check that the provided chain is actually ordered and linked // Do a sanity check that the provided chain is actually ordered and linked
for i := 0; i < len(blockChain); i++ { for i, block := range blockChain {
if i != 0 { if i != 0 {
if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() { prev := blockChain[i-1]
log.Error("Non contiguous receipt insert", "number", blockChain[i].Number(), "hash", blockChain[i].Hash(), "parent", blockChain[i].ParentHash(), if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() {
"prevnumber", blockChain[i-1].Number(), "prevhash", blockChain[i-1].Hash()) log.Error("Non contiguous receipt insert",
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, blockChain[i-1].NumberU64(), "number", block.Number(), "hash", block.Hash(), "parent", block.ParentHash(),
blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4]) "prevnumber", prev.Number(), "prevhash", prev.Hash())
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])",
i-1, prev.NumberU64(), prev.Hash().Bytes()[:4],
i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4])
} }
} }
if blockChain[i].NumberU64() <= ancientLimit { if block.NumberU64() <= ancientLimit {
ancientBlocks, ancientReceipts = append(ancientBlocks, blockChain[i]), append(ancientReceipts, receiptChain[i]) ancientBlocks, ancientReceipts = append(ancientBlocks, block), append(ancientReceipts, receiptChain[i])
} else { } else {
liveBlocks, liveReceipts = append(liveBlocks, blockChain[i]), append(liveReceipts, receiptChain[i]) liveBlocks, liveReceipts = append(liveBlocks, block), append(liveReceipts, receiptChain[i])
}
// Here we also validate that blob transactions in the block do not contain a sidecar.
// While the sidecar does not affect the block hash / tx hash, sending blobs within a block is not allowed.
for txIndex, tx := range block.Transactions() {
if tx.Type() == types.BlobTxType && tx.BlobTxSidecar() != nil {
return 0, fmt.Errorf("block #%d contains unexpected blob sidecar in tx at index %d", block.NumberU64(), txIndex)
}
} }
} }
@ -1174,7 +1246,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
size += int64(batch.ValueSize()) size += int64(batch.ValueSize())
if err = batch.Write(); err != nil { if err = batch.Write(); err != nil {
snapBlock := bc.CurrentSnapBlock().Number.Uint64() snapBlock := bc.CurrentSnapBlock().Number.Uint64()
if err := bc.db.TruncateHead(snapBlock + 1); err != nil { if _, err := bc.db.TruncateHead(snapBlock + 1); err != nil {
log.Error("Can't truncate ancient store after failed insert", "err", err) log.Error("Can't truncate ancient store after failed insert", "err", err)
} }
return 0, err return 0, err
@ -1192,7 +1264,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
if !updateHead(blockChain[len(blockChain)-1]) { if !updateHead(blockChain[len(blockChain)-1]) {
// We end up here if the header chain has reorg'ed, and the blocks/receipts // We end up here if the header chain has reorg'ed, and the blocks/receipts
// don't match the canonical chain. // don't match the canonical chain.
if err := bc.db.TruncateHead(previousSnapBlock + 1); err != nil { if _, err := bc.db.TruncateHead(previousSnapBlock + 1); err != nil {
log.Error("Can't truncate ancient store after failed insert", "err", err) log.Error("Can't truncate ancient store after failed insert", "err", err)
} }
return 0, errSideChainReceipts return 0, errSideChainReceipts
@ -1379,6 +1451,11 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
if err != nil { if err != nil {
return err return err
} }
// If node is running in path mode, skip explicit gc operation
// which is unnecessary in this mode.
if bc.triedb.Scheme() == rawdb.PathScheme {
return nil
}
// If we're running an archive node, always flush // If we're running an archive node, always flush
if bc.cacheConfig.TrieDirtyDisabled { if bc.cacheConfig.TrieDirtyDisabled {
return bc.triedb.Commit(root, false) return bc.triedb.Commit(root, false)
@ -1387,15 +1464,15 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
bc.triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive bc.triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
bc.triegc.Push(root, -int64(block.NumberU64())) bc.triegc.Push(root, -int64(block.NumberU64()))
current := block.NumberU64()
// Flush limits are not considered for the first TriesInMemory blocks. // Flush limits are not considered for the first TriesInMemory blocks.
current := block.NumberU64()
if current <= TriesInMemory { if current <= TriesInMemory {
return nil return nil
} }
// If we exceeded our memory allowance, flush matured singleton nodes to disk // If we exceeded our memory allowance, flush matured singleton nodes to disk
var ( 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 limit = common.StorageSize(bc.cacheConfig.TrieDirtyLimit) * 1024 * 1024
) )
if nodes > limit || imgs > 4*1024*1024 { if nodes > limit || imgs > 4*1024*1024 {
bc.triedb.Cap(limit - ethdb.IdealBatchSize) bc.triedb.Cap(limit - ethdb.IdealBatchSize)
@ -1844,8 +1921,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
stats.processed++ stats.processed++
stats.usedGas += usedGas stats.usedGas += usedGas
dirty, _ := bc.triedb.Size() var snapDiffItems, snapBufItems common.StorageSize
stats.report(chain, it.index, dirty, setHead) 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 { if !setHead {
// After merge we expect few side chains. Simply count // After merge we expect few side chains. Simply count
@ -1992,6 +2073,12 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
) )
parent := it.previous() parent := it.previous()
for parent != nil && !bc.HasState(parent.Root) { for parent != nil && !bc.HasState(parent.Root) {
if bc.stateRecoverable(parent.Root) {
if err := bc.triedb.Recover(parent.Root); err != nil {
return 0, err
}
break
}
hashes = append(hashes, parent.Hash()) hashes = append(hashes, parent.Hash())
numbers = append(numbers, parent.Number.Uint64()) numbers = append(numbers, parent.Number.Uint64())
@ -2048,6 +2135,12 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
parent = block parent = block
) )
for parent != nil && !bc.HasState(parent.Root()) { for parent != nil && !bc.HasState(parent.Root()) {
if bc.stateRecoverable(parent.Root()) {
if err := bc.triedb.Recover(parent.Root()); err != nil {
return common.Hash{}, err
}
break
}
hashes = append(hashes, parent.Hash()) hashes = append(hashes, parent.Hash())
numbers = append(numbers, parent.NumberU64()) numbers = append(numbers, parent.NumberU64())
parent = bc.GetBlock(parent.ParentHash(), parent.NumberU64()-1) parent = bc.GetBlock(parent.ParentHash(), parent.NumberU64()-1)
@ -2084,17 +2177,15 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
// collectLogs collects the logs that were generated or removed during // collectLogs collects the logs that were generated or removed during
// the processing of a block. These logs are later announced as deleted or reborn. // the processing of a block. These logs are later announced as deleted or reborn.
func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log {
var dataGasPrice *big.Int var blobGasPrice *big.Int
excessDataGas := b.ExcessDataGas() excessBlobGas := b.ExcessBlobGas()
if excessDataGas != nil { if excessBlobGas != nil {
dataGasPrice = eip4844.CalcBlobFee(*excessDataGas) blobGasPrice = eip4844.CalcBlobFee(*excessBlobGas)
} }
receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64()) receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64())
if err := receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Time(), b.BaseFee(), dataGasPrice, b.Transactions()); err != nil { if err := receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Time(), b.BaseFee(), blobGasPrice, b.Transactions()); err != nil {
log.Error("Failed to derive block receipts fields", "hash", b.Hash(), "number", b.NumberU64(), "err", err) log.Error("Failed to derive block receipts fields", "hash", b.Hash(), "number", b.NumberU64(), "err", err)
} }
var logs []*types.Log var logs []*types.Log
for _, receipt := range receipts { for _, receipt := range receipts {
for _, log := range receipt.Logs { for _, log := range receipt.Logs {
@ -2394,6 +2485,12 @@ func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
func (bc *BlockChain) indexBlocks(tail *uint64, head uint64, done chan struct{}) { func (bc *BlockChain) indexBlocks(tail *uint64, head uint64, done chan struct{}) {
defer func() { close(done) }() 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 // The tail flag is not existent, it means the node is just initialized
// and all blocks(may from ancient store) are not indexed yet. // and all blocks(may from ancient store) are not indexed yet.
if tail == nil { if tail == nil {
@ -2451,6 +2548,15 @@ func (bc *BlockChain) maintainTxIndex() {
return return
} }
defer sub.Unsubscribe() 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 { for {
select { select {

View file

@ -39,7 +39,7 @@ const statsReportLimit = 8 * time.Second
// report prints statistics if some number of blocks have been processed // report prints statistics if some number of blocks have been processed
// or more than a few seconds have passed since the last message. // 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 // Fetch the timings for the batch
var ( var (
now = mclock.Now() 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 { 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{}{"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 { if st.queued > 0 {
context = append(context, []interface{}{"queued", st.queued}...) context = append(context, []interface{}{"queued", st.queued}...)

View file

@ -293,10 +293,16 @@ func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
return bc.HasState(block.Root()) return bc.HasState(block.Root())
} }
// TrieNode retrieves a blob of data associated with a trie node // stateRecoverable checks if the specified state is recoverable.
// either from ephemeral in-memory cache, or from persistent storage. // Note, this function assumes the state is not present, because
func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) { // state is not treated as recoverable if it's available, thus
return bc.stateCache.TrieDB().Node(hash) // false will be returned in this case.
func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
if bc.triedb.Scheme() == rawdb.HashScheme {
return false
}
result, _ := bc.triedb.Recoverable(root)
return result
} }
// ContractCodeWithPrefix retrieves a blob of data associated with a contract // ContractCodeWithPrefix retrieves a blob of data associated with a contract

View file

@ -22,6 +22,7 @@ package core
import ( import (
"math/big" "math/big"
"path"
"testing" "testing"
"time" "time"
@ -1749,16 +1750,24 @@ func testLongReorgedSnapSyncingDeepRepair(t *testing.T, snapshots bool) {
} }
func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
testRepairWithScheme(t, tt, snapshots, scheme)
}
}
func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme string) {
// It's hard to follow the test case, visualize the input // It's hard to follow the test case, visualize the input
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) //log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
// fmt.Println(tt.dump(true)) // fmt.Println(tt.dump(true))
// Create a temporary persistent database // Create a temporary persistent database
datadir := t.TempDir() datadir := t.TempDir()
ancient := path.Join(datadir, "ancient")
db, err := rawdb.Open(rawdb.OpenOptions{ db, err := rawdb.Open(rawdb.OpenOptions{
Directory: datadir, Directory: datadir,
AncientsDirectory: datadir, AncientsDirectory: ancient,
Ephemeral: true,
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to create persistent database: %v", err) t.Fatalf("Failed to create persistent database: %v", err)
@ -1777,6 +1786,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
TrieDirtyLimit: 256, TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute, TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0, // Disable snapshot by default SnapshotLimit: 0, // Disable snapshot by default
StateScheme: scheme,
} }
) )
defer engine.Close() defer engine.Close()
@ -1806,7 +1816,9 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
t.Fatalf("Failed to import canonical chain start: %v", err) t.Fatalf("Failed to import canonical chain start: %v", err)
} }
if tt.commitBlock > 0 { if tt.commitBlock > 0 {
chain.stateCache.TrieDB().Commit(canonblocks[tt.commitBlock-1].Root(), false) if err := chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false); err != nil {
t.Fatalf("Failed to flush trie state: %v", err)
}
if snapshots { if snapshots {
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil { if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
t.Fatalf("Failed to flatten snapshots: %v", err) t.Fatalf("Failed to flatten snapshots: %v", err)
@ -1828,21 +1840,22 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
rawdb.WriteLastPivotNumber(db, *tt.pivotBlock) rawdb.WriteLastPivotNumber(db, *tt.pivotBlock)
} }
// Pull the plug on the database, simulating a hard crash // Pull the plug on the database, simulating a hard crash
chain.triedb.Close()
db.Close() db.Close()
chain.stopWithoutSaving() chain.stopWithoutSaving()
// Start a new blockchain back up and see where the repair leads us // Start a new blockchain back up and see where the repair leads us
db, err = rawdb.Open(rawdb.OpenOptions{ db, err = rawdb.Open(rawdb.OpenOptions{
Directory: datadir, Directory: datadir,
AncientsDirectory: datadir, AncientsDirectory: ancient,
Ephemeral: true,
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to reopen persistent database: %v", err) t.Fatalf("Failed to reopen persistent database: %v", err)
} }
defer db.Close() defer db.Close()
newChain, err := NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, nil) newChain, err := NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -1885,17 +1898,22 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
// In this case the snapshot layer of B3 is not created because of existent // In this case the snapshot layer of B3 is not created because of existent
// state. // state.
func TestIssue23496(t *testing.T) { func TestIssue23496(t *testing.T) {
testIssue23496(t, rawdb.HashScheme)
testIssue23496(t, rawdb.PathScheme)
}
func testIssue23496(t *testing.T, scheme string) {
// It's hard to follow the test case, visualize the input // It's hard to follow the test case, visualize the input
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) //log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
// Create a temporary persistent database // Create a temporary persistent database
datadir := t.TempDir() datadir := t.TempDir()
ancient := path.Join(datadir, "ancient")
db, err := rawdb.Open(rawdb.OpenOptions{ db, err := rawdb.Open(rawdb.OpenOptions{
Directory: datadir, Directory: datadir,
AncientsDirectory: datadir, AncientsDirectory: ancient,
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to create persistent database: %v", err) t.Fatalf("Failed to create persistent database: %v", err)
} }
@ -1908,15 +1926,8 @@ func TestIssue23496(t *testing.T) {
BaseFee: big.NewInt(params.InitialBaseFee), BaseFee: big.NewInt(params.InitialBaseFee),
} }
engine = ethash.NewFullFaker() engine = ethash.NewFullFaker()
config = &CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256,
SnapshotWait: true,
}
) )
chain, err := NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil, nil) chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create chain: %v", err) t.Fatalf("Failed to create chain: %v", err)
} }
@ -1929,7 +1940,7 @@ func TestIssue23496(t *testing.T) {
if _, err := chain.InsertChain(blocks[:1]); err != nil { if _, err := chain.InsertChain(blocks[:1]); err != nil {
t.Fatalf("Failed to import canonical chain start: %v", err) t.Fatalf("Failed to import canonical chain start: %v", err)
} }
chain.stateCache.TrieDB().Commit(blocks[0].Root(), false) chain.triedb.Commit(blocks[0].Root(), false)
// Insert block B2 and commit the snapshot into disk // Insert block B2 and commit the snapshot into disk
if _, err := chain.InsertChain(blocks[1:2]); err != nil { if _, err := chain.InsertChain(blocks[1:2]); err != nil {
@ -1943,7 +1954,7 @@ func TestIssue23496(t *testing.T) {
if _, err := chain.InsertChain(blocks[2:3]); err != nil { if _, err := chain.InsertChain(blocks[2:3]); err != nil {
t.Fatalf("Failed to import canonical chain start: %v", err) t.Fatalf("Failed to import canonical chain start: %v", err)
} }
chain.stateCache.TrieDB().Commit(blocks[2].Root(), false) chain.triedb.Commit(blocks[2].Root(), false)
// Insert the remaining blocks // Insert the remaining blocks
if _, err := chain.InsertChain(blocks[3:]); err != nil { if _, err := chain.InsertChain(blocks[3:]); err != nil {
@ -1951,20 +1962,22 @@ func TestIssue23496(t *testing.T) {
} }
// Pull the plug on the database, simulating a hard crash // Pull the plug on the database, simulating a hard crash
chain.triedb.Close()
db.Close() db.Close()
chain.stopWithoutSaving() chain.stopWithoutSaving()
// Start a new blockchain back up and see where the repair leads us // Start a new blockchain back up and see where the repair leads us
db, err = rawdb.Open(rawdb.OpenOptions{ db, err = rawdb.Open(rawdb.OpenOptions{
Directory: datadir, Directory: datadir,
AncientsDirectory: datadir, AncientsDirectory: ancient,
Ephemeral: true,
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to reopen persistent database: %v", err) t.Fatalf("Failed to reopen persistent database: %v", err)
} }
defer db.Close() defer db.Close()
chain, err = NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, nil) chain, err = NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -1976,8 +1989,12 @@ func TestIssue23496(t *testing.T) {
if head := chain.CurrentSnapBlock(); head.Number.Uint64() != uint64(4) { if head := chain.CurrentSnapBlock(); head.Number.Uint64() != uint64(4) {
t.Errorf("Head fast block mismatch: have %d, want %d", head.Number, uint64(4)) t.Errorf("Head fast block mismatch: have %d, want %d", head.Number, uint64(4))
} }
if head := chain.CurrentBlock(); head.Number.Uint64() != uint64(1) { expHead := uint64(1)
t.Errorf("Head block mismatch: have %d, want %d", head.Number, uint64(1)) if scheme == rawdb.PathScheme {
expHead = uint64(2)
}
if head := chain.CurrentBlock(); head.Number.Uint64() != expHead {
t.Errorf("Head block mismatch: have %d, want %d", head.Number, expHead)
} }
// Reinsert B2-B4 // Reinsert B2-B4

View file

@ -22,6 +22,7 @@ package core
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"path"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -29,9 +30,13 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
) )
// rewindTest is a test case for chain rollback upon user request. // rewindTest is a test case for chain rollback upon user request.
@ -1949,16 +1954,24 @@ func testLongReorgedSnapSyncingDeepSetHead(t *testing.T, snapshots bool) {
} }
func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) { func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
testSetHeadWithScheme(t, tt, snapshots, scheme)
}
}
func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme string) {
// It's hard to follow the test case, visualize the input // It's hard to follow the test case, visualize the input
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
// fmt.Println(tt.dump(false)) // fmt.Println(tt.dump(false))
// Create a temporary persistent database // Create a temporary persistent database
datadir := t.TempDir() datadir := t.TempDir()
ancient := path.Join(datadir, "ancient")
db, err := rawdb.Open(rawdb.OpenOptions{ db, err := rawdb.Open(rawdb.OpenOptions{
Directory: datadir, Directory: datadir,
AncientsDirectory: datadir, AncientsDirectory: ancient,
Ephemeral: true,
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to create persistent database: %v", err) t.Fatalf("Failed to create persistent database: %v", err)
@ -1977,6 +1990,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
TrieDirtyLimit: 256, TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute, TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0, // Disable snapshot SnapshotLimit: 0, // Disable snapshot
StateScheme: scheme,
} }
) )
if snapshots { if snapshots {
@ -2007,7 +2021,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
t.Fatalf("Failed to import canonical chain start: %v", err) t.Fatalf("Failed to import canonical chain start: %v", err)
} }
if tt.commitBlock > 0 { if tt.commitBlock > 0 {
chain.stateCache.TrieDB().Commit(canonblocks[tt.commitBlock-1].Root(), false) chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false)
if snapshots { if snapshots {
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil { if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
t.Fatalf("Failed to flatten snapshots: %v", err) t.Fatalf("Failed to flatten snapshots: %v", err)
@ -2017,13 +2031,17 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
if _, err := chain.InsertChain(canonblocks[tt.commitBlock:]); err != nil { if _, err := chain.InsertChain(canonblocks[tt.commitBlock:]); err != nil {
t.Fatalf("Failed to import canonical chain tail: %v", err) t.Fatalf("Failed to import canonical chain tail: %v", err)
} }
// Manually dereference anything not committed to not have to work with 128+ tries // Reopen the trie database without persisting in-memory dirty nodes.
for _, block := range sideblocks { chain.triedb.Close()
chain.stateCache.TrieDB().Dereference(block.Root()) dbconfig := &trie.Config{}
} if scheme == rawdb.PathScheme {
for _, block := range canonblocks { dbconfig.PathDB = pathdb.Defaults
chain.stateCache.TrieDB().Dereference(block.Root()) } else {
dbconfig.HashDB = hashdb.Defaults
} }
chain.triedb = trie.NewDatabase(chain.db, dbconfig)
chain.stateCache = state.NewDatabaseWithNodeDB(chain.db, chain.triedb)
// Force run a freeze cycle // Force run a freeze cycle
type freezer interface { type freezer interface {
Freeze(threshold uint64) error Freeze(threshold uint64) error

View file

@ -24,6 +24,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"os" "os"
"path"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -39,6 +40,7 @@ import (
// snapshotTestBasic wraps the common testing fields in the snapshot tests. // snapshotTestBasic wraps the common testing fields in the snapshot tests.
type snapshotTestBasic struct { type snapshotTestBasic struct {
scheme string // Disk scheme used for storing trie nodes
chainBlocks int // Number of blocks to generate for the canonical chain chainBlocks int // Number of blocks to generate for the canonical chain
snapshotBlock uint64 // Block number of the relevant snapshot disk layer snapshotBlock uint64 // Block number of the relevant snapshot disk layer
commitBlock uint64 // Block number for which to commit the state to disk commitBlock uint64 // Block number for which to commit the state to disk
@ -51,6 +53,7 @@ type snapshotTestBasic struct {
// share fields, set in runtime // share fields, set in runtime
datadir string datadir string
ancient string
db ethdb.Database db ethdb.Database
genDb ethdb.Database genDb ethdb.Database
engine consensus.Engine engine consensus.Engine
@ -60,10 +63,12 @@ type snapshotTestBasic struct {
func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Block) { func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Block) {
// Create a temporary persistent database // Create a temporary persistent database
datadir := t.TempDir() datadir := t.TempDir()
ancient := path.Join(datadir, "ancient")
db, err := rawdb.Open(rawdb.OpenOptions{ db, err := rawdb.Open(rawdb.OpenOptions{
Directory: datadir, Directory: datadir,
AncientsDirectory: datadir, AncientsDirectory: ancient,
Ephemeral: true,
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to create persistent database: %v", err) t.Fatalf("Failed to create persistent database: %v", err)
@ -75,13 +80,8 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
Config: params.AllEthashProtocolChanges, Config: params.AllEthashProtocolChanges,
} }
engine = ethash.NewFullFaker() engine = ethash.NewFullFaker()
// Snapshot is enabled, the first snapshot is created from the Genesis.
// The snapshot memory allowance is 256MB, it means no snapshot flush
// will happen during the block insertion.
cacheConfig = defaultCacheConfig
) )
chain, err := NewBlockChain(db, cacheConfig, gspec, nil, engine, vm.Config{}, nil, nil) chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(basic.scheme), gspec, nil, engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create chain: %v", err) t.Fatalf("Failed to create chain: %v", err)
} }
@ -102,7 +102,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
startPoint = point startPoint = point
if basic.commitBlock > 0 && basic.commitBlock == point { if basic.commitBlock > 0 && basic.commitBlock == point {
chain.stateCache.TrieDB().Commit(blocks[point-1].Root(), false) chain.TrieDB().Commit(blocks[point-1].Root(), false)
} }
if basic.snapshotBlock > 0 && basic.snapshotBlock == point { if basic.snapshotBlock > 0 && basic.snapshotBlock == point {
// Flushing the entire snap tree into the disk, the // Flushing the entire snap tree into the disk, the
@ -121,6 +121,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
// Set runtime fields // Set runtime fields
basic.datadir = datadir basic.datadir = datadir
basic.ancient = ancient
basic.db = db basic.db = db
basic.genDb = genDb basic.genDb = genDb
basic.engine = engine basic.engine = engine
@ -210,6 +211,7 @@ func (basic *snapshotTestBasic) teardown() {
basic.db.Close() basic.db.Close()
basic.genDb.Close() basic.genDb.Close()
os.RemoveAll(basic.datadir) os.RemoveAll(basic.datadir)
os.RemoveAll(basic.ancient)
} }
// snapshotTest is a test case type for normal snapshot recovery. // snapshotTest is a test case type for normal snapshot recovery.
@ -226,7 +228,7 @@ func (snaptest *snapshotTest) test(t *testing.T) {
// Restart the chain normally // Restart the chain normally
chain.Stop() chain.Stop()
newchain, err := NewBlockChain(snaptest.db, nil, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) newchain, err := NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -235,7 +237,7 @@ func (snaptest *snapshotTest) test(t *testing.T) {
snaptest.verify(t, newchain, blocks) snaptest.verify(t, newchain, blocks)
} }
// crashSnapshotTest is a test case type for innormal snapshot recovery. // crashSnapshotTest is a test case type for irregular snapshot recovery.
// It can be used for testing that restart Geth after the crash. // It can be used for testing that restart Geth after the crash.
type crashSnapshotTest struct { type crashSnapshotTest struct {
snapshotTestBasic snapshotTestBasic
@ -251,13 +253,14 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
db := chain.db db := chain.db
db.Close() db.Close()
chain.stopWithoutSaving() chain.stopWithoutSaving()
chain.triedb.Close()
// Start a new blockchain back up and see where the repair leads us // Start a new blockchain back up and see where the repair leads us
newdb, err := rawdb.Open(rawdb.OpenOptions{ newdb, err := rawdb.Open(rawdb.OpenOptions{
Directory: snaptest.datadir, Directory: snaptest.datadir,
AncientsDirectory: snaptest.datadir, AncientsDirectory: snaptest.ancient,
Ephemeral: true,
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to reopen persistent database: %v", err) t.Fatalf("Failed to reopen persistent database: %v", err)
} }
@ -267,13 +270,13 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
// the crash, we do restart twice here: one after the crash and one // the crash, we do restart twice here: one after the crash and one
// after the normal stop. It's used to ensure the broken snapshot // after the normal stop. It's used to ensure the broken snapshot
// can be detected all the time. // can be detected all the time.
newchain, err := NewBlockChain(newdb, nil, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) newchain, err := NewBlockChain(newdb, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
newchain.Stop() newchain.Stop()
newchain, err = NewBlockChain(newdb, nil, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) newchain, err = NewBlockChain(newdb, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -300,7 +303,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
// Insert blocks without enabling snapshot if gapping is required. // Insert blocks without enabling snapshot if gapping is required.
chain.Stop() chain.Stop()
gappedBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.genDb, snaptest.gapped, func(i int, b *BlockGen) {}) gappedBlocks, _ := GenerateChain(snaptest.gspec.Config, blocks[len(blocks)-1], snaptest.engine, snaptest.genDb, snaptest.gapped, func(i int, b *BlockGen) {})
// Insert a few more blocks without enabling snapshot // Insert a few more blocks without enabling snapshot
var cacheConfig = &CacheConfig{ var cacheConfig = &CacheConfig{
@ -308,6 +311,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
TrieDirtyLimit: 256, TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute, TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0, SnapshotLimit: 0,
StateScheme: snaptest.scheme,
} }
newchain, err := NewBlockChain(snaptest.db, cacheConfig, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) newchain, err := NewBlockChain(snaptest.db, cacheConfig, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
@ -317,7 +321,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
newchain.Stop() newchain.Stop()
// Restart the chain with enabling the snapshot // Restart the chain with enabling the snapshot
newchain, err = NewBlockChain(snaptest.db, nil, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) newchain, err = NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -345,7 +349,7 @@ func (snaptest *setHeadSnapshotTest) test(t *testing.T) {
chain.SetHead(snaptest.setHead) chain.SetHead(snaptest.setHead)
chain.Stop() chain.Stop()
newchain, err := NewBlockChain(snaptest.db, nil, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) newchain, err := NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
@ -379,22 +383,24 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
TrieDirtyLimit: 256, TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute, TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0, SnapshotLimit: 0,
StateScheme: snaptest.scheme,
} }
newchain, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) newchain, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
newBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.genDb, snaptest.newBlocks, func(i int, b *BlockGen) {}) newBlocks, _ := GenerateChain(snaptest.gspec.Config, blocks[len(blocks)-1], snaptest.engine, snaptest.genDb, snaptest.newBlocks, func(i int, b *BlockGen) {})
newchain.InsertChain(newBlocks) newchain.InsertChain(newBlocks)
newchain.Stop() newchain.Stop()
// Restart the chain, the wiper should starts working // Restart the chain, the wiper should start working
config = &CacheConfig{ config = &CacheConfig{
TrieCleanLimit: 256, TrieCleanLimit: 256,
TrieDirtyLimit: 256, TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute, TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256, SnapshotLimit: 256,
SnapshotWait: false, // Don't wait rebuild SnapshotWait: false, // Don't wait rebuild
StateScheme: snaptest.scheme,
} }
tmp, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) tmp, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
@ -402,14 +408,15 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
} }
// Simulate the blockchain crash. // Simulate the blockchain crash.
tmp.triedb.Close()
tmp.stopWithoutSaving() tmp.stopWithoutSaving()
newchain, err = NewBlockChain(snaptest.db, nil, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) newchain, err = NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
defer newchain.Stop()
snaptest.verify(t, newchain, blocks) snaptest.verify(t, newchain, blocks)
newchain.Stop()
} }
// Tests a Geth restart with valid snapshot. Before the shutdown, all snapshot // Tests a Geth restart with valid snapshot. Before the shutdown, all snapshot
@ -433,20 +440,23 @@ func TestRestartWithNewSnapshot(t *testing.T) {
// Expected head fast block: C8 // Expected head fast block: C8
// Expected head block : C8 // Expected head block : C8
// Expected snapshot disk : G // Expected snapshot disk : G
test := &snapshotTest{ for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
snapshotTestBasic{ test := &snapshotTest{
chainBlocks: 8, snapshotTestBasic{
snapshotBlock: 0, scheme: scheme,
commitBlock: 0, chainBlocks: 8,
expCanonicalBlocks: 8, snapshotBlock: 0,
expHeadHeader: 8, commitBlock: 0,
expHeadFastBlock: 8, expCanonicalBlocks: 8,
expHeadBlock: 8, expHeadHeader: 8,
expSnapshotBottom: 0, // Initial disk layer built from genesis expHeadFastBlock: 8,
}, expHeadBlock: 8,
expSnapshotBottom: 0, // Initial disk layer built from genesis
},
}
test.test(t)
test.teardown()
} }
test.test(t)
test.teardown()
} }
// Tests a Geth was crashed and restarts with a broken snapshot. In this case the // Tests a Geth was crashed and restarts with a broken snapshot. In this case the
@ -472,20 +482,23 @@ func TestNoCommitCrashWithNewSnapshot(t *testing.T) {
// Expected head fast block: C8 // Expected head fast block: C8
// Expected head block : G // Expected head block : G
// Expected snapshot disk : C4 // Expected snapshot disk : C4
test := &crashSnapshotTest{ for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
snapshotTestBasic{ test := &crashSnapshotTest{
chainBlocks: 8, snapshotTestBasic{
snapshotBlock: 4, scheme: scheme,
commitBlock: 0, chainBlocks: 8,
expCanonicalBlocks: 8, snapshotBlock: 4,
expHeadHeader: 8, commitBlock: 0,
expHeadFastBlock: 8, expCanonicalBlocks: 8,
expHeadBlock: 0, expHeadHeader: 8,
expSnapshotBottom: 4, // Last committed disk layer, wait recovery expHeadFastBlock: 8,
}, expHeadBlock: 0,
expSnapshotBottom: 4, // Last committed disk layer, wait recovery
},
}
test.test(t)
test.teardown()
} }
test.test(t)
test.teardown()
} }
// Tests a Geth was crashed and restarts with a broken snapshot. In this case the // Tests a Geth was crashed and restarts with a broken snapshot. In this case the
@ -511,20 +524,23 @@ func TestLowCommitCrashWithNewSnapshot(t *testing.T) {
// Expected head fast block: C8 // Expected head fast block: C8
// Expected head block : C2 // Expected head block : C2
// Expected snapshot disk : C4 // Expected snapshot disk : C4
test := &crashSnapshotTest{ for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
snapshotTestBasic{ test := &crashSnapshotTest{
chainBlocks: 8, snapshotTestBasic{
snapshotBlock: 4, scheme: scheme,
commitBlock: 2, chainBlocks: 8,
expCanonicalBlocks: 8, snapshotBlock: 4,
expHeadHeader: 8, commitBlock: 2,
expHeadFastBlock: 8, expCanonicalBlocks: 8,
expHeadBlock: 2, expHeadHeader: 8,
expSnapshotBottom: 4, // Last committed disk layer, wait recovery expHeadFastBlock: 8,
}, expHeadBlock: 2,
expSnapshotBottom: 4, // Last committed disk layer, wait recovery
},
}
test.test(t)
test.teardown()
} }
test.test(t)
test.teardown()
} }
// Tests a Geth was crashed and restarts with a broken snapshot. In this case // Tests a Geth was crashed and restarts with a broken snapshot. In this case
@ -550,20 +566,27 @@ func TestHighCommitCrashWithNewSnapshot(t *testing.T) {
// Expected head fast block: C8 // Expected head fast block: C8
// Expected head block : G // Expected head block : G
// Expected snapshot disk : C4 // Expected snapshot disk : C4
test := &crashSnapshotTest{ for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
snapshotTestBasic{ expHead := uint64(0)
chainBlocks: 8, if scheme == rawdb.PathScheme {
snapshotBlock: 4, expHead = uint64(4)
commitBlock: 6, }
expCanonicalBlocks: 8, test := &crashSnapshotTest{
expHeadHeader: 8, snapshotTestBasic{
expHeadFastBlock: 8, scheme: scheme,
expHeadBlock: 0, chainBlocks: 8,
expSnapshotBottom: 4, // Last committed disk layer, wait recovery snapshotBlock: 4,
}, commitBlock: 6,
expCanonicalBlocks: 8,
expHeadHeader: 8,
expHeadFastBlock: 8,
expHeadBlock: expHead,
expSnapshotBottom: 4, // Last committed disk layer, wait recovery
},
}
test.test(t)
test.teardown()
} }
test.test(t)
test.teardown()
} }
// Tests a Geth was running with snapshot enabled. Then restarts without // Tests a Geth was running with snapshot enabled. Then restarts without
@ -587,21 +610,24 @@ func TestGappedNewSnapshot(t *testing.T) {
// Expected head fast block: C10 // Expected head fast block: C10
// Expected head block : C10 // Expected head block : C10
// Expected snapshot disk : C10 // Expected snapshot disk : C10
test := &gappedSnapshotTest{ for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
snapshotTestBasic: snapshotTestBasic{ test := &gappedSnapshotTest{
chainBlocks: 8, snapshotTestBasic: snapshotTestBasic{
snapshotBlock: 0, scheme: scheme,
commitBlock: 0, chainBlocks: 8,
expCanonicalBlocks: 10, snapshotBlock: 0,
expHeadHeader: 10, commitBlock: 0,
expHeadFastBlock: 10, expCanonicalBlocks: 10,
expHeadBlock: 10, expHeadHeader: 10,
expSnapshotBottom: 10, // Rebuilt snapshot from the latest HEAD expHeadFastBlock: 10,
}, expHeadBlock: 10,
gapped: 2, expSnapshotBottom: 10, // Rebuilt snapshot from the latest HEAD
},
gapped: 2,
}
test.test(t)
test.teardown()
} }
test.test(t)
test.teardown()
} }
// Tests the Geth was running with snapshot enabled and resetHead is applied. // Tests the Geth was running with snapshot enabled and resetHead is applied.
@ -625,21 +651,24 @@ func TestSetHeadWithNewSnapshot(t *testing.T) {
// Expected head fast block: C4 // Expected head fast block: C4
// Expected head block : C4 // Expected head block : C4
// Expected snapshot disk : G // Expected snapshot disk : G
test := &setHeadSnapshotTest{ for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
snapshotTestBasic: snapshotTestBasic{ test := &setHeadSnapshotTest{
chainBlocks: 8, snapshotTestBasic: snapshotTestBasic{
snapshotBlock: 0, scheme: scheme,
commitBlock: 0, chainBlocks: 8,
expCanonicalBlocks: 4, snapshotBlock: 0,
expHeadHeader: 4, commitBlock: 0,
expHeadFastBlock: 4, expCanonicalBlocks: 4,
expHeadBlock: 4, expHeadHeader: 4,
expSnapshotBottom: 0, // The initial disk layer is built from the genesis expHeadFastBlock: 4,
}, expHeadBlock: 4,
setHead: 4, expSnapshotBottom: 0, // The initial disk layer is built from the genesis
},
setHead: 4,
}
test.test(t)
test.teardown()
} }
test.test(t)
test.teardown()
} }
// Tests the Geth was running with a complete snapshot and then imports a few // Tests the Geth was running with a complete snapshot and then imports a few
@ -663,19 +692,22 @@ func TestRecoverSnapshotFromWipingCrash(t *testing.T) {
// Expected head fast block: C10 // Expected head fast block: C10
// Expected head block : C8 // Expected head block : C8
// Expected snapshot disk : C10 // Expected snapshot disk : C10
test := &wipeCrashSnapshotTest{ for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
snapshotTestBasic: snapshotTestBasic{ test := &wipeCrashSnapshotTest{
chainBlocks: 8, snapshotTestBasic: snapshotTestBasic{
snapshotBlock: 4, scheme: scheme,
commitBlock: 0, chainBlocks: 8,
expCanonicalBlocks: 10, snapshotBlock: 4,
expHeadHeader: 10, commitBlock: 0,
expHeadFastBlock: 10, expCanonicalBlocks: 10,
expHeadBlock: 10, expHeadHeader: 10,
expSnapshotBottom: 10, expHeadFastBlock: 10,
}, expHeadBlock: 10,
newBlocks: 2, expSnapshotBottom: 10,
},
newBlocks: 2,
}
test.test(t)
test.teardown()
} }
test.test(t)
test.teardown()
} }

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc" "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/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -86,6 +88,11 @@ func (b *BlockGen) SetPoS() {
b.header.Difficulty = new(big.Int) 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 // addTx adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address. // been set, the block's coinbase is set to the zero address.
// //
@ -202,7 +209,7 @@ func (b *BlockGen) AddUncle(h *types.Header) {
// The gas limit and price should be derived from the parent // The gas limit and price should be derived from the parent
h.GasLimit = parent.GasLimit h.GasLimit = parent.GasLimit
if b.config.IsLondon(h.Number) { if b.config.IsLondon(h.Number) {
h.BaseFee = misc.CalcBaseFee(b.config, parent) h.BaseFee = eip1559.CalcBaseFee(b.config, parent)
if !b.config.IsLondon(parent.Number) { if !b.config.IsLondon(parent.Number) {
parentGasLimit := parent.GasLimit * b.config.ElasticityMultiplier() parentGasLimit := parent.GasLimit * b.config.ElasticityMultiplier()
h.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit) h.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit)
@ -282,7 +289,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
} }
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
chainreader := &fakeChainReader{config: config} chainreader := &fakeChainReader{config: config}
genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) { genblock := func(i int, parent *types.Block, triedb *trie.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine} b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine}
b.header = makeHeader(chainreader, parent, statedb, b.engine) b.header = makeHeader(chainreader, parent, statedb, b.engine)
@ -325,19 +332,23 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if err != nil { if err != nil {
panic(fmt.Sprintf("state write error: %v", err)) panic(fmt.Sprintf("state write error: %v", err))
} }
if err := statedb.Database().TrieDB().Commit(root, false); err != nil { if err = triedb.Commit(root, false); err != nil {
panic(fmt.Sprintf("trie write error: %v", err)) panic(fmt.Sprintf("trie write error: %v", err))
} }
return block, b.receipts return block, b.receipts
} }
return nil, nil return nil, nil
} }
// Forcibly use hash-based state scheme for retaining all nodes in disk.
triedb := trie.NewDatabase(db, trie.HashDefaults)
defer triedb.Close()
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
statedb, err := state.New(parent.Root(), state.NewDatabase(db), nil) statedb, err := state.New(parent.Root(), state.NewDatabaseWithNodeDB(db, triedb), nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
block, receipt := genblock(i, parent, statedb) block, receipt := genblock(i, parent, triedb, statedb)
blocks[i] = block blocks[i] = block
receipts[i] = receipt receipts[i] = receipt
parent = block parent = block
@ -350,7 +361,10 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
// then generate chain on top. // then generate chain on top.
func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) { func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
_, err := genesis.Commit(db, trie.NewDatabase(db), nil)
triedb := trie.NewDatabase(db, trie.HashDefaults)
defer triedb.Close()
_, err := genesis.Commit(db, triedb, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -380,12 +394,26 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
Time: time, Time: time,
} }
if chain.Config().IsLondon(header.Number) { if chain.Config().IsLondon(header.Number) {
header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header()) header.BaseFee = eip1559.CalcBaseFee(chain.Config(), parent.Header())
if !chain.Config().IsLondon(parent.Number()) { if !chain.Config().IsLondon(parent.Number()) {
parentGasLimit := parent.GasLimit() * chain.Config().ElasticityMultiplier() parentGasLimit := parent.GasLimit() * chain.Config().ElasticityMultiplier()
header.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit) 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 return header
} }

View file

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
) )
func TestGenerateWithdrawalChain(t *testing.T) { func TestGenerateWithdrawalChain(t *testing.T) {
@ -74,8 +75,7 @@ func TestGenerateWithdrawalChain(t *testing.T) {
Storage: storage, Storage: storage,
Code: common.Hex2Bytes("600154600354"), Code: common.Hex2Bytes("600154600354"),
} }
genesis := gspec.MustCommit(gendb, trie.NewDatabase(gendb, trie.HashDefaults))
genesis := gspec.MustCommit(gendb)
chain, _ := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) { chain, _ := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) {
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(address), address, big.NewInt(1000), params.TxGas, new(big.Int).Add(gen.BaseFee(), common.Big1), nil), signer, key) tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(address), address, big.NewInt(1000), params.TxGas, new(big.Int).Add(gen.BaseFee(), common.Big1), nil), signer, key)
@ -146,6 +146,7 @@ func ExampleGenerateChain() {
addr2 = crypto.PubkeyToAddress(key2.PublicKey) addr2 = crypto.PubkeyToAddress(key2.PublicKey)
addr3 = crypto.PubkeyToAddress(key3.PublicKey) addr3 = crypto.PubkeyToAddress(key3.PublicKey)
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
genDb = rawdb.NewMemoryDatabase()
) )
// Ensure that key1 has some funds in the genesis block. // Ensure that key1 has some funds in the genesis block.
@ -153,13 +154,13 @@ func ExampleGenerateChain() {
Config: &params.ChainConfig{HomesteadBlock: new(big.Int)}, Config: &params.ChainConfig{HomesteadBlock: new(big.Int)},
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
} }
genesis := gspec.MustCommit(db) genesis := gspec.MustCommit(genDb, trie.NewDatabase(genDb, trie.HashDefaults))
// This call generates a chain of 5 blocks. The function runs for // This call generates a chain of 5 blocks. The function runs for
// each block and adds different features to gen based on the // each block and adds different features to gen based on the
// block index. // block index.
signer := types.HomesteadSigner{} signer := types.HomesteadSigner{}
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) { chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), genDb, 5, func(i int, gen *BlockGen) {
switch i { switch i {
case 0: case 0:
// In block 1, addr1 sends addr2 some ether. // In block 1, addr1 sends addr2 some ether.
@ -188,7 +189,7 @@ func ExampleGenerateChain() {
}) })
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
blockchain, _ := NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil) blockchain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.HashScheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
defer blockchain.Stop() defer blockchain.Stop()
if i, err := blockchain.InsertChain(chain); err != nil { if i, err := blockchain.InsertChain(chain); err != nil {

View file

@ -83,7 +83,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import contra-fork chain for expansion: %v", err) t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, false); err != nil { if err := bc.triedb.Commit(bc.CurrentHeader().Root, false); err != nil {
t.Fatalf("failed to commit contra-fork head for expansion: %v", err) t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
} }
bc.Stop() bc.Stop()
@ -106,7 +106,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import pro-fork chain for expansion: %v", err) t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, false); err != nil { if err := bc.triedb.Commit(bc.CurrentHeader().Root, false); err != nil {
t.Fatalf("failed to commit pro-fork head for expansion: %v", err) t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
} }
bc.Stop() bc.Stop()
@ -131,7 +131,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import contra-fork chain for expansion: %v", err) t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, false); err != nil { if err := bc.triedb.Commit(bc.CurrentHeader().Root, false); err != nil {
t.Fatalf("failed to commit contra-fork head for expansion: %v", err) t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
} }
blocks, _ = GenerateChain(&proConf, conBc.GetBlockByHash(conBc.CurrentBlock().Hash()), ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {}) blocks, _ = GenerateChain(&proConf, conBc.GetBlockByHash(conBc.CurrentBlock().Hash()), ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {})
@ -149,7 +149,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import pro-fork chain for expansion: %v", err) t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, false); err != nil { if err := bc.triedb.Commit(bc.CurrentHeader().Root, false); err != nil {
t.Fatalf("failed to commit pro-fork head for expansion: %v", err) t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
} }
blocks, _ = GenerateChain(&conConf, proBc.GetBlockByHash(proBc.CurrentBlock().Hash()), ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {}) blocks, _ = GenerateChain(&conConf, proBc.GetBlockByHash(proBc.CurrentBlock().Hash()), ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {})

View file

@ -102,6 +102,6 @@ var (
ErrSenderNoEOA = errors.New("sender not an eoa") ErrSenderNoEOA = errors.New("sender not an eoa")
// ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the // ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the
// data gas fee of the block. // blob gas fee of the block.
ErrBlobFeeCapTooLow = errors.New("max fee per data gas less than block data gas fee") ErrBlobFeeCapTooLow = errors.New("max fee per blob gas less than block blob gas fee")
) )

View file

@ -67,7 +67,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
BaseFee: baseFee, BaseFee: baseFee,
GasLimit: header.GasLimit, GasLimit: header.GasLimit,
Random: random, Random: random,
ExcessDataGas: header.ExcessDataGas, ExcessBlobGas: header.ExcessBlobGas,
} }
} }

View file

@ -26,7 +26,6 @@ import (
"reflect" "reflect"
"strings" "strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -73,12 +72,12 @@ type ID struct {
type Filter func(id ID) error type Filter func(id ID) error
// NewID calculates the Ethereum fork ID from the chain config, genesis hash, head and time. // 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 // 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 // Calculate the current fork checksum and the next fork block
forksByBlock, forksByTime := gatherForks(config) forksByBlock, forksByTime := gatherForks(config, genesis.Time())
for _, fork := range forksByBlock { for _, fork := range forksByBlock {
if fork <= head { if fork <= head {
// Fork already passed, checksum the previous hash and the fork number // Fork already passed, checksum the previous hash and the fork number
@ -104,7 +103,7 @@ func NewIDWithChain(chain Blockchain) ID {
return NewID( return NewID(
chain.Config(), chain.Config(),
chain.Genesis().Hash(), chain.Genesis(),
head.Number.Uint64(), head.Number.Uint64(),
head.Time, head.Time,
) )
@ -115,7 +114,7 @@ func NewIDWithChain(chain Blockchain) ID {
func NewFilter(chain Blockchain) Filter { func NewFilter(chain Blockchain) Filter {
return newFilter( return newFilter(
chain.Config(), chain.Config(),
chain.Genesis().Hash(), chain.Genesis(),
func() (uint64, uint64) { func() (uint64, uint64) {
head := chain.CurrentHeader() head := chain.CurrentHeader()
return head.Number.Uint64(), head.Time return head.Number.Uint64(), head.Time
@ -124,7 +123,7 @@ func NewFilter(chain Blockchain) Filter {
} }
// NewStaticFilter creates a filter at block zero. // NewStaticFilter creates a filter at block zero.
func NewStaticFilter(config *params.ChainConfig, genesis common.Hash) Filter { func NewStaticFilter(config *params.ChainConfig, genesis *types.Block) Filter {
head := func() (uint64, uint64) { return 0, 0 } head := func() (uint64, uint64) { return 0, 0 }
return newFilter(config, genesis, head) return newFilter(config, genesis, head)
} }
@ -132,14 +131,14 @@ func NewStaticFilter(config *params.ChainConfig, genesis common.Hash) Filter {
// newFilter is the internal version of NewFilter, taking closures as its arguments // newFilter is the internal version of NewFilter, taking closures as its arguments
// instead of a chain. The reason is to allow testing it without having to simulate // instead of a chain. The reason is to allow testing it without having to simulate
// an entire blockchain. // an entire blockchain.
func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() (uint64, uint64)) Filter { func newFilter(config *params.ChainConfig, genesis *types.Block, headfn func() (uint64, uint64)) Filter {
// Calculate the all the valid fork hash and fork next combos // Calculate the all the valid fork hash and fork next combos
var ( var (
forksByBlock, forksByTime = gatherForks(config) forksByBlock, forksByTime = gatherForks(config, genesis.Time())
forks = append(append([]uint64{}, forksByBlock...), forksByTime...) forks = append(append([]uint64{}, forksByBlock...), forksByTime...)
sums = make([][4]byte, len(forks)+1) // 0th is the genesis sums = make([][4]byte, len(forks)+1) // 0th is the genesis
) )
hash := crc32.ChecksumIEEE(genesis[:]) hash := crc32.ChecksumIEEE(genesis.Hash().Bytes())
sums[0] = checksumToBytes(hash) sums[0] = checksumToBytes(hash)
for i, fork := range forks { for i, fork := range forks {
hash = checksumUpdate(hash, fork) hash = checksumUpdate(hash, fork)
@ -240,7 +239,7 @@ func checksumToBytes(hash uint32) [4]byte {
// gatherForks gathers all the known forks and creates two sorted lists out of // gatherForks gathers all the known forks and creates two sorted lists out of
// them, one for the block number based forks and the second for the timestamps. // them, one for the block number based forks and the second for the timestamps.
func gatherForks(config *params.ChainConfig) ([]uint64, []uint64) { func gatherForks(config *params.ChainConfig, genesis uint64) ([]uint64, []uint64) {
// Gather all the fork block numbers via reflection // Gather all the fork block numbers via reflection
kind := reflect.TypeOf(params.ChainConfig{}) kind := reflect.TypeOf(params.ChainConfig{})
conf := reflect.ValueOf(config).Elem() conf := reflect.ValueOf(config).Elem()
@ -290,7 +289,8 @@ func gatherForks(config *params.ChainConfig) ([]uint64, []uint64) {
if len(forksByBlock) > 0 && forksByBlock[0] == 0 { if len(forksByBlock) > 0 && forksByBlock[0] == 0 {
forksByBlock = forksByBlock[1:] forksByBlock = forksByBlock[1:]
} }
if len(forksByTime) > 0 && forksByTime[0] == 0 { // Skip any forks before genesis.
for len(forksByTime) > 0 && forksByTime[0] <= genesis {
forksByTime = forksByTime[1:] forksByTime = forksByTime[1:]
} }
return forksByBlock, forksByTime return forksByBlock, forksByTime

View file

@ -18,10 +18,14 @@ package forkid
import ( import (
"bytes" "bytes"
"hash/crc32"
"math" "math"
"math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "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/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -36,13 +40,13 @@ func TestCreation(t *testing.T) {
} }
tests := []struct { tests := []struct {
config *params.ChainConfig config *params.ChainConfig
genesis common.Hash genesis *types.Block
cases []testcase cases []testcase
}{ }{
// Mainnet test cases // Mainnet test cases
{ {
params.MainnetChainConfig, params.MainnetChainConfig,
params.MainnetGenesisHash, core.DefaultGenesisBlock().ToBlock(),
[]testcase{ []testcase{
{0, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced {0, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced
{1149999, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block {1149999, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block
@ -77,7 +81,7 @@ func TestCreation(t *testing.T) {
// Goerli test cases // Goerli test cases
{ {
params.GoerliChainConfig, params.GoerliChainConfig,
params.GoerliGenesisHash, core.DefaultGoerliGenesisBlock().ToBlock(),
[]testcase{ []testcase{
{0, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block {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 {1561650, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Last Petersburg block
@ -94,7 +98,7 @@ func TestCreation(t *testing.T) {
// Sepolia test cases // Sepolia test cases
{ {
params.SepoliaChainConfig, params.SepoliaChainConfig,
params.SepoliaGenesisHash, core.DefaultSepoliaGenesisBlock().ToBlock(),
[]testcase{ []testcase{
{0, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin and first London block {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 {1735370, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Last London block
@ -353,7 +357,7 @@ func TestValidation(t *testing.T) {
//{params.MainnetChainConfig, 20999999, 1677999999, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, ErrLocalIncompatibleOrStale}, //{params.MainnetChainConfig, 20999999, 1677999999, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, ErrLocalIncompatibleOrStale},
} }
for i, tt := range tests { for i, tt := range tests {
filter := newFilter(tt.config, params.MainnetGenesisHash, func() (uint64, uint64) { return tt.head, tt.time }) filter := newFilter(tt.config, core.DefaultGenesisBlock().ToBlock(), func() (uint64, uint64) { return tt.head, tt.time })
if err := filter(tt.id); err != tt.err { if err := filter(tt.id); err != tt.err {
t.Errorf("test %d: validation error mismatch: have %v, want %v", i, err, tt.err) t.Errorf("test %d: validation error mismatch: have %v, want %v", i, err, tt.err)
} }
@ -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 &params.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)
}
}
}

View file

@ -31,8 +31,8 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
GasUsed math.HexOrDecimal64 `json:"gasUsed"` GasUsed math.HexOrDecimal64 `json:"gasUsed"`
ParentHash common.Hash `json:"parentHash"` ParentHash common.Hash `json:"parentHash"`
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
ExcessDataGas *math.HexOrDecimal64 `json:"excessDataGas"` ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
DataGasUsed *math.HexOrDecimal64 `json:"dataGasUsed"` BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
} }
var enc Genesis var enc Genesis
enc.Config = g.Config enc.Config = g.Config
@ -53,8 +53,8 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
enc.GasUsed = math.HexOrDecimal64(g.GasUsed) enc.GasUsed = math.HexOrDecimal64(g.GasUsed)
enc.ParentHash = g.ParentHash enc.ParentHash = g.ParentHash
enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee) enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee)
enc.ExcessDataGas = (*math.HexOrDecimal64)(g.ExcessDataGas) enc.ExcessBlobGas = (*math.HexOrDecimal64)(g.ExcessBlobGas)
enc.DataGasUsed = (*math.HexOrDecimal64)(g.DataGasUsed) enc.BlobGasUsed = (*math.HexOrDecimal64)(g.BlobGasUsed)
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -74,8 +74,8 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
GasUsed *math.HexOrDecimal64 `json:"gasUsed"` GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
ParentHash *common.Hash `json:"parentHash"` ParentHash *common.Hash `json:"parentHash"`
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
ExcessDataGas *math.HexOrDecimal64 `json:"excessDataGas"` ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
DataGasUsed *math.HexOrDecimal64 `json:"dataGasUsed"` BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
} }
var dec Genesis var dec Genesis
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -126,11 +126,11 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
if dec.BaseFee != nil { if dec.BaseFee != nil {
g.BaseFee = (*big.Int)(dec.BaseFee) g.BaseFee = (*big.Int)(dec.BaseFee)
} }
if dec.ExcessDataGas != nil { if dec.ExcessBlobGas != nil {
g.ExcessDataGas = (*uint64)(dec.ExcessDataGas) g.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
} }
if dec.DataGasUsed != nil { if dec.BlobGasUsed != nil {
g.DataGasUsed = (*uint64)(dec.DataGasUsed) g.BlobGasUsed = (*uint64)(dec.BlobGasUsed)
} }
return nil return nil
} }

View file

@ -63,8 +63,8 @@ type Genesis struct {
GasUsed uint64 `json:"gasUsed"` GasUsed uint64 `json:"gasUsed"`
ParentHash common.Hash `json:"parentHash"` ParentHash common.Hash `json:"parentHash"`
BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559 BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559
ExcessDataGas *uint64 `json:"excessDataGas"` // EIP-4844 ExcessBlobGas *uint64 `json:"excessBlobGas"` // EIP-4844
DataGasUsed *uint64 `json:"dataGasUsed"` // EIP-4844 BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844
} }
func ReadGenesis(db ethdb.Database) (*Genesis, error) { func ReadGenesis(db ethdb.Database) (*Genesis, error) {
@ -99,8 +99,8 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
genesis.Mixhash = genesisHeader.MixDigest genesis.Mixhash = genesisHeader.MixDigest
genesis.Coinbase = genesisHeader.Coinbase genesis.Coinbase = genesisHeader.Coinbase
genesis.BaseFee = genesisHeader.BaseFee genesis.BaseFee = genesisHeader.BaseFee
genesis.ExcessDataGas = genesisHeader.ExcessDataGas genesis.ExcessBlobGas = genesisHeader.ExcessBlobGas
genesis.DataGasUsed = genesisHeader.DataGasUsed genesis.BlobGasUsed = genesisHeader.BlobGasUsed
return &genesis, nil return &genesis, nil
} }
@ -130,7 +130,9 @@ func (ga *GenesisAlloc) deriveHash() (common.Hash, error) {
return common.Hash{}, err return common.Hash{}, err
} }
for addr, account := range *ga { for addr, account := range *ga {
statedb.AddBalance(addr, account.Balance, state.BalanceChangeGenesisBalance) if account.Balance != nil {
statedb.AddBalance(addr, account.Balance, state.BalanceChangeGenesisBalance)
}
statedb.SetCode(addr, account.Code) statedb.SetCode(addr, account.Code)
statedb.SetNonce(addr, account.Nonce) statedb.SetNonce(addr, account.Nonce)
for key, value := range account.Storage { for key, value := range account.Storage {
@ -150,7 +152,9 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
return err return err
} }
for addr, account := range *ga { for addr, account := range *ga {
statedb.AddBalance(addr, account.Balance, state.BalanceChangeGenesisBalance) if account.Balance != nil {
statedb.AddBalance(addr, account.Balance, state.BalanceChangeGenesisBalance)
}
statedb.SetCode(addr, account.Code) statedb.SetCode(addr, account.Code)
statedb.SetNonce(addr, account.Nonce) statedb.SetNonce(addr, account.Nonce)
for key, value := range account.Storage { for key, value := range account.Storage {
@ -242,8 +246,8 @@ type genesisSpecMarshaling struct {
Difficulty *math.HexOrDecimal256 Difficulty *math.HexOrDecimal256
Alloc map[common.UnprefixedAddress]GenesisAccount Alloc map[common.UnprefixedAddress]GenesisAccount
BaseFee *math.HexOrDecimal256 BaseFee *math.HexOrDecimal256
ExcessDataGas *math.HexOrDecimal64 ExcessBlobGas *math.HexOrDecimal64
DataGasUsed *math.HexOrDecimal64 BlobGasUsed *math.HexOrDecimal64
} }
type genesisAccountMarshaling struct { type genesisAccountMarshaling struct {
@ -337,10 +341,12 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
applyOverrides(genesis.Config) applyOverrides(genesis.Config)
return genesis.Config, block.Hash(), nil return genesis.Config, block.Hash(), nil
} }
// We have the genesis block in database(perhaps in ancient database) // The genesis block is present(perhaps in ancient database) while the
// but the corresponding state is missing. // state database is not initialized yet. It can happen that the node
// is initialized with an external ancient store. Commit genesis state
// in this case.
header := rawdb.ReadHeader(db, stored, 0) header := rawdb.ReadHeader(db, stored, 0)
if header.Root != types.EmptyRootHash && !rawdb.HasLegacyTrieNode(db, header.Root) { if header.Root != types.EmptyRootHash && !triedb.Initialized(header.Root) {
if genesis == nil { if genesis == nil {
genesis = DefaultGenesisBlock() genesis = DefaultGenesisBlock()
} }
@ -491,13 +497,18 @@ func (g *Genesis) ToBlock() *types.Block {
withdrawals = make([]*types.Withdrawal, 0) withdrawals = make([]*types.Withdrawal, 0)
} }
if conf.IsCancun(num, g.Timestamp) { if conf.IsCancun(num, g.Timestamp) {
head.ExcessDataGas = g.ExcessDataGas // EIP-4788: The parentBeaconBlockRoot of the genesis block is always
head.DataGasUsed = g.DataGasUsed // the zero hash. This is because the genesis block does not have a parent
if head.ExcessDataGas == nil { // by definition.
head.ExcessDataGas = new(uint64) head.ParentBeaconRoot = new(common.Hash)
// EIP-4844 fields
head.ExcessBlobGas = g.ExcessBlobGas
head.BlobGasUsed = g.BlobGasUsed
if head.ExcessBlobGas == nil {
head.ExcessBlobGas = new(uint64)
} }
if head.DataGasUsed == nil { if head.BlobGasUsed == nil {
head.DataGasUsed = new(uint64) head.BlobGasUsed = new(uint64)
} }
} }
} }
@ -545,8 +556,8 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database, bcLogger Bloc
// The block is committed as the canonical head block. // The block is committed as the canonical head block.
// Note the state changes will be committed in hash-based scheme, use Commit // Note the state changes will be committed in hash-based scheme, use Commit
// if path-scheme is preferred. // if path-scheme is preferred.
func (g *Genesis) MustCommit(db ethdb.Database) *types.Block { func (g *Genesis) MustCommit(db ethdb.Database, triedb *trie.Database) *types.Block {
block, err := g.Commit(db, trie.NewDatabase(db), nil) block, err := g.Commit(db, triedb, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -590,6 +601,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. // DeveloperGenesisBlock returns the 'geth --dev' genesis block.
func DeveloperGenesisBlock(gasLimit uint64, faucet common.Address) *Genesis { func DeveloperGenesisBlock(gasLimit uint64, faucet common.Address) *Genesis {
// Override the default period to the user requested one // Override the default period to the user requested one
@ -617,13 +641,34 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet common.Address) *Genesis {
} }
func decodePrealloc(data string) GenesisAlloc { 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 { if err := rlp.NewStream(strings.NewReader(data), 0).Decode(&p); err != nil {
panic(err) panic(err)
} }
ga := make(GenesisAlloc, len(p)) ga := make(GenesisAlloc, len(p))
for _, account := range 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 return ga
} }

File diff suppressed because one or more lines are too long

View file

@ -30,18 +30,24 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
) )
func TestInvalidCliqueConfig(t *testing.T) { func TestInvalidCliqueConfig(t *testing.T) {
block := DefaultGoerliGenesisBlock() block := DefaultGoerliGenesisBlock()
block.ExtraData = []byte{} block.ExtraData = []byte{}
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
if _, err := block.Commit(db, trie.NewDatabase(db), nil); err == nil { if _, err := block.Commit(db, trie.NewDatabase(db, nil), nil); err == nil {
t.Fatal("Expected error on invalid clique config") t.Fatal("Expected error on invalid clique config")
} }
} }
func TestSetupGenesis(t *testing.T) { func TestSetupGenesis(t *testing.T) {
testSetupGenesis(t, rawdb.HashScheme)
testSetupGenesis(t, rawdb.PathScheme)
}
func testSetupGenesis(t *testing.T, scheme string) {
var ( var (
customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50") customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
customg = Genesis{ customg = Genesis{
@ -53,6 +59,7 @@ func TestSetupGenesis(t *testing.T) {
oldcustomg = customg oldcustomg = customg
) )
oldcustomg.Config = &params.ChainConfig{HomesteadBlock: big.NewInt(2)} oldcustomg.Config = &params.ChainConfig{HomesteadBlock: big.NewInt(2)}
tests := []struct { tests := []struct {
name string name string
fn func(ethdb.Database) (*params.ChainConfig, common.Hash, error) fn func(ethdb.Database) (*params.ChainConfig, common.Hash, error)
@ -63,7 +70,7 @@ func TestSetupGenesis(t *testing.T) {
{ {
name: "genesis without ChainConfig", name: "genesis without ChainConfig",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
return SetupGenesisBlock(db, trie.NewDatabase(db), new(Genesis)) return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), new(Genesis))
}, },
wantErr: errGenesisNoConfig, wantErr: errGenesisNoConfig,
wantConfig: params.AllEthashProtocolChanges, wantConfig: params.AllEthashProtocolChanges,
@ -71,7 +78,7 @@ func TestSetupGenesis(t *testing.T) {
{ {
name: "no block in DB, genesis == nil", name: "no block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
return SetupGenesisBlock(db, trie.NewDatabase(db), nil) return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
}, },
wantHash: params.MainnetGenesisHash, wantHash: params.MainnetGenesisHash,
wantConfig: params.MainnetChainConfig, wantConfig: params.MainnetChainConfig,
@ -79,8 +86,8 @@ func TestSetupGenesis(t *testing.T) {
{ {
name: "mainnet block in DB, genesis == nil", name: "mainnet block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
DefaultGenesisBlock().MustCommit(db) DefaultGenesisBlock().MustCommit(db, trie.NewDatabase(db, newDbConfig(scheme)))
return SetupGenesisBlock(db, trie.NewDatabase(db), nil) return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
}, },
wantHash: params.MainnetGenesisHash, wantHash: params.MainnetGenesisHash,
wantConfig: params.MainnetChainConfig, wantConfig: params.MainnetChainConfig,
@ -88,8 +95,9 @@ func TestSetupGenesis(t *testing.T) {
{ {
name: "custom block in DB, genesis == nil", name: "custom block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
customg.MustCommit(db) tdb := trie.NewDatabase(db, newDbConfig(scheme))
return SetupGenesisBlock(db, trie.NewDatabase(db), nil) customg.Commit(db, tdb, nil)
return SetupGenesisBlock(db, tdb, nil)
}, },
wantHash: customghash, wantHash: customghash,
wantConfig: customg.Config, wantConfig: customg.Config,
@ -97,8 +105,9 @@ func TestSetupGenesis(t *testing.T) {
{ {
name: "custom block in DB, genesis == goerli", name: "custom block in DB, genesis == goerli",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
customg.MustCommit(db) tdb := trie.NewDatabase(db, newDbConfig(scheme))
return SetupGenesisBlock(db, trie.NewDatabase(db), DefaultGoerliGenesisBlock()) customg.Commit(db, tdb, nil)
return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
}, },
wantErr: &GenesisMismatchError{Stored: customghash, New: params.GoerliGenesisHash}, wantErr: &GenesisMismatchError{Stored: customghash, New: params.GoerliGenesisHash},
wantHash: params.GoerliGenesisHash, wantHash: params.GoerliGenesisHash,
@ -107,8 +116,9 @@ func TestSetupGenesis(t *testing.T) {
{ {
name: "compatible config in DB", name: "compatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
oldcustomg.MustCommit(db) tdb := trie.NewDatabase(db, newDbConfig(scheme))
return SetupGenesisBlock(db, trie.NewDatabase(db), &customg) oldcustomg.Commit(db, tdb, nil)
return SetupGenesisBlock(db, tdb, &customg)
}, },
wantHash: customghash, wantHash: customghash,
wantConfig: customg.Config, wantConfig: customg.Config,
@ -118,16 +128,17 @@ func TestSetupGenesis(t *testing.T) {
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
// Commit the 'old' genesis block with Homestead transition at #2. // Commit the 'old' genesis block with Homestead transition at #2.
// Advance to block #4, past the homestead transition block of customg. // Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db) tdb := trie.NewDatabase(db, newDbConfig(scheme))
oldcustomg.Commit(db, tdb, nil)
bc, _ := NewBlockChain(db, nil, &oldcustomg, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil) bc, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), &oldcustomg, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
defer bc.Stop() defer bc.Stop()
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil) _, blocks, _ := GenerateChainWithGenesis(&oldcustomg, ethash.NewFaker(), 4, nil)
bc.InsertChain(blocks) bc.InsertChain(blocks)
// This should return a compatibility error. // This should return a compatibility error.
return SetupGenesisBlock(db, trie.NewDatabase(db), &customg) return SetupGenesisBlock(db, tdb, &customg)
}, },
wantHash: customghash, wantHash: customghash,
wantConfig: customg.Config, wantConfig: customg.Config,
@ -175,7 +186,8 @@ func TestGenesisHashes(t *testing.T) {
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash}, {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
} { } {
// Test via MustCommit // Test via MustCommit
if have := c.genesis.MustCommit(rawdb.NewMemoryDatabase()).Hash(); have != c.want { db := rawdb.NewMemoryDatabase()
if have := c.genesis.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults)).Hash(); have != c.want {
t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex()) t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
} }
// Test via ToBlock // Test via ToBlock
@ -193,7 +205,7 @@ func TestGenesis_Commit(t *testing.T) {
} }
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
genesisBlock := genesis.MustCommit(db) genesisBlock := genesis.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
if genesis.Difficulty != nil { if genesis.Difficulty != nil {
t.Fatalf("assumption wrong") t.Fatalf("assumption wrong")
@ -242,3 +254,10 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
} }
} }
} }
func newDbConfig(scheme string) *trie.Config {
if scheme == rawdb.HashScheme {
return trie.HashDefaults
}
return &trie.Config{PathDB: pathdb.Defaults}
}

View file

@ -73,7 +73,7 @@ func TestHeaderInsertion(t *testing.T) {
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges} gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
) )
gspec.Commit(db, trie.NewDatabase(db), nil) gspec.Commit(db, trie.NewDatabase(db, nil), nil)
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false }) hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View file

@ -32,24 +32,51 @@ import (
"os" "os"
"strconv" "strconv"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/exp/slices" "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 { func makelist(g *core.Genesis) []allocItem {
items := make([]allocItem, 0, len(g.Alloc)) items := make([]allocItem, 0, len(g.Alloc))
for addr, account := range g.Alloc { for addr, account := range g.Alloc {
var misc *allocItemMisc
if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 { 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()) 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 { slices.SortFunc(items, func(a, b allocItem) int {
return a.Addr.Cmp(b.Addr) < 0 return a.Addr.Cmp(b.Addr)
}) })
return items return items
} }

View file

@ -381,7 +381,7 @@ func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header
return nil return nil
} }
header := new(types.Header) 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) log.Error("Invalid block header RLP", "hash", hash, "err", err)
return nil return nil
} }
@ -498,7 +498,7 @@ func ReadBody(db ethdb.Reader, hash common.Hash, number uint64) *types.Body {
return nil return nil
} }
body := new(types.Body) 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) log.Error("Invalid block body RLP", "hash", hash, "err", err)
return nil return nil
} }
@ -544,7 +544,7 @@ func ReadTd(db ethdb.Reader, hash common.Hash, number uint64) *big.Int {
return nil return nil
} }
td := new(big.Int) 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) log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
return nil return nil
} }
@ -645,14 +645,12 @@ func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, time uint64,
} else { } else {
baseFee = header.BaseFee baseFee = header.BaseFee
} }
// Compute effective blob gas price.
// Compute effective data gas price. var blobGasPrice *big.Int
var dataGasPrice *big.Int if header != nil && header.ExcessBlobGas != nil {
if header != nil && header.ExcessDataGas != nil { blobGasPrice = eip4844.CalcBlobFee(*header.ExcessBlobGas)
dataGasPrice = eip4844.CalcBlobFee(*header.ExcessDataGas)
} }
if err := receipts.DeriveFields(config, hash, number, time, baseFee, blobGasPrice, body.Transactions); err != nil {
if err := receipts.DeriveFields(config, hash, number, time, baseFee, dataGasPrice, body.Transactions); err != nil {
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err) log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
return nil return nil
} }
@ -733,7 +731,7 @@ func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, t
// ReadLogs retrieves the logs for all transactions in a block. In case // ReadLogs retrieves the logs for all transactions in a block. In case
// receipts is not found, a nil is returned. // receipts is not found, a nil is returned.
// Note: ReadLogs does not derive unstored log fields. // Note: ReadLogs does not derive unstored log fields.
func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) [][]*types.Log { func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log {
// Retrieve the flattened receipt slice // Retrieve the flattened receipt slice
data := ReadReceiptsRLP(db, hash, number) data := ReadReceiptsRLP(db, hash, number)
if len(data) == 0 { if len(data) == 0 {
@ -904,9 +902,9 @@ func WriteBadBlock(db ethdb.KeyValueStore, block *types.Block) {
Header: block.Header(), Header: block.Header(),
Body: block.Body(), Body: block.Body(),
}) })
slices.SortFunc(badBlocks, func(a, b *badBlock) bool { slices.SortFunc(badBlocks, func(a, b *badBlock) int {
// Note: sorting in descending number order. // Note: sorting in descending number order.
return a.Header.Number.Uint64() >= b.Header.Number.Uint64() return -a.Header.Number.Cmp(b.Header.Number)
}) })
if len(badBlocks) > badBlockToKeep { if len(badBlocks) > badBlockToKeep {
badBlocks = badBlocks[:badBlockToKeep] badBlocks = badBlocks[:badBlockToKeep]

View file

@ -85,7 +85,7 @@ func TestBodyStorage(t *testing.T) {
WriteBody(db, hash, 0, body) WriteBody(db, hash, 0, body)
if entry := ReadBody(db, hash, 0); entry == nil { if entry := ReadBody(db, hash, 0); entry == nil {
t.Fatalf("Stored body not found") t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(types.Transactions(body.Transactions), newHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) { } else if types.DeriveSha(types.Transactions(entry.Transactions), newTestHasher()) != types.DeriveSha(types.Transactions(body.Transactions), newTestHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body) t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
} }
if entry := ReadBodyRLP(db, hash, 0); entry == nil { if entry := ReadBodyRLP(db, hash, 0); entry == nil {
@ -139,7 +139,7 @@ func TestBlockStorage(t *testing.T) {
} }
if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil { if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil {
t.Fatalf("Stored body not found") t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(block.Transactions(), newHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) { } else if types.DeriveSha(types.Transactions(entry.Transactions), newTestHasher()) != types.DeriveSha(block.Transactions(), newTestHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body()) t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
} }
// Delete the block and verify the execution // Delete the block and verify the execution
@ -435,12 +435,12 @@ func checkReceiptsRLP(have, want types.Receipts) error {
func TestAncientStorage(t *testing.T) { func TestAncientStorage(t *testing.T) {
// Freezer style fast import the chain. // Freezer style fast import the chain.
frdir := t.TempDir() frdir := t.TempDir()
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false) db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false)
if err != nil { if err != nil {
t.Fatalf("failed to create database with ancient backend") t.Fatalf("failed to create database with ancient backend")
} }
defer db.Close() defer db.Close()
// Create a test block // Create a test block
block := types.NewBlockWithHeader(&types.Header{ block := types.NewBlockWithHeader(&types.Header{
Number: big.NewInt(0), Number: big.NewInt(0),
@ -736,7 +736,7 @@ func TestReadLogs(t *testing.T) {
// Insert the receipt slice into the database and check presence // Insert the receipt slice into the database and check presence
WriteReceipts(db, hash, 0, receipts) WriteReceipts(db, hash, 0, receipts)
logs := ReadLogs(db, hash, 0, params.TestChainConfig) logs := ReadLogs(db, hash, 0)
if len(logs) == 0 { if len(logs) == 0 {
t.Fatalf("no logs returned") t.Fatalf("no logs returned")
} }

View file

@ -29,7 +29,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
var newHasher = blocktest.NewHasher var newTestHasher = blocktest.NewHasher
// Tests that positional lookup metadata can be stored and retrieved. // Tests that positional lookup metadata can be stored and retrieved.
func TestLookupStorage(t *testing.T) { func TestLookupStorage(t *testing.T) {
@ -76,7 +76,7 @@ func TestLookupStorage(t *testing.T) {
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
txs := []*types.Transaction{tx1, tx2, tx3} txs := []*types.Transaction{tx1, tx2, tx3}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, newHasher()) block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, newTestHasher())
// Check that no transactions entries are in a pristine database // Check that no transactions entries are in a pristine database
for i, tx := range txs { for i, tx := range txs {

View file

@ -111,10 +111,10 @@ const crashesToKeep = 10
func PushUncleanShutdownMarker(db ethdb.KeyValueStore) ([]uint64, uint64, error) { func PushUncleanShutdownMarker(db ethdb.KeyValueStore) ([]uint64, uint64, error) {
var uncleanShutdowns crashList var uncleanShutdowns crashList
// Read old data // Read old data
if data, err := db.Get(uncleanShutdownKey); err != nil { if data, err := db.Get(uncleanShutdownKey); err == nil {
log.Warn("Error reading unclean shutdown markers", "error", err) if err := rlp.DecodeBytes(data, &uncleanShutdowns); err != nil {
} else if err := rlp.DecodeBytes(data, &uncleanShutdowns); err != nil { return nil, 0, err
return nil, 0, err }
} }
var discarded = uncleanShutdowns.Discarded var discarded = uncleanShutdowns.Discarded
var previous = make([]uint64, len(uncleanShutdowns.Recent)) var previous = make([]uint64, len(uncleanShutdowns.Recent))

View file

@ -17,6 +17,8 @@
package rawdb package rawdb
import ( import (
"encoding/binary"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -92,3 +94,173 @@ func DeleteCode(db ethdb.KeyValueWriter, hash common.Hash) {
log.Crit("Failed to delete contract code", "err", err) log.Crit("Failed to delete contract code", "err", err)
} }
} }
// ReadStateID retrieves the state id with the provided state root.
func ReadStateID(db ethdb.KeyValueReader, root common.Hash) *uint64 {
data, err := db.Get(stateIDKey(root))
if err != nil || len(data) == 0 {
return nil
}
number := binary.BigEndian.Uint64(data)
return &number
}
// WriteStateID writes the provided state lookup to database.
func WriteStateID(db ethdb.KeyValueWriter, root common.Hash, id uint64) {
var buff [8]byte
binary.BigEndian.PutUint64(buff[:], id)
if err := db.Put(stateIDKey(root), buff[:]); err != nil {
log.Crit("Failed to store state ID", "err", err)
}
}
// DeleteStateID deletes the specified state lookup from the database.
func DeleteStateID(db ethdb.KeyValueWriter, root common.Hash) {
if err := db.Delete(stateIDKey(root)); err != nil {
log.Crit("Failed to delete state ID", "err", err)
}
}
// ReadPersistentStateID retrieves the id of the persistent state from the database.
func ReadPersistentStateID(db ethdb.KeyValueReader) uint64 {
data, _ := db.Get(persistentStateIDKey)
if len(data) != 8 {
return 0
}
return binary.BigEndian.Uint64(data)
}
// WritePersistentStateID stores the id of the persistent state into database.
func WritePersistentStateID(db ethdb.KeyValueWriter, number uint64) {
if err := db.Put(persistentStateIDKey, encodeBlockNumber(number)); err != nil {
log.Crit("Failed to store the persistent state ID", "err", err)
}
}
// ReadTrieJournal retrieves the serialized in-memory trie nodes of layers saved at
// the last shutdown.
func ReadTrieJournal(db ethdb.KeyValueReader) []byte {
data, _ := db.Get(trieJournalKey)
return data
}
// WriteTrieJournal stores the serialized in-memory trie nodes of layers to save at
// shutdown.
func WriteTrieJournal(db ethdb.KeyValueWriter, journal []byte) {
if err := db.Put(trieJournalKey, journal); err != nil {
log.Crit("Failed to store tries journal", "err", err)
}
}
// DeleteTrieJournal deletes the serialized in-memory trie nodes of layers saved at
// the last shutdown.
func DeleteTrieJournal(db ethdb.KeyValueWriter) {
if err := db.Delete(trieJournalKey); err != nil {
log.Crit("Failed to remove tries journal", "err", err)
}
}
// ReadStateHistoryMeta retrieves the metadata corresponding to the specified
// state history. Compute the position of state history in freezer by minus
// one since the id of first state history starts from one(zero for initial
// state).
func ReadStateHistoryMeta(db ethdb.AncientReaderOp, id uint64) []byte {
blob, err := db.Ancient(stateHistoryMeta, id-1)
if err != nil {
return nil
}
return blob
}
// ReadStateHistoryMetaList retrieves a batch of meta objects with the specified
// start position and count. Compute the position of state history in freezer by
// minus one since the id of first state history starts from one(zero for initial
// state).
func ReadStateHistoryMetaList(db ethdb.AncientReaderOp, start uint64, count uint64) ([][]byte, error) {
return db.AncientRange(stateHistoryMeta, start-1, count, 0)
}
// ReadStateAccountIndex retrieves the state root corresponding to the specified
// state history. Compute the position of state history in freezer by minus one
// since the id of first state history starts from one(zero for initial state).
func ReadStateAccountIndex(db ethdb.AncientReaderOp, id uint64) []byte {
blob, err := db.Ancient(stateHistoryAccountIndex, id-1)
if err != nil {
return nil
}
return blob
}
// ReadStateStorageIndex retrieves the state root corresponding to the specified
// state history. Compute the position of state history in freezer by minus one
// since the id of first state history starts from one(zero for initial state).
func ReadStateStorageIndex(db ethdb.AncientReaderOp, id uint64) []byte {
blob, err := db.Ancient(stateHistoryStorageIndex, id-1)
if err != nil {
return nil
}
return blob
}
// ReadStateAccountHistory retrieves the state root corresponding to the specified
// state history. Compute the position of state history in freezer by minus one
// since the id of first state history starts from one(zero for initial state).
func ReadStateAccountHistory(db ethdb.AncientReaderOp, id uint64) []byte {
blob, err := db.Ancient(stateHistoryAccountData, id-1)
if err != nil {
return nil
}
return blob
}
// ReadStateStorageHistory retrieves the state root corresponding to the specified
// state history. Compute the position of state history in freezer by minus one
// since the id of first state history starts from one(zero for initial state).
func ReadStateStorageHistory(db ethdb.AncientReaderOp, id uint64) []byte {
blob, err := db.Ancient(stateHistoryStorageData, id-1)
if err != nil {
return nil
}
return blob
}
// ReadStateHistory retrieves the state history from database with provided id.
// Compute the position of state history in freezer by minus one since the id
// of first state history starts from one(zero for initial state).
func ReadStateHistory(db ethdb.AncientReaderOp, id uint64) ([]byte, []byte, []byte, []byte, []byte, error) {
meta, err := db.Ancient(stateHistoryMeta, id-1)
if err != nil {
return nil, nil, nil, nil, nil, err
}
accountIndex, err := db.Ancient(stateHistoryAccountIndex, id-1)
if err != nil {
return nil, nil, nil, nil, nil, err
}
storageIndex, err := db.Ancient(stateHistoryStorageIndex, id-1)
if err != nil {
return nil, nil, nil, nil, nil, err
}
accountData, err := db.Ancient(stateHistoryAccountData, id-1)
if err != nil {
return nil, nil, nil, nil, nil, err
}
storageData, err := db.Ancient(stateHistoryStorageData, id-1)
if err != nil {
return nil, nil, nil, nil, nil, err
}
return meta, accountIndex, storageIndex, accountData, storageData, nil
}
// WriteStateHistory writes the provided state history to database. Compute the
// position of state history in freezer by minus one since the id of first state
// history starts from one(zero for initial state).
func WriteStateHistory(db ethdb.AncientWriter, id uint64, meta []byte, accountIndex []byte, storageIndex []byte, accounts []byte, storages []byte) {
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
op.AppendRaw(stateHistoryMeta, id-1, meta)
op.AppendRaw(stateHistoryAccountIndex, id-1, accountIndex)
op.AppendRaw(stateHistoryStorageIndex, id-1, storageIndex)
op.AppendRaw(stateHistoryAccountData, id-1, accounts)
op.AppendRaw(stateHistoryStorageData, id-1, storages)
return nil
})
}

View file

@ -17,8 +17,6 @@
package rawdb package rawdb
import ( import (
"bytes"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -53,7 +51,7 @@ func ReadSkeletonHeader(db ethdb.KeyValueReader, number uint64) *types.Header {
return nil return nil
} }
header := new(types.Header) 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) log.Error("Invalid skeleton header RLP", "number", number, "err", err)
return nil return nil
} }

View file

@ -36,7 +36,7 @@ import (
// //
// Now this scheme is still kept for backward compatibility, and it will be used // Now this scheme is still kept for backward compatibility, and it will be used
// for archive node and some other tries(e.g. light trie). // for archive node and some other tries(e.g. light trie).
const HashScheme = "hashScheme" const HashScheme = "hash"
// PathScheme is the new path-based state scheme with which trie nodes are stored // PathScheme is the new path-based state scheme with which trie nodes are stored
// in the disk with node path as the database key. This scheme will only store one // in the disk with node path as the database key. This scheme will only store one
@ -44,23 +44,25 @@ const HashScheme = "hashScheme"
// is native. At the same time, this scheme will put adjacent trie nodes in the same // is native. At the same time, this scheme will put adjacent trie nodes in the same
// area of the disk with good data locality property. But this scheme needs to rely // area of the disk with good data locality property. But this scheme needs to rely
// on extra state diffs to survive deep reorg. // on extra state diffs to survive deep reorg.
const PathScheme = "pathScheme" const PathScheme = "path"
// nodeHasher used to derive the hash of trie node. // hasher is used to compute the sha256 hash of the provided data.
type nodeHasher struct{ sha crypto.KeccakState } type hasher struct{ sha crypto.KeccakState }
var hasherPool = sync.Pool{ var hasherPool = sync.Pool{
New: func() interface{} { return &nodeHasher{sha: sha3.NewLegacyKeccak256().(crypto.KeccakState)} }, New: func() interface{} { return &hasher{sha: sha3.NewLegacyKeccak256().(crypto.KeccakState)} },
} }
func newNodeHasher() *nodeHasher { return hasherPool.Get().(*nodeHasher) } func newHasher() *hasher {
func returnHasherToPool(h *nodeHasher) { hasherPool.Put(h) } return hasherPool.Get().(*hasher)
}
func (h *nodeHasher) hashData(data []byte) (n common.Hash) { func (h *hasher) hash(data []byte) common.Hash {
h.sha.Reset() return crypto.HashData(h.sha, data)
h.sha.Write(data) }
h.sha.Read(n[:])
return n func (h *hasher) release() {
hasherPool.Put(h)
} }
// ReadAccountTrieNode retrieves the account trie node and the associated node // ReadAccountTrieNode retrieves the account trie node and the associated node
@ -70,9 +72,9 @@ func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) ([]byte, common.H
if err != nil { if err != nil {
return nil, common.Hash{} return nil, common.Hash{}
} }
hasher := newNodeHasher() h := newHasher()
defer returnHasherToPool(hasher) defer h.release()
return data, hasher.hashData(data) return data, h.hash(data)
} }
// HasAccountTrieNode checks the account trie node presence with the specified // HasAccountTrieNode checks the account trie node presence with the specified
@ -82,9 +84,9 @@ func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte, hash common.Hash)
if err != nil { if err != nil {
return false return false
} }
hasher := newNodeHasher() h := newHasher()
defer returnHasherToPool(hasher) defer h.release()
return hasher.hashData(data) == hash return h.hash(data) == hash
} }
// WriteAccountTrieNode writes the provided account trie node into database. // WriteAccountTrieNode writes the provided account trie node into database.
@ -108,9 +110,9 @@ func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path
if err != nil { if err != nil {
return nil, common.Hash{} return nil, common.Hash{}
} }
hasher := newNodeHasher() h := newHasher()
defer returnHasherToPool(hasher) defer h.release()
return data, hasher.hashData(data) return data, h.hash(data)
} }
// HasStorageTrieNode checks the storage trie node presence with the provided // HasStorageTrieNode checks the storage trie node presence with the provided
@ -120,9 +122,9 @@ func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path [
if err != nil { if err != nil {
return false return false
} }
hasher := newNodeHasher() h := newHasher()
defer returnHasherToPool(hasher) defer h.release()
return hasher.hashData(data) == hash return h.hash(data) == hash
} }
// WriteStorageTrieNode writes the provided storage trie node into database. // WriteStorageTrieNode writes the provided storage trie node into database.
@ -261,3 +263,25 @@ func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, has
panic(fmt.Sprintf("Unknown scheme %v", scheme)) panic(fmt.Sprintf("Unknown scheme %v", scheme))
} }
} }
// ReadStateScheme reads the state scheme of persistent state, or none
// if the state is not present in database.
func ReadStateScheme(db ethdb.Reader) string {
// Check if state in path-based scheme is present
blob, _ := ReadAccountTrieNode(db, nil)
if len(blob) != 0 {
return PathScheme
}
// In a hash-based scheme, the genesis state is consistently stored
// on the disk. To assess the scheme of the persistent state, it
// suffices to inspect the scheme of the genesis state.
header := ReadHeader(db, ReadCanonicalHash(db, 0), 0)
if header == nil {
return "" // empty datadir
}
blob = ReadLegacyTrieNode(db, header.Root)
if len(blob) == 0 {
return "" // no state in disk
}
return HashScheme
}

View file

@ -16,6 +16,8 @@
package rawdb package rawdb
import "path/filepath"
// The list of table names of chain freezer. // The list of table names of chain freezer.
const ( const (
// ChainFreezerHeaderTable indicates the name of the freezer header table. // ChainFreezerHeaderTable indicates the name of the freezer header table.
@ -44,10 +46,36 @@ var chainFreezerNoSnappy = map[string]bool{
ChainFreezerDifficultyTable: true, ChainFreezerDifficultyTable: true,
} }
const (
// stateHistoryTableSize defines the maximum size of freezer data files.
stateHistoryTableSize = 2 * 1000 * 1000 * 1000
// stateHistoryAccountIndex indicates the name of the freezer state history table.
stateHistoryMeta = "history.meta"
stateHistoryAccountIndex = "account.index"
stateHistoryStorageIndex = "storage.index"
stateHistoryAccountData = "account.data"
stateHistoryStorageData = "storage.data"
)
var stateFreezerNoSnappy = map[string]bool{
stateHistoryMeta: true,
stateHistoryAccountIndex: false,
stateHistoryStorageIndex: false,
stateHistoryAccountData: false,
stateHistoryStorageData: false,
}
// The list of identifiers of ancient stores. // The list of identifiers of ancient stores.
var ( var (
chainFreezerName = "chain" // the folder name of chain segment ancient store. chainFreezerName = "chain" // the folder name of chain segment ancient store.
stateFreezerName = "state" // the folder name of reverse diff ancient store.
) )
// freezers the collections of all builtin freezers. // freezers the collections of all builtin freezers.
var freezers = []string{chainFreezerName} var freezers = []string{chainFreezerName, stateFreezerName}
// NewStateFreezer initializes the freezer for state history.
func NewStateFreezer(ancientDir string, readOnly bool) (*ResettableFreezer, error) {
return NewResettableFreezer(filepath.Join(ancientDir, stateFreezerName), "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
}

View file

@ -50,36 +50,58 @@ func (info *freezerInfo) size() common.StorageSize {
return total return total
} }
func inspect(name string, order map[string]bool, reader ethdb.AncientReader) (freezerInfo, error) {
info := freezerInfo{name: name}
for t := range order {
size, err := reader.AncientSize(t)
if err != nil {
return freezerInfo{}, err
}
info.sizes = append(info.sizes, tableSize{name: t, size: common.StorageSize(size)})
}
// Retrieve the number of last stored item
ancients, err := reader.Ancients()
if err != nil {
return freezerInfo{}, err
}
info.head = ancients - 1
// Retrieve the number of first stored item
tail, err := reader.Tail()
if err != nil {
return freezerInfo{}, err
}
info.tail = tail
return info, nil
}
// inspectFreezers inspects all freezers registered in the system. // inspectFreezers inspects all freezers registered in the system.
func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) { func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) {
var infos []freezerInfo var infos []freezerInfo
for _, freezer := range freezers { for _, freezer := range freezers {
switch freezer { switch freezer {
case chainFreezerName: case chainFreezerName:
// Chain ancient store is a bit special. It's always opened along info, err := inspect(chainFreezerName, chainFreezerNoSnappy, db)
// with the key-value store, inspect the chain store directly.
info := freezerInfo{name: freezer}
// Retrieve storage size of every contained table.
for table := range chainFreezerNoSnappy {
size, err := db.AncientSize(table)
if err != nil {
return nil, err
}
info.sizes = append(info.sizes, tableSize{name: table, size: common.StorageSize(size)})
}
// Retrieve the number of last stored item
ancients, err := db.Ancients()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info.head = ancients - 1 infos = append(infos, info)
// Retrieve the number of first stored item case stateFreezerName:
tail, err := db.Tail() datadir, err := db.AncientDatadir()
if err != nil {
return nil, err
}
f, err := NewStateFreezer(datadir, true)
if err != nil {
return nil, err
}
defer f.Close()
info, err := inspect(stateFreezerName, stateFreezerNoSnappy, f)
if err != nil { if err != nil {
return nil, err return nil, err
} }
info.tail = tail
infos = append(infos, info) infos = append(infos, info)
default: default:

View file

@ -19,12 +19,12 @@ package rawdb
import ( import (
"math/big" "math/big"
"reflect" "reflect"
"sort"
"sync" "sync"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"golang.org/x/exp/slices"
) )
func TestChainIterator(t *testing.T) { func TestChainIterator(t *testing.T) {
@ -34,7 +34,7 @@ func TestChainIterator(t *testing.T) {
var block *types.Block var block *types.Block
var txs []*types.Transaction var txs []*types.Transaction
to := common.BytesToAddress([]byte{0x11}) to := common.BytesToAddress([]byte{0x11})
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newHasher()) // Empty genesis block block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newTestHasher()) // Empty genesis block
WriteBlock(chainDb, block) WriteBlock(chainDb, block)
WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
for i := uint64(1); i <= 10; i++ { for i := uint64(1); i <= 10; i++ {
@ -60,7 +60,7 @@ func TestChainIterator(t *testing.T) {
}) })
} }
txs = append(txs, tx) txs = append(txs, tx)
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher()) block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newTestHasher())
WriteBlock(chainDb, block) WriteBlock(chainDb, block)
WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
} }
@ -92,11 +92,9 @@ func TestChainIterator(t *testing.T) {
} }
} }
if !c.reverse { if !c.reverse {
slices.Sort(numbers) sort.Ints(numbers)
} else { } else {
slices.SortFunc(numbers, func(a, b int) bool { sort.Sort(sort.Reverse(sort.IntSlice(numbers)))
return a > b // Sort descending
})
} }
if !reflect.DeepEqual(numbers, c.expect) { if !reflect.DeepEqual(numbers, c.expect) {
t.Fatalf("Case %d failed, visit element mismatch, want %v, got %v", i, c.expect, numbers) t.Fatalf("Case %d failed, visit element mismatch, want %v, got %v", i, c.expect, numbers)
@ -113,7 +111,7 @@ func TestIndexTransactions(t *testing.T) {
to := common.BytesToAddress([]byte{0x11}) to := common.BytesToAddress([]byte{0x11})
// Write empty genesis block // Write empty genesis block
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newHasher()) block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newTestHasher())
WriteBlock(chainDb, block) WriteBlock(chainDb, block)
WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
@ -140,7 +138,7 @@ func TestIndexTransactions(t *testing.T) {
}) })
} }
txs = append(txs, tx) txs = append(txs, tx)
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher()) block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newTestHasher())
WriteBlock(chainDb, block) WriteBlock(chainDb, block)
WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
} }

View file

@ -123,13 +123,13 @@ func (db *nofreezedb) ModifyAncients(func(ethdb.AncientWriteOp) error) (int64, e
} }
// TruncateHead returns an error as we don't have a backing chain freezer. // TruncateHead returns an error as we don't have a backing chain freezer.
func (db *nofreezedb) TruncateHead(items uint64) error { func (db *nofreezedb) TruncateHead(items uint64) (uint64, error) {
return errNotSupported return 0, errNotSupported
} }
// TruncateTail returns an error as we don't have a backing chain freezer. // TruncateTail returns an error as we don't have a backing chain freezer.
func (db *nofreezedb) TruncateTail(items uint64) error { func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) {
return errNotSupported return 0, errNotSupported
} }
// Sync returns an error as we don't have a backing chain freezer. // Sync returns an error as we don't have a backing chain freezer.
@ -326,10 +326,10 @@ const (
dbLeveldb = "leveldb" 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 // instantiated at that location, and if so, returns the type of database (or the
// empty string). // empty string).
func hasPreexistingDb(path string) string { func PreexistingDatabase(path string) string {
if _, err := os.Stat(filepath.Join(path, "CURRENT")); err != nil { if _, err := os.Stat(filepath.Join(path, "CURRENT")); err != nil {
return "" // No pre-existing db return "" // No pre-existing db
} }
@ -352,6 +352,9 @@ type OpenOptions struct {
Cache int // the capacity(in megabytes) of the data caching Cache int // the capacity(in megabytes) of the data caching
Handles int // number of files to be open simultaneously Handles int // number of files to be open simultaneously
ReadOnly bool ReadOnly bool
// Ephemeral means that filesystem sync operations should be avoided: data integrity in the face of
// a crash is not important. This option should typically be used in tests.
Ephemeral bool
} }
// openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble. // 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 // 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 // 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 { 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) 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 o.Type == dbPebble || existingDb == dbPebble {
if PebbleEnabled { if PebbleEnabled {
log.Info("Using pebble as the backing database") log.Info("Using pebble as the backing database")
return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
} else { } else {
return nil, errors.New("db.engine 'pebble' not supported on this platform") 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. // on supported platforms and LevelDB on anything else.
if PebbleEnabled { if PebbleEnabled {
log.Info("Defaulting to pebble as the backing database") log.Info("Defaulting to pebble as the backing database")
return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
} else { } else {
log.Info("Defaulting to leveldb as the backing database") log.Info("Defaulting to leveldb as the backing database")
return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
@ -463,7 +466,10 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
tds stat tds stat
numHashPairings stat numHashPairings stat
hashNumPairings stat hashNumPairings stat
tries stat legacyTries stat
stateLookups stat
accountTries stat
storageTries stat
codes stat codes stat
txLookups stat txLookups stat
accountSnaps stat accountSnaps stat
@ -504,8 +510,14 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
numHashPairings.Add(size) numHashPairings.Add(size)
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength): case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
hashNumPairings.Add(size) hashNumPairings.Add(size)
case len(key) == common.HashLength: case IsLegacyTrieNode(key, it.Value()):
tries.Add(size) legacyTries.Add(size)
case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
stateLookups.Add(size)
case IsAccountTrieNode(key):
accountTries.Add(size)
case IsStorageTrieNode(key):
storageTries.Add(size)
case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength: case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
codes.Add(size) codes.Add(size)
case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength): case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
@ -543,6 +555,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey, lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey, snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey, uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey,
} { } {
if bytes.Equal(key, meta) { if bytes.Equal(key, meta) {
metadata.Add(size) metadata.Add(size)
@ -571,7 +584,10 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()}, {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
{"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()}, {"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
{"Key-Value store", "Contract codes", codes.Size(), codes.Count()}, {"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
{"Key-Value store", "Trie nodes", tries.Size(), tries.Count()}, {"Key-Value store", "Hash trie nodes", legacyTries.Size(), legacyTries.Count()},
{"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()},
{"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()},
{"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()},
{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()}, {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()}, {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},

View file

@ -28,8 +28,8 @@ const PebbleEnabled = true
// NewPebbleDBDatabase creates a persistent key-value database without a freezer // NewPebbleDBDatabase creates a persistent key-value database without a freezer
// moving immutable chain segments into cold storage. // moving immutable chain segments into cold storage.
func NewPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { func NewPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly, ephemeral bool) (ethdb.Database, error) {
db, err := pebble.New(file, cache, handles, namespace, readonly) db, err := pebble.New(file, cache, handles, namespace, readonly, ephemeral)
if err != nil { if err != nil {
return nil, err return nil, err
} }

Some files were not shown because too many files have changed in this diff Show more