mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-01 12:38:40 +00:00
Merge branch 'master' into bs/cell-blobpool/sparse-v2
This commit is contained in:
commit
3c367a61e2
353 changed files with 26277 additions and 15917 deletions
|
|
@ -145,7 +145,7 @@ jobs:
|
||||||
|
|
||||||
windows:
|
windows:
|
||||||
name: Windows Build
|
name: Windows Build
|
||||||
runs-on: "win-11"
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|
@ -155,24 +155,49 @@ jobs:
|
||||||
go-version: 1.24
|
go-version: 1.24
|
||||||
cache: false
|
cache: false
|
||||||
|
|
||||||
# Note: gcc.exe only works properly if the corresponding bin/ directory is
|
- name: Install cross toolchain
|
||||||
# contained in PATH.
|
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)"
|
- name: "Build (amd64)"
|
||||||
shell: cmd
|
|
||||||
run: |
|
run: |
|
||||||
set PATH=%GETH_MINGW%\bin;%PATH%
|
go run build/ci.go install -dlgo -os windows -arch amd64 -cc x86_64-w64-mingw32-gcc
|
||||||
go run build/ci.go install -dlgo -arch amd64 -cc %GETH_MINGW%\bin\gcc.exe
|
|
||||||
|
- 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:
|
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)"
|
- name: "Build (386)"
|
||||||
shell: cmd
|
|
||||||
run: |
|
run: |
|
||||||
set PATH=%GETH_MINGW%\bin;%PATH%
|
go run build/ci.go install -dlgo -os windows -arch 386 -cc i686-w64-mingw32-gcc
|
||||||
go run build/ci.go install -dlgo -arch 386 -cc %GETH_MINGW%\bin\gcc.exe
|
|
||||||
|
- 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:
|
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:
|
docker:
|
||||||
name: Docker Image
|
name: Docker Image
|
||||||
|
|
|
||||||
29
.github/workflows/freebsd.yml
vendored
Normal file
29
.github/workflows/freebsd.yml
vendored
Normal 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
|
||||||
41
.github/workflows/go.yml
vendored
41
.github/workflows/go.yml
vendored
|
|
@ -97,3 +97,44 @@ jobs:
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: go run build/ci.go test -p 8
|
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
|
||||||
|
|
|
||||||
|
|
@ -183,8 +183,11 @@ var (
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}Event(log *types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}Event(log *types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
||||||
event := "{{.Original.Name}}"
|
event := "{{.Original.Name}}"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != {{ decapitalise $contract.Type}}.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
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}})
|
out := new({{$contract.Type}}{{.Normalized.Name}})
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -360,8 +360,11 @@ func (CrowdsaleFundTransfer) ContractEventName() string {
|
||||||
// Solidity: event FundTransfer(address backer, uint256 amount, bool isContribution)
|
// Solidity: event FundTransfer(address backer, uint256 amount, bool isContribution)
|
||||||
func (crowdsale *Crowdsale) UnpackFundTransferEvent(log *types.Log) (*CrowdsaleFundTransfer, error) {
|
func (crowdsale *Crowdsale) UnpackFundTransferEvent(log *types.Log) (*CrowdsaleFundTransfer, error) {
|
||||||
event := "FundTransfer"
|
event := "FundTransfer"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != crowdsale.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != crowdsale.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(CrowdsaleFundTransfer)
|
out := new(CrowdsaleFundTransfer)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
35
accounts/abi/abigen/testdata/v2/dao.go.txt
vendored
35
accounts/abi/abigen/testdata/v2/dao.go.txt
vendored
|
|
@ -606,8 +606,11 @@ func (DAOChangeOfRules) ContractEventName() string {
|
||||||
// Solidity: event ChangeOfRules(uint256 minimumQuorum, uint256 debatingPeriodInMinutes, int256 majorityMargin)
|
// Solidity: event ChangeOfRules(uint256 minimumQuorum, uint256 debatingPeriodInMinutes, int256 majorityMargin)
|
||||||
func (dAO *DAO) UnpackChangeOfRulesEvent(log *types.Log) (*DAOChangeOfRules, error) {
|
func (dAO *DAO) UnpackChangeOfRulesEvent(log *types.Log) (*DAOChangeOfRules, error) {
|
||||||
event := "ChangeOfRules"
|
event := "ChangeOfRules"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != dAO.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(DAOChangeOfRules)
|
out := new(DAOChangeOfRules)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -648,8 +651,11 @@ func (DAOMembershipChanged) ContractEventName() string {
|
||||||
// Solidity: event MembershipChanged(address member, bool isMember)
|
// Solidity: event MembershipChanged(address member, bool isMember)
|
||||||
func (dAO *DAO) UnpackMembershipChangedEvent(log *types.Log) (*DAOMembershipChanged, error) {
|
func (dAO *DAO) UnpackMembershipChangedEvent(log *types.Log) (*DAOMembershipChanged, error) {
|
||||||
event := "MembershipChanged"
|
event := "MembershipChanged"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != dAO.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(DAOMembershipChanged)
|
out := new(DAOMembershipChanged)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -692,8 +698,11 @@ func (DAOProposalAdded) ContractEventName() string {
|
||||||
// Solidity: event ProposalAdded(uint256 proposalID, address recipient, uint256 amount, string description)
|
// Solidity: event ProposalAdded(uint256 proposalID, address recipient, uint256 amount, string description)
|
||||||
func (dAO *DAO) UnpackProposalAddedEvent(log *types.Log) (*DAOProposalAdded, error) {
|
func (dAO *DAO) UnpackProposalAddedEvent(log *types.Log) (*DAOProposalAdded, error) {
|
||||||
event := "ProposalAdded"
|
event := "ProposalAdded"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != dAO.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(DAOProposalAdded)
|
out := new(DAOProposalAdded)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -736,8 +745,11 @@ func (DAOProposalTallied) ContractEventName() string {
|
||||||
// Solidity: event ProposalTallied(uint256 proposalID, int256 result, uint256 quorum, bool active)
|
// Solidity: event ProposalTallied(uint256 proposalID, int256 result, uint256 quorum, bool active)
|
||||||
func (dAO *DAO) UnpackProposalTalliedEvent(log *types.Log) (*DAOProposalTallied, error) {
|
func (dAO *DAO) UnpackProposalTalliedEvent(log *types.Log) (*DAOProposalTallied, error) {
|
||||||
event := "ProposalTallied"
|
event := "ProposalTallied"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != dAO.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(DAOProposalTallied)
|
out := new(DAOProposalTallied)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -780,8 +792,11 @@ func (DAOVoted) ContractEventName() string {
|
||||||
// Solidity: event Voted(uint256 proposalID, bool position, address voter, string justification)
|
// Solidity: event Voted(uint256 proposalID, bool position, address voter, string justification)
|
||||||
func (dAO *DAO) UnpackVotedEvent(log *types.Log) (*DAOVoted, error) {
|
func (dAO *DAO) UnpackVotedEvent(log *types.Log) (*DAOVoted, error) {
|
||||||
event := "Voted"
|
event := "Voted"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != dAO.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(DAOVoted)
|
out := new(DAOVoted)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,11 @@ func (EventCheckerDynamic) ContractEventName() string {
|
||||||
// Solidity: event dynamic(string indexed idxStr, bytes indexed idxDat, string str, bytes dat)
|
// Solidity: event dynamic(string indexed idxStr, bytes indexed idxDat, string str, bytes dat)
|
||||||
func (eventChecker *EventChecker) UnpackDynamicEvent(log *types.Log) (*EventCheckerDynamic, error) {
|
func (eventChecker *EventChecker) UnpackDynamicEvent(log *types.Log) (*EventCheckerDynamic, error) {
|
||||||
event := "dynamic"
|
event := "dynamic"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(EventCheckerDynamic)
|
out := new(EventCheckerDynamic)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -112,8 +115,11 @@ func (EventCheckerEmpty) ContractEventName() string {
|
||||||
// Solidity: event empty()
|
// Solidity: event empty()
|
||||||
func (eventChecker *EventChecker) UnpackEmptyEvent(log *types.Log) (*EventCheckerEmpty, error) {
|
func (eventChecker *EventChecker) UnpackEmptyEvent(log *types.Log) (*EventCheckerEmpty, error) {
|
||||||
event := "empty"
|
event := "empty"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(EventCheckerEmpty)
|
out := new(EventCheckerEmpty)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -154,8 +160,11 @@ func (EventCheckerIndexed) ContractEventName() string {
|
||||||
// Solidity: event indexed(address indexed addr, int256 indexed num)
|
// Solidity: event indexed(address indexed addr, int256 indexed num)
|
||||||
func (eventChecker *EventChecker) UnpackIndexedEvent(log *types.Log) (*EventCheckerIndexed, error) {
|
func (eventChecker *EventChecker) UnpackIndexedEvent(log *types.Log) (*EventCheckerIndexed, error) {
|
||||||
event := "indexed"
|
event := "indexed"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(EventCheckerIndexed)
|
out := new(EventCheckerIndexed)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -196,8 +205,11 @@ func (EventCheckerMixed) ContractEventName() string {
|
||||||
// Solidity: event mixed(address indexed addr, int256 num)
|
// Solidity: event mixed(address indexed addr, int256 num)
|
||||||
func (eventChecker *EventChecker) UnpackMixedEvent(log *types.Log) (*EventCheckerMixed, error) {
|
func (eventChecker *EventChecker) UnpackMixedEvent(log *types.Log) (*EventCheckerMixed, error) {
|
||||||
event := "mixed"
|
event := "mixed"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(EventCheckerMixed)
|
out := new(EventCheckerMixed)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -238,8 +250,11 @@ func (EventCheckerUnnamed) ContractEventName() string {
|
||||||
// Solidity: event unnamed(uint256 indexed arg0, uint256 indexed arg1)
|
// Solidity: event unnamed(uint256 indexed arg0, uint256 indexed arg1)
|
||||||
func (eventChecker *EventChecker) UnpackUnnamedEvent(log *types.Log) (*EventCheckerUnnamed, error) {
|
func (eventChecker *EventChecker) UnpackUnnamedEvent(log *types.Log) (*EventCheckerUnnamed, error) {
|
||||||
event := "unnamed"
|
event := "unnamed"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(EventCheckerUnnamed)
|
out := new(EventCheckerUnnamed)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -134,8 +134,11 @@ func (NameConflictLog) ContractEventName() string {
|
||||||
// Solidity: event log(int256 msg, int256 _msg)
|
// Solidity: event log(int256 msg, int256 _msg)
|
||||||
func (nameConflict *NameConflict) UnpackLogEvent(log *types.Log) (*NameConflictLog, error) {
|
func (nameConflict *NameConflict) UnpackLogEvent(log *types.Log) (*NameConflictLog, error) {
|
||||||
event := "log"
|
event := "log"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != nameConflict.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != nameConflict.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(NameConflictLog)
|
out := new(NameConflictLog)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -136,8 +136,11 @@ func (NumericMethodNameE1TestEvent) ContractEventName() string {
|
||||||
// Solidity: event _1TestEvent(address _param)
|
// Solidity: event _1TestEvent(address _param)
|
||||||
func (numericMethodName *NumericMethodName) UnpackE1TestEventEvent(log *types.Log) (*NumericMethodNameE1TestEvent, error) {
|
func (numericMethodName *NumericMethodName) UnpackE1TestEventEvent(log *types.Log) (*NumericMethodNameE1TestEvent, error) {
|
||||||
event := "_1TestEvent"
|
event := "_1TestEvent"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != numericMethodName.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != numericMethodName.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(NumericMethodNameE1TestEvent)
|
out := new(NumericMethodNameE1TestEvent)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
14
accounts/abi/abigen/testdata/v2/overload.go.txt
vendored
14
accounts/abi/abigen/testdata/v2/overload.go.txt
vendored
|
|
@ -114,8 +114,11 @@ func (OverloadBar) ContractEventName() string {
|
||||||
// Solidity: event bar(uint256 i)
|
// Solidity: event bar(uint256 i)
|
||||||
func (overload *Overload) UnpackBarEvent(log *types.Log) (*OverloadBar, error) {
|
func (overload *Overload) UnpackBarEvent(log *types.Log) (*OverloadBar, error) {
|
||||||
event := "bar"
|
event := "bar"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != overload.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != overload.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(OverloadBar)
|
out := new(OverloadBar)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -156,8 +159,11 @@ func (OverloadBar0) ContractEventName() string {
|
||||||
// Solidity: event bar(uint256 i, uint256 j)
|
// Solidity: event bar(uint256 i, uint256 j)
|
||||||
func (overload *Overload) UnpackBar0Event(log *types.Log) (*OverloadBar0, error) {
|
func (overload *Overload) UnpackBar0Event(log *types.Log) (*OverloadBar0, error) {
|
||||||
event := "bar0"
|
event := "bar0"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != overload.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != overload.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(OverloadBar0)
|
out := new(OverloadBar0)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
7
accounts/abi/abigen/testdata/v2/token.go.txt
vendored
7
accounts/abi/abigen/testdata/v2/token.go.txt
vendored
|
|
@ -386,8 +386,11 @@ func (TokenTransfer) ContractEventName() string {
|
||||||
// Solidity: event Transfer(address indexed from, address indexed to, uint256 value)
|
// Solidity: event Transfer(address indexed from, address indexed to, uint256 value)
|
||||||
func (token *Token) UnpackTransferEvent(log *types.Log) (*TokenTransfer, error) {
|
func (token *Token) UnpackTransferEvent(log *types.Log) (*TokenTransfer, error) {
|
||||||
event := "Transfer"
|
event := "Transfer"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != token.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != token.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(TokenTransfer)
|
out := new(TokenTransfer)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
14
accounts/abi/abigen/testdata/v2/tuple.go.txt
vendored
14
accounts/abi/abigen/testdata/v2/tuple.go.txt
vendored
|
|
@ -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)
|
// 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) {
|
func (tuple *Tuple) UnpackTupleEventEvent(log *types.Log) (*TupleTupleEvent, error) {
|
||||||
event := "TupleEvent"
|
event := "TupleEvent"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != tuple.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != tuple.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(TupleTupleEvent)
|
out := new(TupleTupleEvent)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -234,8 +237,11 @@ func (TupleTupleEvent2) ContractEventName() string {
|
||||||
// Solidity: event TupleEvent2((uint8,uint8)[] arg0)
|
// Solidity: event TupleEvent2((uint8,uint8)[] arg0)
|
||||||
func (tuple *Tuple) UnpackTupleEvent2Event(log *types.Log) (*TupleTupleEvent2, error) {
|
func (tuple *Tuple) UnpackTupleEvent2Event(log *types.Log) (*TupleTupleEvent2, error) {
|
||||||
event := "TupleEvent2"
|
event := "TupleEvent2"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != tuple.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != tuple.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(TupleTupleEvent2)
|
out := new(TupleTupleEvent2)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,13 @@ var (
|
||||||
// ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
|
// ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
|
||||||
// an empty contract behind.
|
// an empty contract behind.
|
||||||
ErrNoCodeAfterDeploy = bind2.ErrNoCodeAfterDeploy
|
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
|
// ContractCaller defines the methods needed to allow operating with a contract on a read
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ import (
|
||||||
const basefeeWiggleMultiplier = 2
|
const basefeeWiggleMultiplier = 2
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errNoEventSignature = errors.New("no event signature")
|
ErrNoEventSignature = errors.New("no event signature")
|
||||||
errEventSignatureMismatch = errors.New("event signature mismatch")
|
ErrEventSignatureMismatch = errors.New("event signature mismatch")
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignerFn is a signer function callback when a contract requires a method to
|
// 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 {
|
func (c *BoundContract) UnpackLog(out any, event string, log types.Log) error {
|
||||||
// Anonymous events are not supported.
|
// Anonymous events are not supported.
|
||||||
if len(log.Topics) == 0 {
|
if len(log.Topics) == 0 {
|
||||||
return errNoEventSignature
|
return ErrNoEventSignature
|
||||||
}
|
}
|
||||||
if log.Topics[0] != c.abi.Events[event].ID {
|
if log.Topics[0] != c.abi.Events[event].ID {
|
||||||
return errEventSignatureMismatch
|
return ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
|
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 {
|
func (c *BoundContract) UnpackLogIntoMap(out map[string]any, event string, log types.Log) error {
|
||||||
// Anonymous events are not supported.
|
// Anonymous events are not supported.
|
||||||
if len(log.Topics) == 0 {
|
if len(log.Topics) == 0 {
|
||||||
return errNoEventSignature
|
return ErrNoEventSignature
|
||||||
}
|
}
|
||||||
if log.Topics[0] != c.abi.Events[event].ID {
|
if log.Topics[0] != c.abi.Events[event].ID {
|
||||||
return errEventSignatureMismatch
|
return ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
|
if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -276,8 +276,11 @@ func (DBInsert) ContractEventName() string {
|
||||||
// Solidity: event Insert(uint256 key, uint256 value, uint256 length)
|
// Solidity: event Insert(uint256 key, uint256 value, uint256 length)
|
||||||
func (dB *DB) UnpackInsertEvent(log *types.Log) (*DBInsert, error) {
|
func (dB *DB) UnpackInsertEvent(log *types.Log) (*DBInsert, error) {
|
||||||
event := "Insert"
|
event := "Insert"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != dB.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != dB.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(DBInsert)
|
out := new(DBInsert)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -318,8 +321,11 @@ func (DBKeyedInsert) ContractEventName() string {
|
||||||
// Solidity: event KeyedInsert(uint256 indexed key, uint256 value)
|
// Solidity: event KeyedInsert(uint256 indexed key, uint256 value)
|
||||||
func (dB *DB) UnpackKeyedInsertEvent(log *types.Log) (*DBKeyedInsert, error) {
|
func (dB *DB) UnpackKeyedInsertEvent(log *types.Log) (*DBKeyedInsert, error) {
|
||||||
event := "KeyedInsert"
|
event := "KeyedInsert"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != dB.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != dB.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(DBKeyedInsert)
|
out := new(DBKeyedInsert)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,11 @@ func (CBasic1) ContractEventName() string {
|
||||||
// Solidity: event basic1(uint256 indexed id, uint256 data)
|
// Solidity: event basic1(uint256 indexed id, uint256 data)
|
||||||
func (c *C) UnpackBasic1Event(log *types.Log) (*CBasic1, error) {
|
func (c *C) UnpackBasic1Event(log *types.Log) (*CBasic1, error) {
|
||||||
event := "basic1"
|
event := "basic1"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != c.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != c.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(CBasic1)
|
out := new(CBasic1)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
@ -157,8 +160,11 @@ func (CBasic2) ContractEventName() string {
|
||||||
// Solidity: event basic2(bool indexed flag, uint256 data)
|
// Solidity: event basic2(bool indexed flag, uint256 data)
|
||||||
func (c *C) UnpackBasic2Event(log *types.Log) (*CBasic2, error) {
|
func (c *C) UnpackBasic2Event(log *types.Log) (*CBasic2, error) {
|
||||||
event := "basic2"
|
event := "basic2"
|
||||||
if len(log.Topics) == 0 || log.Topics[0] != c.abi.Events[event].ID {
|
if len(log.Topics) == 0 {
|
||||||
return nil, errors.New("event signature mismatch")
|
return nil, bind.ErrNoEventSignature
|
||||||
|
}
|
||||||
|
if log.Topics[0] != c.abi.Events[event].ID {
|
||||||
|
return nil, bind.ErrEventSignatureMismatch
|
||||||
}
|
}
|
||||||
out := new(CBasic2)
|
out := new(CBasic2)
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -379,16 +379,16 @@ func TestEventUnpackEmptyTopics(t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error when unpacking event with empty topics, got nil")
|
t.Fatal("expected error when unpacking event with empty topics, got nil")
|
||||||
}
|
}
|
||||||
if err.Error() != "event signature mismatch" {
|
if err != bind.ErrNoEventSignature {
|
||||||
t.Fatalf("expected 'event signature mismatch' error, got: %v", err)
|
t.Fatalf("expected 'no event signature' error, got: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.UnpackBasic2Event(log)
|
_, err = c.UnpackBasic2Event(log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error when unpacking event with empty topics, got nil")
|
t.Fatal("expected error when unpacking event with empty topics, got nil")
|
||||||
}
|
}
|
||||||
if err.Error() != "event signature mismatch" {
|
if err != bind.ErrNoEventSignature {
|
||||||
t.Fatalf("expected 'event signature mismatch' error, got: %v", err)
|
t.Fatalf("expected 'no event signature' error, got: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -910,7 +910,7 @@ func TestUnpackTuple(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
FieldT: T{
|
FieldT: T{
|
||||||
big.NewInt(0), big.NewInt(1),
|
big.NewInt(0).SetBits([]big.Word{}), big.NewInt(1),
|
||||||
},
|
},
|
||||||
A: big.NewInt(1),
|
A: big.NewInt(1),
|
||||||
}
|
}
|
||||||
|
|
@ -919,7 +919,7 @@ func TestUnpackTuple(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if reflect.DeepEqual(ret, expected) {
|
if !reflect.DeepEqual(ret, expected) {
|
||||||
t.Error("unexpected unpack value")
|
t.Error("unexpected unpack value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,18 +68,27 @@ func waitWatcherStart(ks *KeyStore) bool {
|
||||||
|
|
||||||
func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
|
func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
|
||||||
var list []accounts.Account
|
var list []accounts.Account
|
||||||
|
haveAccounts := false
|
||||||
|
haveChange := false
|
||||||
for t0 := time.Now(); time.Since(t0) < 5*time.Second; time.Sleep(100 * time.Millisecond) {
|
for t0 := time.Now(); time.Since(t0) < 5*time.Second; time.Sleep(100 * time.Millisecond) {
|
||||||
list = ks.Accounts()
|
if !haveAccounts {
|
||||||
if reflect.DeepEqual(list, wantAccounts) {
|
list = ks.Accounts()
|
||||||
// ks should have also received change notifications
|
haveAccounts = reflect.DeepEqual(list, wantAccounts)
|
||||||
|
}
|
||||||
|
if !haveChange {
|
||||||
select {
|
select {
|
||||||
case <-ks.changes:
|
case <-ks.changes:
|
||||||
|
haveChange = true
|
||||||
default:
|
default:
|
||||||
return errors.New("wasn't notified of new accounts")
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if haveAccounts && haveChange {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if haveAccounts {
|
||||||
|
return errors.New("wasn't notified of new accounts")
|
||||||
|
}
|
||||||
return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts)
|
return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build (darwin && !ios && cgo) || freebsd || (linux && !arm64) || netbsd || solaris
|
//go:build (darwin && !ios && cgo) || freebsd || linux || netbsd || solaris
|
||||||
// +build darwin,!ios,cgo freebsd linux,!arm64 netbsd solaris
|
// +build darwin,!ios,cgo freebsd linux netbsd solaris
|
||||||
|
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go: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 linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
// +build darwin,!cgo ios windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
||||||
|
|
||||||
// This is the fallback implementation of directory watching.
|
// This is the fallback implementation of directory watching.
|
||||||
// It is used on unsupported platforms.
|
// It is used on unsupported platforms.
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ func (hub *Hub) readPairings() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *Hub) writePairings() 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -129,11 +129,8 @@ func (hub *Hub) writePairings() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := pairingFile.Write(pairingData); err != nil {
|
_, err = pairingFile.Write(pairingData)
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
|
func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/karalabe/hid"
|
"github.com/ethereum/hid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
|
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/karalabe/hid"
|
"github.com/ethereum/hid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Maximum time between wallet health checks to detect USB unplugs.
|
// Maximum time between wallet health checks to detect USB unplugs.
|
||||||
|
|
|
||||||
39
appveyor.yml
39
appveyor.yml
|
|
@ -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
|
|
||||||
|
|
@ -81,6 +81,7 @@ var (
|
||||||
TooLargeRequest = &EngineAPIError{code: -38004, msg: "Too large request"}
|
TooLargeRequest = &EngineAPIError{code: -38004, msg: "Too large request"}
|
||||||
InvalidParams = &EngineAPIError{code: -32602, msg: "Invalid parameters"}
|
InvalidParams = &EngineAPIError{code: -32602, msg: "Invalid parameters"}
|
||||||
UnsupportedFork = &EngineAPIError{code: -38005, msg: "Unsupported fork"}
|
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_INVALID = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: INVALID}, PayloadID: nil}
|
||||||
STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil}
|
STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
|
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
|
||||||
}
|
}
|
||||||
var enc ExecutableData
|
var enc ExecutableData
|
||||||
enc.ParentHash = e.ParentHash
|
enc.ParentHash = e.ParentHash
|
||||||
|
|
@ -83,7 +83,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
|
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
|
||||||
}
|
}
|
||||||
var dec ExecutableData
|
var dec ExecutableData
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ type ExecutableData struct {
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||||
SlotNumber *uint64 `json:"slotNumber"`
|
SlotNumber *uint64 `json:"slotNumber,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON type overrides for executableData.
|
// 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) {
|
if data.BaseFeePerGas != nil && (data.BaseFeePerGas.Sign() == -1 || data.BaseFeePerGas.BitLen() > 256) {
|
||||||
return nil, fmt.Errorf("invalid baseFeePerGas: %v", data.BaseFeePerGas)
|
return nil, fmt.Errorf("invalid baseFeePerGas: %v", data.BaseFeePerGas)
|
||||||
}
|
}
|
||||||
var blobHashes = make([]common.Hash, 0, len(txs))
|
var blobHashes = make([]common.Hash, 0, len(versionedHashes))
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
blobHashes = append(blobHashes, tx.BlobHashes()...)
|
blobHashes = append(blobHashes, tx.BlobHashes()...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -438,14 +438,11 @@ func (s *serverWithLimits) fail(desc string) {
|
||||||
// failLocked calculates the dynamic failure delay and applies it.
|
// failLocked calculates the dynamic failure delay and applies it.
|
||||||
func (s *serverWithLimits) failLocked(desc string) {
|
func (s *serverWithLimits) failLocked(desc string) {
|
||||||
log.Debug("Server error", "description", desc)
|
log.Debug("Server error", "description", desc)
|
||||||
s.failureDelay *= 2
|
|
||||||
now := s.clock.Now()
|
now := s.clock.Now()
|
||||||
if now > s.failureDelayEnd {
|
if now > s.failureDelayEnd {
|
||||||
s.failureDelay *= math.Pow(2, -float64(now-s.failureDelayEnd)/float64(maxFailureDelay))
|
s.failureDelay *= math.Pow(2, -float64(now-s.failureDelayEnd)/float64(maxFailureDelay))
|
||||||
}
|
}
|
||||||
if s.failureDelay < float64(minFailureDelay) {
|
s.failureDelay = max(min(s.failureDelay*2, float64(maxFailureDelay)), float64(minFailureDelay))
|
||||||
s.failureDelay = float64(minFailureDelay)
|
|
||||||
}
|
|
||||||
s.failureDelayEnd = now + mclock.AbsTime(s.failureDelay)
|
s.failureDelayEnd = now + mclock.AbsTime(s.failureDelay)
|
||||||
s.delay(time.Duration(s.failureDelay))
|
s.delay(time.Duration(s.failureDelay))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ const (
|
||||||
ssNeedParent // cp header slot %32 != 0, need parent to check epoch boundary
|
ssNeedParent // cp header slot %32 != 0, need parent to check epoch boundary
|
||||||
ssParentRequested // cp parent header requested
|
ssParentRequested // cp parent header requested
|
||||||
ssPrintStatus // has all necessary info, print log message if init still not successful
|
ssPrintStatus // has all necessary info, print log message if init still not successful
|
||||||
ssDone // log message printed, no more action required
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type serverState struct {
|
type serverState struct {
|
||||||
|
|
@ -180,7 +179,8 @@ func (s *CheckpointInit) Process(requester request.Requester, events []request.E
|
||||||
default:
|
default:
|
||||||
log.Error("blsync: checkpoint not available, but reported as finalized; specified checkpoint hash might be too old", "server", server.Name())
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,49 +5,49 @@
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0
|
# https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0
|
||||||
a3192784375acec7eaec492799d5c5d0c47a2909a3cc40178898e4ecd20cc416 fixtures_develop.tar.gz
|
a3192784375acec7eaec492799d5c5d0c47a2909a3cc40178898e4ecd20cc416 fixtures_develop.tar.gz
|
||||||
|
|
||||||
# version:golang 1.25.7
|
# version:golang 1.25.10
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
178f2832820274b43e177d32f06a3ebb0129e427dd20a5e4c88df2c1763cf10a go1.25.7.src.tar.gz
|
20cf04a92e5af99748e341bc8996fa28090c9ac98765fa115ec5ddf41d7af41d go1.25.10.src.tar.gz
|
||||||
81bf2a1f20633f62d55d826d82dde3b0570cf1408a91e15781b266037299285b go1.25.7.aix-ppc64.tar.gz
|
a194e767c2ab4216a60acc068b9dbe6bf4fae05c14bb52d6bbdcb5b3ea521308 go1.25.10.aix-ppc64.tar.gz
|
||||||
bf5050a2152f4053837b886e8d9640c829dbacbc3370f913351eb0904cb706f5 go1.25.7.darwin-amd64.tar.gz
|
52321165a3146cd91865ef98371506a846ed4dc4f9f1c9323e5ad90d2a411e06 go1.25.10.darwin-amd64.tar.gz
|
||||||
ff18369ffad05c57d5bed888b660b31385f3c913670a83ef557cdfd98ea9ae1b go1.25.7.darwin-arm64.tar.gz
|
795691a425de7e7cdba3544f354dcd2cebcf52e87dc6898193878f34eb6d634f go1.25.10.darwin-arm64.tar.gz
|
||||||
c5dccd7f192dd7b305dc209fb316ac1917776d74bd8e4d532ef2772f305bf42a go1.25.7.dragonfly-amd64.tar.gz
|
e37b4544ba9e9e9a7ab2ed3116b3fc4d39a88da854baa5a566d9d6d3a9de7d4c go1.25.10.dragonfly-amd64.tar.gz
|
||||||
a2de97c8ac74bf64b0ae73fe9d379e61af530e061bc7f8f825044172ffe61a8b go1.25.7.freebsd-386.tar.gz
|
2a70d1fdabab637aa442ca94599a56e381238efa20cb995d5433b8579bfe482c go1.25.10.freebsd-386.tar.gz
|
||||||
055f9e138787dcafa81eb0314c8ff70c6dd0f6dba1e8a6957fef5d5efd1ab8fd go1.25.7.freebsd-amd64.tar.gz
|
9cdf522d87d47d82fec4a313cc4f8c3c94a7770426e8d443e4150a1f330cba71 go1.25.10.freebsd-amd64.tar.gz
|
||||||
60e7f7a7c990f0b9539ac8ed668155746997d404643a4eecd47b3dee1b7e710b go1.25.7.freebsd-arm.tar.gz
|
6da6183633e9e59ffd9edefab68b5059c89b605596d94aaba650b1681fccd35f go1.25.10.freebsd-arm.tar.gz
|
||||||
631e03d5fd4c526e2f499154d8c6bf4cb081afb2fff171c428722afc9539d53a go1.25.7.freebsd-arm64.tar.gz
|
7adcefeebdd05331f4d45f1ad2dddb5c53537cff6552e82f6595b3b833b95371 go1.25.10.freebsd-arm64.tar.gz
|
||||||
8a264fd685823808140672812e3ad9c43f6ad59444c0dc14cdd3a1351839ddd5 go1.25.7.freebsd-riscv64.tar.gz
|
285f80a1ace21a7d94035cd753196eeada8cacd48e6396fd116ad5eb67aea957 go1.25.10.freebsd-riscv64.tar.gz
|
||||||
57c672447d906a1bcab98f2b11492d54521a791aacbb4994a25169e59cbe289a go1.25.7.illumos-amd64.tar.gz
|
de7461bf0e5068a4f6e7f8713026d70516be6dbd5de5d21f9ced1c182f2f326e go1.25.10.illumos-amd64.tar.gz
|
||||||
2866517e9ca81e6a2e85a930e9b11bc8a05cfeb2fc6dc6cb2765e7fb3c14b715 go1.25.7.linux-386.tar.gz
|
2f574f2e2e19ead5b280fec0e7af5c81b76632685f03b6ac42dfa34c4b773c52 go1.25.10.linux-386.tar.gz
|
||||||
12e6d6a191091ae27dc31f6efc630e3a3b8ba409baf3573d955b196fdf086005 go1.25.7.linux-amd64.tar.gz
|
42d4f7a32316aa66591eca7e89867256057a4264451aca10570a715b3637ba70 go1.25.10.linux-amd64.tar.gz
|
||||||
ba611a53534135a81067240eff9508cd7e256c560edd5d8c2fef54f083c07129 go1.25.7.linux-arm64.tar.gz
|
654da1f9b50a5d1c2a85ccf8ed405aa89c06e94d18384628bf186f7712677b08 go1.25.10.linux-arm64.tar.gz
|
||||||
1ba07e0eb86b839e72467f4b5c7a5597d07f30bcf5563c951410454f7cda5266 go1.25.7.linux-armv6l.tar.gz
|
39f168f158e693887d3ad006168af1b1a3007b19c5993cae4d9d57f82f52aaf8 go1.25.10.linux-armv6l.tar.gz
|
||||||
775753fc5952a334c415f08768df2f0b73a3228a16e8f5f63d545daacb4e3357 go1.25.7.linux-loong64.tar.gz
|
05401fe5ea50ad2bafb9c797ef9bf21574b0661f19ef4d0dd66af8a0fb7323f3 go1.25.10.linux-loong64.tar.gz
|
||||||
1a023bb367c5fbb4c637a2f6dc23ff17c6591ad929ce16ea88c74d857153b307 go1.25.7.linux-mips.tar.gz
|
d5bc2d6155d394a3aae41f21eb7c60da5595a6147aa0f30ed6b27da25e06c3f7 go1.25.10.linux-mips.tar.gz
|
||||||
a8e97223d8aa6fdfd45f132a4784d2f536bbac5f3d63a24b63d33b6bfe1549af go1.25.7.linux-mips64.tar.gz
|
8c64e7493e5953c3ba3153487d2fddd7f8ed142392c77f138e6792a6c1930db4 go1.25.10.linux-mips64.tar.gz
|
||||||
eb9edb6223330d5e20275667c65dea076b064c08e595fe4eba5d7d6055cfaccf go1.25.7.linux-mips64le.tar.gz
|
bd53aa2d558b7c1eadfc6bf01132e1859203a92f458ed7ba75b7f3230f14b095 go1.25.10.linux-mips64le.tar.gz
|
||||||
9c1e693552a5f9bb9e0012d1c5e01456ecefbc59bef53a77305222ce10aba368 go1.25.7.linux-mipsle.tar.gz
|
120b254e2e2980bb06687175db5c4064a85696c53001dc9f59934ad18f74a6bc go1.25.10.linux-mipsle.tar.gz
|
||||||
28a788798e7329acbbc0ac2caa5e4368b1e5ede646cc24429c991214cfb45c63 go1.25.7.linux-ppc64.tar.gz
|
8a6acb21295b0ec974a44608361920ea8dbff5666631a6f556bd7d5f1d56535f go1.25.10.linux-ppc64.tar.gz
|
||||||
42124c0edc92464e2b37b2d7fcd3658f0c47ebd6a098732415a522be8cb88e3f go1.25.7.linux-ppc64le.tar.gz
|
778925fdcdf9a272f823d147fad51545c3334b7ccd8652b2ccaaf2b01800280a go1.25.10.linux-ppc64le.tar.gz
|
||||||
88d59c6893c8425875d6eef8e3434bc2fa2552e5ad4c058c6cd8cd710a0301c8 go1.25.7.linux-riscv64.tar.gz
|
b4f04ad0db48bcfea946db5323919cd21034e0bd2821a557dacd29c1b1013a4b go1.25.10.linux-riscv64.tar.gz
|
||||||
c6b77facf666dc68195ecab05dbf0ebb4e755b2a8b7734c759880557f1c29b0c go1.25.7.linux-s390x.tar.gz
|
936b953e43921a64c12da871f76871ebbeb6d2092a7b8bdc307f5246f3c662cc go1.25.10.linux-s390x.tar.gz
|
||||||
f14c184d9ade0ee04c7735d4071257b90896ecbde1b32adae84135f055e6399b go1.25.7.netbsd-386.tar.gz
|
061470e0bc7132146a5925a3cc28d5bc498eb1b1ff09dedcfaae10f781ff2274 go1.25.10.netbsd-386.tar.gz
|
||||||
7e7389e404dca1088c31f0fc07f1dd60891d7182bcd621469c14f7e79eceb3ff go1.25.7.netbsd-amd64.tar.gz
|
63b2d50d7f8f269a9c82d42a4060e90cffb7f9102299818bb071b067aac8da8f go1.25.10.netbsd-amd64.tar.gz
|
||||||
70388bb3ef2f03dbf1357e9056bd09034a67e018262557354f8cf549766b3f9d go1.25.7.netbsd-arm.tar.gz
|
c35129f68796526aa4dc4b6f481e2d995ef312aedadc88b659b945cc00e1f8f0 go1.25.10.netbsd-arm.tar.gz
|
||||||
8c1cda9d25bfc9b18d24d5f95fc23949dd3ff99fa408a6cfa40e2cf12b07e362 go1.25.7.netbsd-arm64.tar.gz
|
2f541da4e2b298154d992d1f11bbb38c89d0821d91cc50a46776d42bb5e63bca go1.25.10.netbsd-arm64.tar.gz
|
||||||
42f0d1bfbe39b8401cccb84dd66b30795b97bfc9620dfdc17c5cd4fcf6495cb0 go1.25.7.openbsd-386.tar.gz
|
2d42e569b07f1b99fdbfd008e7c22f967d165e2ce02464f46818fbed2aec43f5 go1.25.10.openbsd-386.tar.gz
|
||||||
e514879c0a28bc32123cd52c4c093de912477fe83f36a6d07517d066ef55391a go1.25.7.openbsd-amd64.tar.gz
|
0ad05960e8c9f867328151308c87f938433bec8f22f6a9437a896e22169fc840 go1.25.10.openbsd-amd64.tar.gz
|
||||||
8cd22530695a0218232bf7efea8f162df1697a3106942ac4129b8c3de39ce4ef go1.25.7.openbsd-arm.tar.gz
|
099cc11473f99461c77161912740945308f08f6834980afb262c72bdc915f2d7 go1.25.10.openbsd-arm.tar.gz
|
||||||
938720f6ebc0d1c53d7840321d3a31f29fd02496e84a6538f442a9311dc1cc9a go1.25.7.openbsd-arm64.tar.gz
|
bdf3335d5008c1ddc81fa94892283e4f1fee22566f5351d4e726d9f55a67c838 go1.25.10.openbsd-arm64.tar.gz
|
||||||
a4c378b73b98f89a3596c2ef51aabbb28783d9ca29f7e317d8ca07939660ce6f go1.25.7.openbsd-ppc64.tar.gz
|
0933d418da0a61e0f29de717a77498f16b9b5b50dbe2205e20b2ed7fd4067f75 go1.25.10.openbsd-ppc64.tar.gz
|
||||||
937b58734fbeaa8c7941a0e4285e7e84b7885396e8d11c23f9ab1a8ff10ff20e go1.25.7.openbsd-riscv64.tar.gz
|
191e6f3e75712f8c13d189d53b668e2cac6449f26474c1d86fbd04f6e9846f9c go1.25.10.openbsd-riscv64.tar.gz
|
||||||
61a093c8c5244916f25740316386bb9f141545dcf01b06a79d1c78ece488403e go1.25.7.plan9-386.tar.gz
|
68c053c8acd76c50fc430e92f4a86110ec3d97dd03d27b9339b4eaf793caff5f go1.25.10.plan9-386.tar.gz
|
||||||
7fc8f6689c9de8ccb7689d2278035fa83c2d601409101840df6ddfe09ba58699 go1.25.7.plan9-amd64.tar.gz
|
42e2c46638ae22d93402e79efb40faee5c42cf7c56a01bb3ab47c6bb2512b745 go1.25.10.plan9-amd64.tar.gz
|
||||||
9661dff8eaeeb62f1c3aadbc5ff189a2e6744e1ec885e32dbcb438f58a34def5 go1.25.7.plan9-arm.tar.gz
|
3ef1d5838b1648da16724a07b72e839ccbd7cb8899c3e0426afd6b79d494b91c go1.25.10.plan9-arm.tar.gz
|
||||||
28ecba0e1d7950c8b29a4a04962dd49c3bf5221f55a44f17d98f369f82859cf4 go1.25.7.solaris-amd64.tar.gz
|
631e3716017fbec06500a628d97e1155daec3593f0a7812c2ebfe8fc8c96b2ab go1.25.10.solaris-amd64.tar.gz
|
||||||
baa6b488291801642fa620026169e38bec2da2ac187cd3ae2145721cf826bbc3 go1.25.7.windows-386.zip
|
ddc693d2d9d7cc671ebb72d1d50aa05670f95b059b7d90440611af57976871d5 go1.25.10.windows-386.zip
|
||||||
c75e5f4ff62d085cc0017be3ad19d5536f46825fa05db06ec468941f847e3228 go1.25.7.windows-amd64.zip
|
ca37af2dadd8544464f1a9ca7c3886499d1cdfcb263855d0a1d71f194b2bd222 go1.25.10.windows-amd64.zip
|
||||||
807033f85931bc4a589ca8497535dcbeb1f30d506e47fa200f5f04c4a71c3d9f go1.25.7.windows-arm64.zip
|
38be57e0398bd93673d65bcae6dc7ee3cf151d7038d0dba5c60a5153022872da go1.25.10.windows-arm64.zip
|
||||||
|
|
||||||
# version:golangci 2.10.1
|
# version:golangci 2.10.1
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
|
|
|
||||||
145
build/ci.go
145
build/ci.go
|
|
@ -73,21 +73,9 @@ var (
|
||||||
"./cmd/keeper",
|
"./cmd/keeper",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Files that end up in the geth*.zip archive.
|
// Files that end up in the geth-alltools*.zip archive (and the NSIS installer
|
||||||
gethArchiveFiles = []string{
|
// dev-tools section). Order matches the historical layout produced by ci.go.
|
||||||
"COPYING",
|
allToolsBinaries = []string{"abigen", "evm", "geth", "rlpdump", "clef"}
|
||||||
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"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keeper build targets with their configurations
|
// Keeper build targets with their configurations
|
||||||
keeperTargets = []struct {
|
keeperTargets = []struct {
|
||||||
|
|
@ -107,17 +95,21 @@ var (
|
||||||
Tags: "ziren",
|
Tags: "ziren",
|
||||||
Env: map[string]string{"GOMIPS": "softfloat", "CGO_ENABLED": "0"},
|
Env: map[string]string{"GOMIPS": "softfloat", "CGO_ENABLED": "0"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "womir",
|
||||||
|
GOOS: "wasip1",
|
||||||
|
GOARCH: "wasm",
|
||||||
|
Tags: "womir",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "wasm-js",
|
Name: "wasm-js",
|
||||||
GOOS: "js",
|
GOOS: "js",
|
||||||
GOARCH: "wasm",
|
GOARCH: "wasm",
|
||||||
Tags: "example",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "wasm-wasi",
|
Name: "wasm-wasi",
|
||||||
GOOS: "wasip1",
|
GOOS: "wasip1",
|
||||||
GOARCH: "wasm",
|
GOARCH: "wasm",
|
||||||
Tags: "example",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
|
|
@ -163,11 +155,11 @@ var (
|
||||||
|
|
||||||
// Distros for which packages are created
|
// Distros for which packages are created
|
||||||
debDistros = []string{
|
debDistros = []string{
|
||||||
"xenial", // 16.04, EOL: 04/2026
|
"xenial", // 16.04, EOL: 04/2026
|
||||||
"bionic", // 18.04, EOL: 04/2028
|
"bionic", // 18.04, EOL: 04/2028
|
||||||
"focal", // 20.04, EOL: 04/2030
|
"focal", // 20.04, EOL: 04/2030
|
||||||
"jammy", // 22.04, EOL: 04/2032
|
"jammy", // 22.04, EOL: 04/2032
|
||||||
"noble", // 24.04, EOL: 04/2034
|
"noble", // 24.04, EOL: 04/2034
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is where the tests should be unpacked.
|
// This is where the tests should be unpacked.
|
||||||
|
|
@ -176,13 +168,35 @@ var (
|
||||||
|
|
||||||
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
||||||
|
|
||||||
func executablePath(name string) string {
|
// executablePath returns the path to a built binary in GOBIN, applying the
|
||||||
if runtime.GOOS == "windows" {
|
// platform-specific extension for the given target OS.
|
||||||
|
func executablePath(name, targetOS string) string {
|
||||||
|
if targetOS == "windows" {
|
||||||
name += ".exe"
|
name += ".exe"
|
||||||
}
|
}
|
||||||
return filepath.Join(GOBIN, name)
|
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() {
|
func main() {
|
||||||
log.SetFlags(log.Lshortfile)
|
log.SetFlags(log.Lshortfile)
|
||||||
|
|
||||||
|
|
@ -229,6 +243,7 @@ func main() {
|
||||||
func doInstall(cmdline []string) {
|
func doInstall(cmdline []string) {
|
||||||
var (
|
var (
|
||||||
dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
|
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")
|
arch = flag.String("arch", "", "Architecture to cross build for")
|
||||||
cc = flag.String("cc", "", "C compiler to cross build with")
|
cc = flag.String("cc", "", "C compiler to cross build with")
|
||||||
staticlink = flag.Bool("static", false, "Create statically-linked executable")
|
staticlink = flag.Bool("static", false, "Create statically-linked executable")
|
||||||
|
|
@ -237,7 +252,7 @@ func doInstall(cmdline []string) {
|
||||||
env := build.Env()
|
env := build.Env()
|
||||||
|
|
||||||
// Configure the toolchain.
|
// Configure the toolchain.
|
||||||
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
tc := build.GoToolchain{GOOS: *targetOS, GOARCH: *arch, CC: *cc}
|
||||||
if *dlgo {
|
if *dlgo {
|
||||||
csdb := download.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
tc.Root = build.DownloadGo(csdb)
|
tc.Root = build.DownloadGo(csdb)
|
||||||
|
|
@ -251,7 +266,7 @@ func doInstall(cmdline []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the build.
|
// Configure the build.
|
||||||
gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...)
|
gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags, *targetOS)...)
|
||||||
|
|
||||||
// Show packages during build.
|
// Show packages during build.
|
||||||
gobuild.Args = append(gobuild.Args, "-v")
|
gobuild.Args = append(gobuild.Args, "-v")
|
||||||
|
|
@ -266,7 +281,7 @@ func doInstall(cmdline []string) {
|
||||||
// Do the build!
|
// Do the build!
|
||||||
for _, pkg := range packages {
|
for _, pkg := range packages {
|
||||||
args := slices.Clone(gobuild.Args)
|
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)
|
args = append(args, pkg)
|
||||||
build.MustRun(&exec.Cmd{Path: gobuild.Path, Args: args, Env: gobuild.Env})
|
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.GOARCH = target.GOARCH
|
||||||
tc.GOOS = target.GOOS
|
tc.GOOS = target.GOOS
|
||||||
tc.CC = target.CC
|
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.Dir = "./cmd/keeper"
|
||||||
gobuild.Args = append(gobuild.Args, "-v")
|
gobuild.Args = append(gobuild.Args, "-v")
|
||||||
|
|
||||||
|
|
@ -303,14 +324,15 @@ func doInstallKeeper(cmdline []string) {
|
||||||
outputName := fmt.Sprintf("keeper-%s", target.Name)
|
outputName := fmt.Sprintf("keeper-%s", target.Name)
|
||||||
|
|
||||||
args := slices.Clone(gobuild.Args)
|
args := slices.Clone(gobuild.Args)
|
||||||
args = append(args, "-o", executablePath(outputName))
|
args = append(args, "-o", executablePath(outputName, targetOS))
|
||||||
args = append(args, ".")
|
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.
|
// buildFlags returns the go tool flags for building. targetOS is the OS we
|
||||||
func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
|
// are producing binaries for.
|
||||||
|
func buildFlags(env build.Environment, staticLinking bool, buildTags []string, targetOS string) (flags []string) {
|
||||||
var ld []string
|
var ld []string
|
||||||
// See https://github.com/golang/go/issues/33772#issuecomment-528176001
|
// 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
|
// 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,
|
// 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.
|
// and there is no downside to this, so we just keep doing it.
|
||||||
if runtime.GOOS == "darwin" {
|
if targetOS == "darwin" {
|
||||||
ld = append(ld, "-s")
|
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
|
// Enforce the stacksize to 8M, which is the case on most platforms apart from
|
||||||
// alpine Linux.
|
// alpine Linux.
|
||||||
// See https://sourceware.org/binutils/docs-2.23.1/ld/Options.html#Options
|
// See https://sourceware.org/binutils/docs-2.23.1/ld/Options.html#Options
|
||||||
|
|
@ -678,12 +700,13 @@ func downloadProtoc(cachedir string) string {
|
||||||
// Release Packaging
|
// Release Packaging
|
||||||
func doArchive(cmdline []string) {
|
func doArchive(cmdline []string) {
|
||||||
var (
|
var (
|
||||||
arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging")
|
targetOS = flag.String("os", runtime.GOOS, "Target OS the binaries were built for")
|
||||||
atype = flag.String("type", "zip", "Type of archive to write (zip|tar)")
|
arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging")
|
||||||
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`)
|
atype = flag.String("type", "zip", "Type of archive to write (zip|tar)")
|
||||||
signify = flag.String("signify", "", `Environment variable holding the signify key (e.g. LINUX_SIGNIFY_KEY)`)
|
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`)
|
||||||
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
|
signify = flag.String("signify", "", `Environment variable holding the signify key (e.g. LINUX_SIGNIFY_KEY)`)
|
||||||
ext string
|
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
|
||||||
|
ext string
|
||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
switch *atype {
|
switch *atype {
|
||||||
|
|
@ -697,15 +720,15 @@ func doArchive(cmdline []string) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
env = build.Env()
|
env = build.Env()
|
||||||
basegeth = archiveBasename(*arch, version.Archive(env.Commit))
|
basegeth = archiveBasename(*targetOS, *arch, version.Archive(env.Commit))
|
||||||
geth = "geth-" + basegeth + ext
|
geth = "geth-" + basegeth + ext
|
||||||
alltools = "geth-alltools-" + basegeth + ext
|
alltools = "geth-alltools-" + basegeth + ext
|
||||||
)
|
)
|
||||||
maybeSkipArchive(env)
|
maybeSkipArchive(env)
|
||||||
if err := build.WriteArchive(geth, gethArchiveFiles); err != nil {
|
if err := build.WriteArchive(geth, gethArchiveFiles(*targetOS)); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := build.WriteArchive(alltools, allToolsArchiveFiles); err != nil {
|
if err := build.WriteArchive(alltools, allToolsArchiveFiles(*targetOS)); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
for _, archive := range []string{geth, alltools} {
|
for _, archive := range []string{geth, alltools} {
|
||||||
|
|
@ -731,7 +754,11 @@ func doKeeperArchive(cmdline []string) {
|
||||||
maybeSkipArchive(env)
|
maybeSkipArchive(env)
|
||||||
files := []string{"COPYING"}
|
files := []string{"COPYING"}
|
||||||
for _, target := range keeperTargets {
|
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 {
|
if err := build.WriteArchive(keeper, files); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
@ -741,8 +768,8 @@ func doKeeperArchive(cmdline []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func archiveBasename(arch string, archiveVersion string) string {
|
func archiveBasename(targetOS, arch, archiveVersion string) string {
|
||||||
platform := runtime.GOOS + "-" + arch
|
platform := targetOS + "-" + arch
|
||||||
if arch == "arm" {
|
if arch == "arm" {
|
||||||
platform += os.Getenv("GOARM")
|
platform += os.Getenv("GOARM")
|
||||||
}
|
}
|
||||||
|
|
@ -1205,13 +1232,13 @@ func doWindowsInstaller(cmdline []string) {
|
||||||
env := build.Env()
|
env := build.Env()
|
||||||
maybeSkipArchive(env)
|
maybeSkipArchive(env)
|
||||||
|
|
||||||
// Aggregate binaries that are included in the installer
|
// Aggregate binaries that are included in the installer.
|
||||||
var (
|
var (
|
||||||
devTools []string
|
devTools []string
|
||||||
allTools []string
|
allTools []string
|
||||||
gethTool string
|
gethTool string
|
||||||
)
|
)
|
||||||
for _, file := range allToolsArchiveFiles {
|
for _, file := range allToolsArchiveFiles("windows") {
|
||||||
if file == "COPYING" { // license, copied later
|
if file == "COPYING" { // license, copied later
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -1248,16 +1275,24 @@ func doWindowsInstaller(cmdline []string) {
|
||||||
if env.Commit != "" {
|
if env.Commit != "" {
|
||||||
ver[2] += "-" + env.Commit[:8]
|
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 {
|
if err != nil {
|
||||||
log.Fatalf("Failed to convert installer file path: %v", err)
|
log.Fatalf("Failed to convert installer file path: %v", err)
|
||||||
}
|
}
|
||||||
build.MustRunCommand("makensis.exe",
|
// makensis on Windows is "makensis.exe" with /D-style defines; on Linux
|
||||||
"/DOUTPUTFILE="+installer,
|
// (and other Unixes) the binary is "makensis" and accepts -D.
|
||||||
"/DMAJORVERSION="+ver[0],
|
makensisCmd := "makensis"
|
||||||
"/DMINORVERSION="+ver[1],
|
defineFlag := "-D"
|
||||||
"/DBUILDVERSION="+ver[2],
|
if runtime.GOOS == "windows" {
|
||||||
"/DARCH="+*arch,
|
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"),
|
filepath.Join(*workdir, "geth.nsi"),
|
||||||
)
|
)
|
||||||
// Sign and publish installer.
|
// Sign and publish installer.
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ func generate(c *cli.Context) error {
|
||||||
code string
|
code string
|
||||||
err error
|
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)
|
code, err = abigen.BindV2(types, abis, bins, c.String(pkgFlag.Name), libs, aliases)
|
||||||
} else {
|
} else {
|
||||||
code, err = abigen.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), libs, aliases)
|
code, err = abigen.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), libs, aliases)
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ func formatAttrString(v rlp.RawValue) (string, bool) {
|
||||||
|
|
||||||
func formatAttrIP(v rlp.RawValue) (string, bool) {
|
func formatAttrIP(v rlp.RawValue) (string, bool) {
|
||||||
content, _, err := rlp.SplitString(v)
|
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 "", false
|
||||||
}
|
}
|
||||||
return net.IP(content).String(), true
|
return net.IP(content).String(), true
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,12 @@ type Chain struct {
|
||||||
state map[common.Address]state.DumpAccount // state of head block
|
state map[common.Address]state.DumpAccount // state of head block
|
||||||
senders map[common.Address]*senderInfo
|
senders map[common.Address]*senderInfo
|
||||||
config *params.ChainConfig
|
config *params.ChainConfig
|
||||||
|
|
||||||
|
txInfo txInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type txInfo struct {
|
||||||
|
LargeReceiptBlock *uint64 `json:"tx-largereceipt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewChain takes the given chain.rlp file, and decodes and returns
|
// NewChain takes the given chain.rlp file, and decodes and returns
|
||||||
|
|
@ -74,12 +80,20 @@ func NewChain(dir string) (*Chain, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var txInfo txInfo
|
||||||
|
err = common.LoadJSON(filepath.Join(dir, "txinfo.json"), &txInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &Chain{
|
return &Chain{
|
||||||
genesis: gen,
|
genesis: gen,
|
||||||
blocks: blocks,
|
blocks: blocks,
|
||||||
state: state,
|
state: state,
|
||||||
senders: accounts,
|
senders: accounts,
|
||||||
config: gen.Config,
|
config: gen.Config,
|
||||||
|
txInfo: txInfo,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -345,10 +345,12 @@ loop:
|
||||||
if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
|
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)
|
return fmt.Errorf("wrong fork ID in status: have %v, want %v", have, want)
|
||||||
}
|
}
|
||||||
if have, want := msg.ProtocolVersion, c.ourHighestProtoVersion; have != uint32(want) {
|
for _, cap := range c.caps {
|
||||||
return fmt.Errorf("wrong protocol version: have %v, want %v", have, want)
|
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:
|
case discMsg:
|
||||||
var msg []p2p.DiscReason
|
var msg []p2p.DiscReason
|
||||||
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
|
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,9 @@ func (s *Suite) TestSnapGetAccountRange(t *utesting.T) {
|
||||||
root: root,
|
root: root,
|
||||||
startingHash: zero,
|
startingHash: zero,
|
||||||
limitHash: ffHash,
|
limitHash: ffHash,
|
||||||
expAccounts: 67,
|
expAccounts: 68,
|
||||||
expFirst: firstKey,
|
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.",
|
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,
|
root: root,
|
||||||
startingHash: zero,
|
startingHash: zero,
|
||||||
limitHash: ffHash,
|
limitHash: ffHash,
|
||||||
expAccounts: 49,
|
expAccounts: 50,
|
||||||
expFirst: firstKey,
|
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.",
|
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,
|
root: root,
|
||||||
startingHash: zero,
|
startingHash: zero,
|
||||||
limitHash: ffHash,
|
limitHash: ffHash,
|
||||||
expAccounts: 34,
|
expAccounts: 35,
|
||||||
expFirst: firstKey,
|
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.",
|
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,
|
root: root,
|
||||||
startingHash: firstKey,
|
startingHash: firstKey,
|
||||||
limitHash: ffHash,
|
limitHash: ffHash,
|
||||||
expAccounts: 67,
|
expAccounts: 68,
|
||||||
expFirst: firstKey,
|
expFirst: firstKey,
|
||||||
expLast: common.HexToHash("0x622e662246601dd04f996289ce8b85e86db7bb15bb17f86487ec9d543ddb6f9a"),
|
expLast: common.HexToHash("0x59312f89c13e9e24c1cb8b103aa39a9b2800348d97a92c2c9e2a78fa02b70025"),
|
||||||
desc: `In this test, startingHash is exactly the first available account key.
|
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.`,
|
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,
|
root: root,
|
||||||
startingHash: hashAdd(firstKey, 1),
|
startingHash: hashAdd(firstKey, 1),
|
||||||
limitHash: ffHash,
|
limitHash: ffHash,
|
||||||
expAccounts: 67,
|
expAccounts: 68,
|
||||||
expFirst: secondKey,
|
expFirst: secondKey,
|
||||||
expLast: common.HexToHash("0x66192e4c757fba1cdc776e6737008f42d50370d3cd801db3624274283bf7cd63"),
|
expLast: common.HexToHash("0x59a7c8818f1c16b298a054020dc7c3f403a970d1d1db33f9478b1c36e3a2e509"),
|
||||||
desc: `In this test, startingHash is after the first available key.
|
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.`,
|
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),
|
root: s.chain.RootAt(int(s.chain.Head().Number().Uint64()) - 127),
|
||||||
startingHash: zero,
|
startingHash: zero,
|
||||||
limitHash: ffHash,
|
limitHash: ffHash,
|
||||||
expAccounts: 66,
|
expAccounts: 68,
|
||||||
expFirst: firstKey,
|
expFirst: firstKey,
|
||||||
expLast: common.HexToHash("0x729953a43ed6c913df957172680a17e5735143ad767bda8f58ac84ec62fbec5e"),
|
expLast: common.HexToHash("0x683b6c03cc32afe5db8cb96050f711fdaff8f8ff44c7587a9a848f921d02815e"),
|
||||||
desc: `This test requests data at a state root that is 127 blocks old.
|
desc: `This test requests data at a state root that is 127 blocks old.
|
||||||
We expect the server to have this state available.`,
|
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
|
// 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
|
// 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.
|
// way. So you'll have to update this when the test chain is changed.
|
||||||
common.HexToHash("0x5bdc0d6057b35642a16d27223ea5454e5a17a400e28f7328971a5f2a87773b76"),
|
common.HexToHash("0x4bdecec09691ad38113eebee2df94fadefdff5841c0f182bae1be3c8a6d60bf3"),
|
||||||
common.HexToHash("0x0a76c9812ca90ffed8ee4d191e683f93386b6e50cfe3679c0760d27510aa7fc5"),
|
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,
|
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.
|
// be updated when the test chain is changed.
|
||||||
expHashes: []common.Hash{
|
expHashes: []common.Hash{
|
||||||
empty,
|
empty,
|
||||||
common.HexToHash("0x0a76c9812ca90ffed8ee4d191e683f93386b6e50cfe3679c0760d27510aa7fc5"),
|
common.HexToHash("0x4178696465d4514ff5924ef8c28ce64d41a669634b63184c2c093e252d6b4bc4"),
|
||||||
common.HexToHash("0x5bdc0d6057b35642a16d27223ea5454e5a17a400e28f7328971a5f2a87773b76"),
|
common.HexToHash("0x4bdecec09691ad38113eebee2df94fadefdff5841c0f182bae1be3c8a6d60bf3"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -84,6 +85,7 @@ func (s *Suite) EthTests() []utesting.Test {
|
||||||
// get history
|
// get history
|
||||||
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
||||||
{Name: "GetReceipts", Fn: s.TestGetReceipts},
|
{Name: "GetReceipts", Fn: s.TestGetReceipts},
|
||||||
|
{Name: "GetLargeReceipts", Fn: s.TestGetLargeReceipts},
|
||||||
// test transactions
|
// test transactions
|
||||||
{Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
|
{Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
|
||||||
{Name: "Transaction", Fn: s.TestTransaction},
|
{Name: "Transaction", Fn: s.TestTransaction},
|
||||||
|
|
@ -434,6 +436,9 @@ func (s *Suite) TestGetReceipts(t *utesting.T) {
|
||||||
// Find some blocks containing receipts.
|
// Find some blocks containing receipts.
|
||||||
var hashes = make([]common.Hash, 0, 3)
|
var hashes = make([]common.Hash, 0, 3)
|
||||||
for i := range s.chain.Len() {
|
for i := range s.chain.Len() {
|
||||||
|
if s.chain.txInfo.LargeReceiptBlock != nil && uint64(i) == *s.chain.txInfo.LargeReceiptBlock {
|
||||||
|
continue
|
||||||
|
}
|
||||||
block := s.chain.GetBlock(i)
|
block := s.chain.GetBlock(i)
|
||||||
if len(block.Transactions()) > 0 {
|
if len(block.Transactions()) > 0 {
|
||||||
hashes = append(hashes, block.Hash())
|
hashes = append(hashes, block.Hash())
|
||||||
|
|
@ -442,25 +447,121 @@ func (s *Suite) TestGetReceipts(t *utesting.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if conn.negotiatedProtoVersion < eth.ETH70 {
|
||||||
|
// Create block bodies request.
|
||||||
|
req := ð.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 := ð.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.
|
func (s *Suite) TestGetLargeReceipts(t *utesting.T) {
|
||||||
req := ð.GetReceiptsPacket{
|
t.Log(`This test sends GetReceipts requests to the node for large receipt (>10MiB) in the test chain.
|
||||||
RequestId: 66,
|
This test is meaningful only if the client supports protocol version ETH70 or higher
|
||||||
GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes),
|
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 {
|
defer conn.Close()
|
||||||
t.Fatalf("could not write to connection: %v", err)
|
|
||||||
|
if conn.negotiatedProtoVersion < eth.ETH70 || s.chain.txInfo.LargeReceiptBlock == nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// Wait for response.
|
|
||||||
resp := new(eth.ReceiptsPacket)
|
// Find block with large receipt.
|
||||||
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
|
// Place the large receipt block hash in the middle of the query
|
||||||
t.Fatalf("error reading block bodies msg: %v", err)
|
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, ð.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 := ð.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
BIN
cmd/devp2p/internal/ethtest/testdata/chain.rlp
vendored
BIN
cmd/devp2p/internal/ethtest/testdata/chain.rlp
vendored
Binary file not shown.
|
|
@ -37,7 +37,7 @@
|
||||||
"nonce": "0x0",
|
"nonce": "0x0",
|
||||||
"timestamp": "0x0",
|
"timestamp": "0x0",
|
||||||
"extraData": "0x68697665636861696e",
|
"extraData": "0x68697665636861696e",
|
||||||
"gasLimit": "0x23f3e20",
|
"gasLimit": "0x11e1a300",
|
||||||
"difficulty": "0x20000",
|
"difficulty": "0x20000",
|
||||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||||
|
|
@ -119,6 +119,10 @@
|
||||||
"balance": "0x1",
|
"balance": "0x1",
|
||||||
"nonce": "0x1"
|
"nonce": "0x1"
|
||||||
},
|
},
|
||||||
|
"8dcd17433742f4c0ca53122ab541d0ba67fc27ff": {
|
||||||
|
"code": "0x6202e6306000a0",
|
||||||
|
"balance": "0x0"
|
||||||
|
},
|
||||||
"c7b99a164efd027a93f147376cc7da7c67c6bbe0": {
|
"c7b99a164efd027a93f147376cc7da7c67c6bbe0": {
|
||||||
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
{
|
{
|
||||||
"parentHash": "0x65151b101682b54cd08ba226f640c14c86176865ff9bfc57e0147dadaeac34bb",
|
"parentHash": "0x7e80093a491eba0e5b2c1895837902f64f514100221801318fe391e1e09c96a6",
|
||||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
"miner": "0x0000000000000000000000000000000000000000",
|
"miner": "0x0000000000000000000000000000000000000000",
|
||||||
"stateRoot": "0xce423ebc60fc7764a43f09f1fe3ae61eef25e3eb8d09b1108f7e7eb77dfff5e6",
|
"stateRoot": "0x8fcfb02cfca007773bd55bc1c3e50a3c8612a59c87ce057e5957e8bf17c1728b",
|
||||||
"transactionsRoot": "0x7ec1ae3989efa75d7bcc766e5e2443afa8a89a5fda42ebba90050e7e702980f7",
|
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
"receiptsRoot": "0xfe160832b1ca85f38c6674cb0aae3a24693bc49be56e2ecdf3698b71a794de86",
|
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"difficulty": "0x0",
|
"difficulty": "0x0",
|
||||||
"number": "0x258",
|
"number": "0x258",
|
||||||
"gasLimit": "0x23f3e20",
|
"gasLimit": "0x11e1a300",
|
||||||
"gasUsed": "0x19d36",
|
"gasUsed": "0x0",
|
||||||
"timestamp": "0x1770",
|
"timestamp": "0x1770",
|
||||||
"extraData": "0x",
|
"extraData": "0x",
|
||||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"nonce": "0x0000000000000000",
|
"nonce": "0x0000000000000000",
|
||||||
"baseFeePerGas": "0x7",
|
"baseFeePerGas": "0x7",
|
||||||
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
"withdrawalsRoot": "0x92abfda39de7df7d705c5a8f30386802ad59d31e782a06d5c5b0f9a260056cf0",
|
||||||
"blobGasUsed": "0x0",
|
"blobGasUsed": "0x0",
|
||||||
"excessBlobGas": "0x0",
|
"excessBlobGas": "0x0",
|
||||||
"parentBeaconBlockRoot": "0xf5003fc8f92358e790a114bce93ce1d9c283c85e1787f8d7d56714d3489b49e6",
|
"parentBeaconBlockRoot": "0xf5003fc8f92358e790a114bce93ce1d9c283c85e1787f8d7d56714d3489b49e6",
|
||||||
"requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
"requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||||
"hash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0"
|
"hash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a"
|
||||||
}
|
}
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
"method": "engine_forkchoiceUpdatedV3",
|
"method": "engine_forkchoiceUpdatedV3",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
{
|
||||||
"headBlockHash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0",
|
"headBlockHash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a",
|
||||||
"safeBlockHash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0",
|
"safeBlockHash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a",
|
||||||
"finalizedBlockHash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0"
|
"finalizedBlockHash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a"
|
||||||
},
|
},
|
||||||
null
|
null
|
||||||
]
|
]
|
||||||
|
|
|
||||||
4210
cmd/devp2p/internal/ethtest/testdata/headstate.json
vendored
4210
cmd/devp2p/internal/ethtest/testdata/headstate.json
vendored
File diff suppressed because it is too large
Load diff
10313
cmd/devp2p/internal/ethtest/testdata/newpayload.json
vendored
10313
cmd/devp2p/internal/ethtest/testdata/newpayload.json
vendored
File diff suppressed because it is too large
Load diff
2943
cmd/devp2p/internal/ethtest/testdata/txinfo.json
vendored
2943
cmd/devp2p/internal/ethtest/testdata/txinfo.json
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -257,34 +257,50 @@ that they are returned by FINDNODE.`)
|
||||||
|
|
||||||
// Create bystanders.
|
// Create bystanders.
|
||||||
nodes := make([]*bystander, 5)
|
nodes := make([]*bystander, 5)
|
||||||
added := make(chan enode.ID, len(nodes))
|
liveCh := make(chan enode.ID, len(nodes))
|
||||||
for i := range nodes {
|
for i := range nodes {
|
||||||
nodes[i] = newBystander(t, s, added)
|
nodes[i] = newBystander(t, s, liveCh)
|
||||||
defer nodes[i].close()
|
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
|
timeout := 60 * time.Second
|
||||||
timeoutCh := time.After(timeout)
|
timeoutCh := time.After(timeout)
|
||||||
for count := 0; count < len(nodes); {
|
liveSet := make(map[enode.ID]*enode.Node)
|
||||||
|
for len(liveSet) < requiredLiveNodes {
|
||||||
select {
|
select {
|
||||||
case id := <-added:
|
case id := <-liveCh:
|
||||||
t.Logf("bystander node %v added to remote table", id)
|
for _, bn := range nodes {
|
||||||
count++
|
if bn.id() == id {
|
||||||
|
liveSet[id] = bn.conn.localNode.Node()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Logf("bystander node %v became live", id)
|
||||||
case <-timeoutCh:
|
case <-timeoutCh:
|
||||||
t.Errorf("remote added %d bystander nodes in %v, need %d to continue", count, timeout, len(nodes))
|
t.Errorf("remote revalidated %d bystander nodes in %v, need %d to continue", len(liveSet), timeout, requiredLiveNodes)
|
||||||
t.Logf("this can happen if the node has a non-empty table from previous runs")
|
|
||||||
return
|
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
|
var dists []uint
|
||||||
expect := make(map[enode.ID]*enode.Node)
|
expect := make(map[enode.ID]*enode.Node)
|
||||||
for _, bn := range nodes {
|
for id, n := range liveSet {
|
||||||
n := bn.conn.localNode.Node()
|
expect[id] = n
|
||||||
expect[n.ID()] = n
|
|
||||||
d := uint(enode.LogDist(n.ID(), s.Dest.ID()))
|
d := uint(enode.LogDist(n.ID(), s.Dest.ID()))
|
||||||
if !slices.Contains(dists, d) {
|
if !slices.Contains(dists, d) {
|
||||||
dists = append(dists, d)
|
dists = append(dists, d)
|
||||||
|
|
@ -295,42 +311,63 @@ that they are returned by FINDNODE.`)
|
||||||
t.Log("requesting nodes")
|
t.Log("requesting nodes")
|
||||||
conn, l1 := s.listen1(t)
|
conn, l1 := s.listen1(t)
|
||||||
defer conn.close()
|
defer conn.close()
|
||||||
foundNodes, err := conn.findnode(l1, dists)
|
|
||||||
if err != nil {
|
const maxAttempts = 5
|
||||||
t.Fatal(err)
|
const retryInterval = 2 * time.Second
|
||||||
}
|
|
||||||
t.Logf("remote returned %d nodes for distance list %v", len(foundNodes), dists)
|
for attempt := 1; attempt <= maxAttempts; attempt++ {
|
||||||
for _, n := range foundNodes {
|
foundNodes, err := conn.findnode(l1, dists)
|
||||||
delete(expect, n.ID())
|
if err != nil {
|
||||||
}
|
t.Fatal(err)
|
||||||
if len(expect) > 0 {
|
}
|
||||||
t.Errorf("missing %d nodes in FINDNODE result", len(expect))
|
missing := make(map[enode.ID]struct{})
|
||||||
t.Logf("this can happen if the test is run multiple times in quick succession")
|
for id := range expect {
|
||||||
t.Logf("and the remote node hasn't removed dead nodes from previous runs yet")
|
missing[id] = struct{}{}
|
||||||
} else {
|
}
|
||||||
t.Logf("all %d expected nodes were returned", len(nodes))
|
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.
|
// A bystander is a node whose only purpose is filling a spot in the remote table.
|
||||||
type bystander struct {
|
type bystander struct {
|
||||||
dest *enode.Node
|
dest *enode.Node
|
||||||
conn *conn
|
conn *conn
|
||||||
l net.PacketConn
|
l net.PacketConn
|
||||||
|
known []*enode.Node
|
||||||
|
|
||||||
addedCh chan enode.ID
|
liveCh chan enode.ID
|
||||||
done sync.WaitGroup
|
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, l := s.listen1(t)
|
||||||
conn.setEndpoint(l) // bystander nodes need IP/port to get pinged
|
conn.setEndpoint(l) // bystander nodes need IP/port to get pinged
|
||||||
bn := &bystander{
|
bn := &bystander{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
l: l,
|
l: l,
|
||||||
dest: s.Dest,
|
dest: s.Dest,
|
||||||
addedCh: added,
|
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)
|
bn.done.Add(1)
|
||||||
go bn.loop()
|
go bn.loop()
|
||||||
return bn
|
return bn
|
||||||
|
|
@ -351,48 +388,57 @@ func (bn *bystander) close() {
|
||||||
func (bn *bystander) loop() {
|
func (bn *bystander) loop() {
|
||||||
defer bn.done.Done()
|
defer bn.done.Done()
|
||||||
|
|
||||||
var (
|
|
||||||
lastPing time.Time
|
|
||||||
wasAdded bool
|
|
||||||
)
|
|
||||||
for {
|
for {
|
||||||
// Ping the remote node.
|
p, from := bn.conn.readFrom(bn.l)
|
||||||
if !wasAdded && time.Since(lastPing) > 10*time.Second {
|
switch p := p.(type) {
|
||||||
bn.conn.reqresp(bn.l, &v5wire.Ping{
|
case *v5wire.Whoareyou:
|
||||||
ReqID: bn.conn.nextReqID(),
|
p.Node = bn.dest
|
||||||
ENRSeq: bn.dest.Seq(),
|
if resp, ok := bn.sent[p.Nonce]; ok {
|
||||||
})
|
nonce := bn.conn.writeTo(bn.l, resp, p, from)
|
||||||
lastPing = time.Now()
|
delete(bn.sent, p.Nonce)
|
||||||
}
|
bn.sent[nonce] = resp
|
||||||
// Answer packets.
|
} else {
|
||||||
switch p := bn.conn.read(bn.l).(type) {
|
bn.conn.writeTo(bn.l, &v5wire.Ping{
|
||||||
case *v5wire.Ping:
|
ReqID: bn.conn.nextReqID(),
|
||||||
bn.conn.write(bn.l, &v5wire.Pong{
|
ENRSeq: bn.conn.localNode.Seq(),
|
||||||
ReqID: p.ReqID,
|
}, p, from)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
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() {
|
func (bn *bystander) notifyLive() {
|
||||||
if bn.addedCh != nil {
|
if bn.liveCh != nil {
|
||||||
bn.addedCh <- bn.id()
|
bn.liveCh <- bn.id()
|
||||||
bn.addedCh = nil
|
bn.liveCh = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,14 +127,16 @@ func (tc *conn) nextReqID() []byte {
|
||||||
// The request is retried if a handshake is requested.
|
// The request is retried if a handshake is requested.
|
||||||
func (tc *conn) reqresp(c net.PacketConn, req v5wire.Packet) v5wire.Packet {
|
func (tc *conn) reqresp(c net.PacketConn, req v5wire.Packet) v5wire.Packet {
|
||||||
reqnonce := tc.write(c, req, nil)
|
reqnonce := tc.write(c, req, nil)
|
||||||
switch resp := tc.read(c).(type) {
|
resp, from := tc.readFrom(c)
|
||||||
|
switch resp := resp.(type) {
|
||||||
case *v5wire.Whoareyou:
|
case *v5wire.Whoareyou:
|
||||||
if resp.Nonce != reqnonce {
|
if resp.Nonce != reqnonce {
|
||||||
return readErrorf("wrong nonce %x in WHOAREYOU (want %x)", resp.Nonce[:], reqnonce[:])
|
return readErrorf("wrong nonce %x in WHOAREYOU (want %x)", resp.Nonce[:], reqnonce[:])
|
||||||
}
|
}
|
||||||
resp.Node = tc.remote
|
resp.Node = tc.remote
|
||||||
tc.write(c, req, resp)
|
tc.writeTo(c, req, resp, from)
|
||||||
return tc.read(c)
|
resp2, _ := tc.readFrom(c)
|
||||||
|
return resp2
|
||||||
default:
|
default:
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
@ -150,21 +152,24 @@ func (tc *conn) findnode(c net.PacketConn, dists []uint) ([]*enode.Node, error)
|
||||||
results []*enode.Node
|
results []*enode.Node
|
||||||
)
|
)
|
||||||
for n := 1; n > 0; {
|
for n := 1; n > 0; {
|
||||||
switch resp := tc.read(c).(type) {
|
resp, from := tc.readFrom(c)
|
||||||
|
switch resp := resp.(type) {
|
||||||
case *v5wire.Whoareyou:
|
case *v5wire.Whoareyou:
|
||||||
// Handle handshake.
|
// Handle handshake.
|
||||||
if resp.Nonce == reqnonce {
|
if resp.Nonce == reqnonce {
|
||||||
resp.Node = tc.remote
|
resp.Node = tc.remote
|
||||||
tc.write(c, findnode, resp)
|
tc.writeTo(c, findnode, resp, from)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("unexpected WHOAREYOU (nonce %x), waiting for NODES", resp.Nonce[:])
|
return nil, fmt.Errorf("unexpected WHOAREYOU (nonce %x), waiting for NODES", resp.Nonce[:])
|
||||||
}
|
}
|
||||||
case *v5wire.Ping:
|
case *v5wire.Ping:
|
||||||
// Handle ping from remote.
|
// Handle ping from remote.
|
||||||
tc.write(c, &v5wire.Pong{
|
tc.writeTo(c, &v5wire.Pong{
|
||||||
ReqID: resp.ReqID,
|
ReqID: resp.ReqID,
|
||||||
ENRSeq: tc.localNode.Seq(),
|
ENRSeq: tc.localNode.Seq(),
|
||||||
}, nil)
|
ToIP: from.IP,
|
||||||
|
ToPort: uint16(from.Port),
|
||||||
|
}, nil, from)
|
||||||
case *v5wire.Nodes:
|
case *v5wire.Nodes:
|
||||||
// Got NODES! Check request ID.
|
// Got NODES! Check request ID.
|
||||||
if !bytes.Equal(resp.ReqID, findnode.ReqID) {
|
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.
|
// write sends a packet on the given connection.
|
||||||
func (tc *conn) write(c net.PacketConn, p v5wire.Packet, challenge *v5wire.Whoareyou) v5wire.Nonce {
|
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)
|
packet, nonce, err := tc.codec.Encode(tc.remote.ID(), tc.remoteAddr.String(), p, challenge)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("can't encode %v packet: %v", p.Name(), err))
|
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)
|
tc.logf("Can't send %s: %v", p.Name(), err)
|
||||||
} else {
|
} else {
|
||||||
tc.logf(">> %s", p.Name())
|
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.
|
// read waits for an incoming packet on the given connection.
|
||||||
func (tc *conn) read(c net.PacketConn) v5wire.Packet {
|
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)
|
buf := make([]byte, 1280)
|
||||||
if err := c.SetReadDeadline(time.Now().Add(waitTime)); err != nil {
|
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 {
|
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 {
|
if err != nil {
|
||||||
return &readError{err}
|
return &readError{err}, udpFrom
|
||||||
}
|
}
|
||||||
tc.logf("<< %s", p.Name())
|
tc.logf("<< %s", p.Name())
|
||||||
return p
|
return p, udpFrom
|
||||||
}
|
}
|
||||||
|
|
||||||
// logf prints to the test log.
|
// logf prints to the test log.
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
@ -30,6 +31,31 @@ import (
|
||||||
"github.com/urfave/cli/v2"
|
"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 (
|
var (
|
||||||
rlpxCommand = &cli.Command{
|
rlpxCommand = &cli.Command{
|
||||||
Name: "rlpx",
|
Name: "rlpx",
|
||||||
|
|
@ -103,11 +129,15 @@ func rlpxPing(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
fmt.Printf("%+v\n", h)
|
fmt.Printf("%+v\n", h)
|
||||||
case 1:
|
case 1:
|
||||||
var msg []p2p.DiscReason
|
// The disconnect message is specified as a list containing the reason,
|
||||||
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
|
// but some implementations (including older geth) send the reason as a
|
||||||
return errors.New("invalid disconnect message")
|
// 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:
|
default:
|
||||||
return fmt.Errorf("invalid message code %d, expected handshake (code zero) or disconnect (code one)", code)
|
return fmt.Errorf("invalid message code %d, expected handshake (code zero) or disconnect (code one)", code)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
cmd/devp2p/rlpxcmd_test.go
Normal file
75
cmd/devp2p/rlpxcmd_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -337,9 +337,6 @@ func checkAccumulator(e era.Era) error {
|
||||||
// accumulation across the entire set and are verified at the end.
|
// accumulation across the entire set and are verified at the end.
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
// 1) next() walks the block index, so we're able to implicitly verify it.
|
// 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()
|
block, receipts, err := it.BlockAndReceipts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error reading block %d: %w", it.Number(), err)
|
return fmt.Errorf("error reading block %d: %w", it.Number(), err)
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,12 @@
|
||||||
package t8ntool
|
package t8ntool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
stdmath "math"
|
stdmath "math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
|
@ -47,6 +50,9 @@ type Prestate struct {
|
||||||
Env stEnv `json:"env"`
|
Env stEnv `json:"env"`
|
||||||
Pre types.GenesisAlloc `json:"pre"`
|
Pre types.GenesisAlloc `json:"pre"`
|
||||||
TreeLeaves map[common.Hash]hexutil.Bytes `json:"vkt,omitempty"`
|
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
|
//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
|
return h
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
isEIP4762 = chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
|
isEIP4762 = chainConfig.IsUBT(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
|
||||||
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762)
|
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)
|
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp)
|
||||||
gaspool = core.NewGasPool(pre.Env.GasLimit)
|
gaspool = core.NewGasPool(pre.Env.GasLimit)
|
||||||
blockHash = common.Hash{0x13, 0x37}
|
blockHash = common.Hash{0x13, 0x37}
|
||||||
|
|
@ -253,7 +270,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statedb.SetTxContext(tx.Hash(), len(receipts))
|
statedb.SetTxContext(tx.Hash(), len(receipts), uint32(len(receipts)+1))
|
||||||
var (
|
var (
|
||||||
snapshot = statedb.Snapshot()
|
snapshot = statedb.Snapshot()
|
||||||
gp = gaspool.Snapshot()
|
gp = gaspool.Snapshot()
|
||||||
|
|
@ -315,27 +332,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather the execution-layer triggered requests.
|
// Gather the execution-layer triggered requests.
|
||||||
var requests [][]byte
|
var allLogs []*types.Log
|
||||||
if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) {
|
for _, receipt := range receipts {
|
||||||
requests = [][]byte{}
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
// EIP-6110
|
}
|
||||||
var allLogs []*types.Log
|
requests, err := core.PostExecution(context.Background(), chainConfig, vmContext.BlockNumber, vmContext.Time, allLogs, evm, uint32(len(receipts)+1))
|
||||||
for _, receipt := range receipts {
|
if err != nil {
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("failed to process post-execution: %v", err))
|
||||||
}
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit block
|
// Commit block
|
||||||
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), chainConfig.IsCancun(vmContext.BlockNumber, vmContext.Time))
|
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), chainConfig.IsCancun(vmContext.BlockNumber, vmContext.Time))
|
||||||
if err != nil {
|
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 {
|
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)
|
sdb := state.NewDatabase(tdb, nil)
|
||||||
|
|
||||||
root := types.EmptyRootHash
|
root := types.EmptyRootHash
|
||||||
|
|
@ -414,6 +418,76 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool
|
||||||
return statedb
|
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) {
|
func rlpHash(x any) (h common.Hash) {
|
||||||
hw := keccak.NewLegacyKeccak256()
|
hw := keccak.NewLegacyKeccak256()
|
||||||
rlp.Encode(hw, x)
|
rlp.Encode(hw, x)
|
||||||
|
|
|
||||||
|
|
@ -133,21 +133,21 @@ func Transaction(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
// Check intrinsic gas
|
// Check intrinsic gas
|
||||||
rules := chainConfig.Rules(common.Big0, true, 0)
|
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 {
|
if err != nil {
|
||||||
r.Error = err
|
r.Error = err
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r.IntrinsicGas = gas
|
r.IntrinsicGas = cost.RegularGas
|
||||||
if tx.Gas() < gas {
|
if tx.Gas() < cost.RegularGas {
|
||||||
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas)
|
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), cost.RegularGas)
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// For Prague txs, validate the floor data gas.
|
// For Prague txs, validate the floor data gas.
|
||||||
if rules.IsPrague {
|
if rules.IsPrague {
|
||||||
floorDataGas, err := core.FloorDataGas(tx.Data())
|
floorDataGas, err := core.FloorDataGas(rules, tx.Data(), tx.AccessList())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Error = err
|
r.Error = err
|
||||||
results = append(results, r)
|
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")
|
r.Error = errors.New("gas limit exceeds maximum")
|
||||||
}
|
}
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package t8ntool
|
package t8ntool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -115,11 +116,10 @@ func Transition(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if allocStr != stdinSelector {
|
if allocStr != stdinSelector {
|
||||||
if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil {
|
prestate.AllocPath = allocStr
|
||||||
return err
|
} else {
|
||||||
}
|
prestate.Pre = inputData.Alloc
|
||||||
}
|
}
|
||||||
prestate.Pre = inputData.Alloc
|
|
||||||
|
|
||||||
if btStr != stdinSelector && btStr != "" {
|
if btStr != stdinSelector && btStr != "" {
|
||||||
if err := readFile(btStr, "BT", &inputData.BT); err != nil {
|
if err := readFile(btStr, "BT", &inputData.BT); err != nil {
|
||||||
|
|
@ -223,22 +223,57 @@ func Transition(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Dump the execution result
|
// Dump the execution result.
|
||||||
var (
|
var (
|
||||||
collector = make(Alloc)
|
collector Alloc
|
||||||
btleaves map[common.Hash]hexutil.Bytes
|
btleaves map[common.Hash]hexutil.Bytes
|
||||||
)
|
)
|
||||||
isBinary := chainConfig.IsVerkle(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp)
|
isBinary := chainConfig.IsUBT(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp)
|
||||||
if !isBinary {
|
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)
|
s.DumpToCollector(collector, nil)
|
||||||
} else {
|
default:
|
||||||
btleaves = make(map[common.Hash]hexutil.Bytes)
|
btleaves = make(map[common.Hash]hexutil.Bytes)
|
||||||
if err := s.DumpBinTrieLeaves(btleaves); err != nil {
|
if err := s.DumpBinTrieLeaves(btleaves); err != nil {
|
||||||
return err
|
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 {
|
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 {
|
if addr == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
g[*addr] = dumpAccountToTypesAccount(dumpAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpAccountToTypesAccount(dumpAccount state.DumpAccount) types.Account {
|
||||||
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
|
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
|
||||||
var storage map[common.Hash]common.Hash
|
var storage map[common.Hash]common.Hash
|
||||||
if dumpAccount.Storage != nil {
|
if dumpAccount.Storage != nil {
|
||||||
|
|
@ -335,13 +374,64 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
||||||
storage[k] = common.HexToHash(v)
|
storage[k] = common.HexToHash(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
genesisAccount := types.Account{
|
return types.Account{
|
||||||
Code: dumpAccount.Code,
|
Code: dumpAccount.Code,
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
Balance: balance,
|
Balance: balance,
|
||||||
Nonce: dumpAccount.Nonce,
|
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
|
// 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
|
// dispatchOutput writes the output data to either stderr or stdout, or to the specified
|
||||||
// files
|
// files. An empty allocOutput skips the alloc dispatch, which is used when the
|
||||||
func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes, bt map[common.Hash]hexutil.Bytes) error {
|
// 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{})
|
stdOutObject := make(map[string]interface{})
|
||||||
stdErrObject := make(map[string]interface{})
|
stdErrObject := make(map[string]interface{})
|
||||||
dispatch := func(baseDir, fName, name string, obj interface{}) error {
|
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
|
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
|
return err
|
||||||
}
|
}
|
||||||
if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
|
if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
|
||||||
|
|
@ -452,10 +543,10 @@ func BinKeys(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.VerkleDefaults)
|
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.UBTDefaults)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
bt, err := genBinTrieFromAlloc(alloc, db)
|
bt, err := genBinTrieFromAlloc(alloc, db, triedb.UBTDefaults.BinTrieGroupDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error generating bt: %w", err)
|
return fmt.Errorf("error generating bt: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -496,10 +587,10 @@ func BinTrieRoot(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.VerkleDefaults)
|
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.UBTDefaults)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
bt, err := genBinTrieFromAlloc(alloc, db)
|
bt, err := genBinTrieFromAlloc(alloc, db, triedb.UBTDefaults.BinTrieGroupDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error generating bt: %w", err)
|
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?
|
// TODO(@CPerezz): Should this go to `bintrie` module?
|
||||||
func genBinTrieFromAlloc(alloc core.GenesisAlloc, db database.NodeDatabase) (*bintrie.BinaryTrie, error) {
|
func genBinTrieFromAlloc(alloc core.GenesisAlloc, db database.NodeDatabase, groupDepth int) (*bintrie.BinaryTrie, error) {
|
||||||
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, db)
|
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, db, groupDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ var (
|
||||||
Name: "trace.noreturndata",
|
Name: "trace.noreturndata",
|
||||||
Aliases: []string{"noreturndata"},
|
Aliases: []string{"noreturndata"},
|
||||||
Value: true,
|
Value: true,
|
||||||
Usage: "enable return data output",
|
Usage: "disable return data output",
|
||||||
Category: traceCategory,
|
Category: traceCategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -166,8 +166,11 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, exe
|
||||||
if haveGasUsed != gasUsed {
|
if haveGasUsed != gasUsed {
|
||||||
panic(fmt.Sprintf("gas differs, have %v want %v", haveGasUsed, gasUsed))
|
panic(fmt.Sprintf("gas differs, have %v want %v", haveGasUsed, gasUsed))
|
||||||
}
|
}
|
||||||
if haveErr != err {
|
if (haveErr == nil) != (err == nil) {
|
||||||
panic(fmt.Sprintf("err differs, have %v want %v", haveErr, err))
|
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!
|
// don't mutate the state!
|
||||||
runtimeConfig.State = prestate.Copy()
|
runtimeConfig.State = prestate.Copy()
|
||||||
output, _, gasLeft, err := runtime.Create(input, &runtimeConfig)
|
output, _, gasLeft, err := runtime.Create(input, &runtimeConfig)
|
||||||
return output, gasLeft, err
|
return output, initialGas - gasLeft, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if len(code) > 0 {
|
if len(code) > 0 {
|
||||||
|
|
|
||||||
408
cmd/geth/bintrie_convert.go
Normal file
408
cmd/geth/bintrie_convert.go
Normal 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
|
||||||
|
}
|
||||||
229
cmd/geth/bintrie_convert_test.go
Normal file
229
cmd/geth/bintrie_convert_test.go
Normal 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()
|
||||||
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ var (
|
||||||
utils.OverrideOsaka,
|
utils.OverrideOsaka,
|
||||||
utils.OverrideBPO1,
|
utils.OverrideBPO1,
|
||||||
utils.OverrideBPO2,
|
utils.OverrideBPO2,
|
||||||
utils.OverrideVerkle,
|
utils.OverrideUBT,
|
||||||
}, utils.DatabaseFlags),
|
}, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
The init command initializes a new genesis block and definition for the network.
|
The init command initializes a new genesis block and definition for the network.
|
||||||
|
|
@ -297,15 +297,15 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
v := ctx.Uint64(utils.OverrideBPO2.Name)
|
v := ctx.Uint64(utils.OverrideBPO2.Name)
|
||||||
overrides.OverrideBPO2 = &v
|
overrides.OverrideBPO2 = &v
|
||||||
}
|
}
|
||||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
if ctx.IsSet(utils.OverrideUBT.Name) {
|
||||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
v := ctx.Uint64(utils.OverrideUBT.Name)
|
||||||
overrides.OverrideVerkle = &v
|
overrides.OverrideUBT = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
chaindb := utils.MakeChainDatabase(ctx, stack, false)
|
chaindb := utils.MakeChainDatabase(ctx, stack, false)
|
||||||
defer chaindb.Close()
|
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()
|
defer triedb.Close()
|
||||||
|
|
||||||
_, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides, nil)
|
_, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides, nil)
|
||||||
|
|
@ -325,7 +325,7 @@ func dumpGenesis(ctx *cli.Context) error {
|
||||||
var genesis *core.Genesis
|
var genesis *core.Genesis
|
||||||
if utils.IsNetworkPreset(ctx) {
|
if utils.IsNetworkPreset(ctx) {
|
||||||
genesis = utils.MakeGenesis(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)
|
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.
|
// Determine the prune point based on the history mode.
|
||||||
genesisHash := chain.Genesis().Hash()
|
genesisHash := chain.Genesis().Hash()
|
||||||
prunePoint := history.GetPrunePoint(genesisHash, mode)
|
policy, err := history.NewPolicy(mode, genesisHash)
|
||||||
if prunePoint == nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if policy.Target == nil {
|
||||||
return fmt.Errorf("prune point for %q not found for this network", mode.String())
|
return fmt.Errorf("prune point for %q not found for this network", mode.String())
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
targetBlock = prunePoint.BlockNumber
|
targetBlock = policy.Target.BlockNumber
|
||||||
targetBlockHash = prunePoint.BlockHash
|
targetBlockHash = policy.Target.BlockHash
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check the current freezer tail to see if pruning is needed/possible.
|
// Check the current freezer tail to see if pruning is needed/possible.
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/catalyst"
|
"github.com/ethereum/go-ethereum/eth/catalyst"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"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/flags"
|
||||||
"github.com/ethereum/go-ethereum/internal/telemetry/tracesetup"
|
"github.com/ethereum/go-ethereum/internal/telemetry/tracesetup"
|
||||||
"github.com/ethereum/go-ethereum/internal/version"
|
"github.com/ethereum/go-ethereum/internal/version"
|
||||||
|
|
@ -235,9 +236,9 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
v := ctx.Uint64(utils.OverrideBPO2.Name)
|
v := ctx.Uint64(utils.OverrideBPO2.Name)
|
||||||
cfg.Eth.OverrideBPO2 = &v
|
cfg.Eth.OverrideBPO2 = &v
|
||||||
}
|
}
|
||||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
if ctx.IsSet(utils.OverrideUBT.Name) {
|
||||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
v := ctx.Uint64(utils.OverrideUBT.Name)
|
||||||
cfg.Eth.OverrideVerkle = &v
|
cfg.Eth.OverrideUBT = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start metrics export if enabled.
|
// Start metrics export if enabled.
|
||||||
|
|
@ -269,25 +270,28 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
filterSystem := utils.RegisterFilterAPI(stack, backend, &cfg.Eth)
|
filterSystem := utils.RegisterFilterAPI(stack, backend, &cfg.Eth)
|
||||||
|
|
||||||
// Configure GraphQL if requested.
|
// Configure GraphQL if requested.
|
||||||
if ctx.IsSet(utils.GraphQLEnabledFlag.Name) {
|
if ctx.Bool(utils.GraphQLEnabledFlag.Name) {
|
||||||
utils.RegisterGraphQLService(stack, backend, filterSystem, &cfg.Node)
|
utils.RegisterGraphQLService(stack, backend, filterSystem, &cfg.Node)
|
||||||
}
|
}
|
||||||
// Add the Ethereum Stats daemon if requested.
|
// Add the Ethereum Stats daemon if requested.
|
||||||
if cfg.Ethstats.URL != "" {
|
if cfg.Ethstats.URL != "" {
|
||||||
utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL)
|
utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure synchronization override service
|
// Configure synchronization override service
|
||||||
var synctarget common.Hash
|
syncConfig := syncer.Config{
|
||||||
|
ExitWhenSynced: ctx.Bool(utils.ExitWhenSyncedFlag.Name),
|
||||||
|
}
|
||||||
if ctx.IsSet(utils.SyncTargetFlag.Name) {
|
if ctx.IsSet(utils.SyncTargetFlag.Name) {
|
||||||
target := ctx.String(utils.SyncTargetFlag.Name)
|
target := ctx.String(utils.SyncTargetFlag.Name)
|
||||||
if !common.IsHexHash(target) {
|
if !common.IsHexHash(target) {
|
||||||
utils.Fatalf("sync target hash is not a valid hex hash: %s", 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.
|
// Start dev mode.
|
||||||
simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), cfg.Eth.Miner.PendingFeeRecipient, eth)
|
simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), cfg.Eth.Miner.PendingFeeRecipient, eth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -806,6 +806,24 @@ func (iter *snapshotIterator) Release() {
|
||||||
iter.storage.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.
|
// chainExporters defines the export scheme for all exportable chain data.
|
||||||
var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{
|
var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{
|
||||||
"preimage": 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)
|
storage := db.NewIterator(rawdb.SnapshotStoragePrefix, nil)
|
||||||
return &snapshotIterator{account: account, storage: storage}
|
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 {
|
func exportChaindata(ctx *cli.Context) error {
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"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/console/prompt"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
|
|
@ -64,7 +61,7 @@ var (
|
||||||
utils.OverrideOsaka,
|
utils.OverrideOsaka,
|
||||||
utils.OverrideBPO1,
|
utils.OverrideBPO1,
|
||||||
utils.OverrideBPO2,
|
utils.OverrideBPO2,
|
||||||
utils.OverrideVerkle,
|
utils.OverrideUBT,
|
||||||
utils.OverrideGenesisFlag,
|
utils.OverrideGenesisFlag,
|
||||||
utils.EnablePersonal, // deprecated
|
utils.EnablePersonal, // deprecated
|
||||||
utils.TxPoolLocalsFlag,
|
utils.TxPoolLocalsFlag,
|
||||||
|
|
@ -95,6 +92,7 @@ var (
|
||||||
utils.StateHistoryFlag,
|
utils.StateHistoryFlag,
|
||||||
utils.TrienodeHistoryFlag,
|
utils.TrienodeHistoryFlag,
|
||||||
utils.TrienodeHistoryFullValueCheckpointFlag,
|
utils.TrienodeHistoryFullValueCheckpointFlag,
|
||||||
|
utils.BinTrieGroupDepthFlag,
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
utils.EthRequiredBlocksFlag,
|
utils.EthRequiredBlocksFlag,
|
||||||
utils.LegacyWhitelistFlag, // deprecated
|
utils.LegacyWhitelistFlag, // deprecated
|
||||||
|
|
@ -260,6 +258,8 @@ func init() {
|
||||||
utils.ShowDeprecated,
|
utils.ShowDeprecated,
|
||||||
// See snapshot.go
|
// See snapshot.go
|
||||||
snapshotCommand,
|
snapshotCommand,
|
||||||
|
// See bintrie_convert.go
|
||||||
|
bintrieCommand,
|
||||||
}
|
}
|
||||||
if logTestCommand != nil {
|
if logTestCommand != nil {
|
||||||
app.Commands = append(app.Commands, logTestCommand)
|
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,18 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/state/pruner"
|
"github.com/ethereum/go-ethereum/core/state/pruner"
|
||||||
|
|
@ -36,6 +39,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/urfave/cli/v2"
|
"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",
|
Usage: "Traverse the state with given root hash and perform quick verification",
|
||||||
ArgsUsage: "<root>",
|
ArgsUsage: "<root>",
|
||||||
Action: traverseState,
|
Action: traverseState,
|
||||||
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
|
Flags: slices.Concat([]cli.Flag{
|
||||||
|
utils.AccountFlag,
|
||||||
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
geth snapshot traverse-state <state-root>
|
geth snapshot traverse-state <state-root>
|
||||||
will traverse the whole state from the given state root and will abort if any
|
will traverse the whole state from the given state root and will abort if any
|
||||||
|
|
@ -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.
|
state integrity verification. The default checking target is the HEAD state.
|
||||||
|
|
||||||
It's also usable without snapshot enabled.
|
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",
|
Usage: "Traverse the state with given root hash and perform detailed verification",
|
||||||
ArgsUsage: "<root>",
|
ArgsUsage: "<root>",
|
||||||
Action: traverseRawState,
|
Action: traverseRawState,
|
||||||
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
|
Flags: slices.Concat([]cli.Flag{
|
||||||
|
utils.AccountFlag,
|
||||||
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
geth snapshot traverse-rawstate <state-root>
|
geth snapshot traverse-rawstate <state-root>
|
||||||
will traverse the whole state from the given root and will abort if any referenced
|
will traverse the whole state from the given root and will abort if any referenced
|
||||||
|
|
@ -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.
|
to traverse-state, but the check granularity is smaller.
|
||||||
|
|
||||||
It's also usable without snapshot enabled.
|
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: `
|
Description: `
|
||||||
The export-preimages command exports hash preimages to a flat file, in exactly
|
The export-preimages command exports hash preimages to a flat file, in exactly
|
||||||
the expected order for the overlay tree migration.
|
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 post–EIP-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)
|
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.
|
// traverseState is a helper function used for pruning verification.
|
||||||
// Basically it just iterates the trie, ensure all nodes and associated
|
// Basically it just iterates the trie, ensure all nodes and associated
|
||||||
// contract codes are present.
|
// contract codes are present.
|
||||||
|
|
@ -309,6 +451,30 @@ func traverseState(ctx *cli.Context) error {
|
||||||
root = headBlock.Root()
|
root = headBlock.Root()
|
||||||
log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
|
log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
|
||||||
}
|
}
|
||||||
|
// 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)
|
t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to open trie", "root", root, "err", err)
|
log.Error("Failed to open trie", "root", root, "err", err)
|
||||||
|
|
@ -335,30 +501,10 @@ func traverseState(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if acc.Root != types.EmptyRootHash {
|
if acc.Root != types.EmptyRootHash {
|
||||||
id := trie.StorageTrieID(root, common.BytesToHash(accIter.Key), acc.Root)
|
err := traverseStorage(trie.StorageTrieID(root, common.BytesToHash(accIter.Key), acc.Root), triedb, false, false)
|
||||||
storageTrie, err := trie.NewStateTrie(id, triedb)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
|
|
||||||
return 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 !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||||
if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
|
if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
|
||||||
|
|
@ -418,6 +564,30 @@ func traverseRawState(ctx *cli.Context) error {
|
||||||
root = headBlock.Root()
|
root = headBlock.Root()
|
||||||
log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
|
log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
|
||||||
}
|
}
|
||||||
|
// 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)
|
t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to open trie", "root", root, "err", err)
|
log.Error("Failed to open trie", "root", root, "err", err)
|
||||||
|
|
@ -473,50 +643,10 @@ func traverseRawState(ctx *cli.Context) error {
|
||||||
return errors.New("invalid account")
|
return errors.New("invalid account")
|
||||||
}
|
}
|
||||||
if acc.Root != types.EmptyRootHash {
|
if acc.Root != types.EmptyRootHash {
|
||||||
id := trie.StorageTrieID(root, common.BytesToHash(accIter.LeafKey()), acc.Root)
|
err := traverseStorage(trie.StorageTrieID(root, common.BytesToHash(accIter.LeafKey()), acc.Root), triedb, false, true)
|
||||||
storageTrie, err := trie.NewStateTrie(id, triedb)
|
|
||||||
if err != nil {
|
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
|
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 !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||||
if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
|
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)))
|
log.Info("Checked the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// listEIP7610EligibleAccounts traverses the post–EIP-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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build example
|
//go:build example
|
||||||
|
// +build example
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build wasm
|
//go:build wasm && !womir
|
||||||
// +build wasm
|
// +build wasm,!womir
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
||||||
49
cmd/keeper/getpayload_womir.go
Normal file
49
cmd/keeper/getpayload_womir.go
Normal 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()
|
||||||
|
}
|
||||||
|
|
@ -38,8 +38,8 @@ require (
|
||||||
go.opentelemetry.io/otel v1.40.0 // indirect
|
go.opentelemetry.io/otel v1.40.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.40.0 // indirect
|
go.opentelemetry.io/otel/trace v1.40.0 // indirect
|
||||||
golang.org/x/crypto v0.44.0 // indirect
|
golang.org/x/crypto v0.47.0 // indirect
|
||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.40.0 // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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/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 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
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.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
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 h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
||||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
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.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
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.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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.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.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
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.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
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 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
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=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build !example && !ziren && !wasm
|
//go:build !example && !ziren && !wasm && !womir
|
||||||
// +build !example,!ziren,!wasm
|
// +build !example,!ziren,!wasm,!womir
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -274,40 +274,66 @@ func ImportHistory(chain *core.BlockChain, dir string, network string, from func
|
||||||
reported = time.Now()
|
reported = time.Now()
|
||||||
imported = 0
|
imported = 0
|
||||||
h = sha256.New()
|
h = sha256.New()
|
||||||
scratch = bytes.NewBuffer(nil)
|
buf = bytes.NewBuffer(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
for i, file := range entries {
|
for i, file := range entries {
|
||||||
err := func() error {
|
err := func() error {
|
||||||
path := filepath.Join(dir, file)
|
path := filepath.Join(dir, file)
|
||||||
|
|
||||||
// validate against checksum file in directory
|
// Validate against checksum file in directory.
|
||||||
f, err := os.Open(path)
|
f, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("open %s: %w", path, err)
|
return fmt.Errorf("open %s: %w", path, err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
if _, err := io.Copy(h, f); err != nil {
|
if _, err := io.Copy(h, f); err != nil {
|
||||||
return fmt.Errorf("checksum %s: %w", path, err)
|
return fmt.Errorf("checksum %s: %w", path, err)
|
||||||
}
|
}
|
||||||
got := common.BytesToHash(h.Sum(scratch.Bytes()[:])).Hex()
|
got := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex()
|
||||||
want := checksums[i]
|
|
||||||
h.Reset()
|
h.Reset()
|
||||||
scratch.Reset()
|
buf.Reset()
|
||||||
|
if got != checksums[i] {
|
||||||
if got != want {
|
return fmt.Errorf("%s checksum mismatch: have %s want %s", file, got, checksums[i])
|
||||||
return fmt.Errorf("%s checksum mismatch: have %s want %s", file, got, want)
|
|
||||||
}
|
}
|
||||||
// Import all block data from Era1.
|
// Import all block data from Era1.
|
||||||
e, err := from(f)
|
e, err := from(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error opening era: %w", err)
|
return fmt.Errorf("error opening era: %w", err)
|
||||||
}
|
}
|
||||||
|
defer e.Close()
|
||||||
|
|
||||||
it, err := e.Iterator()
|
it, err := e.Iterator()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating iterator: %w", err)
|
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() {
|
for it.Next() {
|
||||||
block, err := it.Block()
|
block, err := it.Block()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -320,23 +346,18 @@ func ImportHistory(chain *core.BlockChain, dir string, network string, from func
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
||||||
}
|
}
|
||||||
enc := types.EncodeBlockReceiptLists([]types.Receipts{receipts})
|
blocks = append(blocks, block)
|
||||||
if _, err := chain.InsertReceiptChain([]*types.Block{block}, enc, math.MaxUint64); err != nil {
|
receiptsList = append(receiptsList, receipts)
|
||||||
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
if len(blocks) == importBatchSize {
|
||||||
}
|
if err := flush(); err != nil {
|
||||||
imported++
|
return err
|
||||||
|
}
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := it.Error(); err != nil {
|
if err := it.Error(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return flush()
|
||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,10 @@ var (
|
||||||
Usage: "Max number of elements (0 = no limit)",
|
Usage: "Max number of elements (0 = no limit)",
|
||||||
Value: 0,
|
Value: 0,
|
||||||
}
|
}
|
||||||
|
AccountFlag = &cli.StringFlag{
|
||||||
|
Name: "account",
|
||||||
|
Usage: "Specifies the account address or hash to traverse a single storage trie",
|
||||||
|
}
|
||||||
OutputFileFlag = &cli.StringFlag{
|
OutputFileFlag = &cli.StringFlag{
|
||||||
Name: "output",
|
Name: "output",
|
||||||
Usage: "Writes the result in json to the 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",
|
Usage: "Manually specify the bpo2 fork timestamp, overriding the bundled setting",
|
||||||
Category: flags.EthCategory,
|
Category: flags.EthCategory,
|
||||||
}
|
}
|
||||||
OverrideVerkle = &cli.Uint64Flag{
|
OverrideUBT = &cli.Uint64Flag{
|
||||||
Name: "override.verkle",
|
Name: "override.ubt",
|
||||||
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",
|
Usage: "Manually specify the UBT fork timestamp, overriding the bundled setting",
|
||||||
Category: flags.EthCategory,
|
Category: flags.EthCategory,
|
||||||
}
|
}
|
||||||
OverrideGenesisFlag = &cli.StringFlag{
|
OverrideGenesisFlag = &cli.StringFlag{
|
||||||
|
|
@ -293,6 +297,12 @@ var (
|
||||||
Value: ethconfig.Defaults.EnableStateSizeTracking,
|
Value: ethconfig.Defaults.EnableStateSizeTracking,
|
||||||
Category: flags.StateCategory,
|
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{
|
StateHistoryFlag = &cli.Uint64Flag{
|
||||||
Name: "history.state",
|
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)",
|
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{
|
RPCTelemetryEndpointFlag = &cli.StringFlag{
|
||||||
Name: "rpc.telemetry.endpoint",
|
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,
|
Category: flags.APICategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
RPCTelemetryUserFlag = &cli.StringFlag{
|
RPCTelemetryUserFlag = &cli.StringFlag{
|
||||||
Name: "rpc.telemetry.username",
|
Name: "rpc.telemetry.username",
|
||||||
Usage: "HTTP Basic Auth username for OpenTelemetry",
|
Usage: "Basic Auth username for OpenTelemetry",
|
||||||
Category: flags.APICategory,
|
Category: flags.APICategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
RPCTelemetryPasswordFlag = &cli.StringFlag{
|
RPCTelemetryPasswordFlag = &cli.StringFlag{
|
||||||
Name: "rpc.telemetry.password",
|
Name: "rpc.telemetry.password",
|
||||||
Usage: "HTTP Basic Auth password for OpenTelemetry",
|
Usage: "Basic Auth password for OpenTelemetry",
|
||||||
Category: flags.APICategory,
|
Category: flags.APICategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1094,7 +1104,7 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
||||||
RPCTelemetrySampleRatioFlag = &cli.Float64Flag{
|
RPCTelemetrySampleRatioFlag = &cli.Float64Flag{
|
||||||
Name: "rpc.telemetry.sample-ratio",
|
Name: "rpc.telemetry.sample-ratio",
|
||||||
Usage: "Defines the sampling ratio for RPC telemetry (0.0 to 1.0)",
|
Usage: "Defines the sampling ratio for RPC telemetry (0.0 to 1.0)",
|
||||||
Value: 1.0,
|
Value: node.DefaultConfig.OpenTelemetry.SampleRatio,
|
||||||
Category: flags.APICategory,
|
Category: flags.APICategory,
|
||||||
}
|
}
|
||||||
// Era flags are a group of flags related to the era archive format.
|
// 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) {
|
if ctx.IsSet(RPCTelemetryTagsFlag.Name) {
|
||||||
tcfg.Tags = ctx.String(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 {
|
if tcfg.Endpoint != "" && !tcfg.Enabled {
|
||||||
log.Warn(fmt.Sprintf("OpenTelemetry endpoint configured but telemetry is not enabled, use --%s to enable.", RPCTelemetryFlag.Name))
|
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) {
|
if ctx.IsSet(TrienodeHistoryFullValueCheckpointFlag.Name) {
|
||||||
cfg.NodeFullValueCheckpoint = uint32(ctx.Uint(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) {
|
if ctx.IsSet(StateSchemeFlag.Name) {
|
||||||
cfg.StateScheme = ctx.String(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)
|
cfg.StatelessSelfValidation = ctx.Bool(VMStatelessSelfValidationFlag.Name)
|
||||||
}
|
}
|
||||||
// Auto-enable StatelessSelfValidation when witness stats are enabled
|
// Auto-enable StatelessSelfValidation when witness stats are enabled
|
||||||
if ctx.Bool(VMWitnessStatsFlag.Name) {
|
if cfg.EnableWitnessStats {
|
||||||
cfg.StatelessSelfValidation = true
|
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.
|
// RegisterSyncOverrideService adds the synchronization override service into node.
|
||||||
func RegisterSyncOverrideService(stack *node.Node, eth *eth.Ethereum, target common.Hash, exitWhenSynced bool) {
|
func RegisterSyncOverrideService(stack *node.Node, eth *eth.Ethereum, config syncer.Config) {
|
||||||
if target != (common.Hash{}) {
|
if config.TargetBlock != (common.Hash{}) {
|
||||||
log.Info("Registered sync override service", "hash", target, "exitWhenSynced", exitWhenSynced)
|
log.Info("Registered sync override service", "hash", config.TargetBlock, "exitWhenSynced", config.ExitWhenSynced)
|
||||||
} else {
|
} else {
|
||||||
log.Info("Registered sync override service")
|
log.Info("Registered sync override service")
|
||||||
}
|
}
|
||||||
syncer.Register(stack, eth, target, exitWhenSynced)
|
syncer.Register(stack, eth, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupMetrics configures the metrics system.
|
// 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),
|
StateHistory: ctx.Uint64(StateHistoryFlag.Name),
|
||||||
TrienodeHistory: ctx.Int64(TrienodeHistoryFlag.Name),
|
TrienodeHistory: ctx.Int64(TrienodeHistoryFlag.Name),
|
||||||
NodeFullValueCheckpoint: uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name)),
|
NodeFullValueCheckpoint: uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name)),
|
||||||
|
BinTrieGroupDepth: ctx.Int(BinTrieGroupDepthFlag.Name),
|
||||||
|
|
||||||
// Disable transaction indexing/unindexing.
|
// Disable transaction indexing/unindexing.
|
||||||
TxLookupLimit: -1,
|
TxLookupLimit: -1,
|
||||||
|
|
@ -2510,10 +2526,10 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeTrieDatabase constructs a trie database based on the configured scheme.
|
// 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{
|
config := &triedb.Config{
|
||||||
Preimages: preimage,
|
Preimages: preimage,
|
||||||
IsVerkle: isVerkle,
|
IsUBT: isUBT,
|
||||||
}
|
}
|
||||||
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
|
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,9 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.historyPruneBlock = new(uint64)
|
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):
|
case ctx.Bool(testSepoliaFlag.Name):
|
||||||
cfg.fsys = builtinTestFiles
|
cfg.fsys = builtinTestFiles
|
||||||
if ctx.IsSet(filterQueryFileFlag.Name) {
|
if ctx.IsSet(filterQueryFileFlag.Name) {
|
||||||
|
|
@ -180,7 +182,9 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.historyPruneBlock = new(uint64)
|
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:
|
default:
|
||||||
cfg.fsys = os.DirFS(".")
|
cfg.fsys = os.DirFS(".")
|
||||||
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)
|
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,10 @@ func (b *Big) ToInt() *big.Int {
|
||||||
return (*big.Int)(b)
|
return (*big.Int)(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Big) ToUint256() (*uint256.Int, bool) {
|
||||||
|
return uint256.FromBig((*big.Int)(b))
|
||||||
|
}
|
||||||
|
|
||||||
// String returns the hex encoding of b.
|
// String returns the hex encoding of b.
|
||||||
func (b *Big) String() string {
|
func (b *Big) String() string {
|
||||||
return EncodeBig(b.ToInt())
|
return EncodeBig(b.ToInt())
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package beacon
|
package beacon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -26,13 +25,10 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
"github.com/holiman/uint256"
|
"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)
|
amsterdam := chain.Config().IsAmsterdam(header.Number, header.Time)
|
||||||
if amsterdam && header.SlotNumber == nil {
|
if amsterdam {
|
||||||
return errors.New("header is missing slotNumber")
|
if header.BlockAccessListHash == nil {
|
||||||
}
|
return errors.New("header is missing block access list hash")
|
||||||
if !amsterdam && header.SlotNumber != nil {
|
}
|
||||||
return fmt.Errorf("invalid slotNumber: have %d, expected nil", *header.SlotNumber)
|
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
|
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.
|
// 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
|
// Seal generates a new sealing request for the given input block and pushes
|
||||||
// the result into the given channel.
|
// the result into the given channel.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package clique
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -34,7 +33,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -43,7 +41,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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
|
// 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
|
// Authorize injects a private key into the consensus engine to mint new blocks
|
||||||
// with.
|
// with.
|
||||||
func (c *Clique) Authorize(signer common.Address) {
|
func (c *Clique) Authorize(signer common.Address) {
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,9 @@
|
||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -88,13 +86,6 @@ type Engine interface {
|
||||||
// that happen at finalization (e.g. block rewards).
|
// that happen at finalization (e.g. block rewards).
|
||||||
Finalize(chain ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body)
|
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
|
// Seal generates a new sealing request for the given input block and pushes
|
||||||
// the result into the given channel.
|
// the result into the given channel.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package ethash
|
package ethash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -28,14 +27,12 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -512,22 +509,6 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
|
||||||
accumulateRewards(chain.Config(), state, header, body.Uncles)
|
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.
|
// SealHash returns the hash of a block prior to it being sealed.
|
||||||
func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
|
func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
|
||||||
hasher := keccak.NewLegacyKeccak256()
|
hasher := keccak.NewLegacyKeccak256()
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||||
data := make([]byte, nbytes)
|
data := make([]byte, nbytes)
|
||||||
return func(i int, gen *BlockGen) {
|
return func(i int, gen *BlockGen) {
|
||||||
toaddr := common.Address{}
|
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()
|
signer := gen.Signer()
|
||||||
gasPrice := big.NewInt(0)
|
gasPrice := big.NewInt(0)
|
||||||
if gen.header.BaseFee != nil {
|
if gen.header.BaseFee != nil {
|
||||||
|
|
@ -99,7 +99,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||||
Nonce: gen.TxNonce(benchRootAddr),
|
Nonce: gen.TxNonce(benchRootAddr),
|
||||||
To: &toaddr,
|
To: &toaddr,
|
||||||
Value: big.NewInt(1),
|
Value: big.NewInt(1),
|
||||||
Gas: gas,
|
Gas: cost.RegularGas,
|
||||||
Data: data,
|
Data: data,
|
||||||
GasPrice: gasPrice,
|
GasPrice: gasPrice,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testVerkleChainConfig = ¶ms.ChainConfig{
|
testUBTChainConfig = ¶ms.ChainConfig{
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
HomesteadBlock: big.NewInt(0),
|
HomesteadBlock: big.NewInt(0),
|
||||||
EIP150Block: big.NewInt(0),
|
EIP150Block: big.NewInt(0),
|
||||||
|
|
@ -51,30 +51,30 @@ var (
|
||||||
LondonBlock: big.NewInt(0),
|
LondonBlock: big.NewInt(0),
|
||||||
Ethash: new(params.EthashConfig),
|
Ethash: new(params.EthashConfig),
|
||||||
ShanghaiTime: u64(0),
|
ShanghaiTime: u64(0),
|
||||||
VerkleTime: u64(0),
|
UBTTime: u64(0),
|
||||||
TerminalTotalDifficulty: common.Big0,
|
TerminalTotalDifficulty: common.Big0,
|
||||||
EnableVerkleAtGenesis: true,
|
EnableUBTAtGenesis: true,
|
||||||
BlobScheduleConfig: ¶ms.BlobScheduleConfig{
|
BlobScheduleConfig: ¶ms.BlobScheduleConfig{
|
||||||
Verkle: params.DefaultPragueBlobConfig,
|
UBT: params.DefaultPragueBlobConfig,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProcessVerkle(t *testing.T) {
|
func TestProcessUBT(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
|
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
|
// A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness
|
||||||
// will not contain that copied data.
|
// will not contain that copied data.
|
||||||
// Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985
|
// Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985
|
||||||
codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`)
|
codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`)
|
||||||
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true)
|
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true, false)
|
||||||
signer = types.LatestSigner(testVerkleChainConfig)
|
signer = types.LatestSigner(testUBTChainConfig)
|
||||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
||||||
coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
|
coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: testVerkleChainConfig,
|
Config: testUBTChainConfig,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: GenesisAlloc{
|
||||||
coinbase: {
|
coinbase: {
|
||||||
Balance: big.NewInt(1000000000000000000), // 1 ether
|
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.
|
// data is saved into the tree+database.
|
||||||
// genesis := gspec.MustCommit(bcdb, triedb)
|
// genesis := gspec.MustCommit(bcdb, triedb)
|
||||||
options := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
options := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
||||||
options.SnapshotLimit = 0
|
options.SnapshotLimit = 0
|
||||||
|
options.BinTrieGroupDepth = triedb.DefaultBinTrieGroupDepth
|
||||||
blockchain, _ := NewBlockChain(bcdb, gspec, beacon.New(ethash.NewFaker()), options)
|
blockchain, _ := NewBlockChain(bcdb, gspec, beacon.New(ethash.NewFaker()), options)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
txCost1 := params.TxGas
|
txCost1 := params.TxGas
|
||||||
txCost2 := params.TxGas
|
txCost2 := params.TxGas
|
||||||
contractCreationCost := intrinsicContractCreationGas +
|
contractCreationCost := intrinsicContractCreationGas.RegularGas +
|
||||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */
|
||||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */
|
||||||
739 /* execution costs */
|
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 (tx) */
|
||||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */
|
||||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */
|
||||||
|
|
@ -188,7 +189,7 @@ func TestProcessParentBlockHash(t *testing.T) {
|
||||||
// block 1 parent hash is 0x0100....
|
// block 1 parent hash is 0x0100....
|
||||||
// block 2 parent hash is 0x0200....
|
// block 2 parent hash is 0x0200....
|
||||||
// etc
|
// etc
|
||||||
checkBlockHashes := func(statedb *state.StateDB, isVerkle bool) {
|
checkBlockHashes := func(statedb *state.StateDB, isUBT bool) {
|
||||||
statedb.SetNonce(params.HistoryStorageAddress, 1, tracing.NonceChangeUnspecified)
|
statedb.SetNonce(params.HistoryStorageAddress, 1, tracing.NonceChangeUnspecified)
|
||||||
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode, tracing.CodeChangeUnspecified)
|
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode, tracing.CodeChangeUnspecified)
|
||||||
// Process n blocks, from 1 .. num
|
// Process n blocks, from 1 .. num
|
||||||
|
|
@ -196,8 +197,8 @@ func TestProcessParentBlockHash(t *testing.T) {
|
||||||
for i := 1; i <= num; i++ {
|
for i := 1; i <= num; i++ {
|
||||||
header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)}
|
header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)}
|
||||||
chainConfig := params.MergedTestChainConfig
|
chainConfig := params.MergedTestChainConfig
|
||||||
if isVerkle {
|
if isUBT {
|
||||||
chainConfig = testVerkleChainConfig
|
chainConfig = testUBTChainConfig
|
||||||
}
|
}
|
||||||
vmContext := NewEVMBlockContext(header, nil, new(common.Address))
|
vmContext := NewEVMBlockContext(header, nil, new(common.Address))
|
||||||
evm := vm.NewEVM(vmContext, statedb, chainConfig, vm.Config{})
|
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
|
// Read block hashes for block 0 .. num-1
|
||||||
for i := 0; i < num; i++ {
|
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 {
|
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())
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
checkBlockHashes(statedb, false)
|
checkBlockHashes(statedb, false)
|
||||||
})
|
})
|
||||||
t.Run("Verkle", func(t *testing.T) {
|
t.Run("UBT", func(t *testing.T) {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
cacheConfig := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
cacheConfig := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
||||||
|
cacheConfig.BinTrieGroupDepth = triedb.DefaultBinTrieGroupDepth
|
||||||
cacheConfig.SnapshotLimit = 0
|
cacheConfig.SnapshotLimit = 0
|
||||||
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
|
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)
|
checkBlockHashes(statedb, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// getContractStoredBlockHash is a utility method which reads the stored parent blockhash for block 'number'
|
// 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
|
ringIndex := number % params.HistoryServeWindow
|
||||||
var key common.Hash
|
var key common.Hash
|
||||||
binary.BigEndian.PutUint64(key[24:], ringIndex)
|
binary.BigEndian.PutUint64(key[24:], ringIndex)
|
||||||
if isVerkle {
|
if isUBT {
|
||||||
return statedb.GetState(params.HistoryStorageAddress, key)
|
return statedb.GetState(params.HistoryStorageAddress, key)
|
||||||
}
|
}
|
||||||
return statedb.GetState(params.HistoryStorageAddress, key)
|
return statedb.GetState(params.HistoryStorageAddress, key)
|
||||||
|
|
|
||||||
|
|
@ -170,9 +170,10 @@ type BlockChainConfig struct {
|
||||||
TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed
|
TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed
|
||||||
TrieJournalDirectory string // Directory path to the journal used for persisting trie data across node restarts
|
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
|
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
|
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
|
||||||
ArchiveMode bool // Whether to enable the archive mode
|
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.
|
// 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;
|
// 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
|
SnapshotNoBuild bool // Whether the background generation is allowed
|
||||||
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
|
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
|
||||||
|
|
||||||
// This defines the cutoff block for history expiry.
|
// HistoryPolicy defines the chain history pruning intent.
|
||||||
// Blocks before this number may be unavailable in the chain database.
|
HistoryPolicy history.HistoryPolicy
|
||||||
ChainHistoryMode history.HistoryMode
|
|
||||||
|
|
||||||
// Misc options
|
// Misc options
|
||||||
NoPrefetch bool // Whether to disable heuristic state prefetching when processing blocks
|
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!
|
// Note the returned object is safe to modify!
|
||||||
func DefaultConfig() *BlockChainConfig {
|
func DefaultConfig() *BlockChainConfig {
|
||||||
return &BlockChainConfig{
|
return &BlockChainConfig{
|
||||||
TrieCleanLimit: 256,
|
TrieCleanLimit: 256,
|
||||||
TrieDirtyLimit: 256,
|
TrieDirtyLimit: 256,
|
||||||
TrieTimeLimit: 5 * time.Minute,
|
TrieTimeLimit: 5 * time.Minute,
|
||||||
StateScheme: rawdb.HashScheme,
|
StateScheme: rawdb.HashScheme,
|
||||||
SnapshotLimit: 256,
|
SnapshotLimit: 256,
|
||||||
SnapshotWait: true,
|
SnapshotWait: true,
|
||||||
ChainHistoryMode: history.KeepAll,
|
HistoryPolicy: history.HistoryPolicy{Mode: history.KeepAll},
|
||||||
// Transaction indexing is disabled by default.
|
// Transaction indexing is disabled by default.
|
||||||
// This is appropriate for most unit tests.
|
// This is appropriate for most unit tests.
|
||||||
TxLookupLimit: -1,
|
TxLookupLimit: -1,
|
||||||
|
|
@ -259,10 +259,11 @@ func (cfg BlockChainConfig) WithNoAsyncFlush(on bool) *BlockChainConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
// triedbConfig derives the configures for trie database.
|
// 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{
|
config := &triedb.Config{
|
||||||
Preimages: cfg.Preimages,
|
Preimages: cfg.Preimages,
|
||||||
IsVerkle: isVerkle,
|
IsUBT: isUBT,
|
||||||
|
BinTrieGroupDepth: cfg.BinTrieGroupDepth,
|
||||||
}
|
}
|
||||||
if cfg.StateScheme == rawdb.HashScheme {
|
if cfg.StateScheme == rawdb.HashScheme {
|
||||||
config.HashDB = &hashdb.Config{
|
config.HashDB = &hashdb.Config{
|
||||||
|
|
@ -379,7 +380,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open trie database with provided config
|
// Open trie database with provided config
|
||||||
enableVerkle, err := EnableVerkleAtGenesis(db, genesis)
|
enableVerkle, err := EnableUBTAtGenesis(db, genesis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -715,82 +716,44 @@ func (bc *BlockChain) loadLastState() error {
|
||||||
|
|
||||||
// initializeHistoryPruning sets bc.historyPrunePoint.
|
// initializeHistoryPruning sets bc.historyPrunePoint.
|
||||||
func (bc *BlockChain) initializeHistoryPruning(latest uint64) error {
|
func (bc *BlockChain) initializeHistoryPruning(latest uint64) error {
|
||||||
var (
|
freezerTail, _ := bc.db.Tail()
|
||||||
freezerTail, _ = bc.db.Tail()
|
policy := bc.cfg.HistoryPolicy
|
||||||
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")
|
|
||||||
|
|
||||||
case history.KeepPostMerge:
|
switch policy.Mode {
|
||||||
if mergePoint == nil {
|
case history.KeepAll:
|
||||||
return errors.New("history pruning requested for unknown network")
|
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
|
return nil
|
||||||
|
|
||||||
case history.KeepPostPrague:
|
case history.KeepPostMerge, history.KeepPostPrague:
|
||||||
if praguePoint == nil {
|
target := policy.Target
|
||||||
return errors.New("history pruning requested for unknown network")
|
// Already at the target.
|
||||||
}
|
if freezerTail == target.BlockNumber {
|
||||||
// Check if already at the prague prune point.
|
bc.historyPrunePoint.Store(target)
|
||||||
if freezerTail == praguePoint.BlockNumber {
|
|
||||||
bc.historyPrunePoint.Store(praguePoint)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Check if database needs pruning.
|
// Database is pruned beyond the target.
|
||||||
if latest != 0 {
|
if freezerTail > target.BlockNumber {
|
||||||
if freezerTail == 0 {
|
return fmt.Errorf("database pruned beyond requested history (tail=%d, target=%d)", freezerTail, target.BlockNumber)
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
// Fresh database (latest == 0), will sync from prague point.
|
// Database needs pruning (freezerTail < target).
|
||||||
bc.historyPrunePoint.Store(praguePoint)
|
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
|
return nil
|
||||||
|
|
||||||
default:
|
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.
|
// If all checks out, manually set the head block.
|
||||||
|
rawdb.WriteHeadBlockHash(bc.db, hash)
|
||||||
bc.currentBlock.Store(block.Header())
|
bc.currentBlock.Store(block.Header())
|
||||||
headBlockGauge.Update(int64(block.NumberU64()))
|
headBlockGauge.Update(int64(block.NumberU64()))
|
||||||
|
|
||||||
|
|
@ -2155,11 +2119,29 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
statedb *state.StateDB
|
statedb *state.StateDB
|
||||||
interrupt atomic.Bool
|
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
|
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)
|
statedb, err = state.New(parentRoot, sdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// Note: the main processor and prefetcher share the same reader with a local
|
||||||
// cache for mitigating the overhead of state access.
|
// cache for mitigating the overhead of state access.
|
||||||
prefetch, process, err := sdb.ReadersWithCacheStats(parentRoot)
|
prefetch, process, err := warmer.ReadersWithCacheStats(parentRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||||
// useless due to the intermediate root hashing after each transaction.
|
// useless due to the intermediate root hashing after each transaction.
|
||||||
var (
|
var witness *stateless.Witness
|
||||||
witness *stateless.Witness
|
|
||||||
witnessStats *stateless.WitnessStats
|
|
||||||
)
|
|
||||||
if bc.chainConfig.IsByzantium(block.Number()) {
|
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||||
// Generate witnesses either if we're self-testing, or if it's the
|
// Generate witnesses either if we're self-testing, or if it's the
|
||||||
// only block being inserted. A bit crude, but witnesses are huge,
|
// only block being inserted. A bit crude, but witnesses are huge,
|
||||||
// so we refuse to make an entire chain of them.
|
// so we refuse to make an entire chain of them.
|
||||||
if config.StatelessSelfValidation || config.MakeWitness {
|
if config.StatelessSelfValidation || config.MakeWitness {
|
||||||
witness, err = stateless.NewWitness(block.Header(), bc)
|
witness, err = stateless.NewWitness(block.Header(), bc, config.EnableWitnessStats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if config.EnableWitnessStats {
|
|
||||||
witnessStats = stateless.NewWitnessStats()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
statedb.StartPrefetcher("chain", witness, witnessStats)
|
statedb.StartPrefetcher("chain", witness)
|
||||||
defer statedb.StopPrefetcher()
|
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
|
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.DatabaseCommits
|
||||||
}
|
}
|
||||||
// Report the collected witness statistics
|
// Report the collected witness statistics
|
||||||
if witnessStats != nil {
|
if witness != nil {
|
||||||
witnessStats.ReportMetrics(block.NumberU64())
|
witness.ReportMetrics(block.NumberU64())
|
||||||
}
|
}
|
||||||
elapsed := time.Since(startTime) + 1 // prevent zero division
|
elapsed := time.Since(startTime) + 1 // prevent zero division
|
||||||
stats.TotalTime = elapsed
|
stats.TotalTime = elapsed
|
||||||
|
|
@ -2621,13 +2597,19 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
|
||||||
blockReorgAddMeter.Mark(int64(len(newChain)))
|
blockReorgAddMeter.Mark(int64(len(newChain)))
|
||||||
} else {
|
} else {
|
||||||
// len(newChain) == 0 && len(oldChain) > 0
|
// len(newChain) == 0 && len(oldChain) > 0
|
||||||
// rewind the canonical chain to a lower point.
|
// Rewind the canonical chain to a lower point. In EPBs we can reorg to
|
||||||
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))
|
// 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
|
// Acquire the tx-lookup lock before mutation. This step is essential
|
||||||
// as the txlookups should be changed atomically, and all subsequent
|
// as the txlookups should be changed atomically, and all subsequent
|
||||||
// reads should be blocked until the mutation is complete.
|
// reads should be blocked until the mutation is complete.
|
||||||
bc.txLookupLock.Lock()
|
bc.txLookupLock.Lock()
|
||||||
|
defer bc.txLookupLock.Unlock()
|
||||||
|
|
||||||
// Reorg can be executed, start reducing the chain's old blocks and appending
|
// Reorg can be executed, start reducing the chain's old blocks and appending
|
||||||
// the new blocks
|
// 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.
|
// Reset the tx lookup cache to clear stale txlookup cache.
|
||||||
bc.txLookupCache.Purge()
|
bc.txLookupCache.Purge()
|
||||||
|
|
||||||
// Release the tx-lookup lock after mutation.
|
|
||||||
bc.txLookupLock.Unlock()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -296,6 +296,14 @@ func (bc *BlockChain) GetReceiptsRLP(hash common.Hash) rlp.RawValue {
|
||||||
return rawdb.ReadReceiptsRLP(bc.db, hash, number)
|
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
|
// GetUnclesInChain retrieves all the uncles from a given block backwards until
|
||||||
// a specific distance is reached.
|
// a specific distance is reached.
|
||||||
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
|
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.
|
// State returns a new mutable state based on the current HEAD block.
|
||||||
func (bc *BlockChain) State() (*state.StateDB, error) {
|
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.
|
// StateAt returns a new mutable state based on a particular point in time.
|
||||||
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
func (bc *BlockChain) StateAt(header *types.Header) (*state.StateDB, error) {
|
||||||
return state.New(root, state.NewDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps))
|
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`
|
// Live states are not available and won't be served, please use `State`
|
||||||
// or `StateAt` instead.
|
// or `StateAt` instead.
|
||||||
func (bc *BlockChain) HistoricState(root common.Hash) (*state.StateDB, error) {
|
func (bc *BlockChain) HistoricState(header *types.Header) (*state.StateDB, error) {
|
||||||
return state.New(root, state.NewHistoricDatabase(bc.triedb, bc.codedb))
|
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.
|
// Config retrieves the chain's fork configuration.
|
||||||
|
|
@ -468,7 +499,7 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateIndexProgress returns the historical state indexing progress.
|
// StateIndexProgress returns the historical state indexing progress.
|
||||||
func (bc *BlockChain) StateIndexProgress() (uint64, error) {
|
func (bc *BlockChain) StateIndexProgress() (uint64, uint64, error) {
|
||||||
return bc.triedb.IndexProgress()
|
return bc.triedb.IndexProgress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"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/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -3891,7 +3890,7 @@ func TestTransientStorageReset(t *testing.T) {
|
||||||
t.Fatalf("failed to insert into chain: %v", err)
|
t.Fatalf("failed to insert into chain: %v", err)
|
||||||
}
|
}
|
||||||
// Check the storage
|
// Check the storage
|
||||||
state, err := chain.StateAt(chain.CurrentHeader().Root)
|
state, err := chain.StateAt(chain.CurrentHeader())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to load state %v", err)
|
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) {
|
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)))
|
// 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()
|
ghash := genesis.ToBlock().Hash()
|
||||||
cutoffBlock := blocks[cutoff-1]
|
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{})
|
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
options := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
chain, _ := NewBlockChain(db, genesis, beacon.New(ethash.NewFaker()), DefaultConfig().WithStateScheme(rawdb.PathScheme))
|
||||||
chain, _ := NewBlockChain(db, genesis, beacon.New(ethash.NewFaker()), options)
|
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
|
||||||
blockContext = NewEVMBlockContext(b.header, bc, &b.header.Coinbase)
|
blockContext = NewEVMBlockContext(b.header, bc, &b.header.Coinbase)
|
||||||
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
|
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)
|
receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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
|
// Merge the tx-local access event into the "block-local" one, in order to collect
|
||||||
// all values, so that the witness can be built.
|
// 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.statedb.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
b.txs = append(b.txs, tx)
|
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.
|
// off the statedb before executing the system calls.
|
||||||
statedb = statedb.Copy()
|
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, err := PostExecution(context.Background(), b.cm.config, b.header.Number, b.header.Time, blockLogs, evm, uint32(len(b.txs)+1))
|
||||||
requests = [][]byte{}
|
if err != nil {
|
||||||
// EIP-6110 deposits
|
panic(fmt.Sprintf("failed to run post-execution: %v", err))
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return requests
|
return requests
|
||||||
}
|
}
|
||||||
|
|
@ -392,7 +381,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
misc.ApplyDAOHardFork(statedb)
|
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
|
// EIP-2935
|
||||||
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
|
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
|
||||||
blockContext.Random = &common.Hash{} // enable post-merge instruction set
|
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
|
b.header.RequestsHash = &reqHash
|
||||||
}
|
}
|
||||||
|
|
||||||
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals}
|
body := types.Body{
|
||||||
block, err := b.engine.FinalizeAndAssemble(context.Background(), cm, b.header, statedb, &body, b.receipts)
|
Transactions: b.txs,
|
||||||
if err != nil {
|
Uncles: b.uncles,
|
||||||
panic(err)
|
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
|
// 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))
|
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.
|
// Forcibly use hash-based state scheme for retaining all nodes in disk.
|
||||||
var triedbConfig *triedb.Config = triedb.HashDefaults
|
var triedbConfig *triedb.Config = triedb.HashDefaults
|
||||||
if config.IsVerkle(config.ChainID, 0) {
|
if config.IsUBT(config.ChainID, 0) {
|
||||||
triedbConfig = triedb.VerkleDefaults
|
triedbConfig = triedb.UBTDefaults
|
||||||
}
|
}
|
||||||
triedb := triedb.NewDatabase(db, triedbConfig)
|
triedb := triedb.NewDatabase(db, triedbConfig)
|
||||||
defer triedb.Close()
|
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) {
|
func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
var triedbConfig *triedb.Config = triedb.HashDefaults
|
var triedbConfig *triedb.Config = triedb.HashDefaults
|
||||||
if genesis.Config != nil && genesis.Config.IsVerkle(genesis.Config.ChainID, 0) {
|
if genesis.Config != nil && genesis.Config.IsUBT(genesis.Config.ChainID, 0) {
|
||||||
triedbConfig = triedb.VerkleDefaults
|
triedbConfig = triedb.UBTDefaults
|
||||||
}
|
}
|
||||||
genesisTriedb := triedb.NewDatabase(db, triedbConfig)
|
genesisTriedb := triedb.NewDatabase(db, triedbConfig)
|
||||||
block, err := genesis.Commit(db, genesisTriedb, nil)
|
block, err := genesis.Commit(db, genesisTriedb, nil)
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,6 @@ var (
|
||||||
// have enough funds for transfer(topmost call only).
|
// have enough funds for transfer(topmost call only).
|
||||||
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
|
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
|
// ErrInsufficientBalanceWitness is returned if the transaction sender has enough
|
||||||
// funds to cover the transfer, but not enough to pay for witness access/modification
|
// funds to cover the transfer, but not enough to pay for witness access/modification
|
||||||
// costs for the transaction
|
// costs for the transaction
|
||||||
|
|
|
||||||
169
core/eth_transfer_logs_test.go
Normal file
169
core/eth_transfer_logs_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -86,7 +87,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
||||||
func NewEVMTxContext(msg *Message) vm.TxContext {
|
func NewEVMTxContext(msg *Message) vm.TxContext {
|
||||||
ctx := vm.TxContext{
|
ctx := vm.TxContext{
|
||||||
Origin: msg.From,
|
Origin: msg.From,
|
||||||
GasPrice: uint256.MustFromBig(msg.GasPrice),
|
GasPrice: msg.GasPrice,
|
||||||
BlobHashes: msg.BlobHashes,
|
BlobHashes: msg.BlobHashes,
|
||||||
}
|
}
|
||||||
return ctx
|
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
|
// 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.SubBalance(sender, amount, tracing.BalanceChangeTransfer)
|
||||||
db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer)
|
db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer)
|
||||||
|
if rules.IsAmsterdam && !amount.IsZero() && sender != recipient {
|
||||||
|
db.AddLog(types.EthTransferLog(sender, recipient, amount))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,7 @@ func TestSingleMatch(t *testing.T) {
|
||||||
t.Fatalf("Invalid length of matches (got %d, expected 1)", len(matches))
|
t.Fatalf("Invalid length of matches (got %d, expected 1)", len(matches))
|
||||||
}
|
}
|
||||||
if matches[0] != lvIndex {
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,22 +129,23 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// hashAlloc computes the state root according to the genesis specification.
|
// 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
|
// If a genesis-time verkle trie is requested, create a trie config
|
||||||
// with the verkle trie enabled so that the tree can be initialized
|
// with the verkle trie enabled so that the tree can be initialized
|
||||||
// as such.
|
// as such.
|
||||||
var config *triedb.Config
|
var config *triedb.Config
|
||||||
if isVerkle {
|
if isUBT {
|
||||||
config = &triedb.Config{
|
config = &triedb.Config{
|
||||||
PathDB: pathdb.Defaults,
|
PathDB: pathdb.Defaults,
|
||||||
IsVerkle: true,
|
IsUBT: true,
|
||||||
|
BinTrieGroupDepth: triedb.UBTDefaults.BinTrieGroupDepth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create an ephemeral in-memory database for computing hash,
|
// Create an ephemeral in-memory database for computing hash,
|
||||||
// all the derived states will be discarded to not pollute disk.
|
// all the derived states will be discarded to not pollute disk.
|
||||||
emptyRoot := types.EmptyRootHash
|
emptyRoot := types.EmptyRootHash
|
||||||
if isVerkle {
|
if isUBT {
|
||||||
emptyRoot = types.EmptyVerkleHash
|
emptyRoot = types.EmptyBinaryHash
|
||||||
}
|
}
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb.NewDatabase(db, config), nil))
|
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.
|
// generated states will be persisted into the given database.
|
||||||
func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database, tracer *tracing.Hooks) (common.Hash, error) {
|
func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database, tracer *tracing.Hooks) (common.Hash, error) {
|
||||||
emptyRoot := types.EmptyRootHash
|
emptyRoot := types.EmptyRootHash
|
||||||
if triedb.IsVerkle() {
|
if triedb.IsUBT() {
|
||||||
emptyRoot = types.EmptyVerkleHash
|
emptyRoot = types.EmptyBinaryHash
|
||||||
}
|
}
|
||||||
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb, nil))
|
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb, nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -276,10 +277,10 @@ func (e *GenesisMismatchError) Error() string {
|
||||||
|
|
||||||
// ChainOverrides contains the changes to chain config.
|
// ChainOverrides contains the changes to chain config.
|
||||||
type ChainOverrides struct {
|
type ChainOverrides struct {
|
||||||
OverrideOsaka *uint64
|
OverrideOsaka *uint64
|
||||||
OverrideBPO1 *uint64
|
OverrideBPO1 *uint64
|
||||||
OverrideBPO2 *uint64
|
OverrideBPO2 *uint64
|
||||||
OverrideVerkle *uint64
|
OverrideUBT *uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply applies the chain overrides on the supplied chain config.
|
// 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 {
|
if o.OverrideBPO2 != nil {
|
||||||
cfg.BPO2Time = o.OverrideBPO2
|
cfg.BPO2Time = o.OverrideBPO2
|
||||||
}
|
}
|
||||||
if o.OverrideVerkle != nil {
|
if o.OverrideUBT != nil {
|
||||||
cfg.VerkleTime = o.OverrideVerkle
|
cfg.UBTTime = o.OverrideUBT
|
||||||
}
|
}
|
||||||
return cfg.CheckConfigForkOrder()
|
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.
|
// tree at genesis time.
|
||||||
func (g *Genesis) IsVerkle() bool {
|
func (g *Genesis) IsUBT() bool {
|
||||||
return g.Config.IsVerkleGenesis()
|
return g.Config.IsUBTGenesis()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToBlock returns the genesis block according to genesis specification.
|
// ToBlock returns the genesis block according to genesis specification.
|
||||||
func (g *Genesis) ToBlock() *types.Block {
|
func (g *Genesis) ToBlock() *types.Block {
|
||||||
root, err := hashAlloc(&g.Alloc, g.IsVerkle())
|
root, err := hashAlloc(&g.Alloc, g.IsUBT())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -609,24 +610,24 @@ func (g *Genesis) MustCommit(db ethdb.Database, triedb *triedb.Database) *types.
|
||||||
return block
|
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
|
// 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
|
// verkle fork is activated at genesis, and the configured activation date has
|
||||||
// already passed.
|
// already passed.
|
||||||
//
|
//
|
||||||
// In production networks (mainnet and public testnets), verkle activation always
|
// In production networks (mainnet and public testnets), verkle activation always
|
||||||
// occurs after the genesis block, making this function irrelevant in those cases.
|
// 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 != nil {
|
||||||
if genesis.Config == nil {
|
if genesis.Config == nil {
|
||||||
return false, errGenesisNoConfig
|
return false, errGenesisNoConfig
|
||||||
}
|
}
|
||||||
return genesis.Config.EnableVerkleAtGenesis, nil
|
return genesis.Config.EnableUBTAtGenesis, nil
|
||||||
}
|
}
|
||||||
if ghash := rawdb.ReadCanonicalHash(db, 0); ghash != (common.Hash{}) {
|
if ghash := rawdb.ReadCanonicalHash(db, 0); ghash != (common.Hash{}) {
|
||||||
chainCfg := rawdb.ReadChainConfig(db, ghash)
|
chainCfg := rawdb.ReadChainConfig(db, ghash)
|
||||||
if chainCfg != nil {
|
if chainCfg != nil {
|
||||||
return chainCfg.EnableVerkleAtGenesis, nil
|
return chainCfg.EnableUBTAtGenesis, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|
|
||||||
|
|
@ -261,9 +261,9 @@ func newDbConfig(scheme string) *triedb.Config {
|
||||||
return &triedb.Config{PathDB: &config}
|
return &triedb.Config{PathDB: &config}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVerkleGenesisCommit(t *testing.T) {
|
func TestBinaryGenesisCommit(t *testing.T) {
|
||||||
var verkleTime uint64 = 0
|
var ubtTime uint64 = 0
|
||||||
verkleConfig := ¶ms.ChainConfig{
|
ubtConfig := ¶ms.ChainConfig{
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
HomesteadBlock: big.NewInt(0),
|
HomesteadBlock: big.NewInt(0),
|
||||||
DAOForkBlock: nil,
|
DAOForkBlock: nil,
|
||||||
|
|
@ -281,34 +281,34 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
ArrowGlacierBlock: big.NewInt(0),
|
||||||
GrayGlacierBlock: big.NewInt(0),
|
GrayGlacierBlock: big.NewInt(0),
|
||||||
MergeNetsplitBlock: nil,
|
MergeNetsplitBlock: nil,
|
||||||
ShanghaiTime: &verkleTime,
|
ShanghaiTime: &ubtTime,
|
||||||
CancunTime: &verkleTime,
|
CancunTime: &ubtTime,
|
||||||
PragueTime: &verkleTime,
|
PragueTime: &ubtTime,
|
||||||
OsakaTime: &verkleTime,
|
OsakaTime: &ubtTime,
|
||||||
VerkleTime: &verkleTime,
|
UBTTime: &ubtTime,
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
EnableVerkleAtGenesis: true,
|
EnableUBTAtGenesis: true,
|
||||||
Ethash: nil,
|
Ethash: nil,
|
||||||
Clique: nil,
|
Clique: nil,
|
||||||
BlobScheduleConfig: ¶ms.BlobScheduleConfig{
|
BlobScheduleConfig: ¶ms.BlobScheduleConfig{
|
||||||
Cancun: params.DefaultCancunBlobConfig,
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
Prague: params.DefaultPragueBlobConfig,
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
Osaka: params.DefaultOsakaBlobConfig,
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
Verkle: params.DefaultPragueBlobConfig,
|
UBT: params.DefaultPragueBlobConfig,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
genesis := &Genesis{
|
genesis := &Genesis{
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
Config: verkleConfig,
|
Config: ubtConfig,
|
||||||
Timestamp: verkleTime,
|
Timestamp: ubtTime,
|
||||||
Difficulty: big.NewInt(0),
|
Difficulty: big.NewInt(0),
|
||||||
Alloc: types.GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
{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()
|
got := genesis.ToBlock().Root().Bytes()
|
||||||
if !bytes.Equal(got, expected) {
|
if !bytes.Equal(got, expected) {
|
||||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
||||||
|
|
@ -320,17 +320,18 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||||
config.NoAsyncFlush = true
|
config.NoAsyncFlush = true
|
||||||
|
|
||||||
triedb := triedb.NewDatabase(db, &triedb.Config{
|
triedb := triedb.NewDatabase(db, &triedb.Config{
|
||||||
IsVerkle: true,
|
IsUBT: true,
|
||||||
PathDB: &config,
|
PathDB: &config,
|
||||||
|
BinTrieGroupDepth: triedb.DefaultBinTrieGroupDepth,
|
||||||
})
|
})
|
||||||
block := genesis.MustCommit(db, triedb)
|
block := genesis.MustCommit(db, triedb)
|
||||||
if !bytes.Equal(block.Root().Bytes(), expected) {
|
if !bytes.Equal(block.Root().Bytes(), expected) {
|
||||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, block.Root())
|
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, block.Root())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the trie is verkle
|
// Test that the trie is a unified binary trie
|
||||||
if !triedb.IsVerkle() {
|
if !triedb.IsUBT() {
|
||||||
t.Fatalf("expected trie to be verkle")
|
t.Fatalf("expected trie to be a unified binary trie")
|
||||||
}
|
}
|
||||||
vdb := rawdb.NewTable(db, string(rawdb.VerklePrefix))
|
vdb := rawdb.NewTable(db, string(rawdb.VerklePrefix))
|
||||||
if !rawdb.HasAccountTrieNode(vdb, nil) {
|
if !rawdb.HasAccountTrieNode(vdb, nil) {
|
||||||
|
|
|
||||||
|
|
@ -77,57 +77,66 @@ func (m *HistoryMode) UnmarshalText(text []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrunePoint identifies a specific block for history pruning.
|
||||||
type PrunePoint struct {
|
type PrunePoint struct {
|
||||||
BlockNumber uint64
|
BlockNumber uint64
|
||||||
BlockHash common.Hash
|
BlockHash common.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergePrunePoints contains the pre-defined history pruning cutoff blocks for known networks.
|
// staticPrunePoints contains the pre-defined history pruning cutoff blocks for
|
||||||
// They point to the first post-merge block. Any pruning should truncate *up to* but excluding
|
// known networks, keyed by history mode and genesis hash. They point to the first
|
||||||
// the given block.
|
// block after the respective fork. Any pruning should truncate *up to* but
|
||||||
var MergePrunePoints = map[common.Hash]*PrunePoint{
|
// excluding the given block.
|
||||||
// mainnet
|
var staticPrunePoints = map[HistoryMode]map[common.Hash]*PrunePoint{
|
||||||
params.MainnetGenesisHash: {
|
KeepPostMerge: {
|
||||||
BlockNumber: 15537393,
|
params.MainnetGenesisHash: {
|
||||||
BlockHash: common.HexToHash("0x55b11b918355b1ef9c5db810302ebad0bf2544255b530cdce90674d5887bb286"),
|
BlockNumber: 15537393,
|
||||||
|
BlockHash: common.HexToHash("0x55b11b918355b1ef9c5db810302ebad0bf2544255b530cdce90674d5887bb286"),
|
||||||
|
},
|
||||||
|
params.SepoliaGenesisHash: {
|
||||||
|
BlockNumber: 1450409,
|
||||||
|
BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// sepolia
|
KeepPostPrague: {
|
||||||
params.SepoliaGenesisHash: {
|
params.MainnetGenesisHash: {
|
||||||
BlockNumber: 1450409,
|
BlockNumber: 22431084,
|
||||||
BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"),
|
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
|
// HistoryPolicy describes the configured history pruning strategy. It captures
|
||||||
// (Pectra) upgrade. They point to the first post-Prague block. Any pruning should truncate
|
// user intent as opposed to the actual DB state.
|
||||||
// *up to* but excluding the given block.
|
type HistoryPolicy struct {
|
||||||
var PraguePrunePoints = map[common.Hash]*PrunePoint{
|
Mode HistoryMode
|
||||||
// mainnet - first Prague block (May 7, 2025)
|
// Static prune point for PostMerge/PostPrague, nil otherwise.
|
||||||
params.MainnetGenesisHash: {
|
Target *PrunePoint
|
||||||
BlockNumber: 22431084,
|
|
||||||
BlockHash: common.HexToHash("0x50c8cab760b2948349c590461b166773c45d8f4858cccf5a43025ab2960152e8"),
|
|
||||||
},
|
|
||||||
// sepolia - first Prague block (March 5, 2025)
|
|
||||||
params.SepoliaGenesisHash: {
|
|
||||||
BlockNumber: 7836331,
|
|
||||||
BlockHash: common.HexToHash("0xe6571beb68bf24dbd8a6ba354518996920c55a3f8d8fdca423e391b8ad071f22"),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrunePoints is an alias for MergePrunePoints for backward compatibility.
|
// NewPolicy constructs a HistoryPolicy from the given mode and genesis hash.
|
||||||
// Deprecated: Use GetPrunePoint or MergePrunePoints directly.
|
func NewPolicy(mode HistoryMode, genesisHash common.Hash) (HistoryPolicy, error) {
|
||||||
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 {
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case KeepPostMerge:
|
case KeepAll:
|
||||||
return MergePrunePoints[genesisHash]
|
return HistoryPolicy{Mode: KeepAll}, nil
|
||||||
case KeepPostPrague:
|
|
||||||
return PraguePrunePoints[genesisHash]
|
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:
|
default:
|
||||||
return nil
|
return HistoryPolicy{}, fmt.Errorf("invalid history mode: %d", mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
58
core/history/historymode_test.go
Normal file
58
core/history/historymode_test.go
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -71,7 +71,7 @@ func (ts *TransitionState) Copy() *TransitionState {
|
||||||
|
|
||||||
// LoadTransitionState retrieves the Verkle transition state associated with
|
// LoadTransitionState retrieves the Verkle transition state associated with
|
||||||
// the given state root hash from the database.
|
// 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
|
var ts *TransitionState
|
||||||
|
|
||||||
data, _ := rawdb.ReadVerkleTransitionState(db, root)
|
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"
|
// Initialize the first transition state, with the "ended"
|
||||||
// field set to true if the database was created
|
// field set to true if the database was created
|
||||||
// as a verkle database.
|
// 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
|
// Start with a fresh state
|
||||||
ts = &TransitionState{Ended: isVerkle}
|
ts = &TransitionState{Ended: isUBT}
|
||||||
}
|
}
|
||||||
return ts
|
return ts
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"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
|
// 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 {
|
func ReadLastPivotNumber(db ethdb.KeyValueReader) *uint64 {
|
||||||
data, _ := db.Get(lastPivotKey)
|
data, _ := db.Get(lastPivotKey)
|
||||||
if len(data) == 0 {
|
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
|
// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps
|
||||||
// the list of logs. When decoding a stored receipt into this object we
|
// the list of logs. When decoding a stored receipt into this object we
|
||||||
// avoid creating the bloom filter.
|
// avoid creating the bloom filter.
|
||||||
|
|
@ -659,13 +711,25 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
|
||||||
if body == nil {
|
if body == nil {
|
||||||
return 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.
|
// WriteBlock serializes a block into the database, header and body separately.
|
||||||
func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
|
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())
|
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.
|
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,12 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests block header storage and retrieval operations.
|
// Tests block header storage and retrieval operations.
|
||||||
|
|
@ -899,3 +901,78 @@ func TestHeadersRLPStorage(t *testing.T) {
|
||||||
checkSequence(1, 1) // Only block 1
|
checkSequence(1, 1) // Only block 1
|
||||||
checkSequence(1, 2) // Genesis + 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -413,6 +413,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||||
tds stat
|
tds stat
|
||||||
numHashPairings stat
|
numHashPairings stat
|
||||||
hashNumPairings stat
|
hashNumPairings stat
|
||||||
|
blockAccessList stat
|
||||||
legacyTries stat
|
legacyTries stat
|
||||||
stateLookups stat
|
stateLookups stat
|
||||||
accountTries stat
|
accountTries stat
|
||||||
|
|
@ -480,10 +481,13 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||||
receipts.add(size)
|
receipts.add(size)
|
||||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix) && len(key) == (len(headerPrefix)+8+common.HashLength+len(headerTDSuffix)):
|
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix) && len(key) == (len(headerPrefix)+8+common.HashLength+len(headerTDSuffix)):
|
||||||
tds.add(size)
|
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)
|
numHashPairings.add(size)
|
||||||
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
||||||
hashNumPairings.add(size)
|
hashNumPairings.add(size)
|
||||||
|
case bytes.HasPrefix(key, accessListPrefix) && len(key) == len(accessListPrefix)+8+common.HashLength:
|
||||||
|
blockAccessList.add(size)
|
||||||
|
|
||||||
case IsLegacyTrieNode(key, it.Value()):
|
case IsLegacyTrieNode(key, it.Value()):
|
||||||
legacyTries.add(size)
|
legacyTries.add(size)
|
||||||
case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
|
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", "Difficulties (deprecated)", tds.sizeString(), tds.countString()},
|
||||||
{"Key-Value store", "Block number->hash", numHashPairings.sizeString(), numHashPairings.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 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", "Transaction index", txLookups.sizeString(), txLookups.countString()},
|
||||||
{"Key-Value store", "Log index filter-map rows", filterMapRows.sizeString(), filterMapRows.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()},
|
{"Key-Value store", "Log index last-block-of-map", filterMapLastBlock.sizeString(), filterMapLastBlock.countString()},
|
||||||
|
|
|
||||||
|
|
@ -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)))
|
meta, err = openFreezerFileForReadOnly(filepath.Join(path, fmt.Sprintf("%s.meta", name)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
index.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} 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)))
|
meta, err = openFreezerFileForAppend(filepath.Join(path, fmt.Sprintf("%s.meta", name)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
index.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,6 +175,8 @@ func newTable(path string, name string, readMeter, writeMeter *metrics.Meter, si
|
||||||
// is detected.
|
// is detected.
|
||||||
metadata, err := newMetadata(meta)
|
metadata, err := newMetadata(meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
meta.Close()
|
||||||
|
index.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create the table and repair any past inconsistency
|
// Create the table and repair any past inconsistency
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,7 @@ func atomicRename(src, dest string) error {
|
||||||
if err := os.Rename(src, dest); err != nil {
|
if err := os.Rename(src, dest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dir, err := os.Open(filepath.Dir(src))
|
return syncDir(filepath.Dir(src))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer dir.Close()
|
|
||||||
|
|
||||||
return dir.Sync()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyFrom copies data from 'srcPath' at offset 'offset' into 'destPath'.
|
// 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.
|
// we do the final move.
|
||||||
src.Close()
|
src.Close()
|
||||||
|
|
||||||
|
// Permanently persist the content into disk
|
||||||
|
if err := f.Sync(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := f.Close(); err != nil {
|
if err := f.Close(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +134,7 @@ func openFreezerFileForAppend(filename string) (*os.File, error) {
|
||||||
}
|
}
|
||||||
// Seek to end for append
|
// Seek to end for append
|
||||||
if _, err = file.Seek(0, io.SeekEnd); err != nil {
|
if _, err = file.Seek(0, io.SeekEnd); err != nil {
|
||||||
|
file.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return file, nil
|
return file, nil
|
||||||
|
|
|
||||||
49
core/rawdb/freezer_utils_unix.go
Normal file
49
core/rawdb/freezer_utils_unix.go
Normal 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
|
||||||
|
}
|
||||||
26
core/rawdb/freezer_utils_windows.go
Normal file
26
core/rawdb/freezer_utils_windows.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -112,6 +112,7 @@ var (
|
||||||
|
|
||||||
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
|
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
|
||||||
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
|
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
|
txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
|
||||||
bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
|
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()...)
|
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
|
// txLookupKey = txLookupPrefix + hash
|
||||||
func txLookupKey(hash common.Hash) []byte {
|
func txLookupKey(hash common.Hash) []byte {
|
||||||
return append(txLookupPrefix, hash.Bytes()...)
|
return append(txLookupPrefix, hash.Bytes()...)
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||||
"github.com/ethereum/go-ethereum/trie/transitiontrie"
|
"github.com/ethereum/go-ethereum/trie/transitiontrie"
|
||||||
|
|
@ -34,11 +30,34 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"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.
|
// Database wraps access to tries and contract code.
|
||||||
type Database interface {
|
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 returns a state reader associated with the specified state root.
|
||||||
Reader(root common.Hash) (Reader, error)
|
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 opens the main account trie.
|
||||||
OpenTrie(root common.Hash) (Trie, error)
|
OpenTrie(root common.Hash) (Trie, error)
|
||||||
|
|
||||||
|
|
@ -48,13 +67,10 @@ type Database interface {
|
||||||
// TrieDB returns the underlying trie database for managing trie nodes.
|
// TrieDB returns the underlying trie database for managing trie nodes.
|
||||||
TrieDB() *triedb.Database
|
TrieDB() *triedb.Database
|
||||||
|
|
||||||
// Snapshot returns the underlying state snapshot.
|
|
||||||
Snapshot() *snapshot.Tree
|
|
||||||
|
|
||||||
// Commit flushes all pending writes and finalizes the state transition,
|
// Commit flushes all pending writes and finalizes the state transition,
|
||||||
// committing the changes to the underlying storage. It returns an error
|
// committing the changes to the underlying storage. It returns an error
|
||||||
// if the commit fails.
|
// if the commit fails.
|
||||||
Commit(update *stateUpdate) error
|
Commit(update *StateUpdate) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trie is a Ethereum Merkle Patricia trie.
|
// Trie is a Ethereum Merkle Patricia trie.
|
||||||
|
|
@ -138,178 +154,27 @@ type Trie interface {
|
||||||
// with the node that proves the absence of the key.
|
// with the node that proves the absence of the key.
|
||||||
Prove(key []byte, proofDb ethdb.KeyValueWriter) error
|
Prove(key []byte, proofDb ethdb.KeyValueWriter) error
|
||||||
|
|
||||||
// IsVerkle returns true if the trie is verkle-tree based
|
// IsUBT returns true if the trie is unified binary trie based.
|
||||||
IsVerkle() bool
|
IsUBT() 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabase creates a state database with the provided data sources.
|
// NewDatabase creates a state database with the provided data sources.
|
||||||
func NewDatabase(triedb *triedb.Database, codedb *CodeDB) *CachingDB {
|
//
|
||||||
if codedb == nil {
|
// Deprecated, please use NewMPTDatabase or NewUBTDatabase directly.
|
||||||
codedb = NewCodeDB(triedb.Disk())
|
func NewDatabase(tdb *triedb.Database, codedb *CodeDB) Database {
|
||||||
}
|
if tdb.IsUBT() {
|
||||||
return &CachingDB{
|
return NewUBTDatabase(tdb, codedb)
|
||||||
triedb: triedb,
|
|
||||||
codedb: codedb,
|
|
||||||
}
|
}
|
||||||
|
return NewMPTDatabase(tdb, codedb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabaseForTesting is similar to NewDatabase, but it initializes the caching
|
// NewDatabaseForTesting is similar to NewDatabase, but it initializes the caching
|
||||||
// db by using an ephemeral memory db with default config for testing.
|
// db by using an ephemeral memory db with default config for testing.
|
||||||
func NewDatabaseForTesting() *CachingDB {
|
func NewDatabaseForTesting() Database {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
return NewDatabase(triedb.NewDatabase(db, nil), NewCodeDB(db))
|
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.
|
// mustCopyTrie returns a deep-copied trie.
|
||||||
func mustCopyTrie(t Trie) Trie {
|
func mustCopyTrie(t Trie) Trie {
|
||||||
switch t := t.(type) {
|
switch t := t.(type) {
|
||||||
|
|
@ -317,6 +182,8 @@ func mustCopyTrie(t Trie) Trie {
|
||||||
return t.Copy()
|
return t.Copy()
|
||||||
case *transitiontrie.TransitionTrie:
|
case *transitiontrie.TransitionTrie:
|
||||||
return t.Copy()
|
return t.Copy()
|
||||||
|
case *bintrie.BinaryTrie:
|
||||||
|
return t.Copy()
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("unknown trie type %T", t))
|
panic(fmt.Errorf("unknown trie type %T", t))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue