Merge branch 'master' into bs/cell-blobpool/sparse-v2

This commit is contained in:
healthykim 2026-05-18 16:39:09 +02:00
commit 3c367a61e2
353 changed files with 26277 additions and 15917 deletions

View file

@ -145,7 +145,7 @@ jobs:
windows:
name: Windows Build
runs-on: "win-11"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -155,24 +155,49 @@ jobs:
go-version: 1.24
cache: false
# Note: gcc.exe only works properly if the corresponding bin/ directory is
# contained in PATH.
- name: Install cross toolchain
run: |
apt-get update
apt-get -yq --no-install-suggests --no-install-recommends install \
gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 nsis
- name: "Build (amd64)"
shell: cmd
run: |
set PATH=%GETH_MINGW%\bin;%PATH%
go run build/ci.go install -dlgo -arch amd64 -cc %GETH_MINGW%\bin\gcc.exe
go run build/ci.go install -dlgo -os windows -arch amd64 -cc x86_64-w64-mingw32-gcc
- name: "Create/upload archive (amd64)"
run: |
go run build/ci.go archive -os windows -arch amd64 -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
env:
GETH_MINGW: 'C:\msys64\mingw64'
WINDOWS_SIGNING_KEY: ${{ secrets.WINDOWS_SIGNING_KEY }}
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
- name: "Create/upload NSIS installer (amd64)"
run: |
go run build/ci.go nsis -arch amd64 -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
rm -f build/bin/*
env:
WINDOWS_SIGNING_KEY: ${{ secrets.WINDOWS_SIGNING_KEY }}
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
- name: "Build (386)"
shell: cmd
run: |
set PATH=%GETH_MINGW%\bin;%PATH%
go run build/ci.go install -dlgo -arch 386 -cc %GETH_MINGW%\bin\gcc.exe
go run build/ci.go install -dlgo -os windows -arch 386 -cc i686-w64-mingw32-gcc
- name: "Create/upload archive (386)"
run: |
go run build/ci.go archive -os windows -arch 386 -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
env:
GETH_MINGW: 'C:\msys64\mingw32'
WINDOWS_SIGNING_KEY: ${{ secrets.WINDOWS_SIGNING_KEY }}
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
- name: "Create/upload NSIS installer (386)"
run: |
go run build/ci.go nsis -arch 386 -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
rm -f build/bin/*
env:
WINDOWS_SIGNING_KEY: ${{ secrets.WINDOWS_SIGNING_KEY }}
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
docker:
name: Docker Image

29
.github/workflows/freebsd.yml vendored Normal file
View file

@ -0,0 +1,29 @@
on:
push:
branches:
- freebsd-github-action
workflow_dispatch:
jobs:
build:
name: FreeBSD-build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: false
- name: Test in FreeBSD
id: test
uses: vmactions/freebsd-vm@v1
with:
release: "15.0"
usesh: true
prepare: |
pkg install -y go
run: |
freebsd-version
uname -a
go version
go run ./build/ci.go test -p 8

View file

@ -97,3 +97,44 @@ jobs:
- name: Run tests
run: go run build/ci.go test -p 8
windows:
name: Windows ${{ matrix.arch }}
needs: lint
runs-on: [self-hosted, windows, x64]
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
mingw: 'C:\msys64\mingw64'
test: true
- arch: '386'
mingw: 'C:\msys64\mingw32'
test: false
env:
GETH_MINGW: ${{ matrix.mingw }}
GETH_CC: ${{ matrix.mingw }}\bin\gcc.exe
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'
cache: false
- name: Build
shell: cmd
run: |
set PATH=%GETH_MINGW%\bin;%PATH%
go run build/ci.go install -arch ${{ matrix.arch }} -cc %GETH_CC%
- name: Run tests
if: matrix.test
shell: cmd
run: |
set PATH=%GETH_MINGW%\bin;%PATH%
go run build/ci.go test -arch ${{ matrix.arch }} -cc %GETH_CC% -short -p 8

View file

@ -183,8 +183,11 @@ var (
// Solidity: {{.Original.String}}
func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}Event(log *types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
event := "{{.Original.Name}}"
if len(log.Topics) == 0 || log.Topics[0] != {{ decapitalise $contract.Type}}.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != {{ decapitalise $contract.Type}}.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new({{$contract.Type}}{{.Normalized.Name}})
if len(log.Data) > 0 {

View file

@ -360,8 +360,11 @@ func (CrowdsaleFundTransfer) ContractEventName() string {
// Solidity: event FundTransfer(address backer, uint256 amount, bool isContribution)
func (crowdsale *Crowdsale) UnpackFundTransferEvent(log *types.Log) (*CrowdsaleFundTransfer, error) {
event := "FundTransfer"
if len(log.Topics) == 0 || log.Topics[0] != crowdsale.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != crowdsale.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(CrowdsaleFundTransfer)
if len(log.Data) > 0 {

View file

@ -606,8 +606,11 @@ func (DAOChangeOfRules) ContractEventName() string {
// Solidity: event ChangeOfRules(uint256 minimumQuorum, uint256 debatingPeriodInMinutes, int256 majorityMargin)
func (dAO *DAO) UnpackChangeOfRulesEvent(log *types.Log) (*DAOChangeOfRules, error) {
event := "ChangeOfRules"
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != dAO.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(DAOChangeOfRules)
if len(log.Data) > 0 {
@ -648,8 +651,11 @@ func (DAOMembershipChanged) ContractEventName() string {
// Solidity: event MembershipChanged(address member, bool isMember)
func (dAO *DAO) UnpackMembershipChangedEvent(log *types.Log) (*DAOMembershipChanged, error) {
event := "MembershipChanged"
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != dAO.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(DAOMembershipChanged)
if len(log.Data) > 0 {
@ -692,8 +698,11 @@ func (DAOProposalAdded) ContractEventName() string {
// Solidity: event ProposalAdded(uint256 proposalID, address recipient, uint256 amount, string description)
func (dAO *DAO) UnpackProposalAddedEvent(log *types.Log) (*DAOProposalAdded, error) {
event := "ProposalAdded"
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != dAO.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(DAOProposalAdded)
if len(log.Data) > 0 {
@ -736,8 +745,11 @@ func (DAOProposalTallied) ContractEventName() string {
// Solidity: event ProposalTallied(uint256 proposalID, int256 result, uint256 quorum, bool active)
func (dAO *DAO) UnpackProposalTalliedEvent(log *types.Log) (*DAOProposalTallied, error) {
event := "ProposalTallied"
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != dAO.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(DAOProposalTallied)
if len(log.Data) > 0 {
@ -780,8 +792,11 @@ func (DAOVoted) ContractEventName() string {
// Solidity: event Voted(uint256 proposalID, bool position, address voter, string justification)
func (dAO *DAO) UnpackVotedEvent(log *types.Log) (*DAOVoted, error) {
event := "Voted"
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != dAO.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(DAOVoted)
if len(log.Data) > 0 {

View file

@ -72,8 +72,11 @@ func (EventCheckerDynamic) ContractEventName() string {
// Solidity: event dynamic(string indexed idxStr, bytes indexed idxDat, string str, bytes dat)
func (eventChecker *EventChecker) UnpackDynamicEvent(log *types.Log) (*EventCheckerDynamic, error) {
event := "dynamic"
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != eventChecker.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(EventCheckerDynamic)
if len(log.Data) > 0 {
@ -112,8 +115,11 @@ func (EventCheckerEmpty) ContractEventName() string {
// Solidity: event empty()
func (eventChecker *EventChecker) UnpackEmptyEvent(log *types.Log) (*EventCheckerEmpty, error) {
event := "empty"
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != eventChecker.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(EventCheckerEmpty)
if len(log.Data) > 0 {
@ -154,8 +160,11 @@ func (EventCheckerIndexed) ContractEventName() string {
// Solidity: event indexed(address indexed addr, int256 indexed num)
func (eventChecker *EventChecker) UnpackIndexedEvent(log *types.Log) (*EventCheckerIndexed, error) {
event := "indexed"
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != eventChecker.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(EventCheckerIndexed)
if len(log.Data) > 0 {
@ -196,8 +205,11 @@ func (EventCheckerMixed) ContractEventName() string {
// Solidity: event mixed(address indexed addr, int256 num)
func (eventChecker *EventChecker) UnpackMixedEvent(log *types.Log) (*EventCheckerMixed, error) {
event := "mixed"
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != eventChecker.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(EventCheckerMixed)
if len(log.Data) > 0 {
@ -238,8 +250,11 @@ func (EventCheckerUnnamed) ContractEventName() string {
// Solidity: event unnamed(uint256 indexed arg0, uint256 indexed arg1)
func (eventChecker *EventChecker) UnpackUnnamedEvent(log *types.Log) (*EventCheckerUnnamed, error) {
event := "unnamed"
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != eventChecker.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(EventCheckerUnnamed)
if len(log.Data) > 0 {

View file

@ -134,8 +134,11 @@ func (NameConflictLog) ContractEventName() string {
// Solidity: event log(int256 msg, int256 _msg)
func (nameConflict *NameConflict) UnpackLogEvent(log *types.Log) (*NameConflictLog, error) {
event := "log"
if len(log.Topics) == 0 || log.Topics[0] != nameConflict.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != nameConflict.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(NameConflictLog)
if len(log.Data) > 0 {

View file

@ -136,8 +136,11 @@ func (NumericMethodNameE1TestEvent) ContractEventName() string {
// Solidity: event _1TestEvent(address _param)
func (numericMethodName *NumericMethodName) UnpackE1TestEventEvent(log *types.Log) (*NumericMethodNameE1TestEvent, error) {
event := "_1TestEvent"
if len(log.Topics) == 0 || log.Topics[0] != numericMethodName.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != numericMethodName.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(NumericMethodNameE1TestEvent)
if len(log.Data) > 0 {

View file

@ -114,8 +114,11 @@ func (OverloadBar) ContractEventName() string {
// Solidity: event bar(uint256 i)
func (overload *Overload) UnpackBarEvent(log *types.Log) (*OverloadBar, error) {
event := "bar"
if len(log.Topics) == 0 || log.Topics[0] != overload.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != overload.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(OverloadBar)
if len(log.Data) > 0 {
@ -156,8 +159,11 @@ func (OverloadBar0) ContractEventName() string {
// Solidity: event bar(uint256 i, uint256 j)
func (overload *Overload) UnpackBar0Event(log *types.Log) (*OverloadBar0, error) {
event := "bar0"
if len(log.Topics) == 0 || log.Topics[0] != overload.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != overload.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(OverloadBar0)
if len(log.Data) > 0 {

View file

@ -386,8 +386,11 @@ func (TokenTransfer) ContractEventName() string {
// Solidity: event Transfer(address indexed from, address indexed to, uint256 value)
func (token *Token) UnpackTransferEvent(log *types.Log) (*TokenTransfer, error) {
event := "Transfer"
if len(log.Topics) == 0 || log.Topics[0] != token.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != token.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(TokenTransfer)
if len(log.Data) > 0 {

View file

@ -193,8 +193,11 @@ func (TupleTupleEvent) ContractEventName() string {
// Solidity: event TupleEvent((uint256,uint256[],(uint256,uint256)[]) a, (uint256,uint256)[2][] b, (uint256,uint256)[][2] c, (uint256,uint256[],(uint256,uint256)[])[] d, uint256[] e)
func (tuple *Tuple) UnpackTupleEventEvent(log *types.Log) (*TupleTupleEvent, error) {
event := "TupleEvent"
if len(log.Topics) == 0 || log.Topics[0] != tuple.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != tuple.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(TupleTupleEvent)
if len(log.Data) > 0 {
@ -234,8 +237,11 @@ func (TupleTupleEvent2) ContractEventName() string {
// Solidity: event TupleEvent2((uint8,uint8)[] arg0)
func (tuple *Tuple) UnpackTupleEvent2Event(log *types.Log) (*TupleTupleEvent2, error) {
event := "TupleEvent2"
if len(log.Topics) == 0 || log.Topics[0] != tuple.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != tuple.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(TupleTupleEvent2)
if len(log.Data) > 0 {

View file

@ -176,6 +176,13 @@ var (
// ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
// an empty contract behind.
ErrNoCodeAfterDeploy = bind2.ErrNoCodeAfterDeploy
// ErrNoEventSignature is returned when a log entry has no topics.
ErrNoEventSignature = bind2.ErrNoEventSignature
// ErrEventSignatureMismatch is returned when a log's topic[0] does not match
// the expected event signature.
ErrEventSignatureMismatch = bind2.ErrEventSignatureMismatch
)
// ContractCaller defines the methods needed to allow operating with a contract on a read

View file

@ -35,8 +35,8 @@ import (
const basefeeWiggleMultiplier = 2
var (
errNoEventSignature = errors.New("no event signature")
errEventSignatureMismatch = errors.New("event signature mismatch")
ErrNoEventSignature = errors.New("no event signature")
ErrEventSignatureMismatch = errors.New("event signature mismatch")
)
// SignerFn is a signer function callback when a contract requires a method to
@ -536,10 +536,10 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]any)
func (c *BoundContract) UnpackLog(out any, event string, log types.Log) error {
// Anonymous events are not supported.
if len(log.Topics) == 0 {
return errNoEventSignature
return ErrNoEventSignature
}
if log.Topics[0] != c.abi.Events[event].ID {
return errEventSignatureMismatch
return ErrEventSignatureMismatch
}
if len(log.Data) > 0 {
if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
@ -559,10 +559,10 @@ func (c *BoundContract) UnpackLog(out any, event string, log types.Log) error {
func (c *BoundContract) UnpackLogIntoMap(out map[string]any, event string, log types.Log) error {
// Anonymous events are not supported.
if len(log.Topics) == 0 {
return errNoEventSignature
return ErrNoEventSignature
}
if log.Topics[0] != c.abi.Events[event].ID {
return errEventSignatureMismatch
return ErrEventSignatureMismatch
}
if len(log.Data) > 0 {
if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {

View file

@ -276,8 +276,11 @@ func (DBInsert) ContractEventName() string {
// Solidity: event Insert(uint256 key, uint256 value, uint256 length)
func (dB *DB) UnpackInsertEvent(log *types.Log) (*DBInsert, error) {
event := "Insert"
if len(log.Topics) == 0 || log.Topics[0] != dB.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != dB.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(DBInsert)
if len(log.Data) > 0 {
@ -318,8 +321,11 @@ func (DBKeyedInsert) ContractEventName() string {
// Solidity: event KeyedInsert(uint256 indexed key, uint256 value)
func (dB *DB) UnpackKeyedInsertEvent(log *types.Log) (*DBKeyedInsert, error) {
event := "KeyedInsert"
if len(log.Topics) == 0 || log.Topics[0] != dB.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != dB.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(DBKeyedInsert)
if len(log.Data) > 0 {

View file

@ -115,8 +115,11 @@ func (CBasic1) ContractEventName() string {
// Solidity: event basic1(uint256 indexed id, uint256 data)
func (c *C) UnpackBasic1Event(log *types.Log) (*CBasic1, error) {
event := "basic1"
if len(log.Topics) == 0 || log.Topics[0] != c.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != c.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(CBasic1)
if len(log.Data) > 0 {
@ -157,8 +160,11 @@ func (CBasic2) ContractEventName() string {
// Solidity: event basic2(bool indexed flag, uint256 data)
func (c *C) UnpackBasic2Event(log *types.Log) (*CBasic2, error) {
event := "basic2"
if len(log.Topics) == 0 || log.Topics[0] != c.abi.Events[event].ID {
return nil, errors.New("event signature mismatch")
if len(log.Topics) == 0 {
return nil, bind.ErrNoEventSignature
}
if log.Topics[0] != c.abi.Events[event].ID {
return nil, bind.ErrEventSignatureMismatch
}
out := new(CBasic2)
if len(log.Data) > 0 {

View file

@ -379,16 +379,16 @@ func TestEventUnpackEmptyTopics(t *testing.T) {
if err == nil {
t.Fatal("expected error when unpacking event with empty topics, got nil")
}
if err.Error() != "event signature mismatch" {
t.Fatalf("expected 'event signature mismatch' error, got: %v", err)
if err != bind.ErrNoEventSignature {
t.Fatalf("expected 'no event signature' error, got: %v", err)
}
_, err = c.UnpackBasic2Event(log)
if err == nil {
t.Fatal("expected error when unpacking event with empty topics, got nil")
}
if err.Error() != "event signature mismatch" {
t.Fatalf("expected 'event signature mismatch' error, got: %v", err)
if err != bind.ErrNoEventSignature {
t.Fatalf("expected 'no event signature' error, got: %v", err)
}
}
}

View file

@ -910,7 +910,7 @@ func TestUnpackTuple(t *testing.T) {
},
},
FieldT: T{
big.NewInt(0), big.NewInt(1),
big.NewInt(0).SetBits([]big.Word{}), big.NewInt(1),
},
A: big.NewInt(1),
}
@ -919,7 +919,7 @@ func TestUnpackTuple(t *testing.T) {
if err != nil {
t.Error(err)
}
if reflect.DeepEqual(ret, expected) {
if !reflect.DeepEqual(ret, expected) {
t.Error("unexpected unpack value")
}
}

View file

@ -68,18 +68,27 @@ func waitWatcherStart(ks *KeyStore) bool {
func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
var list []accounts.Account
haveAccounts := false
haveChange := false
for t0 := time.Now(); time.Since(t0) < 5*time.Second; time.Sleep(100 * time.Millisecond) {
list = ks.Accounts()
if reflect.DeepEqual(list, wantAccounts) {
// ks should have also received change notifications
if !haveAccounts {
list = ks.Accounts()
haveAccounts = reflect.DeepEqual(list, wantAccounts)
}
if !haveChange {
select {
case <-ks.changes:
haveChange = true
default:
return errors.New("wasn't notified of new accounts")
}
}
if haveAccounts && haveChange {
return nil
}
}
if haveAccounts {
return errors.New("wasn't notified of new accounts")
}
return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts)
}

View file

@ -14,8 +14,8 @@
// 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/>.
//go:build (darwin && !ios && cgo) || freebsd || (linux && !arm64) || netbsd || solaris
// +build darwin,!ios,cgo freebsd linux,!arm64 netbsd solaris
//go:build (darwin && !ios && cgo) || freebsd || linux || netbsd || solaris
// +build darwin,!ios,cgo freebsd linux netbsd solaris
package keystore

View file

@ -14,8 +14,8 @@
// 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/>.
//go:build (darwin && !cgo) || ios || (linux && arm64) || windows || (!darwin && !freebsd && !linux && !netbsd && !solaris)
// +build darwin,!cgo ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
//go:build (darwin && !cgo) || ios || windows || (!darwin && !freebsd && !linux && !netbsd && !solaris)
// +build darwin,!cgo ios windows !darwin,!freebsd,!linux,!netbsd,!solaris
// This is the fallback implementation of directory watching.
// It is used on unsupported platforms.

View file

@ -113,7 +113,7 @@ func (hub *Hub) readPairings() error {
}
func (hub *Hub) writePairings() error {
pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE, 0755)
pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
return err
}
@ -129,11 +129,8 @@ func (hub *Hub) writePairings() error {
return err
}
if _, err := pairingFile.Write(pairingData); err != nil {
return err
}
return nil
_, err = pairingFile.Write(pairingData)
return err
}
func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {

View file

@ -26,7 +26,7 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/karalabe/hid"
"github.com/ethereum/hid"
)
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.

View file

@ -31,7 +31,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/karalabe/hid"
"github.com/ethereum/hid"
)
// Maximum time between wallet health checks to detect USB unplugs.

View file

@ -1,39 +0,0 @@
clone_depth: 5
version: "{branch}.{build}"
image:
- Visual Studio 2019
environment:
matrix:
- GETH_ARCH: amd64
GETH_MINGW: 'C:\msys64\mingw64'
- GETH_ARCH: 386
GETH_MINGW: 'C:\msys64\mingw32'
install:
- git submodule update --init --depth 1 --recursive
- go version
for:
# Windows builds for amd64 + 386.
- matrix:
only:
- image: Visual Studio 2019
environment:
# We use gcc from MSYS2 because it is the most recent compiler version available on
# AppVeyor. Note: gcc.exe only works properly if the corresponding bin/ directory is
# contained in PATH.
GETH_CC: '%GETH_MINGW%\bin\gcc.exe'
PATH: '%GETH_MINGW%\bin;C:\Program Files (x86)\NSIS\;%PATH%'
build_script:
- 'echo %GETH_ARCH%'
- 'echo %GETH_CC%'
- '%GETH_CC% --version'
- go run build/ci.go install -dlgo -arch %GETH_ARCH% -cc %GETH_CC%
after_build:
# Upload builds. Note that ci.go makes this a no-op PR builds.
- go run build/ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
test_script:
- go run build/ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC% -short

View file

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

View file

@ -34,7 +34,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
}
var enc ExecutableData
enc.ParentHash = e.ParentHash
@ -83,7 +83,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
}
var dec ExecutableData
if err := json.Unmarshal(input, &dec); err != nil {

View file

@ -99,7 +99,7 @@ type ExecutableData struct {
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BlobGasUsed *uint64 `json:"blobGasUsed"`
ExcessBlobGas *uint64 `json:"excessBlobGas"`
SlotNumber *uint64 `json:"slotNumber"`
SlotNumber *uint64 `json:"slotNumber,omitempty"`
}
// JSON type overrides for executableData.
@ -281,7 +281,7 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
if data.BaseFeePerGas != nil && (data.BaseFeePerGas.Sign() == -1 || data.BaseFeePerGas.BitLen() > 256) {
return nil, fmt.Errorf("invalid baseFeePerGas: %v", data.BaseFeePerGas)
}
var blobHashes = make([]common.Hash, 0, len(txs))
var blobHashes = make([]common.Hash, 0, len(versionedHashes))
for _, tx := range txs {
blobHashes = append(blobHashes, tx.BlobHashes()...)
}

View file

@ -438,14 +438,11 @@ func (s *serverWithLimits) fail(desc string) {
// failLocked calculates the dynamic failure delay and applies it.
func (s *serverWithLimits) failLocked(desc string) {
log.Debug("Server error", "description", desc)
s.failureDelay *= 2
now := s.clock.Now()
if now > s.failureDelayEnd {
s.failureDelay *= math.Pow(2, -float64(now-s.failureDelayEnd)/float64(maxFailureDelay))
}
if s.failureDelay < float64(minFailureDelay) {
s.failureDelay = float64(minFailureDelay)
}
s.failureDelay = max(min(s.failureDelay*2, float64(maxFailureDelay)), float64(minFailureDelay))
s.failureDelayEnd = now + mclock.AbsTime(s.failureDelay)
s.delay(time.Duration(s.failureDelay))
}

View file

@ -62,7 +62,6 @@ const (
ssNeedParent // cp header slot %32 != 0, need parent to check epoch boundary
ssParentRequested // cp parent header requested
ssPrintStatus // has all necessary info, print log message if init still not successful
ssDone // log message printed, no more action required
)
type serverState struct {
@ -180,7 +179,8 @@ func (s *CheckpointInit) Process(requester request.Requester, events []request.E
default:
log.Error("blsync: checkpoint not available, but reported as finalized; specified checkpoint hash might be too old", "server", server.Name())
}
s.serverState[server] = serverState{state: ssDone}
s.serverState[server] = serverState{state: ssDefault}
requester.Fail(server, "checkpoint init failed")
}
}

View file

@ -5,49 +5,49 @@
# https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0
a3192784375acec7eaec492799d5c5d0c47a2909a3cc40178898e4ecd20cc416 fixtures_develop.tar.gz
# version:golang 1.25.7
# version:golang 1.25.10
# https://go.dev/dl/
178f2832820274b43e177d32f06a3ebb0129e427dd20a5e4c88df2c1763cf10a go1.25.7.src.tar.gz
81bf2a1f20633f62d55d826d82dde3b0570cf1408a91e15781b266037299285b go1.25.7.aix-ppc64.tar.gz
bf5050a2152f4053837b886e8d9640c829dbacbc3370f913351eb0904cb706f5 go1.25.7.darwin-amd64.tar.gz
ff18369ffad05c57d5bed888b660b31385f3c913670a83ef557cdfd98ea9ae1b go1.25.7.darwin-arm64.tar.gz
c5dccd7f192dd7b305dc209fb316ac1917776d74bd8e4d532ef2772f305bf42a go1.25.7.dragonfly-amd64.tar.gz
a2de97c8ac74bf64b0ae73fe9d379e61af530e061bc7f8f825044172ffe61a8b go1.25.7.freebsd-386.tar.gz
055f9e138787dcafa81eb0314c8ff70c6dd0f6dba1e8a6957fef5d5efd1ab8fd go1.25.7.freebsd-amd64.tar.gz
60e7f7a7c990f0b9539ac8ed668155746997d404643a4eecd47b3dee1b7e710b go1.25.7.freebsd-arm.tar.gz
631e03d5fd4c526e2f499154d8c6bf4cb081afb2fff171c428722afc9539d53a go1.25.7.freebsd-arm64.tar.gz
8a264fd685823808140672812e3ad9c43f6ad59444c0dc14cdd3a1351839ddd5 go1.25.7.freebsd-riscv64.tar.gz
57c672447d906a1bcab98f2b11492d54521a791aacbb4994a25169e59cbe289a go1.25.7.illumos-amd64.tar.gz
2866517e9ca81e6a2e85a930e9b11bc8a05cfeb2fc6dc6cb2765e7fb3c14b715 go1.25.7.linux-386.tar.gz
12e6d6a191091ae27dc31f6efc630e3a3b8ba409baf3573d955b196fdf086005 go1.25.7.linux-amd64.tar.gz
ba611a53534135a81067240eff9508cd7e256c560edd5d8c2fef54f083c07129 go1.25.7.linux-arm64.tar.gz
1ba07e0eb86b839e72467f4b5c7a5597d07f30bcf5563c951410454f7cda5266 go1.25.7.linux-armv6l.tar.gz
775753fc5952a334c415f08768df2f0b73a3228a16e8f5f63d545daacb4e3357 go1.25.7.linux-loong64.tar.gz
1a023bb367c5fbb4c637a2f6dc23ff17c6591ad929ce16ea88c74d857153b307 go1.25.7.linux-mips.tar.gz
a8e97223d8aa6fdfd45f132a4784d2f536bbac5f3d63a24b63d33b6bfe1549af go1.25.7.linux-mips64.tar.gz
eb9edb6223330d5e20275667c65dea076b064c08e595fe4eba5d7d6055cfaccf go1.25.7.linux-mips64le.tar.gz
9c1e693552a5f9bb9e0012d1c5e01456ecefbc59bef53a77305222ce10aba368 go1.25.7.linux-mipsle.tar.gz
28a788798e7329acbbc0ac2caa5e4368b1e5ede646cc24429c991214cfb45c63 go1.25.7.linux-ppc64.tar.gz
42124c0edc92464e2b37b2d7fcd3658f0c47ebd6a098732415a522be8cb88e3f go1.25.7.linux-ppc64le.tar.gz
88d59c6893c8425875d6eef8e3434bc2fa2552e5ad4c058c6cd8cd710a0301c8 go1.25.7.linux-riscv64.tar.gz
c6b77facf666dc68195ecab05dbf0ebb4e755b2a8b7734c759880557f1c29b0c go1.25.7.linux-s390x.tar.gz
f14c184d9ade0ee04c7735d4071257b90896ecbde1b32adae84135f055e6399b go1.25.7.netbsd-386.tar.gz
7e7389e404dca1088c31f0fc07f1dd60891d7182bcd621469c14f7e79eceb3ff go1.25.7.netbsd-amd64.tar.gz
70388bb3ef2f03dbf1357e9056bd09034a67e018262557354f8cf549766b3f9d go1.25.7.netbsd-arm.tar.gz
8c1cda9d25bfc9b18d24d5f95fc23949dd3ff99fa408a6cfa40e2cf12b07e362 go1.25.7.netbsd-arm64.tar.gz
42f0d1bfbe39b8401cccb84dd66b30795b97bfc9620dfdc17c5cd4fcf6495cb0 go1.25.7.openbsd-386.tar.gz
e514879c0a28bc32123cd52c4c093de912477fe83f36a6d07517d066ef55391a go1.25.7.openbsd-amd64.tar.gz
8cd22530695a0218232bf7efea8f162df1697a3106942ac4129b8c3de39ce4ef go1.25.7.openbsd-arm.tar.gz
938720f6ebc0d1c53d7840321d3a31f29fd02496e84a6538f442a9311dc1cc9a go1.25.7.openbsd-arm64.tar.gz
a4c378b73b98f89a3596c2ef51aabbb28783d9ca29f7e317d8ca07939660ce6f go1.25.7.openbsd-ppc64.tar.gz
937b58734fbeaa8c7941a0e4285e7e84b7885396e8d11c23f9ab1a8ff10ff20e go1.25.7.openbsd-riscv64.tar.gz
61a093c8c5244916f25740316386bb9f141545dcf01b06a79d1c78ece488403e go1.25.7.plan9-386.tar.gz
7fc8f6689c9de8ccb7689d2278035fa83c2d601409101840df6ddfe09ba58699 go1.25.7.plan9-amd64.tar.gz
9661dff8eaeeb62f1c3aadbc5ff189a2e6744e1ec885e32dbcb438f58a34def5 go1.25.7.plan9-arm.tar.gz
28ecba0e1d7950c8b29a4a04962dd49c3bf5221f55a44f17d98f369f82859cf4 go1.25.7.solaris-amd64.tar.gz
baa6b488291801642fa620026169e38bec2da2ac187cd3ae2145721cf826bbc3 go1.25.7.windows-386.zip
c75e5f4ff62d085cc0017be3ad19d5536f46825fa05db06ec468941f847e3228 go1.25.7.windows-amd64.zip
807033f85931bc4a589ca8497535dcbeb1f30d506e47fa200f5f04c4a71c3d9f go1.25.7.windows-arm64.zip
20cf04a92e5af99748e341bc8996fa28090c9ac98765fa115ec5ddf41d7af41d go1.25.10.src.tar.gz
a194e767c2ab4216a60acc068b9dbe6bf4fae05c14bb52d6bbdcb5b3ea521308 go1.25.10.aix-ppc64.tar.gz
52321165a3146cd91865ef98371506a846ed4dc4f9f1c9323e5ad90d2a411e06 go1.25.10.darwin-amd64.tar.gz
795691a425de7e7cdba3544f354dcd2cebcf52e87dc6898193878f34eb6d634f go1.25.10.darwin-arm64.tar.gz
e37b4544ba9e9e9a7ab2ed3116b3fc4d39a88da854baa5a566d9d6d3a9de7d4c go1.25.10.dragonfly-amd64.tar.gz
2a70d1fdabab637aa442ca94599a56e381238efa20cb995d5433b8579bfe482c go1.25.10.freebsd-386.tar.gz
9cdf522d87d47d82fec4a313cc4f8c3c94a7770426e8d443e4150a1f330cba71 go1.25.10.freebsd-amd64.tar.gz
6da6183633e9e59ffd9edefab68b5059c89b605596d94aaba650b1681fccd35f go1.25.10.freebsd-arm.tar.gz
7adcefeebdd05331f4d45f1ad2dddb5c53537cff6552e82f6595b3b833b95371 go1.25.10.freebsd-arm64.tar.gz
285f80a1ace21a7d94035cd753196eeada8cacd48e6396fd116ad5eb67aea957 go1.25.10.freebsd-riscv64.tar.gz
de7461bf0e5068a4f6e7f8713026d70516be6dbd5de5d21f9ced1c182f2f326e go1.25.10.illumos-amd64.tar.gz
2f574f2e2e19ead5b280fec0e7af5c81b76632685f03b6ac42dfa34c4b773c52 go1.25.10.linux-386.tar.gz
42d4f7a32316aa66591eca7e89867256057a4264451aca10570a715b3637ba70 go1.25.10.linux-amd64.tar.gz
654da1f9b50a5d1c2a85ccf8ed405aa89c06e94d18384628bf186f7712677b08 go1.25.10.linux-arm64.tar.gz
39f168f158e693887d3ad006168af1b1a3007b19c5993cae4d9d57f82f52aaf8 go1.25.10.linux-armv6l.tar.gz
05401fe5ea50ad2bafb9c797ef9bf21574b0661f19ef4d0dd66af8a0fb7323f3 go1.25.10.linux-loong64.tar.gz
d5bc2d6155d394a3aae41f21eb7c60da5595a6147aa0f30ed6b27da25e06c3f7 go1.25.10.linux-mips.tar.gz
8c64e7493e5953c3ba3153487d2fddd7f8ed142392c77f138e6792a6c1930db4 go1.25.10.linux-mips64.tar.gz
bd53aa2d558b7c1eadfc6bf01132e1859203a92f458ed7ba75b7f3230f14b095 go1.25.10.linux-mips64le.tar.gz
120b254e2e2980bb06687175db5c4064a85696c53001dc9f59934ad18f74a6bc go1.25.10.linux-mipsle.tar.gz
8a6acb21295b0ec974a44608361920ea8dbff5666631a6f556bd7d5f1d56535f go1.25.10.linux-ppc64.tar.gz
778925fdcdf9a272f823d147fad51545c3334b7ccd8652b2ccaaf2b01800280a go1.25.10.linux-ppc64le.tar.gz
b4f04ad0db48bcfea946db5323919cd21034e0bd2821a557dacd29c1b1013a4b go1.25.10.linux-riscv64.tar.gz
936b953e43921a64c12da871f76871ebbeb6d2092a7b8bdc307f5246f3c662cc go1.25.10.linux-s390x.tar.gz
061470e0bc7132146a5925a3cc28d5bc498eb1b1ff09dedcfaae10f781ff2274 go1.25.10.netbsd-386.tar.gz
63b2d50d7f8f269a9c82d42a4060e90cffb7f9102299818bb071b067aac8da8f go1.25.10.netbsd-amd64.tar.gz
c35129f68796526aa4dc4b6f481e2d995ef312aedadc88b659b945cc00e1f8f0 go1.25.10.netbsd-arm.tar.gz
2f541da4e2b298154d992d1f11bbb38c89d0821d91cc50a46776d42bb5e63bca go1.25.10.netbsd-arm64.tar.gz
2d42e569b07f1b99fdbfd008e7c22f967d165e2ce02464f46818fbed2aec43f5 go1.25.10.openbsd-386.tar.gz
0ad05960e8c9f867328151308c87f938433bec8f22f6a9437a896e22169fc840 go1.25.10.openbsd-amd64.tar.gz
099cc11473f99461c77161912740945308f08f6834980afb262c72bdc915f2d7 go1.25.10.openbsd-arm.tar.gz
bdf3335d5008c1ddc81fa94892283e4f1fee22566f5351d4e726d9f55a67c838 go1.25.10.openbsd-arm64.tar.gz
0933d418da0a61e0f29de717a77498f16b9b5b50dbe2205e20b2ed7fd4067f75 go1.25.10.openbsd-ppc64.tar.gz
191e6f3e75712f8c13d189d53b668e2cac6449f26474c1d86fbd04f6e9846f9c go1.25.10.openbsd-riscv64.tar.gz
68c053c8acd76c50fc430e92f4a86110ec3d97dd03d27b9339b4eaf793caff5f go1.25.10.plan9-386.tar.gz
42e2c46638ae22d93402e79efb40faee5c42cf7c56a01bb3ab47c6bb2512b745 go1.25.10.plan9-amd64.tar.gz
3ef1d5838b1648da16724a07b72e839ccbd7cb8899c3e0426afd6b79d494b91c go1.25.10.plan9-arm.tar.gz
631e3716017fbec06500a628d97e1155daec3593f0a7812c2ebfe8fc8c96b2ab go1.25.10.solaris-amd64.tar.gz
ddc693d2d9d7cc671ebb72d1d50aa05670f95b059b7d90440611af57976871d5 go1.25.10.windows-386.zip
ca37af2dadd8544464f1a9ca7c3886499d1cdfcb263855d0a1d71f194b2bd222 go1.25.10.windows-amd64.zip
38be57e0398bd93673d65bcae6dc7ee3cf151d7038d0dba5c60a5153022872da go1.25.10.windows-arm64.zip
# version:golangci 2.10.1
# https://github.com/golangci/golangci-lint/releases/

View file

@ -73,21 +73,9 @@ var (
"./cmd/keeper",
}
// Files that end up in the geth*.zip archive.
gethArchiveFiles = []string{
"COPYING",
executablePath("geth"),
}
// Files that end up in the geth-alltools*.zip archive.
allToolsArchiveFiles = []string{
"COPYING",
executablePath("abigen"),
executablePath("evm"),
executablePath("geth"),
executablePath("rlpdump"),
executablePath("clef"),
}
// Files that end up in the geth-alltools*.zip archive (and the NSIS installer
// dev-tools section). Order matches the historical layout produced by ci.go.
allToolsBinaries = []string{"abigen", "evm", "geth", "rlpdump", "clef"}
// Keeper build targets with their configurations
keeperTargets = []struct {
@ -107,17 +95,21 @@ var (
Tags: "ziren",
Env: map[string]string{"GOMIPS": "softfloat", "CGO_ENABLED": "0"},
},
{
Name: "womir",
GOOS: "wasip1",
GOARCH: "wasm",
Tags: "womir",
},
{
Name: "wasm-js",
GOOS: "js",
GOARCH: "wasm",
Tags: "example",
},
{
Name: "wasm-wasi",
GOOS: "wasip1",
GOARCH: "wasm",
Tags: "example",
},
{
Name: "example",
@ -163,11 +155,11 @@ var (
// Distros for which packages are created
debDistros = []string{
"xenial", // 16.04, EOL: 04/2026
"bionic", // 18.04, EOL: 04/2028
"focal", // 20.04, EOL: 04/2030
"jammy", // 22.04, EOL: 04/2032
"noble", // 24.04, EOL: 04/2034
"xenial", // 16.04, EOL: 04/2026
"bionic", // 18.04, EOL: 04/2028
"focal", // 20.04, EOL: 04/2030
"jammy", // 22.04, EOL: 04/2032
"noble", // 24.04, EOL: 04/2034
}
// This is where the tests should be unpacked.
@ -176,13 +168,35 @@ var (
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
func executablePath(name string) string {
if runtime.GOOS == "windows" {
// executablePath returns the path to a built binary in GOBIN, applying the
// platform-specific extension for the given target OS.
func executablePath(name, targetOS string) string {
if targetOS == "windows" {
name += ".exe"
}
return filepath.Join(GOBIN, name)
}
// gethArchiveFiles returns the file list for the geth-{platform}-{ver}.zip
// archive, with binary paths resolved for the target OS.
func gethArchiveFiles(targetOS string) []string {
return []string{
"COPYING",
executablePath("geth", targetOS),
}
}
// allToolsArchiveFiles returns the file list for the
// geth-alltools-{platform}-{ver}.zip archive, with binary paths resolved for
// the target OS.
func allToolsArchiveFiles(targetOS string) []string {
files := []string{"COPYING"}
for _, name := range allToolsBinaries {
files = append(files, executablePath(name, targetOS))
}
return files
}
func main() {
log.SetFlags(log.Lshortfile)
@ -229,6 +243,7 @@ func main() {
func doInstall(cmdline []string) {
var (
dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
targetOS = flag.String("os", runtime.GOOS, "Target OS to cross build for")
arch = flag.String("arch", "", "Architecture to cross build for")
cc = flag.String("cc", "", "C compiler to cross build with")
staticlink = flag.Bool("static", false, "Create statically-linked executable")
@ -237,7 +252,7 @@ func doInstall(cmdline []string) {
env := build.Env()
// Configure the toolchain.
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
tc := build.GoToolchain{GOOS: *targetOS, GOARCH: *arch, CC: *cc}
if *dlgo {
csdb := download.MustLoadChecksums("build/checksums.txt")
tc.Root = build.DownloadGo(csdb)
@ -251,7 +266,7 @@ func doInstall(cmdline []string) {
}
// Configure the build.
gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...)
gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags, *targetOS)...)
// Show packages during build.
gobuild.Args = append(gobuild.Args, "-v")
@ -266,7 +281,7 @@ func doInstall(cmdline []string) {
// Do the build!
for _, pkg := range packages {
args := slices.Clone(gobuild.Args)
args = append(args, "-o", executablePath(path.Base(pkg)))
args = append(args, "-o", executablePath(path.Base(pkg), *targetOS))
args = append(args, pkg)
build.MustRun(&exec.Cmd{Path: gobuild.Path, Args: args, Env: gobuild.Env})
}
@ -293,7 +308,13 @@ func doInstallKeeper(cmdline []string) {
tc.GOARCH = target.GOARCH
tc.GOOS = target.GOOS
tc.CC = target.CC
gobuild := tc.Go("build", buildFlags(env, true, []string{target.Tags})...)
// An empty GOOS means "build for the host OS"; thread that through to
// buildFlags so platform-specific linker flags are picked correctly.
targetOS := target.GOOS
if targetOS == "" {
targetOS = runtime.GOOS
}
gobuild := tc.Go("build", buildFlags(env, true, []string{target.Tags}, targetOS)...)
gobuild.Dir = "./cmd/keeper"
gobuild.Args = append(gobuild.Args, "-v")
@ -303,14 +324,15 @@ func doInstallKeeper(cmdline []string) {
outputName := fmt.Sprintf("keeper-%s", target.Name)
args := slices.Clone(gobuild.Args)
args = append(args, "-o", executablePath(outputName))
args = append(args, "-o", executablePath(outputName, targetOS))
args = append(args, ".")
build.MustRun(&exec.Cmd{Path: gobuild.Path, Args: args, Env: gobuild.Env})
build.MustRun(&exec.Cmd{Path: gobuild.Path, Args: args, Env: gobuild.Env, Dir: gobuild.Dir})
}
}
// buildFlags returns the go tool flags for building.
func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
// buildFlags returns the go tool flags for building. targetOS is the OS we
// are producing binaries for.
func buildFlags(env build.Environment, staticLinking bool, buildTags []string, targetOS string) (flags []string) {
var ld []string
// See https://github.com/golang/go/issues/33772#issuecomment-528176001
// We need to set --buildid to the linker here, and also pass --build-id to the
@ -322,10 +344,10 @@ func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (
}
// Strip DWARF on darwin. This used to be required for certain things,
// and there is no downside to this, so we just keep doing it.
if runtime.GOOS == "darwin" {
if targetOS == "darwin" {
ld = append(ld, "-s")
}
if runtime.GOOS == "linux" {
if targetOS == "linux" {
// Enforce the stacksize to 8M, which is the case on most platforms apart from
// alpine Linux.
// See https://sourceware.org/binutils/docs-2.23.1/ld/Options.html#Options
@ -678,12 +700,13 @@ func downloadProtoc(cachedir string) string {
// Release Packaging
func doArchive(cmdline []string) {
var (
arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging")
atype = flag.String("type", "zip", "Type of archive to write (zip|tar)")
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`)
signify = flag.String("signify", "", `Environment variable holding the signify key (e.g. LINUX_SIGNIFY_KEY)`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
ext string
targetOS = flag.String("os", runtime.GOOS, "Target OS the binaries were built for")
arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging")
atype = flag.String("type", "zip", "Type of archive to write (zip|tar)")
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`)
signify = flag.String("signify", "", `Environment variable holding the signify key (e.g. LINUX_SIGNIFY_KEY)`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
ext string
)
flag.CommandLine.Parse(cmdline)
switch *atype {
@ -697,15 +720,15 @@ func doArchive(cmdline []string) {
var (
env = build.Env()
basegeth = archiveBasename(*arch, version.Archive(env.Commit))
basegeth = archiveBasename(*targetOS, *arch, version.Archive(env.Commit))
geth = "geth-" + basegeth + ext
alltools = "geth-alltools-" + basegeth + ext
)
maybeSkipArchive(env)
if err := build.WriteArchive(geth, gethArchiveFiles); err != nil {
if err := build.WriteArchive(geth, gethArchiveFiles(*targetOS)); err != nil {
log.Fatal(err)
}
if err := build.WriteArchive(alltools, allToolsArchiveFiles); err != nil {
if err := build.WriteArchive(alltools, allToolsArchiveFiles(*targetOS)); err != nil {
log.Fatal(err)
}
for _, archive := range []string{geth, alltools} {
@ -731,7 +754,11 @@ func doKeeperArchive(cmdline []string) {
maybeSkipArchive(env)
files := []string{"COPYING"}
for _, target := range keeperTargets {
files = append(files, executablePath(fmt.Sprintf("keeper-%s", target.Name)))
targetOS := target.GOOS
if targetOS == "" {
targetOS = runtime.GOOS
}
files = append(files, executablePath(fmt.Sprintf("keeper-%s", target.Name), targetOS))
}
if err := build.WriteArchive(keeper, files); err != nil {
log.Fatal(err)
@ -741,8 +768,8 @@ func doKeeperArchive(cmdline []string) {
}
}
func archiveBasename(arch string, archiveVersion string) string {
platform := runtime.GOOS + "-" + arch
func archiveBasename(targetOS, arch, archiveVersion string) string {
platform := targetOS + "-" + arch
if arch == "arm" {
platform += os.Getenv("GOARM")
}
@ -1205,13 +1232,13 @@ func doWindowsInstaller(cmdline []string) {
env := build.Env()
maybeSkipArchive(env)
// Aggregate binaries that are included in the installer
// Aggregate binaries that are included in the installer.
var (
devTools []string
allTools []string
gethTool string
)
for _, file := range allToolsArchiveFiles {
for _, file := range allToolsArchiveFiles("windows") {
if file == "COPYING" { // license, copied later
continue
}
@ -1248,16 +1275,24 @@ func doWindowsInstaller(cmdline []string) {
if env.Commit != "" {
ver[2] += "-" + env.Commit[:8]
}
installer, err := filepath.Abs("geth-" + archiveBasename(*arch, version.Archive(env.Commit)) + ".exe")
installer, err := filepath.Abs("geth-" + archiveBasename("windows", *arch, version.Archive(env.Commit)) + ".exe")
if err != nil {
log.Fatalf("Failed to convert installer file path: %v", err)
}
build.MustRunCommand("makensis.exe",
"/DOUTPUTFILE="+installer,
"/DMAJORVERSION="+ver[0],
"/DMINORVERSION="+ver[1],
"/DBUILDVERSION="+ver[2],
"/DARCH="+*arch,
// makensis on Windows is "makensis.exe" with /D-style defines; on Linux
// (and other Unixes) the binary is "makensis" and accepts -D.
makensisCmd := "makensis"
defineFlag := "-D"
if runtime.GOOS == "windows" {
makensisCmd = "makensis.exe"
defineFlag = "/D"
}
build.MustRunCommand(makensisCmd,
defineFlag+"OUTPUTFILE="+installer,
defineFlag+"MAJORVERSION="+ver[0],
defineFlag+"MINORVERSION="+ver[1],
defineFlag+"BUILDVERSION="+ver[2],
defineFlag+"ARCH="+*arch,
filepath.Join(*workdir, "geth.nsi"),
)
// Sign and publish installer.

View file

@ -215,7 +215,7 @@ func generate(c *cli.Context) error {
code string
err error
)
if c.IsSet(v2Flag.Name) {
if c.Bool(v2Flag.Name) {
code, err = abigen.BindV2(types, abis, bins, c.String(pkgFlag.Name), libs, aliases)
} else {
code, err = abigen.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), libs, aliases)

View file

@ -194,7 +194,7 @@ func formatAttrString(v rlp.RawValue) (string, bool) {
func formatAttrIP(v rlp.RawValue) (string, bool) {
content, _, err := rlp.SplitString(v)
if err != nil || len(content) != 4 && len(content) != 6 {
if err != nil || len(content) != 4 && len(content) != 16 {
return "", false
}
return net.IP(content).String(), true

View file

@ -51,6 +51,12 @@ type Chain struct {
state map[common.Address]state.DumpAccount // state of head block
senders map[common.Address]*senderInfo
config *params.ChainConfig
txInfo txInfo
}
type txInfo struct {
LargeReceiptBlock *uint64 `json:"tx-largereceipt"`
}
// NewChain takes the given chain.rlp file, and decodes and returns
@ -74,12 +80,20 @@ func NewChain(dir string) (*Chain, error) {
if err != nil {
return nil, err
}
var txInfo txInfo
err = common.LoadJSON(filepath.Join(dir, "txinfo.json"), &txInfo)
if err != nil {
return nil, err
}
return &Chain{
genesis: gen,
blocks: blocks,
state: state,
senders: accounts,
config: gen.Config,
txInfo: txInfo,
}, nil
}

View file

@ -345,10 +345,12 @@ loop:
if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
return fmt.Errorf("wrong fork ID in status: have %v, want %v", have, want)
}
if have, want := msg.ProtocolVersion, c.ourHighestProtoVersion; have != uint32(want) {
return fmt.Errorf("wrong protocol version: have %v, want %v", have, want)
for _, cap := range c.caps {
if cap.Name == "eth" && cap.Version == uint(msg.ProtocolVersion) {
break loop
}
}
break loop
return fmt.Errorf("wrong protocol version: have %v, want %v", msg.ProtocolVersion, c.caps)
case discMsg:
var msg []p2p.DiscReason
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {

View file

@ -87,9 +87,9 @@ func (s *Suite) TestSnapGetAccountRange(t *utesting.T) {
root: root,
startingHash: zero,
limitHash: ffHash,
expAccounts: 67,
expAccounts: 68,
expFirst: firstKey,
expLast: common.HexToHash("0x622e662246601dd04f996289ce8b85e86db7bb15bb17f86487ec9d543ddb6f9a"),
expLast: common.HexToHash("0x59312f89c13e9e24c1cb8b103aa39a9b2800348d97a92c2c9e2a78fa02b70025"),
desc: "In this test, we request the entire state range, but limit the response to 4000 bytes.",
},
{
@ -97,9 +97,9 @@ func (s *Suite) TestSnapGetAccountRange(t *utesting.T) {
root: root,
startingHash: zero,
limitHash: ffHash,
expAccounts: 49,
expAccounts: 50,
expFirst: firstKey,
expLast: common.HexToHash("0x445cb5c1278fdce2f9cbdb681bdd76c52f8e50e41dbd9e220242a69ba99ac099"),
expLast: common.HexToHash("0x4615e5f5df5b25349a00ad313c6cd0436b6c08ee5826e33a018661997f85ebaa"),
desc: "In this test, we request the entire state range, but limit the response to 3000 bytes.",
},
{
@ -107,9 +107,9 @@ func (s *Suite) TestSnapGetAccountRange(t *utesting.T) {
root: root,
startingHash: zero,
limitHash: ffHash,
expAccounts: 34,
expAccounts: 35,
expFirst: firstKey,
expLast: common.HexToHash("0x2ef46ebd2073cecde499c2e8df028ad79a26d57bfaa812c4c6f7eb4c9617b913"),
expLast: common.HexToHash("0x2de4bdbddcfbb9c3e195dae6b45f9c38daff897e926764bf34887fb0db5c3284"),
desc: "In this test, we request the entire state range, but limit the response to 2000 bytes.",
},
{
@ -178,9 +178,9 @@ The server should return the first available account.`,
root: root,
startingHash: firstKey,
limitHash: ffHash,
expAccounts: 67,
expAccounts: 68,
expFirst: firstKey,
expLast: common.HexToHash("0x622e662246601dd04f996289ce8b85e86db7bb15bb17f86487ec9d543ddb6f9a"),
expLast: common.HexToHash("0x59312f89c13e9e24c1cb8b103aa39a9b2800348d97a92c2c9e2a78fa02b70025"),
desc: `In this test, startingHash is exactly the first available account key.
The server should return the first available account of the state as the first item.`,
},
@ -189,9 +189,9 @@ The server should return the first available account of the state as the first i
root: root,
startingHash: hashAdd(firstKey, 1),
limitHash: ffHash,
expAccounts: 67,
expAccounts: 68,
expFirst: secondKey,
expLast: common.HexToHash("0x66192e4c757fba1cdc776e6737008f42d50370d3cd801db3624274283bf7cd63"),
expLast: common.HexToHash("0x59a7c8818f1c16b298a054020dc7c3f403a970d1d1db33f9478b1c36e3a2e509"),
desc: `In this test, startingHash is after the first available key.
The server should return the second account of the state as the first item.`,
},
@ -227,9 +227,9 @@ server to return no data because genesis is older than 127 blocks.`,
root: s.chain.RootAt(int(s.chain.Head().Number().Uint64()) - 127),
startingHash: zero,
limitHash: ffHash,
expAccounts: 66,
expAccounts: 68,
expFirst: firstKey,
expLast: common.HexToHash("0x729953a43ed6c913df957172680a17e5735143ad767bda8f58ac84ec62fbec5e"),
expLast: common.HexToHash("0x683b6c03cc32afe5db8cb96050f711fdaff8f8ff44c7587a9a848f921d02815e"),
desc: `This test requests data at a state root that is 127 blocks old.
We expect the server to have this state available.`,
},
@ -658,8 +658,8 @@ The server should reject the request.`,
// It's a bit unfortunate these are hard-coded, but the result depends on
// a lot of aspects of the state trie and can't be guessed in a simple
// way. So you'll have to update this when the test chain is changed.
common.HexToHash("0x5bdc0d6057b35642a16d27223ea5454e5a17a400e28f7328971a5f2a87773b76"),
common.HexToHash("0x0a76c9812ca90ffed8ee4d191e683f93386b6e50cfe3679c0760d27510aa7fc5"),
common.HexToHash("0x4bdecec09691ad38113eebee2df94fadefdff5841c0f182bae1be3c8a6d60bf3"),
common.HexToHash("0x4178696465d4514ff5924ef8c28ce64d41a669634b63184c2c093e252d6b4bc4"),
empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty,
empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty,
empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty,
@ -679,8 +679,8 @@ The server should reject the request.`,
// be updated when the test chain is changed.
expHashes: []common.Hash{
empty,
common.HexToHash("0x0a76c9812ca90ffed8ee4d191e683f93386b6e50cfe3679c0760d27510aa7fc5"),
common.HexToHash("0x5bdc0d6057b35642a16d27223ea5454e5a17a400e28f7328971a5f2a87773b76"),
common.HexToHash("0x4178696465d4514ff5924ef8c28ce64d41a669634b63184c2c093e252d6b4bc4"),
common.HexToHash("0x4bdecec09691ad38113eebee2df94fadefdff5841c0f182bae1be3c8a6d60bf3"),
},
},

View file

@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
)
@ -84,6 +85,7 @@ func (s *Suite) EthTests() []utesting.Test {
// get history
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
{Name: "GetReceipts", Fn: s.TestGetReceipts},
{Name: "GetLargeReceipts", Fn: s.TestGetLargeReceipts},
// test transactions
{Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
{Name: "Transaction", Fn: s.TestTransaction},
@ -434,6 +436,9 @@ func (s *Suite) TestGetReceipts(t *utesting.T) {
// Find some blocks containing receipts.
var hashes = make([]common.Hash, 0, 3)
for i := range s.chain.Len() {
if s.chain.txInfo.LargeReceiptBlock != nil && uint64(i) == *s.chain.txInfo.LargeReceiptBlock {
continue
}
block := s.chain.GetBlock(i)
if len(block.Transactions()) > 0 {
hashes = append(hashes, block.Hash())
@ -442,25 +447,121 @@ func (s *Suite) TestGetReceipts(t *utesting.T) {
break
}
}
if conn.negotiatedProtoVersion < eth.ETH70 {
// Create block bodies request.
req := &eth.GetReceiptsPacket69{
RequestId: 66,
GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes),
}
if err := conn.Write(ethProto, eth.GetReceiptsMsg, req); err != nil {
t.Fatalf("could not write to connection: %v", err)
}
// Wait for response.
resp := new(eth.ReceiptsPacket69)
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
t.Fatalf("error reading block receipts msg: %v", err)
}
if got, want := resp.RequestId, req.RequestId; got != want {
t.Fatalf("unexpected request id in respond", got, want)
}
if resp.List.Len() != len(req.GetReceiptsRequest) {
t.Fatalf("wrong receipts in response: expected %d receipts, got %d", len(req.GetReceiptsRequest), resp.List.Len())
}
} else {
// Create block bodies request.
req := &eth.GetReceiptsPacket70{
RequestId: 66,
FirstBlockReceiptIndex: 0,
GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes),
}
if err := conn.Write(ethProto, eth.GetReceiptsMsg, req); err != nil {
t.Fatalf("could not write to connection: %v", err)
}
// Wait for response.
resp := new(eth.ReceiptsPacket70)
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
t.Fatalf("error reading block receipts msg: %v", err)
}
if got, want := resp.RequestId, req.RequestId; got != want {
t.Fatalf("unexpected request id in respond", got, want)
}
if resp.List.Len() != len(req.GetReceiptsRequest) {
t.Fatalf("wrong receipts in response: expected %d receipts, got %d", len(req.GetReceiptsRequest), resp.List.Len())
}
}
}
// Create receipts request.
req := &eth.GetReceiptsPacket{
RequestId: 66,
GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes),
func (s *Suite) TestGetLargeReceipts(t *utesting.T) {
t.Log(`This test sends GetReceipts requests to the node for large receipt (>10MiB) in the test chain.
This test is meaningful only if the client supports protocol version ETH70 or higher
and LargeReceiptBlock is configured in txInfo.json.`)
conn, err := s.dialAndPeer(nil)
if err != nil {
t.Fatalf("peering failed: %v", err)
}
if err := conn.Write(ethProto, eth.GetReceiptsMsg, req); err != nil {
t.Fatalf("could not write to connection: %v", err)
defer conn.Close()
if conn.negotiatedProtoVersion < eth.ETH70 || s.chain.txInfo.LargeReceiptBlock == nil {
return
}
// Wait for response.
resp := new(eth.ReceiptsPacket)
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
t.Fatalf("error reading block bodies msg: %v", err)
// Find block with large receipt.
// Place the large receipt block hash in the middle of the query
start := max(int(*s.chain.txInfo.LargeReceiptBlock)-2, 0)
end := min(*s.chain.txInfo.LargeReceiptBlock+2, uint64(len(s.chain.blocks)))
var blocks []common.Hash
var receiptHashes []common.Hash
var receipts []*eth.ReceiptList
for i := uint64(start); i < end; i++ {
block := s.chain.GetBlock(int(i))
blocks = append(blocks, block.Hash())
receiptHashes = append(receiptHashes, block.Header().ReceiptHash)
receipts = append(receipts, &eth.ReceiptList{})
}
if got, want := resp.RequestId, req.RequestId; got != want {
t.Fatalf("unexpected request id in respond", got, want)
incomplete := false
lastBlock := 0
for incomplete || lastBlock != len(blocks)-1 {
// Create get receipt request.
req := &eth.GetReceiptsPacket70{
RequestId: 66,
FirstBlockReceiptIndex: uint64(receipts[lastBlock].Derivable().Len()),
GetReceiptsRequest: blocks[lastBlock:],
}
if err := conn.Write(ethProto, eth.GetReceiptsMsg, req); err != nil {
t.Fatalf("could not write to connection: %v", err)
}
// Wait for response.
resp := new(eth.ReceiptsPacket70)
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
t.Fatalf("error reading block receipts msg: %v", err)
}
if got, want := resp.RequestId, req.RequestId; got != want {
t.Fatalf("unexpected request id in respond, want: %d, got: %d", got, want)
}
receiptLists, _ := resp.List.Items()
for i, rc := range receiptLists {
receipts[lastBlock+i].Append(rc)
}
lastBlock += len(receiptLists) - 1
incomplete = resp.LastBlockIncomplete
}
if resp.List.Len() != len(req.GetReceiptsRequest) {
t.Fatalf("wrong receipts in response: expected %d receipts, got %d", len(req.GetReceiptsRequest), resp.List.Len())
hasher := trie.NewStackTrie(nil)
hashes := make([]common.Hash, len(receipts))
for i := range receipts {
hashes[i] = types.DeriveSha(receipts[i].Derivable(), hasher)
}
for i, hash := range hashes {
if receiptHashes[i] != hash {
t.Fatalf("wrong receipt root: want %x, got %x", receiptHashes[i], hash)
}
}
}

Binary file not shown.

View file

@ -37,7 +37,7 @@
"nonce": "0x0",
"timestamp": "0x0",
"extraData": "0x68697665636861696e",
"gasLimit": "0x23f3e20",
"gasLimit": "0x11e1a300",
"difficulty": "0x20000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
@ -119,6 +119,10 @@
"balance": "0x1",
"nonce": "0x1"
},
"8dcd17433742f4c0ca53122ab541d0ba67fc27ff": {
"code": "0x6202e6306000a0",
"balance": "0x0"
},
"c7b99a164efd027a93f147376cc7da7c67c6bbe0": {
"balance": "0xc097ce7bc90715b34b9f1000000000"
},

View file

@ -1,24 +1,24 @@
{
"parentHash": "0x65151b101682b54cd08ba226f640c14c86176865ff9bfc57e0147dadaeac34bb",
"parentHash": "0x7e80093a491eba0e5b2c1895837902f64f514100221801318fe391e1e09c96a6",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"miner": "0x0000000000000000000000000000000000000000",
"stateRoot": "0xce423ebc60fc7764a43f09f1fe3ae61eef25e3eb8d09b1108f7e7eb77dfff5e6",
"transactionsRoot": "0x7ec1ae3989efa75d7bcc766e5e2443afa8a89a5fda42ebba90050e7e702980f7",
"receiptsRoot": "0xfe160832b1ca85f38c6674cb0aae3a24693bc49be56e2ecdf3698b71a794de86",
"stateRoot": "0x8fcfb02cfca007773bd55bc1c3e50a3c8612a59c87ce057e5957e8bf17c1728b",
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"difficulty": "0x0",
"number": "0x258",
"gasLimit": "0x23f3e20",
"gasUsed": "0x19d36",
"gasLimit": "0x11e1a300",
"gasUsed": "0x0",
"timestamp": "0x1770",
"extraData": "0x",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"baseFeePerGas": "0x7",
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"withdrawalsRoot": "0x92abfda39de7df7d705c5a8f30386802ad59d31e782a06d5c5b0f9a260056cf0",
"blobGasUsed": "0x0",
"excessBlobGas": "0x0",
"parentBeaconBlockRoot": "0xf5003fc8f92358e790a114bce93ce1d9c283c85e1787f8d7d56714d3489b49e6",
"requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"hash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0"
"hash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a"
}

View file

@ -4,9 +4,9 @@
"method": "engine_forkchoiceUpdatedV3",
"params": [
{
"headBlockHash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0",
"safeBlockHash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0",
"finalizedBlockHash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0"
"headBlockHash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a",
"safeBlockHash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a",
"finalizedBlockHash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a"
},
null
]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -257,34 +257,50 @@ that they are returned by FINDNODE.`)
// Create bystanders.
nodes := make([]*bystander, 5)
added := make(chan enode.ID, len(nodes))
liveCh := make(chan enode.ID, len(nodes))
for i := range nodes {
nodes[i] = newBystander(t, s, added)
nodes[i] = newBystander(t, s, liveCh)
defer nodes[i].close()
}
// Get them added to the remote table.
// Prefill each bystander with the full bystander set so background FINDNODE
// lookups see useful routing data instead of empty responses.
known := make([]*enode.Node, 0, len(nodes))
for _, bn := range nodes {
known = append(known, bn.conn.localNode.Node())
}
for _, bn := range nodes {
bn.known = append([]*enode.Node(nil), known...)
}
// Wait until enough bystanders have actually become live, i.e. the remote node
// has revalidated them by sending PING and receiving our PONG.
requiredLiveNodes := len(nodes)
timeout := 60 * time.Second
timeoutCh := time.After(timeout)
for count := 0; count < len(nodes); {
liveSet := make(map[enode.ID]*enode.Node)
for len(liveSet) < requiredLiveNodes {
select {
case id := <-added:
t.Logf("bystander node %v added to remote table", id)
count++
case id := <-liveCh:
for _, bn := range nodes {
if bn.id() == id {
liveSet[id] = bn.conn.localNode.Node()
break
}
}
t.Logf("bystander node %v became live", id)
case <-timeoutCh:
t.Errorf("remote added %d bystander nodes in %v, need %d to continue", count, timeout, len(nodes))
t.Logf("this can happen if the node has a non-empty table from previous runs")
t.Errorf("remote revalidated %d bystander nodes in %v, need %d to continue", len(liveSet), timeout, requiredLiveNodes)
return
}
}
t.Logf("all %d bystander nodes were added", len(nodes))
t.Logf("continuing after all %d bystander nodes became live", len(liveSet))
// Collect our nodes by distance.
// Collect live nodes by distance.
var dists []uint
expect := make(map[enode.ID]*enode.Node)
for _, bn := range nodes {
n := bn.conn.localNode.Node()
expect[n.ID()] = n
for id, n := range liveSet {
expect[id] = n
d := uint(enode.LogDist(n.ID(), s.Dest.ID()))
if !slices.Contains(dists, d) {
dists = append(dists, d)
@ -295,42 +311,63 @@ that they are returned by FINDNODE.`)
t.Log("requesting nodes")
conn, l1 := s.listen1(t)
defer conn.close()
foundNodes, err := conn.findnode(l1, dists)
if err != nil {
t.Fatal(err)
}
t.Logf("remote returned %d nodes for distance list %v", len(foundNodes), dists)
for _, n := range foundNodes {
delete(expect, n.ID())
}
if len(expect) > 0 {
t.Errorf("missing %d nodes in FINDNODE result", len(expect))
t.Logf("this can happen if the test is run multiple times in quick succession")
t.Logf("and the remote node hasn't removed dead nodes from previous runs yet")
} else {
t.Logf("all %d expected nodes were returned", len(nodes))
const maxAttempts = 5
const retryInterval = 2 * time.Second
for attempt := 1; attempt <= maxAttempts; attempt++ {
foundNodes, err := conn.findnode(l1, dists)
if err != nil {
t.Fatal(err)
}
missing := make(map[enode.ID]struct{})
for id := range expect {
missing[id] = struct{}{}
}
for _, n := range foundNodes {
delete(missing, n.ID())
}
t.Logf("attempt %d: remote returned %d nodes for distance list %v, missing %d", attempt, len(foundNodes), dists, len(missing))
if len(missing) == 0 {
t.Logf("all %d expected live nodes were returned", len(expect))
return
}
if attempt < maxAttempts {
time.Sleep(retryInterval)
}
}
t.Errorf("missing nodes in FINDNODE result after %d attempts", maxAttempts)
t.Logf("this can happen if the node has a non-empty table from previous runs")
}
// A bystander is a node whose only purpose is filling a spot in the remote table.
type bystander struct {
dest *enode.Node
conn *conn
l net.PacketConn
dest *enode.Node
conn *conn
l net.PacketConn
known []*enode.Node
addedCh chan enode.ID
done sync.WaitGroup
liveCh chan enode.ID
sent map[v5wire.Nonce]v5wire.Packet
done sync.WaitGroup
}
func newBystander(t *utesting.T, s *Suite, added chan enode.ID) *bystander {
func newBystander(t *utesting.T, s *Suite, live chan enode.ID) *bystander {
conn, l := s.listen1(t)
conn.setEndpoint(l) // bystander nodes need IP/port to get pinged
bn := &bystander{
conn: conn,
l: l,
dest: s.Dest,
addedCh: added,
conn: conn,
l: l,
dest: s.Dest,
liveCh: live,
sent: make(map[v5wire.Nonce]v5wire.Packet),
}
// Establish an initial session and let the remote learn this node before
// switching to the passive responder loop below.
conn.reqresp(l, &v5wire.Ping{
ReqID: conn.nextReqID(),
ENRSeq: conn.localNode.Seq(),
})
bn.done.Add(1)
go bn.loop()
return bn
@ -351,48 +388,57 @@ func (bn *bystander) close() {
func (bn *bystander) loop() {
defer bn.done.Done()
var (
lastPing time.Time
wasAdded bool
)
for {
// Ping the remote node.
if !wasAdded && time.Since(lastPing) > 10*time.Second {
bn.conn.reqresp(bn.l, &v5wire.Ping{
ReqID: bn.conn.nextReqID(),
ENRSeq: bn.dest.Seq(),
})
lastPing = time.Now()
}
// Answer packets.
switch p := bn.conn.read(bn.l).(type) {
case *v5wire.Ping:
bn.conn.write(bn.l, &v5wire.Pong{
ReqID: p.ReqID,
ENRSeq: bn.conn.localNode.Seq(),
ToIP: bn.dest.IP(),
ToPort: uint16(bn.dest.UDP()),
}, nil)
wasAdded = true
bn.notifyAdded()
case *v5wire.Findnode:
bn.conn.write(bn.l, &v5wire.Nodes{ReqID: p.ReqID, RespCount: 1}, nil)
wasAdded = true
bn.notifyAdded()
case *v5wire.TalkRequest:
bn.conn.write(bn.l, &v5wire.TalkResponse{ReqID: p.ReqID}, nil)
case *readError:
if !netutil.IsTemporaryError(p.err) {
bn.conn.logf("shutting down: %v", p.err)
return
p, from := bn.conn.readFrom(bn.l)
switch p := p.(type) {
case *v5wire.Whoareyou:
p.Node = bn.dest
if resp, ok := bn.sent[p.Nonce]; ok {
nonce := bn.conn.writeTo(bn.l, resp, p, from)
delete(bn.sent, p.Nonce)
bn.sent[nonce] = resp
} else {
bn.conn.writeTo(bn.l, &v5wire.Ping{
ReqID: bn.conn.nextReqID(),
ENRSeq: bn.conn.localNode.Seq(),
}, p, from)
}
case *v5wire.Ping:
resp := &v5wire.Pong{
ReqID: append([]byte(nil), p.ReqID...),
ENRSeq: bn.conn.localNode.Seq(),
ToIP: from.IP,
ToPort: uint16(from.Port),
}
nonce := bn.conn.writeTo(bn.l, resp, nil, from)
bn.sent[nonce] = resp
bn.notifyLive()
case *v5wire.Findnode:
resp := &v5wire.Nodes{ReqID: append([]byte(nil), p.ReqID...), RespCount: 1}
for _, n := range bn.known {
if slices.Contains(p.Distances, uint(enode.LogDist(n.ID(), bn.id()))) {
resp.Nodes = append(resp.Nodes, n.Record())
}
}
nonce := bn.conn.writeTo(bn.l, resp, nil, from)
bn.sent[nonce] = resp
case *v5wire.TalkRequest:
resp := &v5wire.TalkResponse{ReqID: append([]byte(nil), p.ReqID...)}
nonce := bn.conn.writeTo(bn.l, resp, nil, from)
bn.sent[nonce] = resp
case *readError:
if netutil.IsTemporaryError(p.err) || v5wire.IsInvalidHeader(p.err) {
continue
}
bn.conn.logf("shutting down: %v", p.err)
return
}
}
}
func (bn *bystander) notifyAdded() {
if bn.addedCh != nil {
bn.addedCh <- bn.id()
bn.addedCh = nil
func (bn *bystander) notifyLive() {
if bn.liveCh != nil {
bn.liveCh <- bn.id()
bn.liveCh = nil
}
}

View file

@ -127,14 +127,16 @@ func (tc *conn) nextReqID() []byte {
// The request is retried if a handshake is requested.
func (tc *conn) reqresp(c net.PacketConn, req v5wire.Packet) v5wire.Packet {
reqnonce := tc.write(c, req, nil)
switch resp := tc.read(c).(type) {
resp, from := tc.readFrom(c)
switch resp := resp.(type) {
case *v5wire.Whoareyou:
if resp.Nonce != reqnonce {
return readErrorf("wrong nonce %x in WHOAREYOU (want %x)", resp.Nonce[:], reqnonce[:])
}
resp.Node = tc.remote
tc.write(c, req, resp)
return tc.read(c)
tc.writeTo(c, req, resp, from)
resp2, _ := tc.readFrom(c)
return resp2
default:
return resp
}
@ -150,21 +152,24 @@ func (tc *conn) findnode(c net.PacketConn, dists []uint) ([]*enode.Node, error)
results []*enode.Node
)
for n := 1; n > 0; {
switch resp := tc.read(c).(type) {
resp, from := tc.readFrom(c)
switch resp := resp.(type) {
case *v5wire.Whoareyou:
// Handle handshake.
if resp.Nonce == reqnonce {
resp.Node = tc.remote
tc.write(c, findnode, resp)
tc.writeTo(c, findnode, resp, from)
} else {
return nil, fmt.Errorf("unexpected WHOAREYOU (nonce %x), waiting for NODES", resp.Nonce[:])
}
case *v5wire.Ping:
// Handle ping from remote.
tc.write(c, &v5wire.Pong{
tc.writeTo(c, &v5wire.Pong{
ReqID: resp.ReqID,
ENRSeq: tc.localNode.Seq(),
}, nil)
ToIP: from.IP,
ToPort: uint16(from.Port),
}, nil, from)
case *v5wire.Nodes:
// Got NODES! Check request ID.
if !bytes.Equal(resp.ReqID, findnode.ReqID) {
@ -200,11 +205,16 @@ func (tc *conn) findnode(c net.PacketConn, dists []uint) ([]*enode.Node, error)
// write sends a packet on the given connection.
func (tc *conn) write(c net.PacketConn, p v5wire.Packet, challenge *v5wire.Whoareyou) v5wire.Nonce {
return tc.writeTo(c, p, challenge, tc.remoteAddr)
}
// writeTo sends a packet on the given connection to the given UDP address.
func (tc *conn) writeTo(c net.PacketConn, p v5wire.Packet, challenge *v5wire.Whoareyou, to *net.UDPAddr) v5wire.Nonce {
packet, nonce, err := tc.codec.Encode(tc.remote.ID(), tc.remoteAddr.String(), p, challenge)
if err != nil {
panic(fmt.Errorf("can't encode %v packet: %v", p.Name(), err))
}
if _, err := c.WriteTo(packet, tc.remoteAddr); err != nil {
if _, err := c.WriteTo(packet, to); err != nil {
tc.logf("Can't send %s: %v", p.Name(), err)
} else {
tc.logf(">> %s", p.Name())
@ -214,20 +224,30 @@ func (tc *conn) write(c net.PacketConn, p v5wire.Packet, challenge *v5wire.Whoar
// read waits for an incoming packet on the given connection.
func (tc *conn) read(c net.PacketConn) v5wire.Packet {
p, _ := tc.readFrom(c)
return p
}
// readFrom waits for an incoming packet and returns its source address.
func (tc *conn) readFrom(c net.PacketConn) (v5wire.Packet, *net.UDPAddr) {
buf := make([]byte, 1280)
if err := c.SetReadDeadline(time.Now().Add(waitTime)); err != nil {
return &readError{err}
return &readError{err}, nil
}
n, fromAddr, err := c.ReadFrom(buf)
n, from, err := c.ReadFrom(buf)
if err != nil {
return &readError{err}
return &readError{err}, nil
}
_, _, p, err := tc.codec.Decode(buf[:n], fromAddr.String())
udpFrom, _ := from.(*net.UDPAddr)
// Use tc.remoteAddr for codec/session lookup because the fixture keys sessions
// by the advertised endpoint, but return the actual UDP source so responses can
// comply with the spec and go back to the request envelope address.
_, _, p, err := tc.codec.Decode(buf[:n], tc.remoteAddr.String())
if err != nil {
return &readError{err}
return &readError{err}, udpFrom
}
tc.logf("<< %s", p.Name())
return p
return p, udpFrom
}
// logf prints to the test log.

View file

@ -17,6 +17,7 @@
package main
import (
"bytes"
"errors"
"fmt"
"net"
@ -30,6 +31,31 @@ import (
"github.com/urfave/cli/v2"
)
// decodeRLPxDisconnect parses a disconnect message payload. Per the RLPx spec
// the payload is a list containing a single reason, but some implementations
// (including older geth) sent the reason as a bare byte. Accept both forms.
func decodeRLPxDisconnect(data []byte) (p2p.DiscReason, error) {
s := rlp.NewStream(bytes.NewReader(data), uint64(len(data)))
k, _, err := s.Kind()
if err != nil {
return 0, err
}
var reason p2p.DiscReason
if k == rlp.List {
if _, err := s.List(); err != nil {
return 0, err
}
if err := s.Decode(&reason); err != nil {
return 0, err
}
return reason, nil
}
if err := s.Decode(&reason); err != nil {
return 0, err
}
return reason, nil
}
var (
rlpxCommand = &cli.Command{
Name: "rlpx",
@ -103,11 +129,15 @@ func rlpxPing(ctx *cli.Context) error {
}
fmt.Printf("%+v\n", h)
case 1:
var msg []p2p.DiscReason
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
return errors.New("invalid disconnect message")
// The disconnect message is specified as a list containing the reason,
// but some implementations (including older geth) send the reason as a
// single byte. Handle both forms, and on failure include the raw payload
// so the operator can see what was actually sent.
reason, decErr := decodeRLPxDisconnect(data)
if decErr != nil {
return fmt.Errorf("invalid disconnect message: %v (raw=0x%x)", decErr, data)
}
return fmt.Errorf("received disconnect message: %v", msg[0])
return fmt.Errorf("received disconnect message: %v", reason)
default:
return fmt.Errorf("invalid message code %d, expected handshake (code zero) or disconnect (code one)", code)
}

View file

@ -0,0 +1,75 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"testing"
"github.com/ethereum/go-ethereum/p2p"
)
func TestDecodeRLPxDisconnect(t *testing.T) {
tests := []struct {
name string
payload []byte
want p2p.DiscReason
wantErr bool
}{
{
name: "list form (spec-compliant)",
payload: []byte{0xc1, 0x04}, // [4] = TooManyPeers
want: p2p.DiscTooManyPeers,
},
{
name: "list form with reason zero",
payload: []byte{0xc1, 0x80}, // [0] = Requested
want: p2p.DiscRequested,
},
{
name: "bare byte form (legacy geth)",
payload: []byte{0x04}, // 4 = TooManyPeers
want: p2p.DiscTooManyPeers,
},
{
name: "bare byte form zero",
payload: []byte{0x80}, // 0 = Requested
want: p2p.DiscRequested,
},
{
name: "empty payload",
payload: []byte{},
wantErr: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got, err := decodeRLPxDisconnect(tc.payload)
if tc.wantErr {
if err == nil {
t.Fatalf("expected error, got reason=%v", got)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != tc.want {
t.Fatalf("got reason %v, want %v", got, tc.want)
}
})
}
}

View file

@ -337,9 +337,6 @@ func checkAccumulator(e era.Era) error {
// accumulation across the entire set and are verified at the end.
for it.Next() {
// 1) next() walks the block index, so we're able to implicitly verify it.
if it.Error() != nil {
return fmt.Errorf("error reading block %d: %w", it.Number(), it.Error())
}
block, receipts, err := it.BlockAndReceipts()
if err != nil {
return fmt.Errorf("error reading block %d: %w", it.Number(), err)

View file

@ -17,9 +17,12 @@
package t8ntool
import (
"context"
"encoding/json"
"fmt"
stdmath "math"
"math/big"
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@ -47,6 +50,9 @@ type Prestate struct {
Env stEnv `json:"env"`
Pre types.GenesisAlloc `json:"pre"`
TreeLeaves map[common.Hash]hexutil.Bytes `json:"vkt,omitempty"`
// AllocPath, when non-empty, causes Apply to stream the alloc from disk
// instead of reading Pre, so the full map never materializes in memory.
AllocPath string `json:"-"`
}
//go:generate go run github.com/fjl/gencodec -type ExecutionResult -field-override executionResultMarshaling -out gen_execresult.go
@ -146,8 +152,19 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
return h
}
var (
isEIP4762 = chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762)
isEIP4762 = chainConfig.IsUBT(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
statedb *state.StateDB
)
if pre.AllocPath != "" {
var err error
statedb, err = MakePreStateStreaming(rawdb.NewMemoryDatabase(), pre.AllocPath, isEIP4762)
if err != nil {
return nil, nil, nil, err
}
} else {
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762)
}
var (
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp)
gaspool = core.NewGasPool(pre.Env.GasLimit)
blockHash = common.Hash{0x13, 0x37}
@ -253,7 +270,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
continue
}
}
statedb.SetTxContext(tx.Hash(), len(receipts))
statedb.SetTxContext(tx.Hash(), len(receipts), uint32(len(receipts)+1))
var (
snapshot = statedb.Snapshot()
gp = gaspool.Snapshot()
@ -315,27 +332,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
}
// Gather the execution-layer triggered requests.
var requests [][]byte
if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) {
requests = [][]byte{}
// EIP-6110
var allLogs []*types.Log
for _, receipt := range receipts {
allLogs = append(allLogs, receipt.Logs...)
}
if err := core.ParseDepositLogs(&requests, allLogs, chainConfig); err != nil {
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
}
// EIP-7002
if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process withdrawal requests: %v", err))
}
// EIP-7251
if err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process consolidation requests: %v", err))
}
var allLogs []*types.Log
for _, receipt := range receipts {
allLogs = append(allLogs, receipt.Logs...)
}
requests, err := core.PostExecution(context.Background(), chainConfig, vmContext.BlockNumber, vmContext.Time, allLogs, evm, uint32(len(receipts)+1))
if err != nil {
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("failed to process post-execution: %v", err))
}
// Commit block
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), chainConfig.IsCancun(vmContext.BlockNumber, vmContext.Time))
if err != nil {
@ -378,7 +382,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
}
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool) *state.StateDB {
tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true, IsVerkle: isBintrie})
tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true, IsUBT: isBintrie})
sdb := state.NewDatabase(tdb, nil)
root := types.EmptyRootHash
@ -414,6 +418,76 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool
return statedb
}
// MakePreStateStreaming is like MakePreState, but decodes the alloc from disk
// one account at a time so the full map is never held in memory.
func MakePreStateStreaming(db ethdb.Database, allocPath string, isBintrie bool) (*state.StateDB, error) {
tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true, IsUBT: isBintrie})
sdb := state.NewDatabase(tdb, nil)
root := types.EmptyRootHash
if isBintrie {
root = types.EmptyBinaryHash
}
statedb, err := state.New(root, sdb)
if err != nil {
return nil, NewError(ErrorEVM, fmt.Errorf("failed to create initial statedb: %v", err))
}
f, err := os.Open(allocPath)
if err != nil {
return nil, NewError(ErrorIO, fmt.Errorf("failed reading alloc file: %v", err))
}
defer f.Close()
dec := json.NewDecoder(f)
tok, err := dec.Token()
if err != nil {
return nil, NewError(ErrorJson, fmt.Errorf("failed reading alloc opening token: %v", err))
}
if d, ok := tok.(json.Delim); !ok || d != '{' {
return nil, NewError(ErrorJson, fmt.Errorf("expected alloc object, got %v", tok))
}
for dec.More() {
keyTok, err := dec.Token()
if err != nil {
return nil, NewError(ErrorJson, fmt.Errorf("failed reading alloc key: %v", err))
}
keyStr, ok := keyTok.(string)
if !ok {
return nil, NewError(ErrorJson, fmt.Errorf("alloc key not a string: %v", keyTok))
}
addr := common.HexToAddress(keyStr)
var acct types.Account
if err := dec.Decode(&acct); err != nil {
return nil, NewError(ErrorJson, fmt.Errorf("failed decoding account %s: %v", keyStr, err))
}
statedb.SetCode(addr, acct.Code, tracing.CodeChangeUnspecified)
statedb.SetNonce(addr, acct.Nonce, tracing.NonceChangeGenesis)
if acct.Balance != nil {
statedb.SetBalance(addr, uint256.MustFromBig(acct.Balance), tracing.BalanceIncreaseGenesisBalance)
}
for k, v := range acct.Storage {
statedb.SetState(addr, k, v)
}
}
if _, err := dec.Token(); err != nil {
return nil, NewError(ErrorJson, fmt.Errorf("failed reading alloc closing token: %v", err))
}
root, err = statedb.Commit(0, false, false)
if err != nil {
return nil, NewError(ErrorEVM, fmt.Errorf("failed to commit initial state: %v", err))
}
if isBintrie {
return statedb, nil
}
statedb, err = state.New(root, sdb)
if err != nil {
return nil, NewError(ErrorEVM, fmt.Errorf("failed to reopen state after commit: %v", err))
}
return statedb, nil
}
func rlpHash(x any) (h common.Hash) {
hw := keccak.NewLegacyKeccak256()
rlp.Encode(hw, x)

View file

@ -133,21 +133,21 @@ func Transaction(ctx *cli.Context) error {
}
// Check intrinsic gas
rules := chainConfig.Rules(common.Big0, true, 0)
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai, rules.IsAmsterdam)
if err != nil {
r.Error = err
results = append(results, r)
continue
}
r.IntrinsicGas = gas
if tx.Gas() < gas {
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas)
r.IntrinsicGas = cost.RegularGas
if tx.Gas() < cost.RegularGas {
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), cost.RegularGas)
results = append(results, r)
continue
}
// For Prague txs, validate the floor data gas.
if rules.IsPrague {
floorDataGas, err := core.FloorDataGas(tx.Data())
floorDataGas, err := core.FloorDataGas(rules, tx.Data(), tx.AccessList())
if err != nil {
r.Error = err
results = append(results, r)
@ -185,7 +185,9 @@ func Transaction(ctx *cli.Context) error {
}
}
if chainConfig.IsOsaka(new(big.Int), 0) && tx.Gas() > params.MaxTxGas {
isOsaka := chainConfig.IsOsaka(new(big.Int), 0)
isAmsterdam := chainConfig.IsAmsterdam(new(big.Int), 0)
if isOsaka && !isAmsterdam && tx.Gas() > params.MaxTxGas {
r.Error = errors.New("gas limit exceeds maximum")
}
results = append(results, r)

View file

@ -17,6 +17,7 @@
package t8ntool
import (
"bufio"
"encoding/json"
"errors"
"fmt"
@ -115,11 +116,10 @@ func Transition(ctx *cli.Context) error {
}
}
if allocStr != stdinSelector {
if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil {
return err
}
prestate.AllocPath = allocStr
} else {
prestate.Pre = inputData.Alloc
}
prestate.Pre = inputData.Alloc
if btStr != stdinSelector && btStr != "" {
if err := readFile(btStr, "BT", &inputData.BT); err != nil {
@ -223,22 +223,57 @@ func Transition(ctx *cli.Context) error {
return err
}
}
// Dump the execution result
// Dump the execution result.
var (
collector = make(Alloc)
collector Alloc
btleaves map[common.Hash]hexutil.Bytes
)
isBinary := chainConfig.IsVerkle(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp)
if !isBinary {
isBinary := chainConfig.IsUBT(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp)
allocOutput := ctx.String(OutputAllocFlag.Name)
switch {
case !isBinary && allocOutput != "" && allocOutput != "stdout" && allocOutput != "stderr":
// Stream directly to the output file to avoid materializing the
// whole post-state in memory. dispatchOutput is told to skip alloc
// by clearing the output name.
if err := writeStreamedAlloc(filepath.Join(baseDir, allocOutput), s); err != nil {
return err
}
allocOutput = ""
case !isBinary:
collector = make(Alloc)
s.DumpToCollector(collector, nil)
} else {
default:
btleaves = make(map[common.Hash]hexutil.Bytes)
if err := s.DumpBinTrieLeaves(btleaves); err != nil {
return err
}
}
return dispatchOutput(ctx, baseDir, result, collector, allocOutput, body, btleaves)
}
return dispatchOutput(ctx, baseDir, result, collector, body, btleaves)
// writeStreamedAlloc writes the post-state alloc to path one account at a
// time, producing the same JSON shape as saveFile on an Alloc map.
func writeStreamedAlloc(path string, s *state.StateDB) error {
f, err := os.Create(path)
if err != nil {
return NewError(ErrorIO, fmt.Errorf("failed creating alloc output file: %v", err))
}
bw := bufio.NewWriter(f)
sa := newStreamingAlloc(bw)
s.DumpToCollector(sa, nil)
if err := sa.Close(); err != nil {
f.Close()
return NewError(ErrorIO, fmt.Errorf("failed writing alloc output: %v", err))
}
if err := bw.Flush(); err != nil {
f.Close()
return NewError(ErrorIO, fmt.Errorf("failed flushing alloc output: %v", err))
}
if err := f.Close(); err != nil {
return NewError(ErrorIO, fmt.Errorf("failed closing alloc output file: %v", err))
}
log.Info("Wrote file", "file", path)
return nil
}
func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error {
@ -327,6 +362,10 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
if addr == nil {
return
}
g[*addr] = dumpAccountToTypesAccount(dumpAccount)
}
func dumpAccountToTypesAccount(dumpAccount state.DumpAccount) types.Account {
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
var storage map[common.Hash]common.Hash
if dumpAccount.Storage != nil {
@ -335,13 +374,64 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
storage[k] = common.HexToHash(v)
}
}
genesisAccount := types.Account{
return types.Account{
Code: dumpAccount.Code,
Storage: storage,
Balance: balance,
Nonce: dumpAccount.Nonce,
}
g[*addr] = genesisAccount
}
// streamingAlloc is a DumpCollector that writes each account to w as it is
// visited, emitting a single JSON object keyed by address. Close must be
// called to emit the closing brace.
type streamingAlloc struct {
w io.Writer
wroteOne bool
err error
}
func newStreamingAlloc(w io.Writer) *streamingAlloc {
return &streamingAlloc{w: w}
}
func (s *streamingAlloc) write(b []byte) {
if s.err != nil {
return
}
_, s.err = s.w.Write(b)
}
func (s *streamingAlloc) OnRoot(common.Hash) {
s.write([]byte{'{'})
}
func (s *streamingAlloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
if s.err != nil || addr == nil {
return
}
keyJSON, err := json.Marshal(*addr)
if err != nil {
s.err = err
return
}
valueJSON, err := json.Marshal(dumpAccountToTypesAccount(dumpAccount))
if err != nil {
s.err = err
return
}
if s.wroteOne {
s.write([]byte{','})
}
s.write(keyJSON)
s.write([]byte{':'})
s.write(valueJSON)
s.wroteOne = true
}
func (s *streamingAlloc) Close() error {
s.write([]byte{'}'})
return s.err
}
// saveFile marshals the object to the given file
@ -359,8 +449,9 @@ func saveFile(baseDir, filename string, data interface{}) error {
}
// dispatchOutput writes the output data to either stderr or stdout, or to the specified
// files
func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes, bt map[common.Hash]hexutil.Bytes) error {
// files. An empty allocOutput skips the alloc dispatch, which is used when the
// alloc has already been streamed to disk by the caller.
func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, allocOutput string, body hexutil.Bytes, bt map[common.Hash]hexutil.Bytes) error {
stdOutObject := make(map[string]interface{})
stdErrObject := make(map[string]interface{})
dispatch := func(baseDir, fName, name string, obj interface{}) error {
@ -378,7 +469,7 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a
}
return nil
}
if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
if err := dispatch(baseDir, allocOutput, "alloc", alloc); err != nil {
return err
}
if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
@ -452,10 +543,10 @@ func BinKeys(ctx *cli.Context) error {
return err
}
}
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.VerkleDefaults)
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.UBTDefaults)
defer db.Close()
bt, err := genBinTrieFromAlloc(alloc, db)
bt, err := genBinTrieFromAlloc(alloc, db, triedb.UBTDefaults.BinTrieGroupDepth)
if err != nil {
return fmt.Errorf("error generating bt: %w", err)
}
@ -496,10 +587,10 @@ func BinTrieRoot(ctx *cli.Context) error {
return err
}
}
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.VerkleDefaults)
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.UBTDefaults)
defer db.Close()
bt, err := genBinTrieFromAlloc(alloc, db)
bt, err := genBinTrieFromAlloc(alloc, db, triedb.UBTDefaults.BinTrieGroupDepth)
if err != nil {
return fmt.Errorf("error generating bt: %w", err)
}
@ -509,8 +600,8 @@ func BinTrieRoot(ctx *cli.Context) error {
}
// TODO(@CPerezz): Should this go to `bintrie` module?
func genBinTrieFromAlloc(alloc core.GenesisAlloc, db database.NodeDatabase) (*bintrie.BinaryTrie, error) {
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, db)
func genBinTrieFromAlloc(alloc core.GenesisAlloc, db database.NodeDatabase, groupDepth int) (*bintrie.BinaryTrie, error) {
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, db, groupDepth)
if err != nil {
return nil, err
}

View file

@ -115,7 +115,7 @@ var (
Name: "trace.noreturndata",
Aliases: []string{"noreturndata"},
Value: true,
Usage: "enable return data output",
Usage: "disable return data output",
Category: traceCategory,
}

View file

@ -166,8 +166,11 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, exe
if haveGasUsed != gasUsed {
panic(fmt.Sprintf("gas differs, have %v want %v", haveGasUsed, gasUsed))
}
if haveErr != err {
panic(fmt.Sprintf("err differs, have %v want %v", haveErr, err))
if (haveErr == nil) != (err == nil) {
panic(fmt.Sprintf("err differs in nil-ness, have %v want %v", haveErr, err))
}
if haveErr != nil && err != nil && haveErr.Error() != err.Error() {
panic(fmt.Sprintf("err differs, have %q want %q", haveErr.Error(), err.Error()))
}
}
})
@ -318,7 +321,7 @@ func runCmd(ctx *cli.Context) error {
// don't mutate the state!
runtimeConfig.State = prestate.Copy()
output, _, gasLeft, err := runtime.Create(input, &runtimeConfig)
return output, gasLeft, err
return output, initialGas - gasLeft, err
}
} else {
if len(code) > 0 {

408
cmd/geth/bintrie_convert.go Normal file
View file

@ -0,0 +1,408 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"errors"
"fmt"
"runtime"
"runtime/debug"
"slices"
"time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/bintrie"
"github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/urfave/cli/v2"
)
var (
deleteSourceFlag = &cli.BoolFlag{
Name: "delete-source",
Usage: "Delete MPT trie nodes after conversion",
}
memoryLimitFlag = &cli.Uint64Flag{
Name: "memory-limit",
Usage: "Max heap allocation in MB before forcing a commit cycle",
Value: 16384,
}
bintrieCommand = &cli.Command{
Name: "bintrie",
Usage: "A set of commands for binary trie operations",
Description: "",
Subcommands: []*cli.Command{
{
Name: "convert",
Usage: "Convert MPT state to binary trie",
ArgsUsage: "[state-root]",
Action: convertToBinaryTrie,
Flags: slices.Concat([]cli.Flag{
deleteSourceFlag,
memoryLimitFlag,
}, utils.NetworkFlags, utils.DatabaseFlags),
Description: `
geth bintrie convert [--delete-source] [--memory-limit MB] [state-root]
Reads all state from the Merkle Patricia Trie and writes it into a Binary Trie,
operating offline. Memory-safe via periodic commit-and-reload cycles.
The optional state-root argument specifies which state root to convert.
If omitted, the head block's state root is used.
Flags:
--delete-source Delete MPT trie nodes after successful conversion
--memory-limit Max heap allocation in MB before forcing a commit (default: 16384)
`,
},
},
}
)
type conversionStats struct {
accounts uint64
slots uint64
codes uint64
commits uint64
start time.Time
lastReport time.Time
lastMemChk time.Time
}
func (s *conversionStats) report(force bool) {
if !force && time.Since(s.lastReport) < 8*time.Second {
return
}
elapsed := time.Since(s.start).Seconds()
acctRate := float64(0)
if elapsed > 0 {
acctRate = float64(s.accounts) / elapsed
}
log.Info("Conversion progress",
"accounts", s.accounts,
"slots", s.slots,
"codes", s.codes,
"commits", s.commits,
"accounts/sec", fmt.Sprintf("%.0f", acctRate),
"elapsed", common.PrettyDuration(time.Since(s.start)),
)
s.lastReport = time.Now()
}
func convertToBinaryTrie(ctx *cli.Context) error {
if ctx.NArg() > 1 {
return errors.New("too many arguments")
}
stack, _ := makeConfigNode(ctx)
defer stack.Close()
chaindb := utils.MakeChainDatabase(ctx, stack, false)
defer chaindb.Close()
headBlock := rawdb.ReadHeadBlock(chaindb)
if headBlock == nil {
return errors.New("no head block found")
}
var (
root common.Hash
err error
)
if ctx.NArg() == 1 {
root, err = parseRoot(ctx.Args().First())
if err != nil {
return fmt.Errorf("invalid state root: %w", err)
}
} else {
root = headBlock.Root()
}
log.Info("Starting MPT to binary trie conversion", "root", root, "block", headBlock.NumberU64())
srcTriedb := utils.MakeTrieDatabase(ctx, stack, chaindb, true, true, false)
defer srcTriedb.Close()
destTriedb := triedb.NewDatabase(chaindb, &triedb.Config{
IsUBT: true,
PathDB: &pathdb.Config{
JournalDirectory: stack.ResolvePath("triedb-bintrie"),
},
})
defer destTriedb.Close()
binTrie, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, destTriedb, ctx.Int(utils.BinTrieGroupDepthFlag.Name))
if err != nil {
return fmt.Errorf("failed to create binary trie: %w", err)
}
memLimit := ctx.Uint64(memoryLimitFlag.Name) * 1024 * 1024
currentRoot, err := runConversionLoop(chaindb, srcTriedb, destTriedb, binTrie, root, memLimit)
if err != nil {
return err
}
log.Info("Conversion complete", "binaryRoot", currentRoot)
if ctx.Bool(deleteSourceFlag.Name) {
log.Info("Deleting source MPT data")
if err := deleteMPTData(chaindb, srcTriedb, root); err != nil {
return fmt.Errorf("MPT deletion failed: %w", err)
}
log.Info("Source MPT data deleted")
}
return nil
}
func runConversionLoop(chaindb ethdb.Database, srcTriedb *triedb.Database, destTriedb *triedb.Database, binTrie *bintrie.BinaryTrie, root common.Hash, memLimit uint64) (common.Hash, error) {
currentRoot := types.EmptyBinaryHash
stats := &conversionStats{
start: time.Now(),
lastReport: time.Now(),
lastMemChk: time.Now(),
}
srcTrie, err := trie.NewStateTrie(trie.StateTrieID(root), srcTriedb)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to open source trie: %w", err)
}
acctIt, err := srcTrie.NodeIterator(nil)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to create account iterator: %w", err)
}
accIter := trie.NewIterator(acctIt)
for accIter.Next() {
var acc types.StateAccount
if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil {
return common.Hash{}, fmt.Errorf("invalid account RLP: %w", err)
}
addrBytes := srcTrie.GetKey(accIter.Key)
if addrBytes == nil {
return common.Hash{}, fmt.Errorf("missing preimage for account hash %x (run with --cache.preimages)", accIter.Key)
}
addr := common.BytesToAddress(addrBytes)
var code []byte
codeHash := common.BytesToHash(acc.CodeHash)
if codeHash != types.EmptyCodeHash {
code = rawdb.ReadCode(chaindb, codeHash)
if code == nil {
return common.Hash{}, fmt.Errorf("missing code for hash %x (account %x)", codeHash, addr)
}
stats.codes++
}
if err := binTrie.UpdateAccount(addr, &acc, len(code)); err != nil {
return common.Hash{}, fmt.Errorf("failed to update account %x: %w", addr, err)
}
if len(code) > 0 {
if err := binTrie.UpdateContractCode(addr, codeHash, code); err != nil {
return common.Hash{}, fmt.Errorf("failed to update code for %x: %w", addr, err)
}
}
if acc.Root != types.EmptyRootHash {
addrHash := common.BytesToHash(accIter.Key)
storageTrie, err := trie.NewStateTrie(trie.StorageTrieID(root, addrHash, acc.Root), srcTriedb)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to open storage trie for %x: %w", addr, err)
}
storageNodeIt, err := storageTrie.NodeIterator(nil)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to create storage iterator for %x: %w", addr, err)
}
storageIter := trie.NewIterator(storageNodeIt)
slotCount := uint64(0)
for storageIter.Next() {
slotKey := storageTrie.GetKey(storageIter.Key)
if slotKey == nil {
return common.Hash{}, fmt.Errorf("missing preimage for storage key %x (account %x)", storageIter.Key, addr)
}
_, content, _, err := rlp.Split(storageIter.Value)
if err != nil {
return common.Hash{}, fmt.Errorf("invalid storage RLP for key %x (account %x): %w", slotKey, addr, err)
}
if err := binTrie.UpdateStorage(addr, slotKey, content); err != nil {
return common.Hash{}, fmt.Errorf("failed to update storage %x/%x: %w", addr, slotKey, err)
}
stats.slots++
slotCount++
if slotCount%10000 == 0 {
binTrie, currentRoot, err = maybeCommit(binTrie, currentRoot, destTriedb, memLimit, stats)
if err != nil {
return common.Hash{}, err
}
}
}
if storageIter.Err != nil {
return common.Hash{}, fmt.Errorf("storage iteration error for %x: %w", addr, storageIter.Err)
}
}
stats.accounts++
stats.report(false)
if stats.accounts%1000 == 0 {
binTrie, currentRoot, err = maybeCommit(binTrie, currentRoot, destTriedb, memLimit, stats)
if err != nil {
return common.Hash{}, err
}
}
}
if accIter.Err != nil {
return common.Hash{}, fmt.Errorf("account iteration error: %w", accIter.Err)
}
_, currentRoot, err = commitBinaryTrie(binTrie, currentRoot, destTriedb)
if err != nil {
return common.Hash{}, fmt.Errorf("final commit failed: %w", err)
}
stats.commits++
stats.report(true)
return currentRoot, nil
}
func maybeCommit(bt *bintrie.BinaryTrie, currentRoot common.Hash, destDB *triedb.Database, memLimit uint64, stats *conversionStats) (*bintrie.BinaryTrie, common.Hash, error) {
if time.Since(stats.lastMemChk) < 5*time.Second {
return bt, currentRoot, nil
}
stats.lastMemChk = time.Now()
var m runtime.MemStats
runtime.ReadMemStats(&m)
if m.Alloc < memLimit {
return bt, currentRoot, nil
}
log.Info("Memory limit reached, committing", "alloc", common.StorageSize(m.Alloc), "limit", common.StorageSize(memLimit))
bt, currentRoot, err := commitBinaryTrie(bt, currentRoot, destDB)
if err != nil {
return nil, common.Hash{}, err
}
stats.commits++
stats.report(true)
return bt, currentRoot, nil
}
func commitBinaryTrie(bt *bintrie.BinaryTrie, currentRoot common.Hash, destDB *triedb.Database) (*bintrie.BinaryTrie, common.Hash, error) {
newRoot, nodeSet := bt.Commit(false)
if nodeSet != nil {
merged := trienode.NewWithNodeSet(nodeSet)
if err := destDB.Update(newRoot, currentRoot, 0, merged, triedb.NewStateSet()); err != nil {
return nil, common.Hash{}, fmt.Errorf("triedb update failed: %w", err)
}
if err := destDB.Commit(newRoot, false); err != nil {
return nil, common.Hash{}, fmt.Errorf("triedb commit failed: %w", err)
}
}
runtime.GC()
debug.FreeOSMemory()
bt, err := bintrie.NewBinaryTrie(newRoot, destDB, bt.GroupDepth())
if err != nil {
return nil, common.Hash{}, fmt.Errorf("failed to reload binary trie: %w", err)
}
return bt, newRoot, nil
}
func deleteMPTData(chaindb ethdb.Database, srcTriedb *triedb.Database, root common.Hash) error {
isPathDB := srcTriedb.Scheme() == rawdb.PathScheme
srcTrie, err := trie.NewStateTrie(trie.StateTrieID(root), srcTriedb)
if err != nil {
return fmt.Errorf("failed to open source trie for deletion: %w", err)
}
acctIt, err := srcTrie.NodeIterator(nil)
if err != nil {
return fmt.Errorf("failed to create account iterator for deletion: %w", err)
}
batch := chaindb.NewBatch()
deleted := 0
for acctIt.Next(true) {
if isPathDB {
rawdb.DeleteAccountTrieNode(batch, acctIt.Path())
} else {
node := acctIt.Hash()
if node != (common.Hash{}) {
rawdb.DeleteLegacyTrieNode(batch, node)
}
}
deleted++
if acctIt.Leaf() {
var acc types.StateAccount
if err := rlp.DecodeBytes(acctIt.LeafBlob(), &acc); err != nil {
return fmt.Errorf("invalid account during deletion: %w", err)
}
if acc.Root != types.EmptyRootHash {
addrHash := common.BytesToHash(acctIt.LeafKey())
storageTrie, err := trie.NewStateTrie(trie.StorageTrieID(root, addrHash, acc.Root), srcTriedb)
if err != nil {
return fmt.Errorf("failed to open storage trie for deletion: %w", err)
}
storageIt, err := storageTrie.NodeIterator(nil)
if err != nil {
return fmt.Errorf("failed to create storage iterator for deletion: %w", err)
}
for storageIt.Next(true) {
if isPathDB {
rawdb.DeleteStorageTrieNode(batch, addrHash, storageIt.Path())
} else {
node := storageIt.Hash()
if node != (common.Hash{}) {
rawdb.DeleteLegacyTrieNode(batch, node)
}
}
deleted++
if batch.ValueSize() >= ethdb.IdealBatchSize {
if err := batch.Write(); err != nil {
return fmt.Errorf("batch write failed: %w", err)
}
batch.Reset()
}
}
if storageIt.Error() != nil {
return fmt.Errorf("storage deletion iterator error: %w", storageIt.Error())
}
}
}
if batch.ValueSize() >= ethdb.IdealBatchSize {
if err := batch.Write(); err != nil {
return fmt.Errorf("batch write failed: %w", err)
}
batch.Reset()
}
}
if acctIt.Error() != nil {
return fmt.Errorf("account deletion iterator error: %w", acctIt.Error())
}
if batch.ValueSize() > 0 {
if err := batch.Write(); err != nil {
return fmt.Errorf("final batch write failed: %w", err)
}
}
log.Info("MPT deletion complete", "nodesDeleted", deleted)
return nil
}

View file

@ -0,0 +1,229 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"math"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie/bintrie"
"github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256"
)
func TestBintrieConvert(t *testing.T) {
var (
addr1 = common.HexToAddress("0x1111111111111111111111111111111111111111")
addr2 = common.HexToAddress("0x2222222222222222222222222222222222222222")
slotKey1 = common.HexToHash("0x01")
slotKey2 = common.HexToHash("0x02")
slotVal1 = common.HexToHash("0xdeadbeef")
slotVal2 = common.HexToHash("0xcafebabe")
code = []byte{0x60, 0x42, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3}
)
chaindb := rawdb.NewMemoryDatabase()
srcTriedb := triedb.NewDatabase(chaindb, &triedb.Config{
Preimages: true,
PathDB: pathdb.Defaults,
})
gspec := &core.Genesis{
Config: params.TestChainConfig,
BaseFee: big.NewInt(params.InitialBaseFee),
Alloc: types.GenesisAlloc{
addr1: {
Balance: big.NewInt(1000000),
Nonce: 5,
},
addr2: {
Balance: big.NewInt(2000000),
Nonce: 10,
Code: code,
Storage: map[common.Hash]common.Hash{
slotKey1: slotVal1,
slotKey2: slotVal2,
},
},
},
}
genesisBlock := gspec.MustCommit(chaindb, srcTriedb)
root := genesisBlock.Root()
t.Logf("Genesis root: %x", root)
srcTriedb.Close()
srcTriedb2 := triedb.NewDatabase(chaindb, &triedb.Config{
Preimages: true,
PathDB: &pathdb.Config{ReadOnly: true},
})
defer srcTriedb2.Close()
destTriedb := triedb.NewDatabase(chaindb, &triedb.Config{
IsUBT: true,
PathDB: pathdb.Defaults,
})
defer destTriedb.Close()
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, destTriedb, 8)
if err != nil {
t.Fatalf("failed to create binary trie: %v", err)
}
currentRoot, err := runConversionLoop(chaindb, srcTriedb2, destTriedb, bt, root, math.MaxUint64)
if err != nil {
t.Fatalf("conversion failed: %v", err)
}
t.Logf("Binary trie root: %x", currentRoot)
bt2, err := bintrie.NewBinaryTrie(currentRoot, destTriedb, 8)
if err != nil {
t.Fatalf("failed to reload binary trie: %v", err)
}
acc1, err := bt2.GetAccount(addr1)
if err != nil {
t.Fatalf("failed to get account1: %v", err)
}
if acc1 == nil {
t.Fatal("account1 not found in binary trie")
}
if acc1.Nonce != 5 {
t.Errorf("account1 nonce: got %d, want 5", acc1.Nonce)
}
wantBal1 := uint256.NewInt(1000000)
if acc1.Balance.Cmp(wantBal1) != 0 {
t.Errorf("account1 balance: got %s, want %s", acc1.Balance, wantBal1)
}
acc2, err := bt2.GetAccount(addr2)
if err != nil {
t.Fatalf("failed to get account2: %v", err)
}
if acc2 == nil {
t.Fatal("account2 not found in binary trie")
}
if acc2.Nonce != 10 {
t.Errorf("account2 nonce: got %d, want 10", acc2.Nonce)
}
wantBal2 := uint256.NewInt(2000000)
if acc2.Balance.Cmp(wantBal2) != 0 {
t.Errorf("account2 balance: got %s, want %s", acc2.Balance, wantBal2)
}
treeKey1 := bintrie.GetBinaryTreeKeyStorageSlot(addr2, slotKey1[:])
val1, err := bt2.GetWithHashedKey(treeKey1)
if err != nil {
t.Fatalf("failed to get storage slot1: %v", err)
}
if len(val1) == 0 {
t.Fatal("storage slot1 not found")
}
got1 := common.BytesToHash(val1)
if got1 != slotVal1 {
t.Errorf("storage slot1: got %x, want %x", got1, slotVal1)
}
treeKey2 := bintrie.GetBinaryTreeKeyStorageSlot(addr2, slotKey2[:])
val2, err := bt2.GetWithHashedKey(treeKey2)
if err != nil {
t.Fatalf("failed to get storage slot2: %v", err)
}
if len(val2) == 0 {
t.Fatal("storage slot2 not found")
}
got2 := common.BytesToHash(val2)
if got2 != slotVal2 {
t.Errorf("storage slot2: got %x, want %x", got2, slotVal2)
}
}
func TestBintrieConvertDeleteSource(t *testing.T) {
addr1 := common.HexToAddress("0x3333333333333333333333333333333333333333")
chaindb := rawdb.NewMemoryDatabase()
srcTriedb := triedb.NewDatabase(chaindb, &triedb.Config{
Preimages: true,
PathDB: pathdb.Defaults,
})
gspec := &core.Genesis{
Config: params.TestChainConfig,
BaseFee: big.NewInt(params.InitialBaseFee),
Alloc: types.GenesisAlloc{
addr1: {
Balance: big.NewInt(1000000),
},
},
}
genesisBlock := gspec.MustCommit(chaindb, srcTriedb)
root := genesisBlock.Root()
srcTriedb.Close()
srcTriedb2 := triedb.NewDatabase(chaindb, &triedb.Config{
Preimages: true,
PathDB: &pathdb.Config{ReadOnly: true},
})
destTriedb := triedb.NewDatabase(chaindb, &triedb.Config{
IsUBT: true,
PathDB: pathdb.Defaults,
})
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, destTriedb, 8)
if err != nil {
t.Fatalf("failed to create binary trie: %v", err)
}
newRoot, err := runConversionLoop(chaindb, srcTriedb2, destTriedb, bt, root, math.MaxUint64)
if err != nil {
t.Fatalf("conversion failed: %v", err)
}
if err := deleteMPTData(chaindb, srcTriedb2, root); err != nil {
t.Fatalf("deletion failed: %v", err)
}
srcTriedb2.Close()
bt2, err := bintrie.NewBinaryTrie(newRoot, destTriedb, 8)
if err != nil {
t.Fatalf("failed to reload binary trie after deletion: %v", err)
}
acc, err := bt2.GetAccount(addr1)
if err != nil {
t.Fatalf("failed to get account after deletion: %v", err)
}
if acc == nil {
t.Fatal("account not found after MPT deletion")
}
wantBal := uint256.NewInt(1000000)
if acc.Balance.Cmp(wantBal) != 0 {
t.Errorf("balance after deletion: got %s, want %s", acc.Balance, wantBal)
}
destTriedb.Close()
}

View file

@ -64,7 +64,7 @@ var (
utils.OverrideOsaka,
utils.OverrideBPO1,
utils.OverrideBPO2,
utils.OverrideVerkle,
utils.OverrideUBT,
}, utils.DatabaseFlags),
Description: `
The init command initializes a new genesis block and definition for the network.
@ -297,15 +297,15 @@ func initGenesis(ctx *cli.Context) error {
v := ctx.Uint64(utils.OverrideBPO2.Name)
overrides.OverrideBPO2 = &v
}
if ctx.IsSet(utils.OverrideVerkle.Name) {
v := ctx.Uint64(utils.OverrideVerkle.Name)
overrides.OverrideVerkle = &v
if ctx.IsSet(utils.OverrideUBT.Name) {
v := ctx.Uint64(utils.OverrideUBT.Name)
overrides.OverrideUBT = &v
}
chaindb := utils.MakeChainDatabase(ctx, stack, false)
defer chaindb.Close()
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsUBT())
defer triedb.Close()
_, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides, nil)
@ -325,7 +325,7 @@ func dumpGenesis(ctx *cli.Context) error {
var genesis *core.Genesis
if utils.IsNetworkPreset(ctx) {
genesis = utils.MakeGenesis(ctx)
} else if ctx.IsSet(utils.DeveloperFlag.Name) && !ctx.IsSet(utils.DataDirFlag.Name) {
} else if ctx.Bool(utils.DeveloperFlag.Name) && !ctx.IsSet(utils.DataDirFlag.Name) {
genesis = core.DeveloperGenesisBlock(11_500_000, nil)
}
@ -731,13 +731,16 @@ func pruneHistory(ctx *cli.Context) error {
// Determine the prune point based on the history mode.
genesisHash := chain.Genesis().Hash()
prunePoint := history.GetPrunePoint(genesisHash, mode)
if prunePoint == nil {
policy, err := history.NewPolicy(mode, genesisHash)
if err != nil {
return err
}
if policy.Target == nil {
return fmt.Errorf("prune point for %q not found for this network", mode.String())
}
var (
targetBlock = prunePoint.BlockNumber
targetBlockHash = prunePoint.BlockHash
targetBlock = policy.Target.BlockNumber
targetBlockHash = policy.Target.BlockHash
)
// Check the current freezer tail to see if pruning is needed/possible.

View file

@ -38,6 +38,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/catalyst"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/syncer"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/internal/telemetry/tracesetup"
"github.com/ethereum/go-ethereum/internal/version"
@ -235,9 +236,9 @@ func makeFullNode(ctx *cli.Context) *node.Node {
v := ctx.Uint64(utils.OverrideBPO2.Name)
cfg.Eth.OverrideBPO2 = &v
}
if ctx.IsSet(utils.OverrideVerkle.Name) {
v := ctx.Uint64(utils.OverrideVerkle.Name)
cfg.Eth.OverrideVerkle = &v
if ctx.IsSet(utils.OverrideUBT.Name) {
v := ctx.Uint64(utils.OverrideUBT.Name)
cfg.Eth.OverrideUBT = &v
}
// Start metrics export if enabled.
@ -269,25 +270,28 @@ func makeFullNode(ctx *cli.Context) *node.Node {
filterSystem := utils.RegisterFilterAPI(stack, backend, &cfg.Eth)
// Configure GraphQL if requested.
if ctx.IsSet(utils.GraphQLEnabledFlag.Name) {
if ctx.Bool(utils.GraphQLEnabledFlag.Name) {
utils.RegisterGraphQLService(stack, backend, filterSystem, &cfg.Node)
}
// Add the Ethereum Stats daemon if requested.
if cfg.Ethstats.URL != "" {
utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL)
}
// Configure synchronization override service
var synctarget common.Hash
syncConfig := syncer.Config{
ExitWhenSynced: ctx.Bool(utils.ExitWhenSyncedFlag.Name),
}
if ctx.IsSet(utils.SyncTargetFlag.Name) {
target := ctx.String(utils.SyncTargetFlag.Name)
if !common.IsHexHash(target) {
utils.Fatalf("sync target hash is not a valid hex hash: %s", target)
}
synctarget = common.HexToHash(target)
syncConfig.TargetBlock = common.HexToHash(target)
}
utils.RegisterSyncOverrideService(stack, eth, synctarget, ctx.Bool(utils.ExitWhenSyncedFlag.Name))
utils.RegisterSyncOverrideService(stack, eth, syncConfig)
if ctx.IsSet(utils.DeveloperFlag.Name) {
if ctx.Bool(utils.DeveloperFlag.Name) {
// Start dev mode.
simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), cfg.Eth.Miner.PendingFeeRecipient, eth)
if err != nil {

View file

@ -806,6 +806,24 @@ func (iter *snapshotIterator) Release() {
iter.storage.Release()
}
type codeIterator struct {
iter ethdb.Iterator
}
func (iter *codeIterator) Next() (byte, []byte, []byte, bool) {
for iter.iter.Next() {
key := iter.iter.Key()
if bytes.HasPrefix(key, rawdb.CodePrefix) && len(key) == (len(rawdb.CodePrefix)+common.HashLength) {
return utils.OpBatchAdd, key, iter.iter.Value(), true
}
}
return 0, nil, nil, false
}
func (iter *codeIterator) Release() {
iter.iter.Release()
}
// chainExporters defines the export scheme for all exportable chain data.
var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{
"preimage": func(db ethdb.Database) utils.ChainDataIterator {
@ -817,6 +835,10 @@ var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{
storage := db.NewIterator(rawdb.SnapshotStoragePrefix, nil)
return &snapshotIterator{account: account, storage: storage}
},
"code": func(db ethdb.Database) utils.ChainDataIterator {
iter := db.NewIterator(rawdb.CodePrefix, nil)
return &codeIterator{iter: iter}
},
}
func exportChaindata(ctx *cli.Context) error {

View file

@ -22,13 +22,10 @@ import (
"os"
"slices"
"sort"
"time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console/prompt"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/internal/flags"
@ -64,7 +61,7 @@ var (
utils.OverrideOsaka,
utils.OverrideBPO1,
utils.OverrideBPO2,
utils.OverrideVerkle,
utils.OverrideUBT,
utils.OverrideGenesisFlag,
utils.EnablePersonal, // deprecated
utils.TxPoolLocalsFlag,
@ -95,6 +92,7 @@ var (
utils.StateHistoryFlag,
utils.TrienodeHistoryFlag,
utils.TrienodeHistoryFullValueCheckpointFlag,
utils.BinTrieGroupDepthFlag,
utils.LightKDFFlag,
utils.EthRequiredBlocksFlag,
utils.LegacyWhitelistFlag, // deprecated
@ -260,6 +258,8 @@ func init() {
utils.ShowDeprecated,
// See snapshot.go
snapshotCommand,
// See bintrie_convert.go
bintrieCommand,
}
if logTestCommand != nil {
app.Commands = append(app.Commands, logTestCommand)
@ -384,28 +384,4 @@ func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
}
}
}()
// Spawn a standalone goroutine for status synchronization monitoring,
// close the node when synchronization is complete if user required.
if ctx.Bool(utils.ExitWhenSyncedFlag.Name) {
go func() {
sub := stack.EventMux().Subscribe(downloader.DoneEvent{})
defer sub.Unsubscribe()
for {
event := <-sub.Chan()
if event == nil {
continue
}
done, ok := event.Data.(downloader.DoneEvent)
if !ok {
continue
}
if timestamp := time.Unix(int64(done.Latest.Time), 0); time.Since(timestamp) < 10*time.Minute {
log.Info("Synchronisation completed", "latestnum", done.Latest.Number, "latesthash", done.Latest.Hash(),
"age", common.PrettyAge(timestamp))
stack.Close()
}
}
}()
}
}

View file

@ -18,15 +18,18 @@ package main
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"slices"
"sort"
"time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/pruner"
@ -36,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb"
"github.com/urfave/cli/v2"
)
@ -105,7 +109,9 @@ information about the specified address.
Usage: "Traverse the state with given root hash and perform quick verification",
ArgsUsage: "<root>",
Action: traverseState,
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
Flags: slices.Concat([]cli.Flag{
utils.AccountFlag,
}, utils.NetworkFlags, utils.DatabaseFlags),
Description: `
geth snapshot traverse-state <state-root>
will traverse the whole state from the given state root and will abort if any
@ -113,6 +119,8 @@ referenced trie node or contract code is missing. This command can be used for
state integrity verification. The default checking target is the HEAD state.
It's also usable without snapshot enabled.
If --account is specified, only the storage trie of that account is traversed.
`,
},
{
@ -120,7 +128,9 @@ It's also usable without snapshot enabled.
Usage: "Traverse the state with given root hash and perform detailed verification",
ArgsUsage: "<root>",
Action: traverseRawState,
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
Flags: slices.Concat([]cli.Flag{
utils.AccountFlag,
}, utils.NetworkFlags, utils.DatabaseFlags),
Description: `
geth snapshot traverse-rawstate <state-root>
will traverse the whole state from the given root and will abort if any referenced
@ -129,6 +139,8 @@ verification. The default checking target is the HEAD state. It's basically iden
to traverse-state, but the check granularity is smaller.
It's also usable without snapshot enabled.
If --account is specified, only the storage trie of that account is traversed.
`,
},
{
@ -159,6 +171,22 @@ block is used.
Description: `
The export-preimages command exports hash preimages to a flat file, in exactly
the expected order for the overlay tree migration.
`,
},
{
Name: "list-eip-7610-accounts",
Aliases: []string{"eip7610"},
Usage: "list EIP7610 eligible accounts",
Action: listEIP7610EligibleAccounts,
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
Description: `
geth snapshot list-eip-7610-accounts
traverses the postEIP-161 state and returns all accounts that are eligible
under EIP-7610: accounts with zero nonce, empty runtime code, and non-empty
storage. The traversal will be aborted immediately if the state is prior to
EIP-161.
The exported accounts are identified by their address.
`,
},
},
@ -272,6 +300,120 @@ func checkDanglingStorage(ctx *cli.Context) error {
return snapshot.CheckDanglingStorage(db)
}
// parseAccount parses the account flag value as either an address (20 bytes)
// or an account hash (32 bytes) and returns the hashed account key.
func parseAccount(input string) (common.Hash, error) {
switch len(input) {
case 40, 42: // address
return crypto.Keccak256Hash(common.HexToAddress(input).Bytes()), nil
case 64, 66: // hash
return common.HexToHash(input), nil
default:
return common.Hash{}, errors.New("malformed account address or hash")
}
}
// lookupAccount resolves the account from the state trie using the given
// account hash.
func lookupAccount(accountHash common.Hash, tr *trie.Trie) (*types.StateAccount, error) {
accData, err := tr.Get(accountHash.Bytes())
if err != nil {
return nil, fmt.Errorf("failed to get account %s: %w", accountHash, err)
}
if accData == nil {
return nil, fmt.Errorf("account not found: %s", accountHash)
}
var acc types.StateAccount
if err := rlp.DecodeBytes(accData, &acc); err != nil {
return nil, fmt.Errorf("invalid account data %s: %w", accountHash, err)
}
return &acc, nil
}
func traverseStorage(id *trie.ID, db *triedb.Database, report bool, detail bool) error {
tr, err := trie.NewStateTrie(id, db)
if err != nil {
log.Error("Failed to open storage trie", "account", id.Owner, "root", id.Root, "err", err)
return err
}
var (
slots int
nodes int
lastReport time.Time
start = time.Now()
)
it, err := tr.NodeIterator(nil)
if err != nil {
log.Error("Failed to open storage iterator", "account", id.Owner, "root", id.Root, "err", err)
return err
}
logger := log.Debug
if report {
logger = log.Info
}
logger("Start traversing storage trie", "account", id.Owner, "storageRoot", id.Root)
if !detail {
iter := trie.NewIterator(it)
for iter.Next() {
slots += 1
if time.Since(lastReport) > time.Second*8 {
logger("Traversing storage", "account", id.Owner, "slots", slots, "elapsed", common.PrettyDuration(time.Since(start)))
lastReport = time.Now()
}
}
if iter.Err != nil {
log.Error("Failed to traverse storage trie", "root", id.Root, "err", iter.Err)
return iter.Err
}
logger("Storage is complete", "account", id.Owner, "slots", slots, "elapsed", common.PrettyDuration(time.Since(start)))
} else {
reader, err := db.NodeReader(id.StateRoot)
if err != nil {
log.Error("Failed to open state reader", "err", err)
return err
}
var (
buffer = make([]byte, 32)
hasher = crypto.NewKeccakState()
)
for it.Next(true) {
nodes += 1
node := it.Hash()
// Check the presence for non-empty hash node(embedded node doesn't
// have their own hash).
if node != (common.Hash{}) {
blob, _ := reader.Node(id.Owner, it.Path(), node)
if len(blob) == 0 {
log.Error("Missing trie node(storage)", "hash", node)
return errors.New("missing storage")
}
hasher.Reset()
hasher.Write(blob)
hasher.Read(buffer)
if !bytes.Equal(buffer, node.Bytes()) {
log.Error("Invalid trie node(storage)", "hash", node.Hex(), "value", blob)
return errors.New("invalid storage node")
}
}
if it.Leaf() {
slots += 1
}
if time.Since(lastReport) > time.Second*8 {
logger("Traversing storage", "account", id.Owner, "nodes", nodes, "slots", slots, "elapsed", common.PrettyDuration(time.Since(start)))
lastReport = time.Now()
}
}
if err := it.Error(); err != nil {
log.Error("Failed to traverse storage trie", "root", id.Root, "err", err)
return err
}
logger("Storage is complete", "account", id.Owner, "nodes", nodes, "slots", slots, "elapsed", common.PrettyDuration(time.Since(start)))
}
return nil
}
// traverseState is a helper function used for pruning verification.
// Basically it just iterates the trie, ensure all nodes and associated
// contract codes are present.
@ -309,6 +451,30 @@ func traverseState(ctx *cli.Context) error {
root = headBlock.Root()
log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
}
// If --account is specified, only traverse the storage trie of that account.
if accountStr := ctx.String(utils.AccountFlag.Name); accountStr != "" {
accountHash, err := parseAccount(accountStr)
if err != nil {
log.Error("Failed to parse account", "err", err)
return err
}
// Use raw trie since the account key is already hashed.
t, err := trie.New(trie.StateTrieID(root), triedb)
if err != nil {
log.Error("Failed to open state trie", "root", root, "err", err)
return err
}
acc, err := lookupAccount(accountHash, t)
if err != nil {
log.Error("Failed to look up account", "hash", accountHash, "err", err)
return err
}
if acc.Root == types.EmptyRootHash {
log.Info("Account has no storage", "hash", accountHash)
return nil
}
return traverseStorage(trie.StorageTrieID(root, accountHash, acc.Root), triedb, true, false)
}
t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
if err != nil {
log.Error("Failed to open trie", "root", root, "err", err)
@ -335,30 +501,10 @@ func traverseState(ctx *cli.Context) error {
return err
}
if acc.Root != types.EmptyRootHash {
id := trie.StorageTrieID(root, common.BytesToHash(accIter.Key), acc.Root)
storageTrie, err := trie.NewStateTrie(id, triedb)
err := traverseStorage(trie.StorageTrieID(root, common.BytesToHash(accIter.Key), acc.Root), triedb, false, false)
if err != nil {
log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
return err
}
storageIt, err := storageTrie.NodeIterator(nil)
if err != nil {
log.Error("Failed to open storage iterator", "root", acc.Root, "err", err)
return err
}
storageIter := trie.NewIterator(storageIt)
for storageIter.Next() {
slots += 1
if time.Since(lastReport) > time.Second*8 {
log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
lastReport = time.Now()
}
}
if storageIter.Err != nil {
log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err)
return storageIter.Err
}
}
if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
@ -418,6 +564,30 @@ func traverseRawState(ctx *cli.Context) error {
root = headBlock.Root()
log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
}
// If --account is specified, only traverse the storage trie of that account.
if accountStr := ctx.String(utils.AccountFlag.Name); accountStr != "" {
accountHash, err := parseAccount(accountStr)
if err != nil {
log.Error("Failed to parse account", "err", err)
return err
}
// Use raw trie since the account key is already hashed.
t, err := trie.New(trie.StateTrieID(root), triedb)
if err != nil {
log.Error("Failed to open state trie", "root", root, "err", err)
return err
}
acc, err := lookupAccount(accountHash, t)
if err != nil {
log.Error("Failed to look up account", "hash", accountHash, "err", err)
return err
}
if acc.Root == types.EmptyRootHash {
log.Info("Account has no storage", "hash", accountHash)
return nil
}
return traverseStorage(trie.StorageTrieID(root, accountHash, acc.Root), triedb, true, true)
}
t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
if err != nil {
log.Error("Failed to open trie", "root", root, "err", err)
@ -473,50 +643,10 @@ func traverseRawState(ctx *cli.Context) error {
return errors.New("invalid account")
}
if acc.Root != types.EmptyRootHash {
id := trie.StorageTrieID(root, common.BytesToHash(accIter.LeafKey()), acc.Root)
storageTrie, err := trie.NewStateTrie(id, triedb)
err := traverseStorage(trie.StorageTrieID(root, common.BytesToHash(accIter.LeafKey()), acc.Root), triedb, false, true)
if err != nil {
log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
return errors.New("missing storage trie")
}
storageIter, err := storageTrie.NodeIterator(nil)
if err != nil {
log.Error("Failed to open storage iterator", "root", acc.Root, "err", err)
return err
}
for storageIter.Next(true) {
nodes += 1
node := storageIter.Hash()
// Check the presence for non-empty hash node(embedded node doesn't
// have their own hash).
if node != (common.Hash{}) {
blob, _ := reader.Node(common.BytesToHash(accIter.LeafKey()), storageIter.Path(), node)
if len(blob) == 0 {
log.Error("Missing trie node(storage)", "hash", node)
return errors.New("missing storage")
}
hasher.Reset()
hasher.Write(blob)
hasher.Read(got)
if !bytes.Equal(got, node.Bytes()) {
log.Error("Invalid trie node(storage)", "hash", node.Hex(), "value", blob)
return errors.New("invalid storage node")
}
}
// Bump the counter if it's leaf node.
if storageIter.Leaf() {
slots += 1
}
if time.Since(lastReport) > time.Second*8 {
log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
lastReport = time.Now()
}
}
if storageIter.Error() != nil {
log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error())
return storageIter.Error()
}
}
if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
@ -690,3 +820,92 @@ func checkAccount(ctx *cli.Context) error {
log.Info("Checked the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start)))
return nil
}
// listEIP7610EligibleAccounts traverses the postEIP-161 state and returns all
// accounts that are eligible under EIP-7610: accounts with zero nonce, empty
// runtime code, and non-empty storage.
//
// Such accounts could only have been created before EIP-161, since after that
// all newly created contracts are initialized with a nonce of one.
//
// This helper should be generally applicable to all networks, including the
// Ethereum mainnet. For most networks where EIP-161 was enabled from genesis,
// the resulting set is expected to be empty. Otherwise, network operators are
// responsible for generating the eligible account set themselves.
//
// Notably, the exported accounts are identified by their address.
func listEIP7610EligibleAccounts(ctx *cli.Context) error {
stack, _ := makeConfigNode(ctx)
defer stack.Close()
chaindb := utils.MakeChainDatabase(ctx, stack, true)
defer chaindb.Close()
headBlock := rawdb.ReadHeadBlock(chaindb)
if headBlock == nil {
log.Error("Failed to load head block")
return nil
}
config, _, err := core.LoadChainConfig(chaindb, utils.MakeGenesis(ctx))
if err != nil {
log.Error("Failed to load chain config", "err", err)
return err
}
if !config.IsEIP158(headBlock.Number()) {
log.Info("Local head is prior to EIP-161", "head", headBlock.Number(), "eip-161", *config.EIP158Block)
return nil
}
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
defer triedb.Close()
if triedb.Scheme() != rawdb.PathScheme {
log.Error("Hash scheme is not supported")
return nil
}
iter, err := triedb.AccountIterator(headBlock.Root(), common.Hash{})
if err != nil {
log.Error("Failed to get account iterator", "err", err)
return err
}
var (
start = time.Now()
accounts []common.Address
)
for iter.Next() {
blob := iter.Account()
if blob == nil {
log.Error("Failed to get account blob")
return nil
}
var account types.SlimAccount
if err := rlp.DecodeBytes(blob, &account); err != nil {
log.Error("Failed to decode", "err", err)
return err
}
// EIP-7610 account eligibility:
// - account.nonce == 0
// - account.runtime_code == empty
// - account.storage != empty
if len(account.CodeHash) == 0 && account.Nonce == 0 && len(account.Root) != 0 {
preimage := rawdb.ReadPreimage(chaindb, iter.Hash())
if preimage == nil {
log.Error("Failed to read preimage", "hash", iter.Hash().Hex())
return nil
}
accounts = append(accounts, common.BytesToAddress(preimage))
}
}
if len(accounts) == 0 {
log.Info("Traversed state", "eligible", len(accounts), "elapsed", common.PrettyDuration(time.Since(start)))
} else {
sort.Slice(accounts, func(i, j int) bool {
return accounts[i].Cmp(accounts[j]) < 0
})
buf := make([]byte, len(accounts)*common.AddressLength)
for i, h := range accounts {
copy(buf[i*common.AddressLength:], h[:])
}
log.Info("Traversed state", "eligible", len(accounts), "elapsed", common.PrettyDuration(time.Since(start)), "output", hex.EncodeToString(buf))
}
return nil
}

View file

@ -15,6 +15,7 @@
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//go:build example
// +build example
package main

View file

@ -14,8 +14,8 @@
// 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/>.
//go:build wasm
// +build wasm
//go:build wasm && !womir
// +build wasm,!womir
package main

View file

@ -0,0 +1,49 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//go:build womir
package main
import "unsafe"
// These match the WOMIR guest-io imports (env module).
// Protocol: __hint_input prepares next item, __hint_buffer reads words.
// Each item has format: [byte_len_u32_le, ...data_words_padded_to_4bytes]
//
//go:wasmimport env __hint_input
func hintInput()
//go:wasmimport env __hint_buffer
func hintBuffer(ptr unsafe.Pointer, numWords uint32)
func readWord() uint32 {
var buf [4]byte
hintBuffer(unsafe.Pointer(&buf[0]), 1)
return uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
}
func readBytes() []byte {
hintInput()
byteLen := readWord()
numWords := (byteLen + 3) / 4
data := make([]byte, numWords*4)
hintBuffer(unsafe.Pointer(&data[0]), numWords)
return data[:byteLen]
}
// getInput reads the RLP-encoded Payload from the WOMIR hint stream.
func getInput() []byte {
return readBytes()
}

View file

@ -38,8 +38,8 @@ require (
go.opentelemetry.io/otel v1.40.0 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
golang.org/x/crypto v0.44.0 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

View file

@ -127,20 +127,20 @@ go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ7
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -14,8 +14,8 @@
// 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/>.
//go:build !example && !ziren && !wasm
// +build !example,!ziren,!wasm
//go:build !example && !ziren && !wasm && !womir
// +build !example,!ziren,!wasm,!womir
package main

View file

@ -274,40 +274,66 @@ func ImportHistory(chain *core.BlockChain, dir string, network string, from func
reported = time.Now()
imported = 0
h = sha256.New()
scratch = bytes.NewBuffer(nil)
buf = bytes.NewBuffer(nil)
)
for i, file := range entries {
err := func() error {
path := filepath.Join(dir, file)
// validate against checksum file in directory
// Validate against checksum file in directory.
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("open %s: %w", path, err)
}
defer f.Close()
if _, err := io.Copy(h, f); err != nil {
return fmt.Errorf("checksum %s: %w", path, err)
}
got := common.BytesToHash(h.Sum(scratch.Bytes()[:])).Hex()
want := checksums[i]
got := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex()
h.Reset()
scratch.Reset()
if got != want {
return fmt.Errorf("%s checksum mismatch: have %s want %s", file, got, want)
buf.Reset()
if got != checksums[i] {
return fmt.Errorf("%s checksum mismatch: have %s want %s", file, got, checksums[i])
}
// Import all block data from Era1.
e, err := from(f)
if err != nil {
return fmt.Errorf("error opening era: %w", err)
}
defer e.Close()
it, err := e.Iterator()
if err != nil {
return fmt.Errorf("error creating iterator: %w", err)
}
var (
blocks = make([]*types.Block, 0, importBatchSize)
receiptsList = make([]types.Receipts, 0, importBatchSize)
flush = func() error {
if len(blocks) == 0 {
return nil
}
enc := types.EncodeBlockReceiptLists(receiptsList)
if _, err := chain.InsertReceiptChain(blocks, enc, math.MaxUint64); err != nil {
return fmt.Errorf("error inserting blocks %d-%d: %w",
blocks[0].NumberU64(), blocks[len(blocks)-1].NumberU64(), err)
}
imported += len(blocks)
if time.Since(reported) >= 8*time.Second {
head := blocks[len(blocks)-1].NumberU64()
log.Info("Importing Era files", "head", head, "imported", imported,
"elapsed", common.PrettyDuration(time.Since(start)))
imported = 0
reported = time.Now()
}
blocks = blocks[:0]
receiptsList = receiptsList[:0]
return nil
}
)
for it.Next() {
block, err := it.Block()
if err != nil {
@ -320,23 +346,18 @@ func ImportHistory(chain *core.BlockChain, dir string, network string, from func
if err != nil {
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
}
enc := types.EncodeBlockReceiptLists([]types.Receipts{receipts})
if _, err := chain.InsertReceiptChain([]*types.Block{block}, enc, math.MaxUint64); err != nil {
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
}
imported++
if time.Since(reported) >= 8*time.Second {
log.Info("Importing Era files", "head", it.Number(), "imported", imported,
"elapsed", common.PrettyDuration(time.Since(start)))
imported = 0
reported = time.Now()
blocks = append(blocks, block)
receiptsList = append(receiptsList, receipts)
if len(blocks) == importBatchSize {
if err := flush(); err != nil {
return err
}
}
}
if err := it.Error(); err != nil {
return err
}
return nil
return flush()
}()
if err != nil {
return err

View file

@ -218,6 +218,10 @@ var (
Usage: "Max number of elements (0 = no limit)",
Value: 0,
}
AccountFlag = &cli.StringFlag{
Name: "account",
Usage: "Specifies the account address or hash to traverse a single storage trie",
}
OutputFileFlag = &cli.StringFlag{
Name: "output",
Usage: "Writes the result in json to the output",
@ -260,9 +264,9 @@ var (
Usage: "Manually specify the bpo2 fork timestamp, overriding the bundled setting",
Category: flags.EthCategory,
}
OverrideVerkle = &cli.Uint64Flag{
Name: "override.verkle",
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",
OverrideUBT = &cli.Uint64Flag{
Name: "override.ubt",
Usage: "Manually specify the UBT fork timestamp, overriding the bundled setting",
Category: flags.EthCategory,
}
OverrideGenesisFlag = &cli.StringFlag{
@ -293,6 +297,12 @@ var (
Value: ethconfig.Defaults.EnableStateSizeTracking,
Category: flags.StateCategory,
}
BinTrieGroupDepthFlag = &cli.IntFlag{
Name: "bintrie.groupdepth",
Usage: "Number of levels per serialized group in binary trie (1-8, default 5). Lower values create smaller groups with more nodes.",
Value: 5,
Category: flags.StateCategory,
}
StateHistoryFlag = &cli.Uint64Flag{
Name: "history.state",
Usage: "Number of recent blocks to retain state history for, only relevant in state.scheme=path (default = 90,000 blocks, 0 = entire chain)",
@ -1063,19 +1073,19 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
RPCTelemetryEndpointFlag = &cli.StringFlag{
Name: "rpc.telemetry.endpoint",
Usage: "Defines where RPC telemetry is sent (e.g., http://localhost:4318)",
Usage: "Defines where RPC telemetry is sent (e.g., http://localhost:4318 or grpc://localhost:4317)",
Category: flags.APICategory,
}
RPCTelemetryUserFlag = &cli.StringFlag{
Name: "rpc.telemetry.username",
Usage: "HTTP Basic Auth username for OpenTelemetry",
Usage: "Basic Auth username for OpenTelemetry",
Category: flags.APICategory,
}
RPCTelemetryPasswordFlag = &cli.StringFlag{
Name: "rpc.telemetry.password",
Usage: "HTTP Basic Auth password for OpenTelemetry",
Usage: "Basic Auth password for OpenTelemetry",
Category: flags.APICategory,
}
@ -1094,7 +1104,7 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
RPCTelemetrySampleRatioFlag = &cli.Float64Flag{
Name: "rpc.telemetry.sample-ratio",
Usage: "Defines the sampling ratio for RPC telemetry (0.0 to 1.0)",
Value: 1.0,
Value: node.DefaultConfig.OpenTelemetry.SampleRatio,
Category: flags.APICategory,
}
// Era flags are a group of flags related to the era archive format.
@ -1580,7 +1590,9 @@ func setOpenTelemetry(ctx *cli.Context, cfg *node.Config) {
if ctx.IsSet(RPCTelemetryTagsFlag.Name) {
tcfg.Tags = ctx.String(RPCTelemetryTagsFlag.Name)
}
tcfg.SampleRatio = ctx.Float64(RPCTelemetrySampleRatioFlag.Name)
if ctx.IsSet(RPCTelemetrySampleRatioFlag.Name) {
tcfg.SampleRatio = ctx.Float64(RPCTelemetrySampleRatioFlag.Name)
}
if tcfg.Endpoint != "" && !tcfg.Enabled {
log.Warn(fmt.Sprintf("OpenTelemetry endpoint configured but telemetry is not enabled, use --%s to enable.", RPCTelemetryFlag.Name))
@ -1811,6 +1823,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.IsSet(TrienodeHistoryFullValueCheckpointFlag.Name) {
cfg.NodeFullValueCheckpoint = uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name))
}
if ctx.IsSet(BinTrieGroupDepthFlag.Name) {
cfg.BinTrieGroupDepth = ctx.Int(BinTrieGroupDepthFlag.Name)
}
if ctx.IsSet(StateSchemeFlag.Name) {
cfg.StateScheme = ctx.String(StateSchemeFlag.Name)
}
@ -1893,7 +1908,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
cfg.StatelessSelfValidation = ctx.Bool(VMStatelessSelfValidationFlag.Name)
}
// Auto-enable StatelessSelfValidation when witness stats are enabled
if ctx.Bool(VMWitnessStatsFlag.Name) {
if cfg.EnableWitnessStats {
cfg.StatelessSelfValidation = true
}
@ -2222,13 +2237,13 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf
}
// RegisterSyncOverrideService adds the synchronization override service into node.
func RegisterSyncOverrideService(stack *node.Node, eth *eth.Ethereum, target common.Hash, exitWhenSynced bool) {
if target != (common.Hash{}) {
log.Info("Registered sync override service", "hash", target, "exitWhenSynced", exitWhenSynced)
func RegisterSyncOverrideService(stack *node.Node, eth *eth.Ethereum, config syncer.Config) {
if config.TargetBlock != (common.Hash{}) {
log.Info("Registered sync override service", "hash", config.TargetBlock, "exitWhenSynced", config.ExitWhenSynced)
} else {
log.Info("Registered sync override service")
}
syncer.Register(stack, eth, target, exitWhenSynced)
syncer.Register(stack, eth, config)
}
// SetupMetrics configures the metrics system.
@ -2427,6 +2442,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
StateHistory: ctx.Uint64(StateHistoryFlag.Name),
TrienodeHistory: ctx.Int64(TrienodeHistoryFlag.Name),
NodeFullValueCheckpoint: uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name)),
BinTrieGroupDepth: ctx.Int(BinTrieGroupDepthFlag.Name),
// Disable transaction indexing/unindexing.
TxLookupLimit: -1,
@ -2510,10 +2526,10 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
}
// MakeTrieDatabase constructs a trie database based on the configured scheme.
func MakeTrieDatabase(ctx *cli.Context, stack *node.Node, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database {
func MakeTrieDatabase(ctx *cli.Context, stack *node.Node, disk ethdb.Database, preimage bool, readOnly bool, isUBT bool) *triedb.Database {
config := &triedb.Config{
Preimages: preimage,
IsVerkle: isVerkle,
IsUBT: isUBT,
}
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
if err != nil {

View file

@ -155,7 +155,9 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
}
cfg.historyPruneBlock = new(uint64)
*cfg.historyPruneBlock = history.PrunePoints[params.MainnetGenesisHash].BlockNumber
if p, err := history.NewPolicy(history.KeepPostMerge, params.MainnetGenesisHash); err == nil {
*cfg.historyPruneBlock = p.Target.BlockNumber
}
case ctx.Bool(testSepoliaFlag.Name):
cfg.fsys = builtinTestFiles
if ctx.IsSet(filterQueryFileFlag.Name) {
@ -180,7 +182,9 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
}
cfg.historyPruneBlock = new(uint64)
*cfg.historyPruneBlock = history.PrunePoints[params.SepoliaGenesisHash].BlockNumber
if p, err := history.NewPolicy(history.KeepPostMerge, params.SepoliaGenesisHash); err == nil {
*cfg.historyPruneBlock = p.Target.BlockNumber
}
default:
cfg.fsys = os.DirFS(".")
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)

View file

@ -204,6 +204,10 @@ func (b *Big) ToInt() *big.Int {
return (*big.Int)(b)
}
func (b *Big) ToUint256() (*uint256.Int, bool) {
return uint256.FromBig((*big.Int)(b))
}
// String returns the hex encoding of b.
func (b *Big) String() string {
return EncodeBig(b.ToInt())

View file

@ -17,7 +17,6 @@
package beacon
import (
"context"
"errors"
"fmt"
"math/big"
@ -26,13 +25,10 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/internal/telemetry"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
)
@ -275,12 +271,22 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
}
}
// Verify the existence / non-existence of Amsterdam-specific header fields
amsterdam := chain.Config().IsAmsterdam(header.Number, header.Time)
if amsterdam && header.SlotNumber == nil {
return errors.New("header is missing slotNumber")
}
if !amsterdam && header.SlotNumber != nil {
return fmt.Errorf("invalid slotNumber: have %d, expected nil", *header.SlotNumber)
if amsterdam {
if header.BlockAccessListHash == nil {
return errors.New("header is missing block access list hash")
}
if header.SlotNumber == nil {
return errors.New("header is missing slotNumber")
}
} else {
if header.BlockAccessListHash != nil {
return fmt.Errorf("invalid block access list hash: have %x, expected nil", *header.BlockAccessListHash)
}
if header.SlotNumber != nil {
return fmt.Errorf("invalid slotNumber: have %d, expected nil", *header.SlotNumber)
}
}
return nil
}
@ -351,48 +357,6 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
// No block reward which is issued by consensus layer instead.
}
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
// assembling the block.
func (beacon *Beacon) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (result *types.Block, err error) {
ctx, _, spanEnd := telemetry.StartSpan(ctx, "consensus.beacon.FinalizeAndAssemble",
telemetry.Int64Attribute("block.number", int64(header.Number.Uint64())),
telemetry.Int64Attribute("txs.count", int64(len(body.Transactions))),
telemetry.Int64Attribute("withdrawals.count", int64(len(body.Withdrawals))),
)
defer spanEnd(&err)
if !beacon.IsPoSHeader(header) {
block, delegateErr := beacon.ethone.FinalizeAndAssemble(ctx, chain, header, state, body, receipts)
return block, delegateErr
}
shanghai := chain.Config().IsShanghai(header.Number, header.Time)
if shanghai {
// All blocks after Shanghai must include a withdrawals root.
if body.Withdrawals == nil {
body.Withdrawals = make([]*types.Withdrawal, 0)
}
} else {
if len(body.Withdrawals) > 0 {
return nil, errors.New("withdrawals set before Shanghai activation")
}
}
// Finalize and assemble the block.
_, _, finalizeSpanEnd := telemetry.StartSpan(ctx, "consensus.beacon.Finalize")
beacon.Finalize(chain, header, state, body)
finalizeSpanEnd(nil)
// Assign the final state root to header.
_, _, rootSpanEnd := telemetry.StartSpan(ctx, "consensus.beacon.IntermediateRoot")
header.Root = state.IntermediateRoot(true)
rootSpanEnd(nil)
// Assemble the final block.
_, _, blockSpanEnd := telemetry.StartSpan(ctx, "consensus.beacon.NewBlock")
block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
blockSpanEnd(nil)
return block, nil
}
// Seal generates a new sealing request for the given input block and pushes
// the result into the given channel.
//

View file

@ -19,7 +19,6 @@ package clique
import (
"bytes"
"context"
"errors"
"fmt"
"io"
@ -34,7 +33,6 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
@ -43,7 +41,6 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
const (
@ -580,22 +577,6 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
// No block rewards in PoA, so the state remains as is
}
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block.
func (c *Clique) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
if len(body.Withdrawals) > 0 {
return nil, errors.New("clique does not support withdrawals")
}
// Finalize block
c.Finalize(chain, header, state, body)
// Assign the final state root to header.
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// Assemble and return the final block for sealing.
return types.NewBlock(header, &types.Body{Transactions: body.Transactions}, receipts, trie.NewStackTrie(nil)), nil
}
// Authorize injects a private key into the consensus engine to mint new blocks
// with.
func (c *Clique) Authorize(signer common.Address) {

View file

@ -18,11 +18,9 @@
package consensus
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
@ -88,13 +86,6 @@ type Engine interface {
// that happen at finalization (e.g. block rewards).
Finalize(chain ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body)
// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block
// rewards or process withdrawals) and assembles the final block.
//
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
FinalizeAndAssemble(ctx context.Context, chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error)
// Seal generates a new sealing request for the given input block and pushes
// the result into the given channel.
//

View file

@ -17,7 +17,6 @@
package ethash
import (
"context"
"errors"
"fmt"
"math/big"
@ -28,14 +27,12 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
)
@ -512,22 +509,6 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
accumulateRewards(chain.Config(), state, header, body.Uncles)
}
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// uncle rewards, setting the final state and assembling the block.
func (ethash *Ethash) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
if len(body.Withdrawals) > 0 {
return nil, errors.New("ethash does not support withdrawals")
}
// Finalize block
ethash.Finalize(chain, header, state, body)
// Assign the final state root to header.
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// Header seems complete, assemble into a block and return
return types.NewBlock(header, &types.Body{Transactions: body.Transactions, Uncles: body.Uncles}, receipts, trie.NewStackTrie(nil)), nil
}
// SealHash returns the hash of a block prior to it being sealed.
func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
hasher := keccak.NewLegacyKeccak256()

View file

@ -89,7 +89,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
data := make([]byte, nbytes)
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false)
cost, _ := IntrinsicGas(data, nil, nil, false, false, false, false, false)
signer := gen.Signer()
gasPrice := big.NewInt(0)
if gen.header.BaseFee != nil {
@ -99,7 +99,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
Nonce: gen.TxNonce(benchRootAddr),
To: &toaddr,
Value: big.NewInt(1),
Gas: gas,
Gas: cost.RegularGas,
Data: data,
GasPrice: gasPrice,
})

View file

@ -36,7 +36,7 @@ import (
)
var (
testVerkleChainConfig = &params.ChainConfig{
testUBTChainConfig = &params.ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
@ -51,30 +51,30 @@ var (
LondonBlock: big.NewInt(0),
Ethash: new(params.EthashConfig),
ShanghaiTime: u64(0),
VerkleTime: u64(0),
UBTTime: u64(0),
TerminalTotalDifficulty: common.Big0,
EnableVerkleAtGenesis: true,
EnableUBTAtGenesis: true,
BlobScheduleConfig: &params.BlobScheduleConfig{
Verkle: params.DefaultPragueBlobConfig,
UBT: params.DefaultPragueBlobConfig,
},
}
)
func TestProcessVerkle(t *testing.T) {
func TestProcessUBT(t *testing.T) {
var (
code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, true, true, true)
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, true, true, true, false)
// A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness
// will not contain that copied data.
// Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985
codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`)
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true)
signer = types.LatestSigner(testVerkleChainConfig)
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true, false)
signer = types.LatestSigner(testUBTChainConfig)
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
gspec = &Genesis{
Config: testVerkleChainConfig,
Config: testUBTChainConfig,
Alloc: GenesisAlloc{
coinbase: {
Balance: big.NewInt(1000000000000000000), // 1 ether
@ -87,21 +87,22 @@ func TestProcessVerkle(t *testing.T) {
},
}
)
// Verkle trees use the snapshot, which must be enabled before the
// UBTs use the snapshot, which must be enabled before the
// data is saved into the tree+database.
// genesis := gspec.MustCommit(bcdb, triedb)
options := DefaultConfig().WithStateScheme(rawdb.PathScheme)
options.SnapshotLimit = 0
options.BinTrieGroupDepth = triedb.DefaultBinTrieGroupDepth
blockchain, _ := NewBlockChain(bcdb, gspec, beacon.New(ethash.NewFaker()), options)
defer blockchain.Stop()
txCost1 := params.TxGas
txCost2 := params.TxGas
contractCreationCost := intrinsicContractCreationGas +
contractCreationCost := intrinsicContractCreationGas.RegularGas +
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */
739 /* execution costs */
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas +
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas.RegularGas +
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */
@ -188,7 +189,7 @@ func TestProcessParentBlockHash(t *testing.T) {
// block 1 parent hash is 0x0100....
// block 2 parent hash is 0x0200....
// etc
checkBlockHashes := func(statedb *state.StateDB, isVerkle bool) {
checkBlockHashes := func(statedb *state.StateDB, isUBT bool) {
statedb.SetNonce(params.HistoryStorageAddress, 1, tracing.NonceChangeUnspecified)
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode, tracing.CodeChangeUnspecified)
// Process n blocks, from 1 .. num
@ -196,8 +197,8 @@ func TestProcessParentBlockHash(t *testing.T) {
for i := 1; i <= num; i++ {
header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)}
chainConfig := params.MergedTestChainConfig
if isVerkle {
chainConfig = testVerkleChainConfig
if isUBT {
chainConfig = testUBTChainConfig
}
vmContext := NewEVMBlockContext(header, nil, new(common.Address))
evm := vm.NewEVM(vmContext, statedb, chainConfig, vm.Config{})
@ -205,9 +206,9 @@ func TestProcessParentBlockHash(t *testing.T) {
}
// Read block hashes for block 0 .. num-1
for i := 0; i < num; i++ {
have, want := getContractStoredBlockHash(statedb, uint64(i), isVerkle), common.Hash{byte(i + 1)}
have, want := getContractStoredBlockHash(statedb, uint64(i), isUBT), common.Hash{byte(i + 1)}
if have != want {
t.Errorf("block %d, verkle=%v, have parent hash %v, want %v", i, isVerkle, have, want)
t.Errorf("block %d, verkle=%v, have parent hash %v, want %v", i, isUBT, have, want)
}
}
}
@ -215,22 +216,23 @@ func TestProcessParentBlockHash(t *testing.T) {
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
checkBlockHashes(statedb, false)
})
t.Run("Verkle", func(t *testing.T) {
t.Run("UBT", func(t *testing.T) {
db := rawdb.NewMemoryDatabase()
cacheConfig := DefaultConfig().WithStateScheme(rawdb.PathScheme)
cacheConfig.BinTrieGroupDepth = triedb.DefaultBinTrieGroupDepth
cacheConfig.SnapshotLimit = 0
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil))
statedb, _ := state.New(types.EmptyBinaryHash, state.NewDatabase(triedb, nil))
checkBlockHashes(statedb, true)
})
}
// getContractStoredBlockHash is a utility method which reads the stored parent blockhash for block 'number'
func getContractStoredBlockHash(statedb *state.StateDB, number uint64, isVerkle bool) common.Hash {
func getContractStoredBlockHash(statedb *state.StateDB, number uint64, isUBT bool) common.Hash {
ringIndex := number % params.HistoryServeWindow
var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex)
if isVerkle {
if isUBT {
return statedb.GetState(params.HistoryStorageAddress, key)
}
return statedb.GetState(params.HistoryStorageAddress, key)

View file

@ -170,9 +170,10 @@ type BlockChainConfig struct {
TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed
TrieJournalDirectory string // Directory path to the journal used for persisting trie data across node restarts
Preimages bool // Whether to store preimage of trie key to the disk
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
ArchiveMode bool // Whether to enable the archive mode
Preimages bool // Whether to store preimage of trie key to the disk
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
ArchiveMode bool // Whether to enable the archive mode
BinTrieGroupDepth int // Number of levels per serialized group in binary trie (1-8)
// Number of blocks from the chain head for which state histories are retained.
// If set to 0, all state histories across the entire chain will be retained;
@ -194,9 +195,8 @@ type BlockChainConfig struct {
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
// This defines the cutoff block for history expiry.
// Blocks before this number may be unavailable in the chain database.
ChainHistoryMode history.HistoryMode
// HistoryPolicy defines the chain history pruning intent.
HistoryPolicy history.HistoryPolicy
// Misc options
NoPrefetch bool // Whether to disable heuristic state prefetching when processing blocks
@ -227,13 +227,13 @@ type BlockChainConfig struct {
// Note the returned object is safe to modify!
func DefaultConfig() *BlockChainConfig {
return &BlockChainConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
StateScheme: rawdb.HashScheme,
SnapshotLimit: 256,
SnapshotWait: true,
ChainHistoryMode: history.KeepAll,
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
StateScheme: rawdb.HashScheme,
SnapshotLimit: 256,
SnapshotWait: true,
HistoryPolicy: history.HistoryPolicy{Mode: history.KeepAll},
// Transaction indexing is disabled by default.
// This is appropriate for most unit tests.
TxLookupLimit: -1,
@ -259,10 +259,11 @@ func (cfg BlockChainConfig) WithNoAsyncFlush(on bool) *BlockChainConfig {
}
// triedbConfig derives the configures for trie database.
func (cfg *BlockChainConfig) triedbConfig(isVerkle bool) *triedb.Config {
func (cfg *BlockChainConfig) triedbConfig(isUBT bool) *triedb.Config {
config := &triedb.Config{
Preimages: cfg.Preimages,
IsVerkle: isVerkle,
Preimages: cfg.Preimages,
IsUBT: isUBT,
BinTrieGroupDepth: cfg.BinTrieGroupDepth,
}
if cfg.StateScheme == rawdb.HashScheme {
config.HashDB = &hashdb.Config{
@ -379,7 +380,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
}
// Open trie database with provided config
enableVerkle, err := EnableVerkleAtGenesis(db, genesis)
enableVerkle, err := EnableUBTAtGenesis(db, genesis)
if err != nil {
return nil, err
}
@ -715,82 +716,44 @@ func (bc *BlockChain) loadLastState() error {
// initializeHistoryPruning sets bc.historyPrunePoint.
func (bc *BlockChain) initializeHistoryPruning(latest uint64) error {
var (
freezerTail, _ = bc.db.Tail()
genesisHash = bc.genesisBlock.Hash()
mergePoint = history.MergePrunePoints[genesisHash]
praguePoint = history.PraguePrunePoints[genesisHash]
)
switch bc.cfg.ChainHistoryMode {
case history.KeepAll:
if freezerTail == 0 {
return nil
}
// The database was pruned somehow, so we need to figure out if it's a known
// configuration or an error.
if mergePoint != nil && freezerTail == mergePoint.BlockNumber {
bc.historyPrunePoint.Store(mergePoint)
return nil
}
if praguePoint != nil && freezerTail == praguePoint.BlockNumber {
bc.historyPrunePoint.Store(praguePoint)
return nil
}
log.Error("Chain history database is pruned with unknown configuration", "tail", freezerTail)
return errors.New("unexpected database tail")
freezerTail, _ := bc.db.Tail()
policy := bc.cfg.HistoryPolicy
case history.KeepPostMerge:
if mergePoint == nil {
return errors.New("history pruning requested for unknown network")
switch policy.Mode {
case history.KeepAll:
if freezerTail > 0 {
// Database was pruned externally. Record the actual state.
log.Warn("Chain history database is pruned", "tail", freezerTail, "mode", policy.Mode)
bc.historyPrunePoint.Store(&history.PrunePoint{
BlockNumber: freezerTail,
BlockHash: bc.GetCanonicalHash(freezerTail),
})
}
if freezerTail == 0 && latest != 0 {
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned.", bc.cfg.ChainHistoryMode.String()))
log.Error("Run 'geth prune-history --history.chain postmerge' to prune pre-merge history.")
return errors.New("history pruning requested via configuration")
}
// Check if DB is pruned further than requested (to Prague).
if praguePoint != nil && freezerTail == praguePoint.BlockNumber {
log.Error("Chain history database is pruned to Prague block, but postmerge mode was requested.")
log.Error("History cannot be unpruned. To restore history, use 'geth import-history'.")
log.Error("If you intended to keep post-Prague history, use '--history.chain postprague' instead.")
return errors.New("database pruned beyond requested history mode")
}
if freezerTail > 0 && freezerTail != mergePoint.BlockNumber {
return errors.New("chain history database pruned to unknown block")
}
bc.historyPrunePoint.Store(mergePoint)
return nil
case history.KeepPostPrague:
if praguePoint == nil {
return errors.New("history pruning requested for unknown network")
}
// Check if already at the prague prune point.
if freezerTail == praguePoint.BlockNumber {
bc.historyPrunePoint.Store(praguePoint)
case history.KeepPostMerge, history.KeepPostPrague:
target := policy.Target
// Already at the target.
if freezerTail == target.BlockNumber {
bc.historyPrunePoint.Store(target)
return nil
}
// Check if database needs pruning.
if latest != 0 {
if freezerTail == 0 {
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned.", bc.cfg.ChainHistoryMode.String()))
log.Error("Run 'geth prune-history --history.chain postprague' to prune pre-Prague history.")
return errors.New("history pruning requested via configuration")
}
if mergePoint != nil && freezerTail == mergePoint.BlockNumber {
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is only pruned to merge block.", bc.cfg.ChainHistoryMode.String()))
log.Error("Run 'geth prune-history --history.chain postprague' to prune pre-Prague history.")
return errors.New("history pruning requested via configuration")
}
log.Error("Chain history database is pruned to unknown block", "tail", freezerTail)
return errors.New("unexpected database tail")
// Database is pruned beyond the target.
if freezerTail > target.BlockNumber {
return fmt.Errorf("database pruned beyond requested history (tail=%d, target=%d)", freezerTail, target.BlockNumber)
}
// Fresh database (latest == 0), will sync from prague point.
bc.historyPrunePoint.Store(praguePoint)
// Database needs pruning (freezerTail < target).
if latest != 0 {
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned to the target block.", policy.Mode.String()))
log.Error(fmt.Sprintf("Run 'geth prune-history --history.chain %s' to prune history.", policy.Mode.String()))
return errors.New("history pruning required")
}
// Fresh database (latest == 0), will sync from target point.
bc.historyPrunePoint.Store(target)
return nil
default:
return fmt.Errorf("invalid history mode: %d", bc.cfg.ChainHistoryMode)
return fmt.Errorf("invalid history mode: %d", policy.Mode)
}
}
@ -1223,6 +1186,7 @@ func (bc *BlockChain) SnapSyncComplete(hash common.Hash) error {
}
// If all checks out, manually set the head block.
rawdb.WriteHeadBlockHash(bc.db, hash)
bc.currentBlock.Store(block.Header())
headBlockGauge.Update(int64(block.NumberU64()))
@ -2155,11 +2119,29 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
startTime = time.Now()
statedb *state.StateDB
interrupt atomic.Bool
sdb = state.NewDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps)
sdb state.Database
)
defer interrupt.Store(true) // terminate the prefetch at the end
if bc.cfg.NoPrefetch {
if bc.chainConfig.IsUBT(block.Number(), block.Time()) {
sdb = state.NewUBTDatabase(bc.triedb, bc.codedb)
} else {
sdb = state.NewMPTDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps)
}
// If prefetching is enabled, run that against the current state to pre-cache
// transactions and probabilistically some of the account/storage trie nodes.
//
// Note: the main processor and prefetcher share the same reader with a local
// cache for mitigating the overhead of state access.
type prewarmReader interface {
// ReadersWithCacheStats creates a pair of state readers that share the
// same underlying state reader and internal state cache, while maintaining
// separate statistics respectively.
ReadersWithCacheStats(stateRoot common.Hash) (state.Reader, state.Reader, error)
}
warmer, ok := sdb.(prewarmReader)
if bc.cfg.NoPrefetch || !ok {
statedb, err = state.New(parentRoot, sdb)
if err != nil {
return nil, err
@ -2170,7 +2152,7 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
//
// Note: the main processor and prefetcher share the same reader with a local
// cache for mitigating the overhead of state access.
prefetch, process, err := sdb.ReadersWithCacheStats(parentRoot)
prefetch, process, err := warmer.ReadersWithCacheStats(parentRoot)
if err != nil {
return nil, err
}
@ -2209,24 +2191,18 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
// If we are past Byzantium, enable prefetching to pull in trie node paths
// while processing transactions. Before Byzantium the prefetcher is mostly
// useless due to the intermediate root hashing after each transaction.
var (
witness *stateless.Witness
witnessStats *stateless.WitnessStats
)
var witness *stateless.Witness
if bc.chainConfig.IsByzantium(block.Number()) {
// Generate witnesses either if we're self-testing, or if it's the
// only block being inserted. A bit crude, but witnesses are huge,
// so we refuse to make an entire chain of them.
if config.StatelessSelfValidation || config.MakeWitness {
witness, err = stateless.NewWitness(block.Header(), bc)
witness, err = stateless.NewWitness(block.Header(), bc, config.EnableWitnessStats)
if err != nil {
return nil, err
}
if config.EnableWitnessStats {
witnessStats = stateless.NewWitnessStats()
}
}
statedb.StartPrefetcher("chain", witness, witnessStats)
statedb.StartPrefetcher("chain", witness)
defer statedb.StopPrefetcher()
}
@ -2345,8 +2321,8 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.DatabaseCommits
}
// Report the collected witness statistics
if witnessStats != nil {
witnessStats.ReportMetrics(block.NumberU64())
if witness != nil {
witness.ReportMetrics(block.NumberU64())
}
elapsed := time.Since(startTime) + 1 // prevent zero division
stats.TotalTime = elapsed
@ -2621,13 +2597,19 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
blockReorgAddMeter.Mark(int64(len(newChain)))
} else {
// len(newChain) == 0 && len(oldChain) > 0
// rewind the canonical chain to a lower point.
log.Error("Impossible reorg, please file an issue", "oldnum", oldHead.Number, "oldhash", oldHead.Hash(), "oldblocks", len(oldChain), "newnum", newHead.Number, "newhash", newHead.Hash(), "newblocks", len(newChain))
// Rewind the canonical chain to a lower point. In EPBs we can reorg to
// a parent of the head within 32 blocks.
if len(oldChain) > 32 {
log.Error("Impossible reorg, please file an issue", "oldnum", oldHead.Number, "oldhash", oldHead.Hash(), "oldblocks", len(oldChain))
} else {
log.Info("Shorten chain", "del", len(oldChain), "number", oldHead.Number, "hash", oldHead.Hash())
}
}
// Acquire the tx-lookup lock before mutation. This step is essential
// as the txlookups should be changed atomically, and all subsequent
// reads should be blocked until the mutation is complete.
bc.txLookupLock.Lock()
defer bc.txLookupLock.Unlock()
// Reorg can be executed, start reducing the chain's old blocks and appending
// the new blocks
@ -2730,9 +2712,6 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
// Reset the tx lookup cache to clear stale txlookup cache.
bc.txLookupCache.Purge()
// Release the tx-lookup lock after mutation.
bc.txLookupLock.Unlock()
return nil
}

View file

@ -296,6 +296,14 @@ func (bc *BlockChain) GetReceiptsRLP(hash common.Hash) rlp.RawValue {
return rawdb.ReadReceiptsRLP(bc.db, hash, number)
}
func (bc *BlockChain) GetAccessListRLP(hash common.Hash) rlp.RawValue {
number, ok := rawdb.ReadHeaderNumber(bc.db, hash)
if !ok {
return nil
}
return rawdb.ReadAccessListRLP(bc.db, hash, number)
}
// GetUnclesInChain retrieves all the uncles from a given block backwards until
// a specific distance is reached.
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
@ -408,19 +416,42 @@ func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) []byte {
// State returns a new mutable state based on the current HEAD block.
func (bc *BlockChain) State() (*state.StateDB, error) {
return bc.StateAt(bc.CurrentBlock().Root)
return bc.StateAt(bc.CurrentBlock())
}
// StateAt returns a new mutable state based on a particular point in time.
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
return state.New(root, state.NewDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps))
func (bc *BlockChain) StateAt(header *types.Header) (*state.StateDB, error) {
if bc.chainConfig.IsUBT(header.Number, header.Time) {
return state.New(header.Root, state.NewUBTDatabase(bc.triedb, bc.codedb))
}
return state.New(header.Root, state.NewMPTDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps))
}
// HistoricState returns a historic state specified by the given root.
// StateAtForkBoundary returns a new mutable state based on the parent state
// and the given header, handling the transition across the UBT fork.
func (bc *BlockChain) StateAtForkBoundary(parent *types.Header, header *types.Header) (*state.StateDB, error) {
// The parent is already in the UBT fork.
if bc.chainConfig.IsUBT(parent.Number, parent.Time) {
return state.New(parent.Root, state.NewUBTDatabase(bc.triedb, bc.codedb))
}
// The current block is the first block in the UBT fork
// (i.e., the parent is the last MPT block).
if bc.chainConfig.IsUBT(header.Number, header.Time) {
// TODO(gballet): register chain context if needed
return state.New(parent.Root, state.NewUBTDatabase(bc.triedb, bc.codedb))
}
// Both the parent and current block are in the MPT fork.
return state.New(parent.Root, state.NewMPTDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps))
}
// HistoricState returns a historic state specified by the given header.
// Live states are not available and won't be served, please use `State`
// or `StateAt` instead.
func (bc *BlockChain) HistoricState(root common.Hash) (*state.StateDB, error) {
return state.New(root, state.NewHistoricDatabase(bc.triedb, bc.codedb))
func (bc *BlockChain) HistoricState(header *types.Header) (*state.StateDB, error) {
if bc.chainConfig.IsUBT(header.Number, header.Time) {
return nil, errors.New("historical state over ubt is not yet supported")
}
return state.New(header.Root, state.NewHistoricDatabase(bc.triedb, bc.codedb))
}
// Config retrieves the chain's fork configuration.
@ -468,7 +499,7 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
}
// StateIndexProgress returns the historical state indexing progress.
func (bc *BlockChain) StateIndexProgress() (uint64, error) {
func (bc *BlockChain) StateIndexProgress() (uint64, uint64, error) {
return bc.triedb.IndexProgress()
}

View file

@ -36,7 +36,6 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
@ -3891,7 +3890,7 @@ func TestTransientStorageReset(t *testing.T) {
t.Fatalf("failed to insert into chain: %v", err)
}
// Check the storage
state, err := chain.StateAt(chain.CurrentHeader().Root)
state, err := chain.StateAt(chain.CurrentHeader())
if err != nil {
t.Fatalf("Failed to load state %v", err)
}
@ -4337,26 +4336,13 @@ func TestInsertChainWithCutoff(t *testing.T) {
func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64, genesis *Genesis, blocks []*types.Block, receipts []types.Receipts) {
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true)))
// Add a known pruning point for the duration of the test.
ghash := genesis.ToBlock().Hash()
cutoffBlock := blocks[cutoff-1]
history.PrunePoints[ghash] = &history.PrunePoint{
BlockNumber: cutoffBlock.NumberU64(),
BlockHash: cutoffBlock.Hash(),
}
defer func() {
delete(history.PrunePoints, ghash)
}()
// Enable pruning in cache config.
config := DefaultConfig().WithStateScheme(rawdb.PathScheme)
config.ChainHistoryMode = history.KeepPostMerge
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
defer db.Close()
options := DefaultConfig().WithStateScheme(rawdb.PathScheme)
chain, _ := NewBlockChain(db, genesis, beacon.New(ethash.NewFaker()), options)
chain, _ := NewBlockChain(db, genesis, beacon.New(ethash.NewFaker()), DefaultConfig().WithStateScheme(rawdb.PathScheme))
defer chain.Stop()
var (

View file

@ -117,7 +117,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
blockContext = NewEVMBlockContext(b.header, bc, &b.header.Coinbase)
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
)
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
b.statedb.SetTxContext(tx.Hash(), len(b.txs), uint32(len(b.txs)+1))
receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx)
if err != nil {
panic(err)
@ -126,7 +126,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
// Merge the tx-local access event into the "block-local" one, in order to collect
// all values, so that the witness can be built.
if b.statedb.Database().TrieDB().IsVerkle() {
if b.statedb.Database().Type().Is(state.TypeUBT) {
b.statedb.AccessEvents().Merge(evm.AccessEvents)
}
b.txs = append(b.txs, tx)
@ -315,28 +315,17 @@ func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte) {
// off the statedb before executing the system calls.
statedb = statedb.Copy()
}
var blockLogs []*types.Log
for _, r := range b.receipts {
blockLogs = append(blockLogs, r.Logs...)
}
// TODO use the shared EVM throughout the entire generation cycle
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{})
if b.cm.config.IsPrague(b.header.Number, b.header.Time) {
requests = [][]byte{}
// EIP-6110 deposits
var blockLogs []*types.Log
for _, r := range b.receipts {
blockLogs = append(blockLogs, r.Logs...)
}
if err := ParseDepositLogs(&requests, blockLogs, b.cm.config); err != nil {
panic(fmt.Sprintf("failed to parse deposit log: %v", err))
}
// create EVM for system calls
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{})
// EIP-7002
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
panic(fmt.Sprintf("could not process withdrawal requests: %v", err))
}
// EIP-7251
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
panic(fmt.Sprintf("could not process consolidation requests: %v", err))
}
requests, err := PostExecution(context.Background(), b.cm.config, b.header.Number, b.header.Time, blockLogs, evm, uint32(len(b.txs)+1))
if err != nil {
panic(fmt.Sprintf("failed to run post-execution: %v", err))
}
return requests
}
@ -392,7 +381,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
misc.ApplyDAOHardFork(statedb)
}
if config.IsPrague(b.header.Number, b.header.Time) || config.IsVerkle(b.header.Number, b.header.Time) {
if config.IsPrague(b.header.Number, b.header.Time) || config.IsUBT(b.header.Number, b.header.Time) {
// EIP-2935
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
blockContext.Random = &common.Hash{} // enable post-merge instruction set
@ -411,11 +400,22 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
b.header.RequestsHash = &reqHash
}
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals}
block, err := b.engine.FinalizeAndAssemble(context.Background(), cm, b.header, statedb, &body, b.receipts)
if err != nil {
panic(err)
body := types.Body{
Transactions: b.txs,
Uncles: b.uncles,
Withdrawals: b.withdrawals,
}
if !config.IsShanghai(b.header.Number, b.header.Time) {
if body.Withdrawals != nil {
panic("unexpected withdrawal before shanghai")
}
} else {
if body.Withdrawals == nil {
body.Withdrawals = make([]*types.Withdrawal, 0)
}
}
// Assemble the block for delivery.
block := AssembleBlock(b.engine, cm, b.header, statedb, &body, b.receipts)
// Write state changes to db
root, err := statedb.Commit(b.header.Number.Uint64(), config.IsEIP158(b.header.Number), config.IsCancun(b.header.Number, b.header.Time))
@ -430,8 +430,8 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
// Forcibly use hash-based state scheme for retaining all nodes in disk.
var triedbConfig *triedb.Config = triedb.HashDefaults
if config.IsVerkle(config.ChainID, 0) {
triedbConfig = triedb.VerkleDefaults
if config.IsUBT(config.ChainID, 0) {
triedbConfig = triedb.UBTDefaults
}
triedb := triedb.NewDatabase(db, triedbConfig)
defer triedb.Close()
@ -479,8 +479,8 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) {
db := rawdb.NewMemoryDatabase()
var triedbConfig *triedb.Config = triedb.HashDefaults
if genesis.Config != nil && genesis.Config.IsVerkle(genesis.Config.ChainID, 0) {
triedbConfig = triedb.VerkleDefaults
if genesis.Config != nil && genesis.Config.IsUBT(genesis.Config.ChainID, 0) {
triedbConfig = triedb.UBTDefaults
}
genesisTriedb := triedb.NewDatabase(db, triedbConfig)
block, err := genesis.Commit(db, genesisTriedb, nil)

View file

@ -66,10 +66,6 @@ var (
// have enough funds for transfer(topmost call only).
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
// ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger
// than init code size limit.
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
// ErrInsufficientBalanceWitness is returned if the transaction sender has enough
// funds to cover the transfer, but not enough to pay for witness access/modification
// costs for the transaction

View file

@ -0,0 +1,169 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"encoding/binary"
"math/big"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
var ethTransferTestCode = common.FromHex("6080604052600436106100345760003560e01c8063574ffc311461003957806366e41cb714610090578063f8a8fd6d1461009a575b600080fd5b34801561004557600080fd5b5061004e6100a4565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100986100ac565b005b6100a26100f5565b005b63deadbeef81565b7f38e80b5c85ba49b7280ccc8f22548faa62ae30d5a008a1b168fba5f47f5d1ee560405160405180910390a1631234567873ffffffffffffffffffffffffffffffffffffffff16ff5b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405160405180910390a163deadbeef73ffffffffffffffffffffffffffffffffffffffff166002348161014657fe5b046040516024016040516020818303038152906040527f66e41cb7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b602083106101fd57805182526020820191506020810190506020830392506101da565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d806000811461025f576040519150601f19603f3d011682016040523d82523d6000602084013e610264565b606091505b50505056fea265627a7a723158202cce817a434785d8560c200762f972d453ccd30694481be7545f9035a512826364736f6c63430005100032")
/*
pragma solidity >=0.4.22 <0.6.0;
contract TestLogs {
address public constant target_contract = 0x00000000000000000000000000000000DeaDBeef;
address payable constant selfdestruct_addr = 0x0000000000000000000000000000000012345678;
event Response(bool success, bytes data);
event TestEvent();
event TestEvent2();
function test() public payable {
emit TestEvent();
target_contract.call.value(msg.value/2)(abi.encodeWithSignature("test2()"));
}
function test2() public payable {
emit TestEvent2();
selfdestruct(selfdestruct_addr);
}
}
*/
// TestEthTransferLogs tests EIP-7708 ETH transfer log output by simulating a
// scenario including transaction, CALL and SELFDESTRUCT value transfers, and
// also "ordinary" logs emitted. The same scenario is also tested with no value
// transferred.
func TestEthTransferLogs(t *testing.T) {
testEthTransferLogs(t, 1_000_000_000)
testEthTransferLogs(t, 0)
}
func testEthTransferLogs(t *testing.T, value uint64) {
var (
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = common.HexToAddress("cafebabe") // caller
addr3 = common.HexToAddress("deadbeef") // callee
addr4 = common.HexToAddress("12345678") // selfdestruct target
testEvent = crypto.Keccak256Hash([]byte("TestEvent()"))
testEvent2 = crypto.Keccak256Hash([]byte("TestEvent2()"))
config = *params.MergedTestChainConfig
signer = types.LatestSigner(&config)
engine = beacon.New(ethash.NewFaker())
)
//TODO remove this hacky config initialization when final Amsterdam config is available
config.AmsterdamTime = new(uint64)
blobConfig := *config.BlobScheduleConfig
blobConfig.Amsterdam = blobConfig.Osaka
config.BlobScheduleConfig = &blobConfig
gspec := &Genesis{
Config: &config,
Alloc: types.GenesisAlloc{
addr1: {Balance: newGwei(1000000000)},
addr2: {Code: ethTransferTestCode},
addr3: {Code: ethTransferTestCode},
},
}
_, blocks, receipts := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
tx := types.MustSignNewTx(key1, signer, &types.DynamicFeeTx{
ChainID: gspec.Config.ChainID,
Nonce: 0,
To: &addr2,
Gas: 500_000,
GasFeeCap: newGwei(5),
GasTipCap: newGwei(5),
Value: big.NewInt(int64(value)),
Data: common.FromHex("f8a8fd6d"),
})
b.AddTx(tx)
})
blockHash := blocks[0].Hash()
txHash := blocks[0].Transactions()[0].Hash()
addr2hash := func(addr common.Address) (hash common.Hash) {
copy(hash[12:], addr[:])
return
}
u256 := func(amount uint64) []byte {
data := make([]byte, 32)
binary.BigEndian.PutUint64(data[24:], amount)
return data
}
var expLogs = []*types.Log{
{
Address: params.SystemAddress,
Topics: []common.Hash{params.EthTransferLogEvent, addr2hash(addr1), addr2hash(addr2)},
Data: u256(value),
},
{
Address: addr2,
Topics: []common.Hash{testEvent},
Data: nil,
},
{
Address: params.SystemAddress,
Topics: []common.Hash{params.EthTransferLogEvent, addr2hash(addr2), addr2hash(addr3)},
Data: u256(value / 2),
},
{
Address: addr3,
Topics: []common.Hash{testEvent2},
Data: nil,
},
{
Address: params.SystemAddress,
Topics: []common.Hash{params.EthTransferLogEvent, addr2hash(addr3), addr2hash(addr4)},
Data: u256(value / 2),
},
}
if value == 0 {
// no ETH transfer logs expected with zero value
expLogs = []*types.Log{expLogs[1], expLogs[3]}
}
for i, log := range expLogs {
log.BlockNumber = 1
log.BlockHash = blockHash
log.BlockTimestamp = 10
log.TxIndex = 0
log.TxHash = txHash
log.Index = uint(i)
}
if len(expLogs) != len(receipts[0][0].Logs) {
t.Fatalf("Incorrect number of logs (expected: %d, got: %d)", len(expLogs), len(receipts[0][0].Logs))
}
for i, log := range receipts[0][0].Logs {
if !reflect.DeepEqual(expLogs[i], log) {
t.Fatalf("Incorrect log at index %d (expected: %v, got: %v)", i, expLogs[i], log)
}
}
}

View file

@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
@ -86,7 +87,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
func NewEVMTxContext(msg *Message) vm.TxContext {
ctx := vm.TxContext{
Origin: msg.From,
GasPrice: uint256.MustFromBig(msg.GasPrice),
GasPrice: msg.GasPrice,
BlobHashes: msg.BlobHashes,
}
return ctx
@ -138,7 +139,10 @@ func CanTransfer(db vm.StateDB, addr common.Address, amount *uint256.Int) bool {
}
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int) {
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int, rules *params.Rules) {
db.SubBalance(sender, amount, tracing.BalanceChangeTransfer)
db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer)
if rules.IsAmsterdam && !amount.IsZero() && sender != recipient {
db.AddLog(types.EthTransferLog(sender, recipient, amount))
}
}

View file

@ -41,9 +41,7 @@ func TestSingleMatch(t *testing.T) {
t.Fatalf("Invalid length of matches (got %d, expected 1)", len(matches))
}
if matches[0] != lvIndex {
if len(matches) != 1 {
t.Fatalf("Incorrect match returned (got %d, expected %d)", matches[0], lvIndex)
}
t.Fatalf("Incorrect match returned (got %d, expected %d)", matches[0], lvIndex)
}
}
}

View file

@ -129,22 +129,23 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
}
// hashAlloc computes the state root according to the genesis specification.
func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
func hashAlloc(ga *types.GenesisAlloc, isUBT bool) (common.Hash, error) {
// If a genesis-time verkle trie is requested, create a trie config
// with the verkle trie enabled so that the tree can be initialized
// as such.
var config *triedb.Config
if isVerkle {
if isUBT {
config = &triedb.Config{
PathDB: pathdb.Defaults,
IsVerkle: true,
PathDB: pathdb.Defaults,
IsUBT: true,
BinTrieGroupDepth: triedb.UBTDefaults.BinTrieGroupDepth,
}
}
// Create an ephemeral in-memory database for computing hash,
// all the derived states will be discarded to not pollute disk.
emptyRoot := types.EmptyRootHash
if isVerkle {
emptyRoot = types.EmptyVerkleHash
if isUBT {
emptyRoot = types.EmptyBinaryHash
}
db := rawdb.NewMemoryDatabase()
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb.NewDatabase(db, config), nil))
@ -168,8 +169,8 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
// generated states will be persisted into the given database.
func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database, tracer *tracing.Hooks) (common.Hash, error) {
emptyRoot := types.EmptyRootHash
if triedb.IsVerkle() {
emptyRoot = types.EmptyVerkleHash
if triedb.IsUBT() {
emptyRoot = types.EmptyBinaryHash
}
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb, nil))
if err != nil {
@ -276,10 +277,10 @@ func (e *GenesisMismatchError) Error() string {
// ChainOverrides contains the changes to chain config.
type ChainOverrides struct {
OverrideOsaka *uint64
OverrideBPO1 *uint64
OverrideBPO2 *uint64
OverrideVerkle *uint64
OverrideOsaka *uint64
OverrideBPO1 *uint64
OverrideBPO2 *uint64
OverrideUBT *uint64
}
// apply applies the chain overrides on the supplied chain config.
@ -296,8 +297,8 @@ func (o *ChainOverrides) apply(cfg *params.ChainConfig) error {
if o.OverrideBPO2 != nil {
cfg.BPO2Time = o.OverrideBPO2
}
if o.OverrideVerkle != nil {
cfg.VerkleTime = o.OverrideVerkle
if o.OverrideUBT != nil {
cfg.UBTTime = o.OverrideUBT
}
return cfg.CheckConfigForkOrder()
}
@ -469,15 +470,15 @@ func (g *Genesis) chainConfigOrDefault(ghash common.Hash, stored *params.ChainCo
}
}
// IsVerkle indicates whether the state is already stored in a verkle
// IsUBT indicates whether the state is already stored in a verkle
// tree at genesis time.
func (g *Genesis) IsVerkle() bool {
return g.Config.IsVerkleGenesis()
func (g *Genesis) IsUBT() bool {
return g.Config.IsUBTGenesis()
}
// ToBlock returns the genesis block according to genesis specification.
func (g *Genesis) ToBlock() *types.Block {
root, err := hashAlloc(&g.Alloc, g.IsVerkle())
root, err := hashAlloc(&g.Alloc, g.IsUBT())
if err != nil {
panic(err)
}
@ -609,24 +610,24 @@ func (g *Genesis) MustCommit(db ethdb.Database, triedb *triedb.Database) *types.
return block
}
// EnableVerkleAtGenesis indicates whether the verkle fork should be activated
// EnableUBTAtGenesis indicates whether the verkle fork should be activated
// at genesis. This is a temporary solution only for verkle devnet testing, where
// verkle fork is activated at genesis, and the configured activation date has
// already passed.
//
// In production networks (mainnet and public testnets), verkle activation always
// occurs after the genesis block, making this function irrelevant in those cases.
func EnableVerkleAtGenesis(db ethdb.Database, genesis *Genesis) (bool, error) {
func EnableUBTAtGenesis(db ethdb.Database, genesis *Genesis) (bool, error) {
if genesis != nil {
if genesis.Config == nil {
return false, errGenesisNoConfig
}
return genesis.Config.EnableVerkleAtGenesis, nil
return genesis.Config.EnableUBTAtGenesis, nil
}
if ghash := rawdb.ReadCanonicalHash(db, 0); ghash != (common.Hash{}) {
chainCfg := rawdb.ReadChainConfig(db, ghash)
if chainCfg != nil {
return chainCfg.EnableVerkleAtGenesis, nil
return chainCfg.EnableUBTAtGenesis, nil
}
}
return false, nil

View file

@ -261,9 +261,9 @@ func newDbConfig(scheme string) *triedb.Config {
return &triedb.Config{PathDB: &config}
}
func TestVerkleGenesisCommit(t *testing.T) {
var verkleTime uint64 = 0
verkleConfig := &params.ChainConfig{
func TestBinaryGenesisCommit(t *testing.T) {
var ubtTime uint64 = 0
ubtConfig := &params.ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
@ -281,34 +281,34 @@ func TestVerkleGenesisCommit(t *testing.T) {
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
MergeNetsplitBlock: nil,
ShanghaiTime: &verkleTime,
CancunTime: &verkleTime,
PragueTime: &verkleTime,
OsakaTime: &verkleTime,
VerkleTime: &verkleTime,
ShanghaiTime: &ubtTime,
CancunTime: &ubtTime,
PragueTime: &ubtTime,
OsakaTime: &ubtTime,
UBTTime: &ubtTime,
TerminalTotalDifficulty: big.NewInt(0),
EnableVerkleAtGenesis: true,
EnableUBTAtGenesis: true,
Ethash: nil,
Clique: nil,
BlobScheduleConfig: &params.BlobScheduleConfig{
Cancun: params.DefaultCancunBlobConfig,
Prague: params.DefaultPragueBlobConfig,
Osaka: params.DefaultOsakaBlobConfig,
Verkle: params.DefaultPragueBlobConfig,
UBT: params.DefaultPragueBlobConfig,
},
}
genesis := &Genesis{
BaseFee: big.NewInt(params.InitialBaseFee),
Config: verkleConfig,
Timestamp: verkleTime,
Config: ubtConfig,
Timestamp: ubtTime,
Difficulty: big.NewInt(0),
Alloc: types.GenesisAlloc{
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
},
}
expected := common.FromHex("1fd154971d9a386c4ec75fe7138c17efb569bfc2962e46e94a376ba997e3fadc")
expected := common.FromHex("0870fd587c41dc778019de8c5cb3193fe4ef1f417976461952d3712ba39163f5")
got := genesis.ToBlock().Root().Bytes()
if !bytes.Equal(got, expected) {
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
@ -320,17 +320,18 @@ func TestVerkleGenesisCommit(t *testing.T) {
config.NoAsyncFlush = true
triedb := triedb.NewDatabase(db, &triedb.Config{
IsVerkle: true,
PathDB: &config,
IsUBT: true,
PathDB: &config,
BinTrieGroupDepth: triedb.DefaultBinTrieGroupDepth,
})
block := genesis.MustCommit(db, triedb)
if !bytes.Equal(block.Root().Bytes(), expected) {
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, block.Root())
}
// Test that the trie is verkle
if !triedb.IsVerkle() {
t.Fatalf("expected trie to be verkle")
// Test that the trie is a unified binary trie
if !triedb.IsUBT() {
t.Fatalf("expected trie to be a unified binary trie")
}
vdb := rawdb.NewTable(db, string(rawdb.VerklePrefix))
if !rawdb.HasAccountTrieNode(vdb, nil) {

View file

@ -77,57 +77,66 @@ func (m *HistoryMode) UnmarshalText(text []byte) error {
return nil
}
// PrunePoint identifies a specific block for history pruning.
type PrunePoint struct {
BlockNumber uint64
BlockHash common.Hash
}
// MergePrunePoints contains the pre-defined history pruning cutoff blocks for known networks.
// They point to the first post-merge block. Any pruning should truncate *up to* but excluding
// the given block.
var MergePrunePoints = map[common.Hash]*PrunePoint{
// mainnet
params.MainnetGenesisHash: {
BlockNumber: 15537393,
BlockHash: common.HexToHash("0x55b11b918355b1ef9c5db810302ebad0bf2544255b530cdce90674d5887bb286"),
// staticPrunePoints contains the pre-defined history pruning cutoff blocks for
// known networks, keyed by history mode and genesis hash. They point to the first
// block after the respective fork. Any pruning should truncate *up to* but
// excluding the given block.
var staticPrunePoints = map[HistoryMode]map[common.Hash]*PrunePoint{
KeepPostMerge: {
params.MainnetGenesisHash: {
BlockNumber: 15537393,
BlockHash: common.HexToHash("0x55b11b918355b1ef9c5db810302ebad0bf2544255b530cdce90674d5887bb286"),
},
params.SepoliaGenesisHash: {
BlockNumber: 1450409,
BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"),
},
},
// sepolia
params.SepoliaGenesisHash: {
BlockNumber: 1450409,
BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"),
KeepPostPrague: {
params.MainnetGenesisHash: {
BlockNumber: 22431084,
BlockHash: common.HexToHash("0x50c8cab760b2948349c590461b166773c45d8f4858cccf5a43025ab2960152e8"),
},
params.SepoliaGenesisHash: {
BlockNumber: 7836331,
BlockHash: common.HexToHash("0xe6571beb68bf24dbd8a6ba354518996920c55a3f8d8fdca423e391b8ad071f22"),
},
params.HoodiGenesisHash: {
BlockNumber: 60412,
BlockHash: common.HexToHash("0x1562792812ef418eaafc8f1f093d84d9634971e9dd6b0771302eb5b9fd4d2c46"),
},
},
}
// PraguePrunePoints contains the pre-defined history pruning cutoff blocks for the Prague
// (Pectra) upgrade. They point to the first post-Prague block. Any pruning should truncate
// *up to* but excluding the given block.
var PraguePrunePoints = map[common.Hash]*PrunePoint{
// mainnet - first Prague block (May 7, 2025)
params.MainnetGenesisHash: {
BlockNumber: 22431084,
BlockHash: common.HexToHash("0x50c8cab760b2948349c590461b166773c45d8f4858cccf5a43025ab2960152e8"),
},
// sepolia - first Prague block (March 5, 2025)
params.SepoliaGenesisHash: {
BlockNumber: 7836331,
BlockHash: common.HexToHash("0xe6571beb68bf24dbd8a6ba354518996920c55a3f8d8fdca423e391b8ad071f22"),
},
// HistoryPolicy describes the configured history pruning strategy. It captures
// user intent as opposed to the actual DB state.
type HistoryPolicy struct {
Mode HistoryMode
// Static prune point for PostMerge/PostPrague, nil otherwise.
Target *PrunePoint
}
// PrunePoints is an alias for MergePrunePoints for backward compatibility.
// Deprecated: Use GetPrunePoint or MergePrunePoints directly.
var PrunePoints = MergePrunePoints
// GetPrunePoint returns the prune point for the given genesis hash and history mode.
// Returns nil if no prune point is defined for the given combination.
func GetPrunePoint(genesisHash common.Hash, mode HistoryMode) *PrunePoint {
// NewPolicy constructs a HistoryPolicy from the given mode and genesis hash.
func NewPolicy(mode HistoryMode, genesisHash common.Hash) (HistoryPolicy, error) {
switch mode {
case KeepPostMerge:
return MergePrunePoints[genesisHash]
case KeepPostPrague:
return PraguePrunePoints[genesisHash]
case KeepAll:
return HistoryPolicy{Mode: KeepAll}, nil
case KeepPostMerge, KeepPostPrague:
point := staticPrunePoints[mode][genesisHash]
if point == nil {
return HistoryPolicy{}, fmt.Errorf("%s history pruning not available for network %s", mode, genesisHash.Hex())
}
return HistoryPolicy{Mode: mode, Target: point}, nil
default:
return nil
return HistoryPolicy{}, fmt.Errorf("invalid history mode: %d", mode)
}
}

View file

@ -0,0 +1,58 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package history
import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)
func TestNewPolicy(t *testing.T) {
// KeepAll: no target.
p, err := NewPolicy(KeepAll, params.MainnetGenesisHash)
if err != nil {
t.Fatalf("KeepAll: %v", err)
}
if p.Mode != KeepAll || p.Target != nil {
t.Errorf("KeepAll: unexpected policy %+v", p)
}
// PostMerge: resolves known mainnet prune point.
p, err = NewPolicy(KeepPostMerge, params.MainnetGenesisHash)
if err != nil {
t.Fatalf("PostMerge: %v", err)
}
if p.Target == nil || p.Target.BlockNumber != 15537393 {
t.Errorf("PostMerge: unexpected target %+v", p.Target)
}
// PostPrague: resolves known mainnet prune point.
p, err = NewPolicy(KeepPostPrague, params.MainnetGenesisHash)
if err != nil {
t.Fatalf("PostPrague: %v", err)
}
if p.Target == nil || p.Target.BlockNumber != 22431084 {
t.Errorf("PostPrague: unexpected target %+v", p.Target)
}
// PostMerge on unknown network: error.
if _, err = NewPolicy(KeepPostMerge, common.HexToHash("0xdeadbeef")); err == nil {
t.Fatal("PostMerge unknown network: expected error")
}
}

View file

@ -71,7 +71,7 @@ func (ts *TransitionState) Copy() *TransitionState {
// LoadTransitionState retrieves the Verkle transition state associated with
// the given state root hash from the database.
func LoadTransitionState(db ethdb.KeyValueReader, root common.Hash, isVerkle bool) *TransitionState {
func LoadTransitionState(db ethdb.KeyValueReader, root common.Hash, isUBT bool) *TransitionState {
var ts *TransitionState
data, _ := rawdb.ReadVerkleTransitionState(db, root)
@ -97,10 +97,10 @@ func LoadTransitionState(db ethdb.KeyValueReader, root common.Hash, isVerkle boo
// Initialize the first transition state, with the "ended"
// field set to true if the database was created
// as a verkle database.
log.Debug("no transition state found, starting fresh", "verkle", isVerkle)
log.Debug("no transition state found, starting fresh", "verkle", isUBT)
// Start with a fresh state
ts = &TransitionState{Ended: isVerkle}
ts = &TransitionState{Ended: isUBT}
}
return ts
}

View file

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types/bal"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
@ -174,7 +175,9 @@ func WriteFinalizedBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
}
// ReadLastPivotNumber retrieves the number of the last pivot block. If the node
// full synced, the last pivot will always be nil.
// has never attempted snap sync, the last pivot will always be nil. The marker
// is written during snap sync and never cleared, so that a rollback past the
// pivot can re-enable snap sync.
func ReadLastPivotNumber(db ethdb.KeyValueReader) *uint64 {
data, _ := db.Get(lastPivotKey)
if len(data) == 0 {
@ -605,6 +608,55 @@ func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
}
}
// HasAccessList verifies the existence of a block access list for a block.
func HasAccessList(db ethdb.Reader, hash common.Hash, number uint64) bool {
has, _ := db.Has(accessListKey(number, hash))
return has
}
// ReadAccessListRLP retrieves the RLP-encoded block access list for a block from KV.
func ReadAccessListRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
data, _ := db.Get(accessListKey(number, hash))
return data
}
// ReadAccessList retrieves and decodes the block access list for a block.
func ReadAccessList(db ethdb.Reader, hash common.Hash, number uint64) *bal.BlockAccessList {
data := ReadAccessListRLP(db, hash, number)
if len(data) == 0 {
return nil
}
b := new(bal.BlockAccessList)
if err := rlp.DecodeBytes(data, b); err != nil {
log.Error("Invalid BAL RLP", "hash", hash, "err", err)
return nil
}
return b
}
// WriteAccessList RLP-encodes and stores a block access list in the active KV store.
func WriteAccessList(db ethdb.KeyValueWriter, hash common.Hash, number uint64, b *bal.BlockAccessList) {
bytes, err := rlp.EncodeToBytes(b)
if err != nil {
log.Crit("Failed to encode BAL", "err", err)
}
WriteAccessListRLP(db, hash, number, bytes)
}
// WriteAccessListRLP stores a pre-encoded block access list in the active KV store.
func WriteAccessListRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, encoded rlp.RawValue) {
if err := db.Put(accessListKey(number, hash), encoded); err != nil {
log.Crit("Failed to store BAL", "err", err)
}
}
// DeleteAccessList removes a block access list from the active KV store.
func DeleteAccessList(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
if err := db.Delete(accessListKey(number, hash)); err != nil {
log.Crit("Failed to delete BAL", "err", err)
}
}
// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps
// the list of logs. When decoding a stored receipt into this object we
// avoid creating the bloom filter.
@ -659,13 +711,25 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
if body == nil {
return nil
}
return types.NewBlockWithHeader(header).WithBody(*body)
block := types.NewBlockWithHeader(header).WithBody(*body)
// Best-effort assembly of the block access list from the database.
if header.BlockAccessListHash != nil {
al := ReadAccessList(db, hash, number)
block = block.WithAccessListUnsafe(al)
}
return block
}
// WriteBlock serializes a block into the database, header and body separately.
func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
hash, number := block.Hash(), block.NumberU64()
WriteBody(db, hash, number, block.Body())
WriteHeader(db, block.Header())
if accessList := block.AccessList(); accessList != nil {
WriteAccessList(db, hash, number, accessList)
}
}
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.

View file

@ -27,10 +27,12 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types/bal"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
// Tests block header storage and retrieval operations.
@ -899,3 +901,78 @@ func TestHeadersRLPStorage(t *testing.T) {
checkSequence(1, 1) // Only block 1
checkSequence(1, 2) // Genesis + block 1
}
func makeTestBAL(t *testing.T) (rlp.RawValue, *bal.BlockAccessList) {
t.Helper()
cb := bal.NewConstructionBlockAccessList()
addr := common.HexToAddress("0x1111111111111111111111111111111111111111")
cb.AccountRead(addr)
cb.StorageRead(addr, common.BytesToHash([]byte{0x01}))
cb.StorageWrite(0, addr, common.BytesToHash([]byte{0x02}), common.BytesToHash([]byte{0xaa}))
cb.BalanceChange(0, addr, uint256.NewInt(100))
cb.NonceChange(addr, 0, 1)
var buf bytes.Buffer
if err := cb.EncodeRLP(&buf); err != nil {
t.Fatalf("failed to encode BAL: %v", err)
}
encoded := buf.Bytes()
var decoded bal.BlockAccessList
if err := rlp.DecodeBytes(encoded, &decoded); err != nil {
t.Fatalf("failed to decode BAL: %v", err)
}
return encoded, &decoded
}
// TestBALStorage tests write/read/delete of BALs in the KV store.
func TestBALStorage(t *testing.T) {
db := NewMemoryDatabase()
hash := common.BytesToHash([]byte{0x03, 0x14})
number := uint64(42)
// Check that no BAL exists in a new database.
if HasAccessList(db, hash, number) {
t.Fatal("BAL found in new database")
}
if b := ReadAccessList(db, hash, number); b != nil {
t.Fatalf("non existent BAL returned: %v", b)
}
// Write a BAL and verify it can be read back.
encoded, testBAL := makeTestBAL(t)
WriteAccessList(db, hash, number, testBAL)
if !HasAccessList(db, hash, number) {
t.Fatal("HasAccessList returned false after write")
}
if blob := ReadAccessListRLP(db, hash, number); len(blob) == 0 {
t.Fatal("ReadAccessListRLP returned empty after write")
}
if b := ReadAccessList(db, hash, number); b == nil {
t.Fatal("ReadAccessList returned nil after write")
} else if b.Hash() != testBAL.Hash() {
t.Fatalf("BAL hash mismatch: got %x, want %x", b.Hash(), testBAL.Hash())
}
// Also test WriteAccessListRLP with pre-encoded data.
hash2 := common.BytesToHash([]byte{0x03, 0x15})
WriteAccessListRLP(db, hash2, number, encoded)
if b := ReadAccessList(db, hash2, number); b == nil {
t.Fatal("ReadAccessList returned nil after WriteAccessListRLP")
} else if b.Hash() != testBAL.Hash() {
t.Fatalf("BAL hash mismatch after WriteAccessListRLP: got %x, want %x", b.Hash(), testBAL.Hash())
}
// Delete the BAL and verify it's gone.
DeleteAccessList(db, hash, number)
if HasAccessList(db, hash, number) {
t.Fatal("HasAccessList returned true after delete")
}
if b := ReadAccessList(db, hash, number); b != nil {
t.Fatalf("deleted BAL returned: %v", b)
}
}

View file

@ -413,6 +413,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
tds stat
numHashPairings stat
hashNumPairings stat
blockAccessList stat
legacyTries stat
stateLookups stat
accountTries stat
@ -480,10 +481,13 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
receipts.add(size)
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix) && len(key) == (len(headerPrefix)+8+common.HashLength+len(headerTDSuffix)):
tds.add(size)
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix) && len(key) == (len(headerPrefix)+8+common.HashLength+len(headerHashSuffix)):
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix) && len(key) == (len(headerPrefix)+8+len(headerHashSuffix)):
numHashPairings.add(size)
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
hashNumPairings.add(size)
case bytes.HasPrefix(key, accessListPrefix) && len(key) == len(accessListPrefix)+8+common.HashLength:
blockAccessList.add(size)
case IsLegacyTrieNode(key, it.Value()):
legacyTries.add(size)
case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
@ -625,6 +629,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
{"Key-Value store", "Difficulties (deprecated)", tds.sizeString(), tds.countString()},
{"Key-Value store", "Block number->hash", numHashPairings.sizeString(), numHashPairings.countString()},
{"Key-Value store", "Block hash->number", hashNumPairings.sizeString(), hashNumPairings.countString()},
{"Key-Value store", "Block accessList", blockAccessList.sizeString(), blockAccessList.countString()},
{"Key-Value store", "Transaction index", txLookups.sizeString(), txLookups.countString()},
{"Key-Value store", "Log index filter-map rows", filterMapRows.sizeString(), filterMapRows.countString()},
{"Key-Value store", "Log index last-block-of-map", filterMapLastBlock.sizeString(), filterMapLastBlock.countString()},

View file

@ -157,6 +157,7 @@ func newTable(path string, name string, readMeter, writeMeter *metrics.Meter, si
}
meta, err = openFreezerFileForReadOnly(filepath.Join(path, fmt.Sprintf("%s.meta", name)))
if err != nil {
index.Close()
return nil, err
}
} else {
@ -166,6 +167,7 @@ func newTable(path string, name string, readMeter, writeMeter *metrics.Meter, si
}
meta, err = openFreezerFileForAppend(filepath.Join(path, fmt.Sprintf("%s.meta", name)))
if err != nil {
index.Close()
return nil, err
}
}
@ -173,6 +175,8 @@ func newTable(path string, name string, readMeter, writeMeter *metrics.Meter, si
// is detected.
metadata, err := newMetadata(meta)
if err != nil {
meta.Close()
index.Close()
return nil, err
}
// Create the table and repair any past inconsistency

View file

@ -26,13 +26,7 @@ func atomicRename(src, dest string) error {
if err := os.Rename(src, dest); err != nil {
return err
}
dir, err := os.Open(filepath.Dir(src))
if err != nil {
return err
}
defer dir.Close()
return dir.Sync()
return syncDir(filepath.Dir(src))
}
// copyFrom copies data from 'srcPath' at offset 'offset' into 'destPath'.
@ -82,6 +76,11 @@ func copyFrom(srcPath, destPath string, offset uint64, before func(f *os.File) e
// we do the final move.
src.Close()
// Permanently persist the content into disk
if err := f.Sync(); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
@ -135,6 +134,7 @@ func openFreezerFileForAppend(filename string) (*os.File, error) {
}
// Seek to end for append
if _, err = file.Seek(0, io.SeekEnd); err != nil {
file.Close()
return nil, err
}
return file, nil

View file

@ -0,0 +1,49 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//go:build !windows
// +build !windows
package rawdb
import (
"errors"
"os"
"syscall"
)
// syncDir ensures that the directory metadata (e.g. newly renamed files)
// is flushed to durable storage.
func syncDir(name string) error {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()
// Some file systems do not support fsyncing directories (e.g. some FUSE
// mounts). Ignore EINVAL in those cases.
if err := f.Sync(); err != nil {
if errors.Is(err, os.ErrInvalid) {
return nil
}
if patherr, ok := err.(*os.PathError); ok && patherr.Err == syscall.EINVAL {
return nil
}
return err
}
return nil
}

View file

@ -0,0 +1,26 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//go:build windows
// +build windows
package rawdb
// syncDir is a no-op on Windows. Fsyncing a directory handle is not
// supported and returns "Access is denied".
func syncDir(name string) error {
return nil
}

View file

@ -112,6 +112,7 @@ var (
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
accessListPrefix = []byte("j") // accessListPrefix + num (uint64 big endian) + hash -> block access list
txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
@ -214,6 +215,11 @@ func blockReceiptsKey(number uint64, hash common.Hash) []byte {
return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
}
// accessListKey = accessListPrefix + num (uint64 big endian) + hash
func accessListKey(number uint64, hash common.Hash) []byte {
return append(append(accessListPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
}
// txLookupKey = txLookupPrefix + hash
func txLookupKey(hash common.Hash) []byte {
return append(txLookupPrefix, hash.Bytes()...)

View file

@ -20,13 +20,9 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/overlay"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/bintrie"
"github.com/ethereum/go-ethereum/trie/transitiontrie"
@ -34,11 +30,34 @@ import (
"github.com/ethereum/go-ethereum/triedb"
)
// DatabaseType represents the type of trie backing the state database.
type DatabaseType int
const (
// TypeMPT indicates a Merkle Patricia Trie (MPT) backed database.
TypeMPT DatabaseType = iota
// TypeUBT indicates a Unified Binary Trie (UBT) backed database.
TypeUBT
)
// Is returns the flag indicating the database type equals to the given one.
func (typ DatabaseType) Is(t DatabaseType) bool {
return typ == t
}
// Database wraps access to tries and contract code.
type Database interface {
// Type returns the trie type backing this database (MPT or UBT).
Type() DatabaseType
// Reader returns a state reader associated with the specified state root.
Reader(root common.Hash) (Reader, error)
// Iteratee returns a state iteratee associated with the specified state root,
// through which the account iterator and storage iterator can be created.
Iteratee(root common.Hash) (Iteratee, error)
// OpenTrie opens the main account trie.
OpenTrie(root common.Hash) (Trie, error)
@ -48,13 +67,10 @@ type Database interface {
// TrieDB returns the underlying trie database for managing trie nodes.
TrieDB() *triedb.Database
// Snapshot returns the underlying state snapshot.
Snapshot() *snapshot.Tree
// Commit flushes all pending writes and finalizes the state transition,
// committing the changes to the underlying storage. It returns an error
// if the commit fails.
Commit(update *stateUpdate) error
Commit(update *StateUpdate) error
}
// Trie is a Ethereum Merkle Patricia trie.
@ -138,178 +154,27 @@ type Trie interface {
// with the node that proves the absence of the key.
Prove(key []byte, proofDb ethdb.KeyValueWriter) error
// IsVerkle returns true if the trie is verkle-tree based
IsVerkle() bool
}
// CachingDB is an implementation of Database interface. It leverages both trie and
// state snapshot to provide functionalities for state access. It's meant to be a
// long-live object and has a few caches inside for sharing between blocks.
type CachingDB struct {
triedb *triedb.Database
codedb *CodeDB
snap *snapshot.Tree
// IsUBT returns true if the trie is unified binary trie based.
IsUBT() bool
}
// NewDatabase creates a state database with the provided data sources.
func NewDatabase(triedb *triedb.Database, codedb *CodeDB) *CachingDB {
if codedb == nil {
codedb = NewCodeDB(triedb.Disk())
}
return &CachingDB{
triedb: triedb,
codedb: codedb,
//
// Deprecated, please use NewMPTDatabase or NewUBTDatabase directly.
func NewDatabase(tdb *triedb.Database, codedb *CodeDB) Database {
if tdb.IsUBT() {
return NewUBTDatabase(tdb, codedb)
}
return NewMPTDatabase(tdb, codedb)
}
// NewDatabaseForTesting is similar to NewDatabase, but it initializes the caching
// db by using an ephemeral memory db with default config for testing.
func NewDatabaseForTesting() *CachingDB {
func NewDatabaseForTesting() Database {
db := rawdb.NewMemoryDatabase()
return NewDatabase(triedb.NewDatabase(db, nil), NewCodeDB(db))
}
// WithSnapshot configures the provided contract code cache. Note that this
// registration must be performed before the cachingDB is used.
func (db *CachingDB) WithSnapshot(snapshot *snapshot.Tree) *CachingDB {
db.snap = snapshot
return db
}
// StateReader returns a state reader associated with the specified state root.
func (db *CachingDB) StateReader(stateRoot common.Hash) (StateReader, error) {
var readers []StateReader
// Configure the state reader using the standalone snapshot in hash mode.
// This reader offers improved performance but is optional and only
// partially useful if the snapshot is not fully generated.
if db.TrieDB().Scheme() == rawdb.HashScheme && db.snap != nil {
snap := db.snap.Snapshot(stateRoot)
if snap != nil {
readers = append(readers, newFlatReader(snap))
}
}
// Configure the state reader using the path database in path mode.
// This reader offers improved performance but is optional and only
// partially useful if the snapshot data in path database is not
// fully generated.
if db.TrieDB().Scheme() == rawdb.PathScheme {
reader, err := db.triedb.StateReader(stateRoot)
if err == nil {
readers = append(readers, newFlatReader(reader))
}
}
// Configure the trie reader, which is expected to be available as the
// gatekeeper unless the state is corrupted.
tr, err := newTrieReader(stateRoot, db.triedb)
if err != nil {
return nil, err
}
readers = append(readers, tr)
return newMultiStateReader(readers...)
}
// Reader implements Database, returning a reader associated with the specified
// state root.
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
sr, err := db.StateReader(stateRoot)
if err != nil {
return nil, err
}
return newReader(db.codedb.Reader(), sr), nil
}
// ReadersWithCacheStats creates a pair of state readers that share the same
// underlying state reader and internal state cache, while maintaining separate
// statistics respectively.
func (db *CachingDB) ReadersWithCacheStats(stateRoot common.Hash) (Reader, Reader, error) {
r, err := db.StateReader(stateRoot)
if err != nil {
return nil, nil, err
}
sr := newStateReaderWithCache(r)
ra := newReader(db.codedb.Reader(), newStateReaderWithStats(sr))
rb := newReader(db.codedb.Reader(), newStateReaderWithStats(sr))
return ra, rb, nil
}
// OpenTrie opens the main account trie at a specific root hash.
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
if db.triedb.IsVerkle() {
ts := overlay.LoadTransitionState(db.TrieDB().Disk(), root, db.triedb.IsVerkle())
if ts.InTransition() {
panic("state tree transition isn't supported yet")
}
if ts.Transitioned() {
// Use BinaryTrie instead of VerkleTrie when IsVerkle is set
// (IsVerkle actually means Binary Trie mode in this codebase)
return bintrie.NewBinaryTrie(root, db.triedb)
}
}
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
if err != nil {
return nil, err
}
return tr, nil
}
// OpenStorageTrie opens the storage trie of an account.
func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
if db.triedb.IsVerkle() {
return self, nil
}
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
if err != nil {
return nil, err
}
return tr, nil
}
// TrieDB retrieves any intermediate trie-node caching layer.
func (db *CachingDB) TrieDB() *triedb.Database {
return db.triedb
}
// Snapshot returns the underlying state snapshot.
func (db *CachingDB) Snapshot() *snapshot.Tree {
return db.snap
}
// Commit flushes all pending writes and finalizes the state transition,
// committing the changes to the underlying storage. It returns an error
// if the commit fails.
func (db *CachingDB) Commit(update *stateUpdate) error {
// Short circuit if nothing to commit
if update.empty() {
return nil
}
// Commit dirty contract code if any exists
if len(update.codes) > 0 {
batch := db.codedb.NewBatchWithSize(len(update.codes))
for _, code := range update.codes {
batch.Put(code.hash, code.blob)
}
if err := batch.Commit(); err != nil {
return err
}
}
// If snapshotting is enabled, update the snapshot tree with this new version
if db.snap != nil && db.snap.Snapshot(update.originRoot) != nil {
if err := db.snap.Update(update.root, update.originRoot, update.accounts, update.storages); err != nil {
log.Warn("Failed to update snapshot tree", "from", update.originRoot, "to", update.root, "err", err)
}
// Keep 128 diff layers in the memory, persistent layer is 129th.
// - head layer is paired with HEAD state
// - head-1 layer is paired with HEAD-1 state
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
if err := db.snap.Cap(update.root, TriesInMemory); err != nil {
log.Warn("Failed to cap snapshot tree", "root", update.root, "layers", TriesInMemory, "err", err)
}
}
return db.triedb.Update(update.root, update.originRoot, update.blockNumber, update.nodes, update.stateSet())
}
// mustCopyTrie returns a deep-copied trie.
func mustCopyTrie(t Trie) Trie {
switch t := t.(type) {
@ -317,6 +182,8 @@ func mustCopyTrie(t Trie) Trie {
return t.Copy()
case *transitiontrie.TransitionTrie:
return t.Copy()
case *bintrie.BinaryTrie:
return t.Copy()
default:
panic(fmt.Errorf("unknown trie type %T", t))
}

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