mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
merge master branch
This commit is contained in:
commit
1d7fbc0fbc
186 changed files with 12588 additions and 2929 deletions
22
.gitea/workflows/release-azure-cleanup.yml
Normal file
22
.gitea/workflows/release-azure-cleanup.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 14 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
azure-cleanup:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 1.24
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Run cleanup script
|
||||||
|
run: |
|
||||||
|
go run build/ci.go purge -store gethstore/builds -days 14
|
||||||
|
env:
|
||||||
|
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
|
||||||
36
.gitea/workflows/release-ppa.yml
Normal file
36
.gitea/workflows/release-ppa.yml
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 15 * * *'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "release/*"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ppa:
|
||||||
|
name: PPA Upload
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 1.24
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Install deb toolchain
|
||||||
|
run: |
|
||||||
|
apt-get update
|
||||||
|
apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot
|
||||||
|
|
||||||
|
- name: Add launchpad to known_hosts
|
||||||
|
run: |
|
||||||
|
echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
|
- name: Run ci.go
|
||||||
|
run: |
|
||||||
|
go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
||||||
|
env:
|
||||||
|
PPA_SIGNING_KEY: ${{ secrets.PPA_SIGNING_KEY }}
|
||||||
|
PPA_SSH_KEY: ${{ secrets.PPA_SSH_KEY }}
|
||||||
147
.gitea/workflows/release.yml
Normal file
147
.gitea/workflows/release.yml
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux-intel:
|
||||||
|
name: Linux Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 1.24
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Install cross toolchain
|
||||||
|
run: |
|
||||||
|
apt-get update
|
||||||
|
apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib
|
||||||
|
|
||||||
|
- name: Build (amd64)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go install -arch amd64 -dlgo
|
||||||
|
|
||||||
|
- name: Create/upload archive (amd64)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go archive -arch amd64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
rm -f build/bin/*
|
||||||
|
env:
|
||||||
|
LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }}
|
||||||
|
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build (386)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go install -arch 386 -dlgo
|
||||||
|
|
||||||
|
- name: Create/upload archive (386)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
rm -f build/bin/*
|
||||||
|
env:
|
||||||
|
LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }}
|
||||||
|
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
|
||||||
|
|
||||||
|
linux-arm:
|
||||||
|
name: Linux Build (arm)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 1.24
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Install cross toolchain
|
||||||
|
run: |
|
||||||
|
apt-get update
|
||||||
|
apt-get -yq --no-install-suggests --no-install-recommends install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||||
|
ln -s /usr/include/asm-generic /usr/include/asm
|
||||||
|
|
||||||
|
- name: Build (arm64)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc
|
||||||
|
|
||||||
|
- name: Create/upload archive (arm64)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
rm -fr build/bin/*
|
||||||
|
env:
|
||||||
|
LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }}
|
||||||
|
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Run build (arm5)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc
|
||||||
|
env:
|
||||||
|
GOARM: "5"
|
||||||
|
|
||||||
|
- name: Create/upload archive (arm5)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
env:
|
||||||
|
GOARM: "5"
|
||||||
|
LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }}
|
||||||
|
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Run build (arm6)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc
|
||||||
|
env:
|
||||||
|
GOARM: "6"
|
||||||
|
|
||||||
|
- name: Create/upload archive (arm6)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
rm -fr build/bin/*
|
||||||
|
env:
|
||||||
|
GOARM: "6"
|
||||||
|
LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }}
|
||||||
|
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Run build (arm7)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc
|
||||||
|
env:
|
||||||
|
GOARM: "7"
|
||||||
|
|
||||||
|
- name: Create/upload archive (arm7)
|
||||||
|
run: |
|
||||||
|
go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
rm -fr build/bin/*
|
||||||
|
env:
|
||||||
|
GOARM: "7"
|
||||||
|
LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }}
|
||||||
|
AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }}
|
||||||
|
|
||||||
|
docker:
|
||||||
|
name: Docker Image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 1.24
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Run docker build
|
||||||
|
env:
|
||||||
|
DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
|
DOCKER_HUB_PASSWORD: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
go run build/ci.go dockerx -platform linux/amd64,linux/arm64,linux/riscv64 -upload
|
||||||
7
.mailmap
7
.mailmap
|
|
@ -50,6 +50,10 @@ Boqin Qin <bobbqqin@bupt.edu.cn> <Bobbqqin@gmail.com>
|
||||||
|
|
||||||
Casey Detrio <cdetrio@gmail.com>
|
Casey Detrio <cdetrio@gmail.com>
|
||||||
|
|
||||||
|
Charlotte <tqpcharlie@proton.me>
|
||||||
|
Charlotte <tqpcharlie@proton.me> <Zachinquarantine@protonmail.com>
|
||||||
|
Charlotte <tqpcharlie@proton.me> <zachinquarantine@yahoo.com>
|
||||||
|
|
||||||
Cheng Li <lob4tt@gmail.com>
|
Cheng Li <lob4tt@gmail.com>
|
||||||
|
|
||||||
Chris Ziogas <ziogaschr@gmail.com>
|
Chris Ziogas <ziogaschr@gmail.com>
|
||||||
|
|
@ -301,9 +305,6 @@ Yohann Léon <sybiload@gmail.com>
|
||||||
yzb <335357057@qq.com>
|
yzb <335357057@qq.com>
|
||||||
yzb <335357057@qq.com> <flyingyzb@gmail.com>
|
yzb <335357057@qq.com> <flyingyzb@gmail.com>
|
||||||
|
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com>
|
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com> <zachinquarantine@yahoo.com>
|
|
||||||
|
|
||||||
Ziyuan Zhong <zzy.albert@163.com>
|
Ziyuan Zhong <zzy.albert@163.com>
|
||||||
|
|
||||||
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
||||||
|
|
|
||||||
65
.travis.yml
65
.travis.yml
|
|
@ -5,7 +5,7 @@ jobs:
|
||||||
include:
|
include:
|
||||||
# This builder create and push the Docker images for all architectures
|
# This builder create and push the Docker images for all architectures
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = push
|
if: type = push && tag ~= /^v[0-9]/
|
||||||
os: linux
|
os: linux
|
||||||
arch: amd64
|
arch: amd64
|
||||||
dist: focal
|
dist: focal
|
||||||
|
|
@ -21,9 +21,25 @@ jobs:
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go dockerx -platform "linux/amd64,linux/arm64,linux/riscv64" -hub ethereum/client-go -upload
|
- go run build/ci.go dockerx -platform "linux/amd64,linux/arm64,linux/riscv64" -hub ethereum/client-go -upload
|
||||||
|
|
||||||
|
# This builder does the Ubuntu PPA nightly uploads
|
||||||
|
- stage: build
|
||||||
|
if: type = push && tag ~= /^v[0-9]/
|
||||||
|
os: linux
|
||||||
|
dist: focal
|
||||||
|
go: 1.24.x
|
||||||
|
env:
|
||||||
|
- ubuntu-ppa
|
||||||
|
git:
|
||||||
|
submodules: false # avoid cloning ethereum/tests
|
||||||
|
before_install:
|
||||||
|
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot
|
||||||
|
script:
|
||||||
|
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
||||||
|
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
||||||
|
|
||||||
# This builder does the Linux Azure uploads
|
# This builder does the Linux Azure uploads
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = push
|
if: type = push && tag ~= /^v[0-9]/
|
||||||
os: linux
|
os: linux
|
||||||
dist: focal
|
dist: focal
|
||||||
sudo: required
|
sudo: required
|
||||||
|
|
@ -56,40 +72,6 @@ jobs:
|
||||||
- go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc
|
- go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc
|
||||||
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
||||||
|
|
||||||
# These builders run the tests
|
|
||||||
- stage: build
|
|
||||||
if: type = push
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
dist: focal
|
|
||||||
go: 1.24.x
|
|
||||||
script:
|
|
||||||
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
|
||||||
|
|
||||||
- stage: build
|
|
||||||
if: type = push
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
go: 1.23.x
|
|
||||||
script:
|
|
||||||
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
|
||||||
|
|
||||||
# This builder does the Ubuntu PPA nightly uploads
|
|
||||||
- stage: build
|
|
||||||
if: type = cron || (type = push && tag ~= /^v[0-9]/)
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
go: 1.24.x
|
|
||||||
env:
|
|
||||||
- ubuntu-ppa
|
|
||||||
git:
|
|
||||||
submodules: false # avoid cloning ethereum/tests
|
|
||||||
before_install:
|
|
||||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot
|
|
||||||
script:
|
|
||||||
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
|
||||||
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
|
||||||
|
|
||||||
# This builder does the Azure archive purges to avoid accumulating junk
|
# This builder does the Azure archive purges to avoid accumulating junk
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = cron
|
if: type = cron
|
||||||
|
|
@ -102,14 +84,3 @@ jobs:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go purge -store gethstore/builds -days 14
|
- go run build/ci.go purge -store gethstore/builds -days 14
|
||||||
|
|
||||||
# This builder executes race tests
|
|
||||||
- stage: build
|
|
||||||
if: type = cron
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
go: 1.24.x
|
|
||||||
env:
|
|
||||||
- racetests
|
|
||||||
script:
|
|
||||||
- travis_wait 60 go run build/ci.go test -race $TEST_PACKAGES
|
|
||||||
|
|
|
||||||
2
AUTHORS
2
AUTHORS
|
|
@ -123,6 +123,7 @@ Ceyhun Onur <ceyhun.onur@avalabs.org>
|
||||||
chabashilah <doumodoumo@gmail.com>
|
chabashilah <doumodoumo@gmail.com>
|
||||||
changhong <changhong.yu@shanbay.com>
|
changhong <changhong.yu@shanbay.com>
|
||||||
Charles Cooper <cooper.charles.m@gmail.com>
|
Charles Cooper <cooper.charles.m@gmail.com>
|
||||||
|
Charlotte <tqpcharlie@proton.me>
|
||||||
Chase Wright <mysticryuujin@gmail.com>
|
Chase Wright <mysticryuujin@gmail.com>
|
||||||
Chawin Aiemvaravutigul <nick41746@hotmail.com>
|
Chawin Aiemvaravutigul <nick41746@hotmail.com>
|
||||||
Chen Quan <terasum@163.com>
|
Chen Quan <terasum@163.com>
|
||||||
|
|
@ -839,7 +840,6 @@ ywzqwwt <39263032+ywzqwwt@users.noreply.github.com>
|
||||||
yzb <335357057@qq.com>
|
yzb <335357057@qq.com>
|
||||||
zaccoding <zaccoding725@gmail.com>
|
zaccoding <zaccoding725@gmail.com>
|
||||||
Zach <zach.ramsay@gmail.com>
|
Zach <zach.ramsay@gmail.com>
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com>
|
|
||||||
zah <zahary@gmail.com>
|
zah <zahary@gmail.com>
|
||||||
Zahoor Mohamed <zahoor@zahoor.in>
|
Zahoor Mohamed <zahoor@zahoor.in>
|
||||||
Zak Cole <zak@beattiecole.com>
|
Zak Cole <zak@beattiecole.com>
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ accessible from the outside.
|
||||||
|
|
||||||
As a developer, sooner rather than later you'll want to start interacting with `geth` and the
|
As a developer, sooner rather than later you'll want to start interacting with `geth` and the
|
||||||
Ethereum network via your own programs and not manually through the console. To aid
|
Ethereum network via your own programs and not manually through the console. To aid
|
||||||
this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://ethereum.github.io/execution-apis/api-documentation/)
|
this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://ethereum.org/en/developers/docs/apis/json-rpc/)
|
||||||
and [`geth` specific APIs](https://geth.ethereum.org/docs/interacting-with-geth/rpc)).
|
and [`geth` specific APIs](https://geth.ethereum.org/docs/interacting-with-geth/rpc)).
|
||||||
These can be exposed via HTTP, WebSockets and IPC (UNIX sockets on UNIX based
|
These can be exposed via HTTP, WebSockets and IPC (UNIX sockets on UNIX based
|
||||||
platforms, and named pipes on Windows).
|
platforms, and named pipes on Windows).
|
||||||
|
|
|
||||||
116
accounts/abi/abigen/testdata/v2/structs-abi.go.txt
vendored
116
accounts/abi/abigen/testdata/v2/structs-abi.go.txt
vendored
|
|
@ -1,116 +0,0 @@
|
||||||
// Code generated via abigen V2 - DO NOT EDIT.
|
|
||||||
// This file is a generated binding and any manual changes will be lost.
|
|
||||||
|
|
||||||
package v1bindtests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/v2"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var (
|
|
||||||
_ = bytes.Equal
|
|
||||||
_ = errors.New
|
|
||||||
_ = big.NewInt
|
|
||||||
_ = common.Big1
|
|
||||||
_ = types.BloomLookup
|
|
||||||
_ = abi.ConvertType
|
|
||||||
)
|
|
||||||
|
|
||||||
// Struct0 is an auto generated low-level Go binding around an user-defined struct.
|
|
||||||
type Struct0 struct {
|
|
||||||
B [32]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructsMetaData contains all meta data concerning the Structs contract.
|
|
||||||
var StructsMetaData = bind.MetaData{
|
|
||||||
ABI: "[{\"inputs\":[],\"name\":\"F\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[]\",\"name\":\"c\",\"type\":\"uint256[]\"},{\"internalType\":\"bool[]\",\"name\":\"d\",\"type\":\"bool[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"G\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
|
|
||||||
ID: "Structs",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Structs is an auto generated Go binding around an Ethereum contract.
|
|
||||||
type Structs struct {
|
|
||||||
abi abi.ABI
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStructs creates a new instance of Structs.
|
|
||||||
func NewStructs() *Structs {
|
|
||||||
parsed, err := StructsMetaData.ParseABI()
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.New("invalid ABI: " + err.Error()))
|
|
||||||
}
|
|
||||||
return &Structs{abi: *parsed}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instance creates a wrapper for a deployed contract instance at the given address.
|
|
||||||
<<<<<<< HEAD
|
|
||||||
// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc.
|
|
||||||
func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract {
|
|
||||||
return bind.NewBoundContract(addr, c.abi, backend, backend, backend)
|
|
||||||
=======
|
|
||||||
// Use this to create the instance object passed to abigen v2 library functions Call,
|
|
||||||
// Transact, etc.
|
|
||||||
func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) bind.BoundContract {
|
|
||||||
return bind.NewBoundContract(backend, addr, c.abi)
|
|
||||||
>>>>>>> 854c25e086 (accounts/abi/abigen: improve v2 template)
|
|
||||||
}
|
|
||||||
|
|
||||||
// F is the Go binding used to pack the parameters required for calling
|
|
||||||
// the contract method 0x28811f59.
|
|
||||||
//
|
|
||||||
// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d)
|
|
||||||
func (structs *Structs) PackF() ([]byte, error) {
|
|
||||||
return structs.abi.Pack("F")
|
|
||||||
}
|
|
||||||
|
|
||||||
// FOutput serves as a container for the return parameters of contract
|
|
||||||
// method F.
|
|
||||||
type FOutput struct {
|
|
||||||
A []Struct0
|
|
||||||
C []*big.Int
|
|
||||||
D []bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnpackF is the Go binding that unpacks the parameters returned
|
|
||||||
// from invoking the contract method with ID 0x28811f59.
|
|
||||||
//
|
|
||||||
// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d)
|
|
||||||
func (structs *Structs) UnpackF(data []byte) (*FOutput, error) {
|
|
||||||
out, err := structs.abi.Unpack("F", data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret := new(FOutput)
|
|
||||||
ret.A = *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0)
|
|
||||||
ret.C = *abi.ConvertType(out[1], new([]*big.Int)).(*[]*big.Int)
|
|
||||||
ret.D = *abi.ConvertType(out[2], new([]bool)).(*[]bool)
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// G is the Go binding used to pack the parameters required for calling
|
|
||||||
// the contract method 0x6fecb623.
|
|
||||||
//
|
|
||||||
// Solidity: function G() view returns((bytes32)[] a)
|
|
||||||
func (structs *Structs) PackG() ([]byte, error) {
|
|
||||||
return structs.abi.Pack("G")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnpackG is the Go binding that unpacks the parameters returned
|
|
||||||
// from invoking the contract method with ID 0x6fecb623.
|
|
||||||
//
|
|
||||||
// Solidity: function G() view returns((bytes32)[] a)
|
|
||||||
func (structs *Structs) UnpackG(data []byte) (*[]Struct0, error) {
|
|
||||||
out, err := structs.abi.Unpack("G", data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
out0 := *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0)
|
|
||||||
return &out0, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +1,58 @@
|
||||||
# This file contains sha256 checksums of optional build dependencies.
|
# This file contains sha256 checksums of optional build dependencies.
|
||||||
|
|
||||||
# version:spec-tests pectra-devnet-6@v1.0.0
|
# version:spec-tests v4.5.0
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases
|
# https://github.com/ethereum/execution-spec-tests/releases
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/
|
# https://github.com/ethereum/execution-spec-tests/releases/download/v4.5.0/
|
||||||
b69211752a3029083c020dc635fe12156ca1a6725a08559da540a0337586a77e fixtures_pectra-devnet-6.tar.gz
|
58afb92a0075a2cb7c4dec1281f7cb88b21b02afbedad096b580f3f8cc14c54c fixtures_develop.tar.gz
|
||||||
|
|
||||||
# version:golang 1.24.2
|
# version:golang 1.24.3
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
9dc77ffadc16d837a1bf32d99c624cb4df0647cee7b119edd9e7b1bcc05f2e00 go1.24.2.src.tar.gz
|
229c08b600b1446798109fae1f569228102c8473caba8104b6418cb5bc032878 go1.24.3.src.tar.gz
|
||||||
427b373540d8fd51dbcc46bdecd340af109cd41514443c000d3dcde72b2c65a3 go1.24.2.aix-ppc64.tar.gz
|
6f6901497547db3b77c14f7f953fbcef9fa5fb84199ee2ee14a5686e66bed5a6 go1.24.3.aix-ppc64.tar.gz
|
||||||
238d9c065d09ff6af229d2e3b8b5e85e688318d69f4006fb85a96e41c216ea83 go1.24.2.darwin-amd64.tar.gz
|
a05fa7e4043a4fec66897135219e3b8ab2202b5ef351c60c2fbb531dfb8f2900 go1.24.3.darwin-amd64.pkg
|
||||||
535ed9ff283fee39575a7fb9b6d8b1901b6dc640d06dc71fd7d3faeefdaf8030 go1.24.2.darwin-amd64.pkg
|
13e6fe3fcf65689d77d40e633de1e31c6febbdbcb846eb05fc2434ed2213e92b go1.24.3.darwin-amd64.tar.gz
|
||||||
b70f8b3c5b4ccb0ad4ffa5ee91cd38075df20fdbd953a1daedd47f50fbcff47a go1.24.2.darwin-arm64.tar.gz
|
97055ff4214043b39dc32e043fdd5c565df7c0a4e2fc0174e779a134c347ae0e go1.24.3.darwin-arm64.pkg
|
||||||
4732f607a47ce4d898c0af01ff68f07e0820a6b50603aef5d5c777d1102505e2 go1.24.2.darwin-arm64.pkg
|
64a3fa22142f627e78fac3018ce3d4aeace68b743eff0afda8aae0411df5e4fb go1.24.3.darwin-arm64.tar.gz
|
||||||
c17686b5fd61a663fbfafccfa177961be59386cf294e935ce35866b9dcb8e78a go1.24.2.dragonfly-amd64.tar.gz
|
32de3fd44d5055973978436a7f1f0ffbaae85c1b603ec6105e5c38d8a674c721 go1.24.3.dragonfly-amd64.tar.gz
|
||||||
026f1dd906189acff714c7625686bbc4ed91042618ba010d45b671461acc9e63 go1.24.2.freebsd-386.tar.gz
|
9fe6101b3797919bd7337ee5ce591954f85d59db7ae88983904db29fd64c3dd1 go1.24.3.freebsd-386.tar.gz
|
||||||
49399ba759b570a8f87d12179133403da6c2dd296d63a8830dee309161b9c40c go1.24.2.freebsd-amd64.tar.gz
|
6ccf4cca287e90cc28cd7954b6172f5d177a17e20b072b65f7f39636c325e2fb go1.24.3.freebsd-amd64.tar.gz
|
||||||
1f48f47183794d97c29736004247ab541177cf984ac6322c78bc43828daa1172 go1.24.2.freebsd-arm.tar.gz
|
ce45ebf389066f82a7b056b66dd650efb51fde6f8bf92a2a3ab6990f02788ebf go1.24.3.freebsd-arm.tar.gz
|
||||||
ef856428b60a8c0bd9a2cba596e83024be6f1c2d5574e89cb1ff2262b08df8b9 go1.24.2.freebsd-arm64.tar.gz
|
8f6494a12a874d0ea57c67987829359e016960ce3ba0673273609d6ac2af589a go1.24.3.freebsd-arm64.tar.gz
|
||||||
ec2088823e16df00600a6d0f72e9a7dc6d2f80c9c140c2043c0cf20e1404d1a9 go1.24.2.freebsd-riscv64.tar.gz
|
f9db392560cf0851f0bc8f2190e1978e01b4603038c27fecfc8658a695b71616 go1.24.3.freebsd-riscv64.tar.gz
|
||||||
e030e7cedbb8688f1d75cb80f3de6ee2e6617a67d34051e794e5992b53462147 go1.24.2.illumos-amd64.tar.gz
|
01717fff64c5d98457272002fa825d0a15e307bf6e189f2b0c23817fa033b61c go1.24.3.illumos-amd64.tar.gz
|
||||||
4c382776d52313266f3026236297a224a6688751256a2dffa3f524d8d6f6c0ba go1.24.2.linux-386.tar.gz
|
41b1051063e68cbd2b919bf12326764fe33937cf1d32b5c529dd1a4f43dce578 go1.24.3.linux-386.tar.gz
|
||||||
68097bd680839cbc9d464a0edce4f7c333975e27a90246890e9f1078c7e702ad go1.24.2.linux-amd64.tar.gz
|
3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8 go1.24.3.linux-amd64.tar.gz
|
||||||
756274ea4b68fa5535eb9fe2559889287d725a8da63c6aae4d5f23778c229f4b go1.24.2.linux-arm64.tar.gz
|
a463cb59382bd7ae7d8f4c68846e73c4d589f223c589ac76871b66811ded7836 go1.24.3.linux-arm64.tar.gz
|
||||||
438d5d3d7dcb239b58d893a715672eabe670b9730b1fd1c8fc858a46722a598a go1.24.2.linux-armv6l.tar.gz
|
17a392d7e826625dd12a32099df0b00b85c32d8132ed86fe917183ee5c3f88ed go1.24.3.linux-armv6l.tar.gz
|
||||||
6aefd3bf59c3c5592eda4fb287322207f119c2210f3795afa9be48d3ccb73e1b go1.24.2.linux-loong64.tar.gz
|
e4b003c04c902edc140153d279b42167f1ad7c229f48f1f729bbef5e65e88d1f go1.24.3.linux-loong64.tar.gz
|
||||||
93e49bb4692783b0e9a2deab9558c6e8d2867f35592aeff285adda60924167f3 go1.24.2.linux-mips.tar.gz
|
1c79d89edf835edf9d4336ccea7cb89bc5c0ca82b12b36b218d599a5400d60fe go1.24.3.linux-mips.tar.gz
|
||||||
6e86e703675016f3faf6604b8f68f20dc1bba75849136e6dd4f43f69c8a4a9d9 go1.24.2.linux-mips64.tar.gz
|
0b64fe147d69f4d681d8e8a035c760477531432f83d831f18d37cb9bf3652488 go1.24.3.linux-mips64.tar.gz
|
||||||
f233d237538ca1559a7d7cf519a29f0147923a951377bc4e467af4c059e68851 go1.24.2.linux-mips64le.tar.gz
|
396b784c255b64512dc00c302c053e43a3cbfc77518664c6ac5569aafad4d1e6 go1.24.3.linux-mips64le.tar.gz
|
||||||
545e1b9a7939f923fd53bde98334b987ef42eb353ee3e0bfede8aa06079d6b24 go1.24.2.linux-mipsle.tar.gz
|
93898313887f14e8efbe9d7386d5da4792b2d6c492bee562993fd4c9daa75c6d go1.24.3.linux-mipsle.tar.gz
|
||||||
6eab31481f2f46187bc1b6c887662eef06fc9d7271a8390854072cdb387c8d74 go1.24.2.linux-ppc64.tar.gz
|
873ae3a6a6655a7b6f820e095d9965507e8dfd3cf76bc92d75c564ecbca385f6 go1.24.3.linux-ppc64.tar.gz
|
||||||
5fff857791d541c71d8ea0171c73f6f99770d15ff7e2ad979104856d01f36563 go1.24.2.linux-ppc64le.tar.gz
|
341a749d168f47b1d4dad25e32cae70849b7ceed7c290823b853c9e6b0df0856 go1.24.3.linux-ppc64le.tar.gz
|
||||||
91bda1558fcbd1c92769ad86c8f5cf796f8c67b0d9d9c19f76eecfc75ce71527 go1.24.2.linux-riscv64.tar.gz
|
fa482f53ccb4ba280316b8c5751ea67291507280d9166f2a38fe4d9b5d5fb64b go1.24.3.linux-riscv64.tar.gz
|
||||||
1cb3448166d6abb515a85a3ee5afbdf932081fb58ad7143a8fb666fbc06146d9 go1.24.2.linux-s390x.tar.gz
|
a87b0c2a079a0bece1620fb29a00e02b4dba17507850f837e754af7d57cda282 go1.24.3.linux-s390x.tar.gz
|
||||||
a9a2c0db2e826f20f00b02bee01dfdaeb49591c2f6ffacb78dc64a950894f7ff go1.24.2.netbsd-386.tar.gz
|
63155382308db1306200aff7821aa26bf2a2dda23537dd637a9704b485b6ddf0 go1.24.3.netbsd-386.tar.gz
|
||||||
cd1a35b76ed9c7b6c0c1616741bd319699a77867ade0be9924f32496c0a87a3f go1.24.2.netbsd-amd64.tar.gz
|
fe2c5c79482958b867c08a4fc2a10a998de9c0206b08d5b3ebcb2232e8d2777c go1.24.3.netbsd-amd64.tar.gz
|
||||||
8c666388d066e479155cc5116950eeb435df28087ef277c18f1dc7479f836e60 go1.24.2.netbsd-arm.tar.gz
|
e8ff77aef21521b5dd94e44282a3243309b80717414cf12f72835a45886a049f go1.24.3.netbsd-arm.tar.gz
|
||||||
5d42f0be04f58da5be788a1e260f8747c316b8ce182bf0b273c2e4c691feaa1a go1.24.2.netbsd-arm64.tar.gz
|
b337fbaf82822685940ffaa76fbcf4be5d2f0258bc819cd80bc408b491f45c04 go1.24.3.netbsd-arm64.tar.gz
|
||||||
688effa23ea3973cc8b0fdf4246712cbeef55ff20c45f3a9e28b0c2db04246cf go1.24.2.openbsd-386.tar.gz
|
c1bb9dd8418480aa7f65452b08de3759da3bf89702be71b5a9fc084836b24ad5 go1.24.3.openbsd-386.tar.gz
|
||||||
e5daf95f1048d8026b1366450a3f8044d668b0639db6422ad9a83755c6745cf7 go1.24.2.openbsd-amd64.tar.gz
|
531218de748b0caaf6d1ad18921206fc12baaa89bf483a0a5e60a571c206fe6f go1.24.3.openbsd-amd64.tar.gz
|
||||||
aeadaf74bd544d1a12ba9b14c0e7cdb1964de3ba9a52acb4619e91dbae7def7b go1.24.2.openbsd-arm.tar.gz
|
bcd0dc959986fc346969b5d4111c3c8031882d8bf8d87a2c2ecf1328962a91f2 go1.24.3.openbsd-arm.tar.gz
|
||||||
9e222d9adb0ce836a5b3c8d5aadbd167c8869c030b113f4a81aa88e9a200f279 go1.24.2.openbsd-arm64.tar.gz
|
00ee6f8f1c41fd2e28ad386bd7e39acce7cab84af6de835855b29d1c597335c4 go1.24.3.openbsd-arm64.tar.gz
|
||||||
192fffa34536adc3cd1bb7c1ee785b8bc156ae7afd10bbf5db99ec8f2e93066e go1.24.2.openbsd-ppc64.tar.gz
|
9f4ec0a9203ed3c54ce1a2a390ad3d45838cdb7efd85baeff857e37dfde04edd go1.24.3.openbsd-ppc64.tar.gz
|
||||||
a23e90b451a390549042c2a7efbec6f29ed98b2d5618c8d2a35704e21be96e09 go1.24.2.openbsd-riscv64.tar.gz
|
da4d6f80e2373250d8c31c32dcd1e08775c327c0d610923604660cc0e07e8cba go1.24.3.openbsd-riscv64.tar.gz
|
||||||
5cdcafe455d859b02779611a5a1e1d63e498b922e05818fb3debe410a5959e9e go1.24.2.plan9-386.tar.gz
|
f5d02149132eedda6c2d46b360d7da462b8a5f9e3f8567db100c2d7bff0ddcd7 go1.24.3.plan9-386.tar.gz
|
||||||
81351659804fa505c1b3ec6fdf9599f7f88df08614307eeb96071bf5e2e74beb go1.24.2.plan9-amd64.tar.gz
|
175f3d79f4762a3c545d2c6393bf6b8bac24e838026869dafab06b930735c94f go1.24.3.plan9-amd64.tar.gz
|
||||||
6e337d5def14ed0123423c1c32e2e6d8b19161e5d5ffaa7356dad48ee0fd80b4 go1.24.2.plan9-arm.tar.gz
|
d1e4ac15095da1611659261c2228c2058756cf87d61d9fad262f76755ef26849 go1.24.3.plan9-arm.tar.gz
|
||||||
07e6926ebc476c044d7d5b17706abfc52be52bccc2073d1734174efe63c6b35e go1.24.2.solaris-amd64.tar.gz
|
e644220a6ced3c07a7acc1364193cb709a97737dd8b6792a07a8ec6d9996713e go1.24.3.solaris-amd64.tar.gz
|
||||||
13d86cb818bba331da75fcd18246ab31a1067b44fb4a243b6dfd93097eda7f37 go1.24.2.windows-386.zip
|
0d7e7dc0a31ba0cdd487415709d03b02fc9490ef111e8dfd22788a6d63316f37 go1.24.3.windows-386.msi
|
||||||
8a702d9f7104a15bd935f4191c58c24c0b6389e066b9d5661b93915114a2bef0 go1.24.2.windows-386.msi
|
c27c463a61ab849266baa0c17a6c5c4256a574ab642f609ba25c96ec965dc184 go1.24.3.windows-386.zip
|
||||||
29c553aabee0743e2ffa3e9fa0cda00ef3b3cc4ff0bc92007f31f80fd69892e1 go1.24.2.windows-amd64.zip
|
d5b7637e7e138be877d96a4501709d480e050d86a8f402bc950e72112b5aedc5 go1.24.3.windows-amd64.msi
|
||||||
acefb191e72fea0bdb1a3f5f8f6f5ab18b42b3bbce0c7183f189f25953aff275 go1.24.2.windows-amd64.msi
|
be9787cb08998b1860fe3513e48a5fe5b96302d358a321b58e651184fa9638b3 go1.24.3.windows-amd64.zip
|
||||||
ab267f7f9a3366d48d7664be9e627ce3e63273231430cce5f7783fb910f14148 go1.24.2.windows-arm64.zip
|
7efde2e5e8468e9caf2c7fc94f4da78a726a5031a1ed63acff7899527cdddff6 go1.24.3.windows-arm64.msi
|
||||||
d187bfe539356c39573d2f46766d1d08122b4f33da00fd14d12485fa9e241ff5 go1.24.2.windows-arm64.msi
|
eec9fa736056b54dd88ecb669db2bfad39b0c48f6f9080f036dfa1ca42dc4bb5 go1.24.3.windows-arm64.zip
|
||||||
|
|
||||||
# version:golangci 2.0.2
|
# version:golangci 2.0.2
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
|
|
|
||||||
57
build/ci.go
57
build/ci.go
|
|
@ -59,6 +59,7 @@ import (
|
||||||
"github.com/cespare/cp"
|
"github.com/cespare/cp"
|
||||||
"github.com/ethereum/go-ethereum/crypto/signify"
|
"github.com/ethereum/go-ethereum/crypto/signify"
|
||||||
"github.com/ethereum/go-ethereum/internal/build"
|
"github.com/ethereum/go-ethereum/internal/build"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/download"
|
||||||
"github.com/ethereum/go-ethereum/internal/version"
|
"github.com/ethereum/go-ethereum/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -190,7 +191,7 @@ func doInstall(cmdline []string) {
|
||||||
// Configure the toolchain.
|
// Configure the toolchain.
|
||||||
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
||||||
if *dlgo {
|
if *dlgo {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
tc.Root = build.DownloadGo(csdb)
|
tc.Root = build.DownloadGo(csdb)
|
||||||
}
|
}
|
||||||
// Disable CLI markdown doc generation in release builds.
|
// Disable CLI markdown doc generation in release builds.
|
||||||
|
|
@ -285,7 +286,7 @@ func doTest(cmdline []string) {
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
|
||||||
// Get test fixtures.
|
// Get test fixtures.
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
downloadSpecTestFixtures(csdb, *cachedir)
|
downloadSpecTestFixtures(csdb, *cachedir)
|
||||||
|
|
||||||
// Configure the toolchain.
|
// Configure the toolchain.
|
||||||
|
|
@ -329,16 +330,11 @@ func doTest(cmdline []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures.
|
// downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures.
|
||||||
func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
|
func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string {
|
||||||
executionSpecTestsVersion, err := build.Version(csdb, "spec-tests")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
ext := ".tar.gz"
|
ext := ".tar.gz"
|
||||||
base := "fixtures_pectra-devnet-6" // TODO(s1na) rename once the version becomes part of the filename
|
base := "fixtures_develop"
|
||||||
url := fmt.Sprintf("https://github.com/ethereum/execution-spec-tests/releases/download/%s/%s%s", executionSpecTestsVersion, base, ext)
|
|
||||||
archivePath := filepath.Join(cachedir, base+ext)
|
archivePath := filepath.Join(cachedir, base+ext)
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil {
|
if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil {
|
||||||
|
|
@ -444,14 +440,13 @@ func doLint(cmdline []string) {
|
||||||
|
|
||||||
// downloadLinter downloads and unpacks golangci-lint.
|
// downloadLinter downloads and unpacks golangci-lint.
|
||||||
func downloadLinter(cachedir string) string {
|
func downloadLinter(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
version, err := build.Version(csdb, "golangci")
|
version, err := csdb.FindVersion("golangci")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
arch := runtime.GOARCH
|
arch := runtime.GOARCH
|
||||||
ext := ".tar.gz"
|
ext := ".tar.gz"
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
ext = ".zip"
|
ext = ".zip"
|
||||||
}
|
}
|
||||||
|
|
@ -459,9 +454,8 @@ func downloadLinter(cachedir string) string {
|
||||||
arch += "v" + os.Getenv("GOARM")
|
arch += "v" + os.Getenv("GOARM")
|
||||||
}
|
}
|
||||||
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, arch)
|
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, arch)
|
||||||
url := fmt.Sprintf("https://github.com/golangci/golangci-lint/releases/download/v%s/%s%s", version, base, ext)
|
|
||||||
archivePath := filepath.Join(cachedir, base+ext)
|
archivePath := filepath.Join(cachedir, base+ext)
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := build.ExtractArchive(archivePath, cachedir); err != nil {
|
if err := build.ExtractArchive(archivePath, cachedir); err != nil {
|
||||||
|
|
@ -497,8 +491,8 @@ func protocArchiveBaseName() (string, error) {
|
||||||
// in the generate command. It returns the full path of the directory
|
// in the generate command. It returns the full path of the directory
|
||||||
// containing the 'protoc-gen-go' executable.
|
// containing the 'protoc-gen-go' executable.
|
||||||
func downloadProtocGenGo(cachedir string) string {
|
func downloadProtocGenGo(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
version, err := build.Version(csdb, "protoc-gen-go")
|
version, err := csdb.FindVersion("protoc-gen-go")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -510,10 +504,8 @@ func downloadProtocGenGo(cachedir string) string {
|
||||||
archiveName += ".tar.gz"
|
archiveName += ".tar.gz"
|
||||||
}
|
}
|
||||||
|
|
||||||
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf-go/releases/download/v%s/%s", version, archiveName)
|
|
||||||
|
|
||||||
archivePath := path.Join(cachedir, archiveName)
|
archivePath := path.Join(cachedir, archiveName)
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
extractDest := filepath.Join(cachedir, baseName)
|
extractDest := filepath.Join(cachedir, baseName)
|
||||||
|
|
@ -531,8 +523,8 @@ func downloadProtocGenGo(cachedir string) string {
|
||||||
// files as a CI step. It returns the full path to the directory containing
|
// files as a CI step. It returns the full path to the directory containing
|
||||||
// the protoc executable.
|
// the protoc executable.
|
||||||
func downloadProtoc(cachedir string) string {
|
func downloadProtoc(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
version, err := build.Version(csdb, "protoc")
|
version, err := csdb.FindVersion("protoc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -543,10 +535,8 @@ func downloadProtoc(cachedir string) string {
|
||||||
|
|
||||||
fileName := fmt.Sprintf("protoc-%s-%s", version, baseName)
|
fileName := fmt.Sprintf("protoc-%s-%s", version, baseName)
|
||||||
archiveFileName := fileName + ".zip"
|
archiveFileName := fileName + ".zip"
|
||||||
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf/releases/download/v%s/%s", version, archiveFileName)
|
|
||||||
archivePath := filepath.Join(cachedir, archiveFileName)
|
archivePath := filepath.Join(cachedir, archiveFileName)
|
||||||
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
extractDest := filepath.Join(cachedir, fileName)
|
extractDest := filepath.Join(cachedir, fileName)
|
||||||
|
|
@ -826,18 +816,17 @@ func doDebianSource(cmdline []string) {
|
||||||
// downloadGoBootstrapSources downloads the Go source tarball(s) that will be used
|
// downloadGoBootstrapSources downloads the Go source tarball(s) that will be used
|
||||||
// to bootstrap the builder Go.
|
// to bootstrap the builder Go.
|
||||||
func downloadGoBootstrapSources(cachedir string) []string {
|
func downloadGoBootstrapSources(cachedir string) []string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
|
|
||||||
var bundles []string
|
var bundles []string
|
||||||
for _, booter := range []string{"ppa-builder-1.19", "ppa-builder-1.21", "ppa-builder-1.23"} {
|
for _, booter := range []string{"ppa-builder-1.19", "ppa-builder-1.21", "ppa-builder-1.23"} {
|
||||||
gobootVersion, err := build.Version(csdb, booter)
|
gobootVersion, err := csdb.FindVersion(booter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion)
|
file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion)
|
||||||
url := "https://dl.google.com/go/" + file
|
|
||||||
dst := filepath.Join(cachedir, file)
|
dst := filepath.Join(cachedir, file)
|
||||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(dst); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
bundles = append(bundles, dst)
|
bundles = append(bundles, dst)
|
||||||
|
|
@ -847,15 +836,14 @@ func downloadGoBootstrapSources(cachedir string) []string {
|
||||||
|
|
||||||
// downloadGoSources downloads the Go source tarball.
|
// downloadGoSources downloads the Go source tarball.
|
||||||
func downloadGoSources(cachedir string) string {
|
func downloadGoSources(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
dlgoVersion, err := build.Version(csdb, "golang")
|
dlgoVersion, err := csdb.FindVersion("golang")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
file := fmt.Sprintf("go%s.src.tar.gz", dlgoVersion)
|
file := fmt.Sprintf("go%s.src.tar.gz", dlgoVersion)
|
||||||
url := "https://dl.google.com/go/" + file
|
|
||||||
dst := filepath.Join(cachedir, file)
|
dst := filepath.Join(cachedir, file)
|
||||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(dst); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
|
|
@ -1181,5 +1169,6 @@ func doPurge(cmdline []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func doSanityCheck() {
|
func doSanityCheck() {
|
||||||
build.DownloadAndVerifyChecksums(build.MustLoadChecksums("build/checksums.txt"))
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
|
csdb.DownloadAndVerifyAll()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,9 @@ func (s *Suite) dialAs(key *ecdsa.PrivateKey) (*Conn, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conn.caps = []p2p.Cap{
|
conn.caps = []p2p.Cap{
|
||||||
{Name: "eth", Version: 67},
|
{Name: "eth", Version: 69},
|
||||||
{Name: "eth", Version: 68},
|
|
||||||
}
|
}
|
||||||
conn.ourHighestProtoVersion = 68
|
conn.ourHighestProtoVersion = 69
|
||||||
return &conn, nil
|
return &conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,7 +155,7 @@ func (c *Conn) ReadEth() (any, error) {
|
||||||
var msg any
|
var msg any
|
||||||
switch int(code) {
|
switch int(code) {
|
||||||
case eth.StatusMsg:
|
case eth.StatusMsg:
|
||||||
msg = new(eth.StatusPacket)
|
msg = new(eth.StatusPacket69)
|
||||||
case eth.GetBlockHeadersMsg:
|
case eth.GetBlockHeadersMsg:
|
||||||
msg = new(eth.GetBlockHeadersPacket)
|
msg = new(eth.GetBlockHeadersPacket)
|
||||||
case eth.BlockHeadersMsg:
|
case eth.BlockHeadersMsg:
|
||||||
|
|
@ -229,9 +228,21 @@ func (c *Conn) ReadSnap() (any, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dialAndPeer creates a peer connection and runs the handshake.
|
||||||
|
func (s *Suite) dialAndPeer(status *eth.StatusPacket69) (*Conn, error) {
|
||||||
|
c, err := s.dial()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = c.peer(s.chain, status); err != nil {
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
// peer performs both the protocol handshake and the status message
|
// peer performs both the protocol handshake and the status message
|
||||||
// exchange with the node in order to peer with it.
|
// exchange with the node in order to peer with it.
|
||||||
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket) error {
|
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket69) error {
|
||||||
if err := c.handshake(); err != nil {
|
if err := c.handshake(); err != nil {
|
||||||
return fmt.Errorf("handshake failed: %v", err)
|
return fmt.Errorf("handshake failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -304,7 +315,7 @@ func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// statusExchange performs a `Status` message exchange with the given node.
|
// statusExchange performs a `Status` message exchange with the given node.
|
||||||
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket) error {
|
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket69) error {
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
code, data, err := c.Read()
|
code, data, err := c.Read()
|
||||||
|
|
@ -313,12 +324,16 @@ loop:
|
||||||
}
|
}
|
||||||
switch code {
|
switch code {
|
||||||
case eth.StatusMsg + protoOffset(ethProto):
|
case eth.StatusMsg + protoOffset(ethProto):
|
||||||
msg := new(eth.StatusPacket)
|
msg := new(eth.StatusPacket69)
|
||||||
if err := rlp.DecodeBytes(data, &msg); err != nil {
|
if err := rlp.DecodeBytes(data, &msg); err != nil {
|
||||||
return fmt.Errorf("error decoding status packet: %w", err)
|
return fmt.Errorf("error decoding status packet: %w", err)
|
||||||
}
|
}
|
||||||
if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want {
|
if have, want := msg.LatestBlock, chain.blocks[chain.Len()-1].NumberU64(); have != want {
|
||||||
return fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x",
|
return fmt.Errorf("wrong head block in status, want: %d, have %d",
|
||||||
|
want, have)
|
||||||
|
}
|
||||||
|
if have, want := msg.LatestBlockHash, chain.blocks[chain.Len()-1].Hash(); have != want {
|
||||||
|
return fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x",
|
||||||
want, chain.blocks[chain.Len()-1].NumberU64(), have)
|
want, chain.blocks[chain.Len()-1].NumberU64(), have)
|
||||||
}
|
}
|
||||||
if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
|
if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
|
||||||
|
|
@ -348,13 +363,14 @@ loop:
|
||||||
}
|
}
|
||||||
if status == nil {
|
if status == nil {
|
||||||
// default status message
|
// default status message
|
||||||
status = ð.StatusPacket{
|
status = ð.StatusPacket69{
|
||||||
ProtocolVersion: uint32(c.negotiatedProtoVersion),
|
ProtocolVersion: uint32(c.negotiatedProtoVersion),
|
||||||
NetworkID: chain.config.ChainID.Uint64(),
|
NetworkID: chain.config.ChainID.Uint64(),
|
||||||
TD: chain.TD(),
|
|
||||||
Head: chain.blocks[chain.Len()-1].Hash(),
|
|
||||||
Genesis: chain.blocks[0].Hash(),
|
Genesis: chain.blocks[0].Hash(),
|
||||||
ForkID: chain.ForkID(),
|
ForkID: chain.ForkID(),
|
||||||
|
EarliestBlock: 0,
|
||||||
|
LatestBlock: chain.blocks[chain.Len()-1].NumberU64(),
|
||||||
|
LatestBlockHash: chain.blocks[chain.Len()-1].Hash(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := c.Write(ethProto, eth.StatusMsg, status); err != nil {
|
if err := c.Write(ethProto, eth.StatusMsg, status); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const (
|
||||||
// Unexported devp2p protocol lengths from p2p package.
|
// Unexported devp2p protocol lengths from p2p package.
|
||||||
const (
|
const (
|
||||||
baseProtoLen = 16
|
baseProtoLen = 16
|
||||||
ethProtoLen = 17
|
ethProtoLen = 18
|
||||||
snapProtoLen = 8
|
snapProtoLen = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,16 +68,19 @@ func (s *Suite) EthTests() []utesting.Test {
|
||||||
return []utesting.Test{
|
return []utesting.Test{
|
||||||
// status
|
// status
|
||||||
{Name: "Status", Fn: s.TestStatus},
|
{Name: "Status", Fn: s.TestStatus},
|
||||||
|
{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
||||||
|
{Name: "BlockRangeUpdateExpired", Fn: s.TestBlockRangeUpdateHistoryExp},
|
||||||
|
{Name: "BlockRangeUpdateFuture", Fn: s.TestBlockRangeUpdateFuture},
|
||||||
|
{Name: "BlockRangeUpdateInvalid", Fn: s.TestBlockRangeUpdateInvalid},
|
||||||
// get block headers
|
// get block headers
|
||||||
{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
||||||
{Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders},
|
{Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders},
|
||||||
{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
|
{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
|
||||||
{Name: "SameRequestID", Fn: s.TestSameRequestID},
|
{Name: "SameRequestID", Fn: s.TestSameRequestID},
|
||||||
{Name: "ZeroRequestID", Fn: s.TestZeroRequestID},
|
{Name: "ZeroRequestID", Fn: s.TestZeroRequestID},
|
||||||
// get block bodies
|
// get history
|
||||||
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
||||||
// // malicious handshakes + status
|
{Name: "GetReceipts", Fn: s.TestGetReceipts},
|
||||||
{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
|
||||||
// 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},
|
||||||
|
|
@ -101,15 +104,11 @@ func (s *Suite) SnapTests() []utesting.Test {
|
||||||
|
|
||||||
func (s *Suite) TestStatus(t *utesting.T) {
|
func (s *Suite) TestStatus(t *utesting.T) {
|
||||||
t.Log(`This test is just a sanity check. It performs an eth protocol handshake.`)
|
t.Log(`This test is just a sanity check. It performs an eth protocol handshake.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
conn, err := s.dial()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("dial failed: %v", err)
|
t.Fatal("peering failed:", err)
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
if err := conn.peer(s.chain, nil); err != nil {
|
|
||||||
t.Fatalf("peering failed: %v", err)
|
|
||||||
}
|
}
|
||||||
|
conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// headersMatch returns whether the received headers match the given request
|
// headersMatch returns whether the received headers match the given request
|
||||||
|
|
@ -119,15 +118,12 @@ func headersMatch(expected []*types.Header, headers []*types.Header) bool {
|
||||||
|
|
||||||
func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
||||||
t.Log(`This test requests block headers from the node.`)
|
t.Log(`This test requests block headers from the node.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
conn, err := s.dial()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
if err = conn.peer(s.chain, nil); err != nil {
|
|
||||||
t.Fatalf("peering failed: %v", err)
|
t.Fatalf("peering failed: %v", err)
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
// Send headers request.
|
// Send headers request.
|
||||||
req := ð.GetBlockHeadersPacket{
|
req := ð.GetBlockHeadersPacket{
|
||||||
RequestId: 33,
|
RequestId: 33,
|
||||||
|
|
@ -162,16 +158,11 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
||||||
func (s *Suite) TestGetNonexistentBlockHeaders(t *utesting.T) {
|
func (s *Suite) TestGetNonexistentBlockHeaders(t *utesting.T) {
|
||||||
t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value)
|
t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value)
|
||||||
to check if the node disconnects after receiving multiple invalid requests.`)
|
to check if the node disconnects after receiving multiple invalid requests.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
conn, err := s.dial()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
if err := conn.peer(s.chain, nil); err != nil {
|
|
||||||
t.Fatalf("peering failed: %v", err)
|
t.Fatalf("peering failed: %v", err)
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
// Create request with max uint64 value for a nonexistent block
|
// Create request with max uint64 value for a nonexistent block
|
||||||
badReq := ð.GetBlockHeadersPacket{
|
badReq := ð.GetBlockHeadersPacket{
|
||||||
|
|
@ -204,15 +195,11 @@ to check if the node disconnects after receiving multiple invalid requests.`)
|
||||||
func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
|
func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
|
||||||
t.Log(`This test requests blocks headers from the node, performing two requests
|
t.Log(`This test requests blocks headers from the node, performing two requests
|
||||||
concurrently, with different request IDs.`)
|
concurrently, with different request IDs.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
conn, err := s.dial()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
if err := conn.peer(s.chain, nil); err != nil {
|
|
||||||
t.Fatalf("peering failed: %v", err)
|
t.Fatalf("peering failed: %v", err)
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
// Create two different requests.
|
// Create two different requests.
|
||||||
req1 := ð.GetBlockHeadersPacket{
|
req1 := ð.GetBlockHeadersPacket{
|
||||||
|
|
@ -278,15 +265,11 @@ concurrently, with different request IDs.`)
|
||||||
func (s *Suite) TestSameRequestID(t *utesting.T) {
|
func (s *Suite) TestSameRequestID(t *utesting.T) {
|
||||||
t.Log(`This test requests block headers, performing two concurrent requests with the
|
t.Log(`This test requests block headers, performing two concurrent requests with the
|
||||||
same request ID. The node should handle the request by responding to both requests.`)
|
same request ID. The node should handle the request by responding to both requests.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
conn, err := s.dial()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
if err := conn.peer(s.chain, nil); err != nil {
|
|
||||||
t.Fatalf("peering failed: %v", err)
|
t.Fatalf("peering failed: %v", err)
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
// Create two different requests with the same ID.
|
// Create two different requests with the same ID.
|
||||||
reqID := uint64(1234)
|
reqID := uint64(1234)
|
||||||
|
|
@ -349,15 +332,12 @@ same request ID. The node should handle the request by responding to both reques
|
||||||
func (s *Suite) TestZeroRequestID(t *utesting.T) {
|
func (s *Suite) TestZeroRequestID(t *utesting.T) {
|
||||||
t.Log(`This test sends a GetBlockHeaders message with a request-id of zero,
|
t.Log(`This test sends a GetBlockHeaders message with a request-id of zero,
|
||||||
and expects a response.`)
|
and expects a response.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
conn, err := s.dial()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
if err := conn.peer(s.chain, nil); err != nil {
|
|
||||||
t.Fatalf("peering failed: %v", err)
|
t.Fatalf("peering failed: %v", err)
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
req := ð.GetBlockHeadersPacket{
|
req := ð.GetBlockHeadersPacket{
|
||||||
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
||||||
Origin: eth.HashOrNumber{Number: 0},
|
Origin: eth.HashOrNumber{Number: 0},
|
||||||
|
|
@ -384,15 +364,12 @@ and expects a response.`)
|
||||||
|
|
||||||
func (s *Suite) TestGetBlockBodies(t *utesting.T) {
|
func (s *Suite) TestGetBlockBodies(t *utesting.T) {
|
||||||
t.Log(`This test sends GetBlockBodies requests to the node for known blocks in the test chain.`)
|
t.Log(`This test sends GetBlockBodies requests to the node for known blocks in the test chain.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
conn, err := s.dial()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
if err := conn.peer(s.chain, nil); err != nil {
|
|
||||||
t.Fatalf("peering failed: %v", err)
|
t.Fatalf("peering failed: %v", err)
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
// Create block bodies request.
|
// Create block bodies request.
|
||||||
req := ð.GetBlockBodiesPacket{
|
req := ð.GetBlockBodiesPacket{
|
||||||
RequestId: 55,
|
RequestId: 55,
|
||||||
|
|
@ -418,6 +395,47 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestGetReceipts(t *utesting.T) {
|
||||||
|
t.Log(`This test sends GetReceipts requests to the node for known blocks in the test chain.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("peering failed: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Find some blocks containing receipts.
|
||||||
|
var hashes = make([]common.Hash, 0, 3)
|
||||||
|
for i := range s.chain.Len() {
|
||||||
|
block := s.chain.GetBlock(i)
|
||||||
|
if len(block.Transactions()) > 0 {
|
||||||
|
hashes = append(hashes, block.Hash())
|
||||||
|
}
|
||||||
|
if len(hashes) == cap(hashes) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create block bodies request.
|
||||||
|
req := ð.GetReceiptsPacket{
|
||||||
|
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.ReceiptsPacket[*eth.ReceiptList69])
|
||||||
|
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
|
||||||
|
t.Fatalf("error reading block bodies msg: %v", err)
|
||||||
|
}
|
||||||
|
if got, want := resp.RequestId, req.RequestId; got != want {
|
||||||
|
t.Fatalf("unexpected request id in respond", got, want)
|
||||||
|
}
|
||||||
|
if len(resp.List) != len(req.GetReceiptsRequest) {
|
||||||
|
t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetReceiptsRequest), len(resp.List))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// randBuf makes a random buffer size kilobytes large.
|
// randBuf makes a random buffer size kilobytes large.
|
||||||
func randBuf(size int) []byte {
|
func randBuf(size int) []byte {
|
||||||
buf := make([]byte, size*1024)
|
buf := make([]byte, size*1024)
|
||||||
|
|
@ -500,6 +518,97 @@ func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestBlockRangeUpdateInvalid(t *utesting.T) {
|
||||||
|
t.Log(`This test sends an invalid BlockRangeUpdate message to the node and expects to be disconnected.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{
|
||||||
|
EarliestBlock: 10,
|
||||||
|
LatestBlock: 8,
|
||||||
|
LatestBlockHash: s.chain.GetBlock(8).Hash(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if code, _, err := conn.Read(); err != nil {
|
||||||
|
t.Fatalf("expected disconnect, got err: %v", err)
|
||||||
|
} else if code != discMsg {
|
||||||
|
t.Fatalf("expected disconnect message, got msg code %d", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestBlockRangeUpdateFuture(t *utesting.T) {
|
||||||
|
t.Log(`This test sends a BlockRangeUpdate that is beyond the chain head.
|
||||||
|
The node should accept the update and should not disonnect.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
head := s.chain.Head().NumberU64()
|
||||||
|
var hash common.Hash
|
||||||
|
rand.Read(hash[:])
|
||||||
|
conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{
|
||||||
|
EarliestBlock: head + 10,
|
||||||
|
LatestBlock: head + 50,
|
||||||
|
LatestBlockHash: hash,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ensure the node does not disconnect us.
|
||||||
|
// Just send a few ping messages.
|
||||||
|
for range 10 {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
if err := conn.Write(baseProto, pingMsg, []any{}); err != nil {
|
||||||
|
t.Fatal("write error:", err)
|
||||||
|
}
|
||||||
|
code, _, err := conn.Read()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
t.Fatal("read error:", err)
|
||||||
|
case code == discMsg:
|
||||||
|
t.Fatal("got disconnect")
|
||||||
|
case code == pongMsg:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestBlockRangeUpdateHistoryExp(t *utesting.T) {
|
||||||
|
t.Log(`This test sends a BlockRangeUpdate announcing incomplete (expired) history.
|
||||||
|
The node should accept the update and should not disonnect.`)
|
||||||
|
conn, err := s.dialAndPeer(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
head := s.chain.Head()
|
||||||
|
conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{
|
||||||
|
EarliestBlock: head.NumberU64() - 10,
|
||||||
|
LatestBlock: head.NumberU64(),
|
||||||
|
LatestBlockHash: head.Hash(),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ensure the node does not disconnect us.
|
||||||
|
// Just send a few ping messages.
|
||||||
|
for range 10 {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
if err := conn.Write(baseProto, pingMsg, []any{}); err != nil {
|
||||||
|
t.Fatal("write error:", err)
|
||||||
|
}
|
||||||
|
code, _, err := conn.Read()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
t.Fatal("read error:", err)
|
||||||
|
case code == discMsg:
|
||||||
|
t.Fatal("got disconnect")
|
||||||
|
case code == pongMsg:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Suite) TestTransaction(t *utesting.T) {
|
func (s *Suite) TestTransaction(t *utesting.T) {
|
||||||
t.Log(`This test sends a valid transaction to the node and checks if the
|
t.Log(`This test sends a valid transaction to the node and checks if the
|
||||||
transaction gets propagated.`)
|
transaction gets propagated.`)
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -39,8 +42,10 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/internal/era"
|
"github.com/ethereum/go-ethereum/internal/era"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/era/eradl"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
@ -190,7 +195,7 @@ This command dumps out the state for a given block (or latest, if none provided)
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
pruneCommand = &cli.Command{
|
pruneHistoryCommand = &cli.Command{
|
||||||
Action: pruneHistory,
|
Action: pruneHistory,
|
||||||
Name: "prune-history",
|
Name: "prune-history",
|
||||||
Usage: "Prune blockchain history (block bodies and receipts) up to the merge block",
|
Usage: "Prune blockchain history (block bodies and receipts) up to the merge block",
|
||||||
|
|
@ -201,6 +206,42 @@ The prune-history command removes historical block bodies and receipts from the
|
||||||
blockchain database up to the merge block, while preserving block headers. This
|
blockchain database up to the merge block, while preserving block headers. This
|
||||||
helps reduce storage requirements for nodes that don't need full historical data.`,
|
helps reduce storage requirements for nodes that don't need full historical data.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadEraCommand = &cli.Command{
|
||||||
|
Action: downloadEra,
|
||||||
|
Name: "download-era",
|
||||||
|
Usage: "Fetches era1 files (pre-merge history) from an HTTP endpoint",
|
||||||
|
ArgsUsage: "",
|
||||||
|
Flags: slices.Concat(
|
||||||
|
utils.DatabaseFlags,
|
||||||
|
utils.NetworkFlags,
|
||||||
|
[]cli.Flag{
|
||||||
|
eraBlockFlag,
|
||||||
|
eraEpochFlag,
|
||||||
|
eraAllFlag,
|
||||||
|
eraServerFlag,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
eraBlockFlag = &cli.StringFlag{
|
||||||
|
Name: "block",
|
||||||
|
Usage: "Block number to fetch. (can also be a range <start>-<end>)",
|
||||||
|
}
|
||||||
|
eraEpochFlag = &cli.StringFlag{
|
||||||
|
Name: "epoch",
|
||||||
|
Usage: "Epoch number to fetch (can also be a range <start>-<end>)",
|
||||||
|
}
|
||||||
|
eraAllFlag = &cli.BoolFlag{
|
||||||
|
Name: "all",
|
||||||
|
Usage: "Download all available era1 files",
|
||||||
|
}
|
||||||
|
eraServerFlag = &cli.StringFlag{
|
||||||
|
Name: "server",
|
||||||
|
Usage: "era1 server URL",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// initGenesis will initialise the given JSON format genesis file and writes it as
|
// initGenesis will initialise the given JSON format genesis file and writes it as
|
||||||
|
|
@ -237,10 +278,7 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
overrides.OverrideVerkle = &v
|
overrides.OverrideVerkle = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
chaindb, err := stack.OpenDatabaseWithFreezer("chaindata", 0, 0, ctx.String(utils.AncientFlag.Name), "", false)
|
chaindb := utils.MakeChainDatabase(ctx, stack, false)
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to open database: %v", err)
|
|
||||||
}
|
|
||||||
defer chaindb.Close()
|
defer chaindb.Close()
|
||||||
|
|
||||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||||
|
|
@ -277,7 +315,7 @@ func dumpGenesis(ctx *cli.Context) error {
|
||||||
// dump whatever already exists in the datadir
|
// dump whatever already exists in the datadir
|
||||||
stack, _ := makeConfigNode(ctx)
|
stack, _ := makeConfigNode(ctx)
|
||||||
|
|
||||||
db, err := stack.OpenDatabase("chaindata", 0, 0, "", true)
|
db, err := stack.OpenDatabaseWithOptions("chaindata", node.DatabaseOptions{ReadOnly: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -665,3 +703,83 @@ func pruneHistory(ctx *cli.Context) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// downladEra is the era1 file downloader tool.
|
||||||
|
func downloadEra(ctx *cli.Context) error {
|
||||||
|
flags.CheckExclusive(ctx, eraBlockFlag, eraEpochFlag, eraAllFlag)
|
||||||
|
|
||||||
|
// Resolve the network.
|
||||||
|
var network = "mainnet"
|
||||||
|
if utils.IsNetworkPreset(ctx) {
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet(utils.MainnetFlag.Name):
|
||||||
|
case ctx.IsSet(utils.SepoliaFlag.Name):
|
||||||
|
network = "sepolia"
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported network, no known era1 checksums")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the destination directory.
|
||||||
|
stack, _ := makeConfigNode(ctx)
|
||||||
|
defer stack.Close()
|
||||||
|
ancients := stack.ResolveAncient("chaindata", "")
|
||||||
|
dir := filepath.Join(ancients, "era")
|
||||||
|
|
||||||
|
baseURL := ctx.String(eraServerFlag.Name)
|
||||||
|
if baseURL == "" {
|
||||||
|
return fmt.Errorf("need --%s flag to download", eraServerFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := eradl.New(baseURL, network)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet(eraAllFlag.Name):
|
||||||
|
return l.DownloadAll(dir)
|
||||||
|
|
||||||
|
case ctx.IsSet(eraBlockFlag.Name):
|
||||||
|
s := ctx.String(eraBlockFlag.Name)
|
||||||
|
start, end, ok := parseRange(s)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid block range: %q", s)
|
||||||
|
}
|
||||||
|
return l.DownloadBlockRange(start, end, dir)
|
||||||
|
|
||||||
|
case ctx.IsSet(eraEpochFlag.Name):
|
||||||
|
s := ctx.String(eraEpochFlag.Name)
|
||||||
|
start, end, ok := parseRange(s)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid epoch range: %q", s)
|
||||||
|
}
|
||||||
|
return l.DownloadEpochRange(start, end, dir)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("specify one of --%s, --%s, or --%s to download", eraAllFlag.Name, eraBlockFlag.Name, eraEpochFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRange(s string) (start uint64, end uint64, ok bool) {
|
||||||
|
if m, _ := regexp.MatchString("[0-9]+", s); m {
|
||||||
|
start, err := strconv.ParseUint(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
end = start
|
||||||
|
return start, end, true
|
||||||
|
}
|
||||||
|
if m, _ := regexp.MatchString("[0-9]+-[0-9]+", s); m {
|
||||||
|
s1, s2, _ := strings.Cut(s, "-")
|
||||||
|
start, err := strconv.ParseUint(s1, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
end, err = strconv.ParseUint(s2, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
return start, end, true
|
||||||
|
}
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"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/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"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/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
|
|
@ -180,6 +181,45 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
||||||
return stack, cfg
|
return stack, cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// constructs the disclaimer text block which will be printed in the logs upon
|
||||||
|
// startup when Geth is running in dev mode.
|
||||||
|
func constructDevModeBanner(ctx *cli.Context, cfg gethConfig) string {
|
||||||
|
devModeBanner := `You are running Geth in --dev mode. Please note the following:
|
||||||
|
|
||||||
|
1. This mode is only intended for fast, iterative development without assumptions on
|
||||||
|
security or persistence.
|
||||||
|
2. The database is created in memory unless specified otherwise. Therefore, shutting down
|
||||||
|
your computer or losing power will wipe your entire block data and chain state for
|
||||||
|
your dev environment.
|
||||||
|
3. A random, pre-allocated developer account will be available and unlocked as
|
||||||
|
eth.coinbase, which can be used for testing. The random dev account is temporary,
|
||||||
|
stored on a ramdisk, and will be lost if your machine is restarted.
|
||||||
|
4. Mining is enabled by default. However, the client will only seal blocks if transactions
|
||||||
|
are pending in the mempool. The miner's minimum accepted gas price is 1.
|
||||||
|
5. Networking is disabled; there is no listen-address, the maximum number of peers is set
|
||||||
|
to 0, and discovery is disabled.
|
||||||
|
`
|
||||||
|
if !ctx.IsSet(utils.DataDirFlag.Name) {
|
||||||
|
devModeBanner += fmt.Sprintf(`
|
||||||
|
|
||||||
|
Running in ephemeral mode. The following account has been prefunded in the genesis:
|
||||||
|
|
||||||
|
Account
|
||||||
|
------------------
|
||||||
|
0x%x (10^49 ETH)
|
||||||
|
`, cfg.Eth.Miner.PendingFeeRecipient)
|
||||||
|
if cfg.Eth.Miner.PendingFeeRecipient == utils.DeveloperAddr {
|
||||||
|
devModeBanner += fmt.Sprintf(`
|
||||||
|
Private Key
|
||||||
|
------------------
|
||||||
|
0x%x
|
||||||
|
`, crypto.FromECDSA(utils.DeveloperKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return devModeBanner
|
||||||
|
}
|
||||||
|
|
||||||
// makeFullNode loads geth configuration and creates the Ethereum backend.
|
// makeFullNode loads geth configuration and creates the Ethereum backend.
|
||||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
stack, cfg := makeConfigNode(ctx)
|
stack, cfg := makeConfigNode(ctx)
|
||||||
|
|
@ -239,6 +279,11 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
}
|
}
|
||||||
catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon)
|
catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon)
|
||||||
stack.RegisterLifecycle(simBeacon)
|
stack.RegisterLifecycle(simBeacon)
|
||||||
|
|
||||||
|
banner := constructDevModeBanner(ctx, cfg)
|
||||||
|
for _, line := range strings.Split(banner, "\n") {
|
||||||
|
log.Warn(line)
|
||||||
|
}
|
||||||
} else if ctx.IsSet(utils.BeaconApiFlag.Name) {
|
} else if ctx.IsSet(utils.BeaconApiFlag.Name) {
|
||||||
// Start blsync mode.
|
// Start blsync mode.
|
||||||
srv := rpc.NewServer()
|
srv := rpc.NewServer()
|
||||||
|
|
|
||||||
|
|
@ -102,17 +102,17 @@ func TestAttachWelcome(t *testing.T) {
|
||||||
"--http", "--http.port", httpPort,
|
"--http", "--http.port", httpPort,
|
||||||
"--ws", "--ws.port", wsPort)
|
"--ws", "--ws.port", wsPort)
|
||||||
t.Run("ipc", func(t *testing.T) {
|
t.Run("ipc", func(t *testing.T) {
|
||||||
waitForEndpoint(t, ipc, 4*time.Second)
|
waitForEndpoint(t, ipc, 2*time.Minute)
|
||||||
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
||||||
})
|
})
|
||||||
t.Run("http", func(t *testing.T) {
|
t.Run("http", func(t *testing.T) {
|
||||||
endpoint := "http://127.0.0.1:" + httpPort
|
endpoint := "http://127.0.0.1:" + httpPort
|
||||||
waitForEndpoint(t, endpoint, 4*time.Second)
|
waitForEndpoint(t, endpoint, 2*time.Minute)
|
||||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||||
})
|
})
|
||||||
t.Run("ws", func(t *testing.T) {
|
t.Run("ws", func(t *testing.T) {
|
||||||
endpoint := "ws://127.0.0.1:" + wsPort
|
endpoint := "ws://127.0.0.1:" + wsPort
|
||||||
waitForEndpoint(t, endpoint, 4*time.Second)
|
waitForEndpoint(t, endpoint, 2*time.Minute)
|
||||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||||
})
|
})
|
||||||
geth.Kill()
|
geth.Kill()
|
||||||
|
|
|
||||||
|
|
@ -91,13 +91,7 @@ var (
|
||||||
utils.LogNoHistoryFlag,
|
utils.LogNoHistoryFlag,
|
||||||
utils.LogExportCheckpointsFlag,
|
utils.LogExportCheckpointsFlag,
|
||||||
utils.StateHistoryFlag,
|
utils.StateHistoryFlag,
|
||||||
utils.LightServeFlag, // deprecated
|
|
||||||
utils.LightIngressFlag, // deprecated
|
|
||||||
utils.LightEgressFlag, // deprecated
|
|
||||||
utils.LightMaxPeersFlag, // deprecated
|
|
||||||
utils.LightNoPruneFlag, // deprecated
|
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
utils.LightNoSyncServeFlag, // deprecated
|
|
||||||
utils.EthRequiredBlocksFlag,
|
utils.EthRequiredBlocksFlag,
|
||||||
utils.LegacyWhitelistFlag, // deprecated
|
utils.LegacyWhitelistFlag, // deprecated
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
|
|
@ -225,7 +219,8 @@ func init() {
|
||||||
removedbCommand,
|
removedbCommand,
|
||||||
dumpCommand,
|
dumpCommand,
|
||||||
dumpGenesisCommand,
|
dumpGenesisCommand,
|
||||||
pruneCommand,
|
pruneHistoryCommand,
|
||||||
|
downloadEraCommand,
|
||||||
// See accountcmd.go:
|
// See accountcmd.go:
|
||||||
accountCommand,
|
accountCommand,
|
||||||
walletCommand,
|
walletCommand,
|
||||||
|
|
@ -299,24 +294,6 @@ func prepare(ctx *cli.Context) {
|
||||||
case ctx.IsSet(utils.HoodiFlag.Name):
|
case ctx.IsSet(utils.HoodiFlag.Name):
|
||||||
log.Info("Starting Geth on Hoodi testnet...")
|
log.Info("Starting Geth on Hoodi testnet...")
|
||||||
|
|
||||||
case ctx.IsSet(utils.DeveloperFlag.Name):
|
|
||||||
log.Info("Starting Geth in ephemeral dev mode...")
|
|
||||||
log.Warn(`You are running Geth in --dev mode. Please note the following:
|
|
||||||
|
|
||||||
1. This mode is only intended for fast, iterative development without assumptions on
|
|
||||||
security or persistence.
|
|
||||||
2. The database is created in memory unless specified otherwise. Therefore, shutting down
|
|
||||||
your computer or losing power will wipe your entire block data and chain state for
|
|
||||||
your dev environment.
|
|
||||||
3. A random, pre-allocated developer account will be available and unlocked as
|
|
||||||
eth.coinbase, which can be used for testing. The random dev account is temporary,
|
|
||||||
stored on a ramdisk, and will be lost if your machine is restarted.
|
|
||||||
4. Mining is enabled by default. However, the client will only seal blocks if transactions
|
|
||||||
are pending in the mempool. The miner's minimum accepted gas price is 1.
|
|
||||||
5. Networking is disabled; there is no listen-address, the maximum number of peers is set
|
|
||||||
to 0, and discovery is disabled.
|
|
||||||
`)
|
|
||||||
|
|
||||||
case !ctx.IsSet(utils.NetworkIdFlag.Name):
|
case !ctx.IsSet(utils.NetworkIdFlag.Name):
|
||||||
log.Info("Starting Geth on Ethereum mainnet...")
|
log.Info("Starting Geth on Ethereum mainnet...")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -309,7 +309,8 @@ func ImportHistory(chain *core.BlockChain, dir string, network string) error {
|
||||||
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)
|
||||||
}
|
}
|
||||||
if _, err := chain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{receipts}, 2^64-1); err != nil {
|
encReceipts := types.EncodeBlockReceiptLists([]types.Receipts{receipts})
|
||||||
|
if _, err := chain.InsertReceiptChain([]*types.Block{block}, encReceipts, 2^64-1); err != nil {
|
||||||
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
||||||
}
|
}
|
||||||
imported += 1
|
imported += 1
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,11 @@ var (
|
||||||
Usage: "Root directory for ancient data (default = inside chaindata)",
|
Usage: "Root directory for ancient data (default = inside chaindata)",
|
||||||
Category: flags.EthCategory,
|
Category: flags.EthCategory,
|
||||||
}
|
}
|
||||||
|
EraFlag = &flags.DirectoryFlag{
|
||||||
|
Name: "datadir.era",
|
||||||
|
Usage: "Root directory for era1 history (default = inside ancient/chain)",
|
||||||
|
Category: flags.EthCategory,
|
||||||
|
}
|
||||||
MinFreeDiskSpaceFlag = &flags.DirectoryFlag{
|
MinFreeDiskSpaceFlag = &flags.DirectoryFlag{
|
||||||
Name: "datadir.minfreedisk",
|
Name: "datadir.minfreedisk",
|
||||||
Usage: "Minimum free disk space in MB, once reached triggers auto shut down (default = --cache.gc converted to MB, 0 = disabled)",
|
Usage: "Minimum free disk space in MB, once reached triggers auto shut down (default = --cache.gc converted to MB, 0 = disabled)",
|
||||||
|
|
@ -977,6 +982,7 @@ var (
|
||||||
DatabaseFlags = []cli.Flag{
|
DatabaseFlags = []cli.Flag{
|
||||||
DataDirFlag,
|
DataDirFlag,
|
||||||
AncientFlag,
|
AncientFlag,
|
||||||
|
EraFlag,
|
||||||
RemoteDBFlag,
|
RemoteDBFlag,
|
||||||
DBEngineFlag,
|
DBEngineFlag,
|
||||||
StateSchemeFlag,
|
StateSchemeFlag,
|
||||||
|
|
@ -984,6 +990,12 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// default account to prefund when running Geth in dev mode
|
||||||
|
var (
|
||||||
|
DeveloperKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
DeveloperAddr = crypto.PubkeyToAddress(DeveloperKey.PublicKey)
|
||||||
|
)
|
||||||
|
|
||||||
// MakeDataDir retrieves the currently requested data directory, terminating
|
// MakeDataDir retrieves the currently requested data directory, terminating
|
||||||
// if none (or the empty string) is specified. If the node is starting a testnet,
|
// if none (or the empty string) is specified. If the node is starting a testnet,
|
||||||
// then a subdirectory of the specified datadir will be used.
|
// then a subdirectory of the specified datadir will be used.
|
||||||
|
|
@ -1245,28 +1257,6 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setLes shows the deprecation warnings for LES flags.
|
|
||||||
func setLes(ctx *cli.Context, cfg *ethconfig.Config) {
|
|
||||||
if ctx.IsSet(LightServeFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightServeFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.IsSet(LightIngressFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightIngressFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.IsSet(LightEgressFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightEgressFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.IsSet(LightMaxPeersFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightMaxPeersFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.IsSet(LightNoPruneFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightNoPruneFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.IsSet(LightNoSyncServeFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightNoSyncServeFlag.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
||||||
// for Geth and returns half of the allowance to assign to the database.
|
// for Geth and returns half of the allowance to assign to the database.
|
||||||
func MakeDatabaseHandles(max int) int {
|
func MakeDatabaseHandles(max int) int {
|
||||||
|
|
@ -1582,7 +1572,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
setBlobPool(ctx, &cfg.BlobPool)
|
setBlobPool(ctx, &cfg.BlobPool)
|
||||||
setMiner(ctx, &cfg.Miner)
|
setMiner(ctx, &cfg.Miner)
|
||||||
setRequiredBlocks(ctx, cfg)
|
setRequiredBlocks(ctx, cfg)
|
||||||
setLes(ctx, cfg)
|
|
||||||
|
|
||||||
// Cap the cache allowance and tune the garbage collector
|
// Cap the cache allowance and tune the garbage collector
|
||||||
mem, err := gopsutil.VirtualMemory()
|
mem, err := gopsutil.VirtualMemory()
|
||||||
|
|
@ -1630,6 +1619,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
if ctx.IsSet(AncientFlag.Name) {
|
if ctx.IsSet(AncientFlag.Name) {
|
||||||
cfg.DatabaseFreezer = ctx.String(AncientFlag.Name)
|
cfg.DatabaseFreezer = ctx.String(AncientFlag.Name)
|
||||||
}
|
}
|
||||||
|
if ctx.IsSet(EraFlag.Name) {
|
||||||
|
cfg.DatabaseEra = ctx.String(EraFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
|
if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
|
||||||
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
|
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
|
||||||
|
|
@ -1712,7 +1704,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ctx.IsSet(VMEnableDebugFlag.Name) {
|
if ctx.IsSet(VMEnableDebugFlag.Name) {
|
||||||
// TODO(fjl): force-enable this in --dev mode
|
|
||||||
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1761,6 +1752,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
case ctx.Bool(DeveloperFlag.Name):
|
case ctx.Bool(DeveloperFlag.Name):
|
||||||
cfg.NetworkId = 1337
|
cfg.NetworkId = 1337
|
||||||
cfg.SyncMode = ethconfig.FullSync
|
cfg.SyncMode = ethconfig.FullSync
|
||||||
|
cfg.EnablePreimageRecording = true
|
||||||
// Create new developer account or reuse existing one
|
// Create new developer account or reuse existing one
|
||||||
var (
|
var (
|
||||||
developer accounts.Account
|
developer accounts.Account
|
||||||
|
|
@ -1792,9 +1784,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
} else if accs := ks.Accounts(); len(accs) > 0 {
|
} else if accs := ks.Accounts(); len(accs) > 0 {
|
||||||
developer = ks.Accounts()[0]
|
developer = ks.Accounts()[0]
|
||||||
} else {
|
} else {
|
||||||
developer, err = ks.NewAccount(passphrase)
|
developer, err = ks.ImportECDSA(DeveloperKey, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Failed to create developer account: %v", err)
|
Fatalf("Failed to import developer account: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Make sure the address is configured as fee recipient, otherwise
|
// Make sure the address is configured as fee recipient, otherwise
|
||||||
|
|
@ -1809,14 +1801,18 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
}
|
}
|
||||||
log.Info("Using developer account", "address", developer.Address)
|
log.Info("Using developer account", "address", developer.Address)
|
||||||
|
|
||||||
// Create a new developer genesis block or reuse existing one
|
// configure default developer genesis which will be used unless a
|
||||||
|
// datadir is specified and a chain is preexisting at that location.
|
||||||
cfg.Genesis = core.DeveloperGenesisBlock(ctx.Uint64(DeveloperGasLimitFlag.Name), &developer.Address)
|
cfg.Genesis = core.DeveloperGenesisBlock(ctx.Uint64(DeveloperGasLimitFlag.Name), &developer.Address)
|
||||||
|
|
||||||
|
// If a datadir is specified, ensure that any preexisting chain in that location
|
||||||
|
// has a configuration that is compatible with dev mode: it must be merged at genesis.
|
||||||
if ctx.IsSet(DataDirFlag.Name) {
|
if ctx.IsSet(DataDirFlag.Name) {
|
||||||
chaindb := tryMakeReadOnlyDatabase(ctx, stack)
|
chaindb := tryMakeReadOnlyDatabase(ctx, stack)
|
||||||
if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) {
|
if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) {
|
||||||
cfg.Genesis = nil // fallback to db content
|
// signal fallback to preexisting chain on disk
|
||||||
|
cfg.Genesis = nil
|
||||||
|
|
||||||
//validate genesis has PoS enabled in block 0
|
|
||||||
genesis, err := core.ReadGenesis(chaindb)
|
genesis, err := core.ReadGenesis(chaindb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Could not read genesis from database: %v", err)
|
Fatalf("Could not read genesis from database: %v", err)
|
||||||
|
|
@ -2043,7 +2039,6 @@ func SetupMetrics(cfg *metrics.Config) {
|
||||||
log.Info("Enabling metrics export to InfluxDB")
|
log.Info("Enabling metrics export to InfluxDB")
|
||||||
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap)
|
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap)
|
||||||
} else if enableExportV2 {
|
} else if enableExportV2 {
|
||||||
tagsMap := SplitTagsFlag(cfg.InfluxDBTags)
|
|
||||||
log.Info("Enabling metrics export to InfluxDB (v2)")
|
log.Info("Enabling metrics export to InfluxDB (v2)")
|
||||||
go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "geth.", tagsMap)
|
go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "geth.", tagsMap)
|
||||||
}
|
}
|
||||||
|
|
@ -2096,7 +2091,15 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
|
||||||
}
|
}
|
||||||
chainDb = remotedb.New(client)
|
chainDb = remotedb.New(client)
|
||||||
default:
|
default:
|
||||||
chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "eth/db/chaindata/", readonly)
|
options := node.DatabaseOptions{
|
||||||
|
ReadOnly: readonly,
|
||||||
|
Cache: cache,
|
||||||
|
Handles: handles,
|
||||||
|
AncientsDirectory: ctx.String(AncientFlag.Name),
|
||||||
|
MetricsNamespace: "eth/db/chaindata/",
|
||||||
|
EraDirectory: ctx.String(EraFlag.Name),
|
||||||
|
}
|
||||||
|
chainDb, err = stack.OpenDatabaseWithOptions("chaindata", options)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Could not open database: %v", err)
|
Fatalf("Could not open database: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,6 @@ var DeprecatedFlags = []cli.Flag{
|
||||||
CacheTrieRejournalFlag,
|
CacheTrieRejournalFlag,
|
||||||
LegacyDiscoveryV5Flag,
|
LegacyDiscoveryV5Flag,
|
||||||
TxLookupLimitFlag,
|
TxLookupLimitFlag,
|
||||||
LightServeFlag,
|
|
||||||
LightIngressFlag,
|
|
||||||
LightEgressFlag,
|
|
||||||
LightMaxPeersFlag,
|
|
||||||
LightNoPruneFlag,
|
|
||||||
LightNoSyncServeFlag,
|
|
||||||
LogBacktraceAtFlag,
|
LogBacktraceAtFlag,
|
||||||
LogDebugFlag,
|
LogDebugFlag,
|
||||||
MinerNewPayloadTimeoutFlag,
|
MinerNewPayloadTimeoutFlag,
|
||||||
|
|
@ -88,37 +82,6 @@ var (
|
||||||
Value: ethconfig.Defaults.TransactionHistory,
|
Value: ethconfig.Defaults.TransactionHistory,
|
||||||
Category: flags.DeprecatedCategory,
|
Category: flags.DeprecatedCategory,
|
||||||
}
|
}
|
||||||
// Light server and client settings, Deprecated November 2023
|
|
||||||
LightServeFlag = &cli.IntFlag{
|
|
||||||
Name: "light.serve",
|
|
||||||
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
LightIngressFlag = &cli.IntFlag{
|
|
||||||
Name: "light.ingress",
|
|
||||||
Usage: "Incoming bandwidth limit for serving light clients (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
LightEgressFlag = &cli.IntFlag{
|
|
||||||
Name: "light.egress",
|
|
||||||
Usage: "Outgoing bandwidth limit for serving light clients (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
LightMaxPeersFlag = &cli.IntFlag{
|
|
||||||
Name: "light.maxpeers",
|
|
||||||
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
LightNoPruneFlag = &cli.BoolFlag{
|
|
||||||
Name: "light.nopruning",
|
|
||||||
Usage: "Disable ancient light chain data pruning (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
LightNoSyncServeFlag = &cli.BoolFlag{
|
|
||||||
Name: "light.nosyncserve",
|
|
||||||
Usage: "Enables serving light clients before syncing (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
// Deprecated November 2023
|
// Deprecated November 2023
|
||||||
LogBacktraceAtFlag = &cli.StringFlag{
|
LogBacktraceAtFlag = &cli.StringFlag{
|
||||||
Name: "log.backtrace",
|
Name: "log.backtrace",
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ func TestHistoryImportAndExport(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now import Era.
|
// Now import Era.
|
||||||
db2, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db2, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,30 +47,37 @@ func NewBasicLRU[K comparable, V any](capacity int) BasicLRU[K, V] {
|
||||||
|
|
||||||
// Add adds a value to the cache. Returns true if an item was evicted to store the new item.
|
// Add adds a value to the cache. Returns true if an item was evicted to store the new item.
|
||||||
func (c *BasicLRU[K, V]) Add(key K, value V) (evicted bool) {
|
func (c *BasicLRU[K, V]) Add(key K, value V) (evicted bool) {
|
||||||
|
_, _, evicted = c.Add3(key, value)
|
||||||
|
return evicted
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add3 adds a value to the cache. If an item was evicted to store the new one, it returns the evicted item.
|
||||||
|
func (c *BasicLRU[K, V]) Add3(key K, value V) (ek K, ev V, evicted bool) {
|
||||||
item, ok := c.items[key]
|
item, ok := c.items[key]
|
||||||
if ok {
|
if ok {
|
||||||
// Already exists in cache.
|
|
||||||
item.value = value
|
item.value = value
|
||||||
c.items[key] = item
|
c.items[key] = item
|
||||||
c.list.moveToFront(item.elem)
|
c.list.moveToFront(item.elem)
|
||||||
return false
|
return ek, ev, false
|
||||||
}
|
}
|
||||||
|
|
||||||
var elem *listElem[K]
|
var elem *listElem[K]
|
||||||
if c.Len() >= c.cap {
|
if c.Len() >= c.cap {
|
||||||
elem = c.list.removeLast()
|
elem = c.list.removeLast()
|
||||||
delete(c.items, elem.v)
|
|
||||||
evicted = true
|
evicted = true
|
||||||
|
ek = elem.v
|
||||||
|
ev = c.items[ek].value
|
||||||
|
delete(c.items, ek)
|
||||||
} else {
|
} else {
|
||||||
elem = new(listElem[K])
|
elem = new(listElem[K])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the new item.
|
// Store the new item.
|
||||||
// Note that, if another item was evicted, we re-use its list element here.
|
// Note that if another item was evicted, we re-use its list element here.
|
||||||
elem.v = key
|
elem.v = key
|
||||||
c.items[key] = cacheItem[K, V]{elem, value}
|
c.items[key] = cacheItem[K, V]{elem, value}
|
||||||
c.list.pushElem(elem)
|
c.list.pushElem(elem)
|
||||||
return evicted
|
return ek, ev, evicted
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains reports whether the given key exists in the cache.
|
// Contains reports whether the given key exists in the cache.
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
@ -445,11 +444,6 @@ func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uin
|
||||||
return beaconDifficulty
|
return beaconDifficulty
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIs implements consensus.Engine, returning the user facing RPC APIs.
|
|
||||||
func (beacon *Beacon) APIs(chain consensus.ChainHeaderReader) []rpc.API {
|
|
||||||
return beacon.ethone.APIs(chain)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close shutdowns the consensus engine
|
// Close shutdowns the consensus engine
|
||||||
func (beacon *Beacon) Close() error {
|
func (beacon *Beacon) Close() error {
|
||||||
return beacon.ethone.Close()
|
return beacon.ethone.Close()
|
||||||
|
|
|
||||||
|
|
@ -1,235 +0,0 @@
|
||||||
// Copyright 2017 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 clique
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// API is a user facing RPC API to allow controlling the signer and voting
|
|
||||||
// mechanisms of the proof-of-authority scheme.
|
|
||||||
type API struct {
|
|
||||||
chain consensus.ChainHeaderReader
|
|
||||||
clique *Clique
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSnapshot retrieves the state snapshot at a given block.
|
|
||||||
func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
|
|
||||||
// Retrieve the requested block number (or current if none requested)
|
|
||||||
var header *types.Header
|
|
||||||
if number == nil || *number == rpc.LatestBlockNumber {
|
|
||||||
header = api.chain.CurrentHeader()
|
|
||||||
} else {
|
|
||||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
|
||||||
}
|
|
||||||
// Ensure we have an actually valid block and return its snapshot
|
|
||||||
if header == nil {
|
|
||||||
return nil, errUnknownBlock
|
|
||||||
}
|
|
||||||
return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSnapshotAtHash retrieves the state snapshot at a given block.
|
|
||||||
func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
|
|
||||||
header := api.chain.GetHeaderByHash(hash)
|
|
||||||
if header == nil {
|
|
||||||
return nil, errUnknownBlock
|
|
||||||
}
|
|
||||||
return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSigners retrieves the list of authorized signers at the specified block.
|
|
||||||
func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) {
|
|
||||||
// Retrieve the requested block number (or current if none requested)
|
|
||||||
var header *types.Header
|
|
||||||
if number == nil || *number == rpc.LatestBlockNumber {
|
|
||||||
header = api.chain.CurrentHeader()
|
|
||||||
} else {
|
|
||||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
|
||||||
}
|
|
||||||
// Ensure we have an actually valid block and return the signers from its snapshot
|
|
||||||
if header == nil {
|
|
||||||
return nil, errUnknownBlock
|
|
||||||
}
|
|
||||||
snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return snap.signers(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSignersAtHash retrieves the list of authorized signers at the specified block.
|
|
||||||
func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) {
|
|
||||||
header := api.chain.GetHeaderByHash(hash)
|
|
||||||
if header == nil {
|
|
||||||
return nil, errUnknownBlock
|
|
||||||
}
|
|
||||||
snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return snap.signers(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proposals returns the current proposals the node tries to uphold and vote on.
|
|
||||||
func (api *API) Proposals() map[common.Address]bool {
|
|
||||||
api.clique.lock.RLock()
|
|
||||||
defer api.clique.lock.RUnlock()
|
|
||||||
|
|
||||||
proposals := make(map[common.Address]bool)
|
|
||||||
for address, auth := range api.clique.proposals {
|
|
||||||
proposals[address] = auth
|
|
||||||
}
|
|
||||||
return proposals
|
|
||||||
}
|
|
||||||
|
|
||||||
// Propose injects a new authorization proposal that the signer will attempt to
|
|
||||||
// push through.
|
|
||||||
func (api *API) Propose(address common.Address, auth bool) {
|
|
||||||
api.clique.lock.Lock()
|
|
||||||
defer api.clique.lock.Unlock()
|
|
||||||
|
|
||||||
api.clique.proposals[address] = auth
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discard drops a currently running proposal, stopping the signer from casting
|
|
||||||
// further votes (either for or against).
|
|
||||||
func (api *API) Discard(address common.Address) {
|
|
||||||
api.clique.lock.Lock()
|
|
||||||
defer api.clique.lock.Unlock()
|
|
||||||
|
|
||||||
delete(api.clique.proposals, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
type status struct {
|
|
||||||
InturnPercent float64 `json:"inturnPercent"`
|
|
||||||
SigningStatus map[common.Address]int `json:"sealerActivity"`
|
|
||||||
NumBlocks uint64 `json:"numBlocks"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status returns the status of the last N blocks,
|
|
||||||
// - the number of active signers,
|
|
||||||
// - the number of signers,
|
|
||||||
// - the percentage of in-turn blocks
|
|
||||||
func (api *API) Status() (*status, error) {
|
|
||||||
var (
|
|
||||||
numBlocks = uint64(64)
|
|
||||||
header = api.chain.CurrentHeader()
|
|
||||||
diff = uint64(0)
|
|
||||||
optimals = 0
|
|
||||||
)
|
|
||||||
snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
signers = snap.signers()
|
|
||||||
end = header.Number.Uint64()
|
|
||||||
start = end - numBlocks
|
|
||||||
)
|
|
||||||
if numBlocks > end {
|
|
||||||
start = 1
|
|
||||||
numBlocks = end - start
|
|
||||||
}
|
|
||||||
signStatus := make(map[common.Address]int)
|
|
||||||
for _, s := range signers {
|
|
||||||
signStatus[s] = 0
|
|
||||||
}
|
|
||||||
for n := start; n < end; n++ {
|
|
||||||
h := api.chain.GetHeaderByNumber(n)
|
|
||||||
if h == nil {
|
|
||||||
return nil, fmt.Errorf("missing block %d", n)
|
|
||||||
}
|
|
||||||
if h.Difficulty.Cmp(diffInTurn) == 0 {
|
|
||||||
optimals++
|
|
||||||
}
|
|
||||||
diff += h.Difficulty.Uint64()
|
|
||||||
sealer, err := api.clique.Author(h)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
signStatus[sealer]++
|
|
||||||
}
|
|
||||||
return &status{
|
|
||||||
InturnPercent: float64(100*optimals) / float64(numBlocks),
|
|
||||||
SigningStatus: signStatus,
|
|
||||||
NumBlocks: numBlocks,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type blockNumberOrHashOrRLP struct {
|
|
||||||
*rpc.BlockNumberOrHash
|
|
||||||
RLP hexutil.Bytes `json:"rlp,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error {
|
|
||||||
bnOrHash := new(rpc.BlockNumberOrHash)
|
|
||||||
// Try to unmarshal bNrOrHash
|
|
||||||
if err := bnOrHash.UnmarshalJSON(data); err == nil {
|
|
||||||
sb.BlockNumberOrHash = bnOrHash
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Try to unmarshal RLP
|
|
||||||
var input string
|
|
||||||
if err := json.Unmarshal(data, &input); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
blob, err := hexutil.Decode(input)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sb.RLP = blob
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSigner returns the signer for a specific clique block.
|
|
||||||
// Can be called with a block number, a block hash or a rlp encoded blob.
|
|
||||||
// The RLP encoded blob can either be a block or a header.
|
|
||||||
func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (common.Address, error) {
|
|
||||||
if len(rlpOrBlockNr.RLP) == 0 {
|
|
||||||
blockNrOrHash := rlpOrBlockNr.BlockNumberOrHash
|
|
||||||
var header *types.Header
|
|
||||||
if blockNrOrHash == nil {
|
|
||||||
header = api.chain.CurrentHeader()
|
|
||||||
} else if hash, ok := blockNrOrHash.Hash(); ok {
|
|
||||||
header = api.chain.GetHeaderByHash(hash)
|
|
||||||
} else if number, ok := blockNrOrHash.Number(); ok {
|
|
||||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
|
||||||
}
|
|
||||||
if header == nil {
|
|
||||||
return common.Address{}, fmt.Errorf("missing block %v", blockNrOrHash.String())
|
|
||||||
}
|
|
||||||
return api.clique.Author(header)
|
|
||||||
}
|
|
||||||
block := new(types.Block)
|
|
||||||
if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, block); err == nil {
|
|
||||||
return api.clique.Author(block.Header())
|
|
||||||
}
|
|
||||||
header := new(types.Header)
|
|
||||||
if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, header); err != nil {
|
|
||||||
return common.Address{}, err
|
|
||||||
}
|
|
||||||
return api.clique.Author(header)
|
|
||||||
}
|
|
||||||
|
|
@ -41,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/rpc"
|
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
@ -641,15 +640,6 @@ func (c *Clique) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIs implements consensus.Engine, returning the user facing RPC API to allow
|
|
||||||
// controlling the signer voting.
|
|
||||||
func (c *Clique) APIs(chain consensus.ChainHeaderReader) []rpc.API {
|
|
||||||
return []rpc.API{{
|
|
||||||
Namespace: "clique",
|
|
||||||
Service: &API{chain: chain, clique: c},
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 SealHash(header *types.Header) (hash common.Hash) {
|
func SealHash(header *types.Header) (hash common.Hash) {
|
||||||
hasher := sha3.NewLegacyKeccak256()
|
hasher := sha3.NewLegacyKeccak256()
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChainHeaderReader defines a small collection of methods needed to access the local
|
// ChainHeaderReader defines a small collection of methods needed to access the local
|
||||||
|
|
@ -109,9 +108,6 @@ type Engine interface {
|
||||||
// that a new block should have.
|
// that a new block should have.
|
||||||
CalcDifficulty(chain ChainHeaderReader, time uint64, parent *types.Header) *big.Int
|
CalcDifficulty(chain ChainHeaderReader, time uint64, parent *types.Header) *big.Int
|
||||||
|
|
||||||
// APIs returns the RPC APIs this consensus engine provides.
|
|
||||||
APIs(chain ChainHeaderReader) []rpc.API
|
|
||||||
|
|
||||||
// Close terminates any background threads maintained by the consensus engine.
|
// Close terminates any background threads maintained by the consensus engine.
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ethash is a consensus engine based on proof-of-work implementing the ethash
|
// Ethash is a consensus engine based on proof-of-work implementing the ethash
|
||||||
|
|
@ -71,12 +70,6 @@ func (ethash *Ethash) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIs implements consensus.Engine, returning no APIs as ethash is an empty
|
|
||||||
// shell in the post-merge world.
|
|
||||||
func (ethash *Ethash) APIs(chain consensus.ChainHeaderReader) []rpc.API {
|
|
||||||
return []rpc.API{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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. For the ethash engine, this method will
|
// the result into the given channel. For the ethash engine, this method will
|
||||||
// just panic as sealing is not supported anymore.
|
// just panic as sealing is not supported anymore.
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,8 @@ var (
|
||||||
headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
|
headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
|
||||||
headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil)
|
headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil)
|
||||||
|
|
||||||
chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
|
chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
|
||||||
|
chainMgaspsGauge = metrics.NewRegisteredGauge("chain/mgasps", nil)
|
||||||
|
|
||||||
accountReadTimer = metrics.NewRegisteredResettingTimer("chain/account/reads", nil)
|
accountReadTimer = metrics.NewRegisteredResettingTimer("chain/account/reads", nil)
|
||||||
accountHashTimer = metrics.NewRegisteredResettingTimer("chain/account/hashes", nil)
|
accountHashTimer = metrics.NewRegisteredResettingTimer("chain/account/hashes", nil)
|
||||||
|
|
@ -92,8 +93,10 @@ var (
|
||||||
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
||||||
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
||||||
|
|
||||||
blockPrefetchExecuteTimer = metrics.NewRegisteredTimer("chain/prefetch/executes", nil)
|
blockPrefetchExecuteTimer = metrics.NewRegisteredResettingTimer("chain/prefetch/executes", nil)
|
||||||
blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil)
|
blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil)
|
||||||
|
blockPrefetchTxsInvalidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/invalid", nil)
|
||||||
|
blockPrefetchTxsValidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/valid", nil)
|
||||||
|
|
||||||
errInsertionInterrupted = errors.New("insertion is interrupted")
|
errInsertionInterrupted = errors.New("insertion is interrupted")
|
||||||
errChainStopped = errors.New("blockchain is stopped")
|
errChainStopped = errors.New("blockchain is stopped")
|
||||||
|
|
@ -180,8 +183,13 @@ func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config {
|
||||||
}
|
}
|
||||||
if c.StateScheme == rawdb.PathScheme {
|
if c.StateScheme == rawdb.PathScheme {
|
||||||
config.PathDB = &pathdb.Config{
|
config.PathDB = &pathdb.Config{
|
||||||
StateHistory: c.StateHistory,
|
StateHistory: c.StateHistory,
|
||||||
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
TrieCleanSize: c.TrieCleanLimit * 1024 * 1024,
|
||||||
|
StateCleanSize: c.SnapshotLimit * 1024 * 1024,
|
||||||
|
|
||||||
|
// TODO(rjl493456442): The write buffer represents the memory limit used
|
||||||
|
// for flushing both trie data and state data to disk. The config name
|
||||||
|
// should be updated to eliminate the confusion.
|
||||||
WriteBufferSize: c.TrieDirtyLimit * 1024 * 1024,
|
WriteBufferSize: c.TrieDirtyLimit * 1024 * 1024,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -377,11 +385,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
// Do nothing here until the state syncer picks it up.
|
// Do nothing here until the state syncer picks it up.
|
||||||
log.Info("Genesis state is missing, wait state sync")
|
log.Info("Genesis state is missing, wait state sync")
|
||||||
} else {
|
} else {
|
||||||
// Head state is missing, before the state recovery, find out the
|
// Head state is missing, before the state recovery, find out the disk
|
||||||
// disk layer point of snapshot(if it's enabled). Make sure the
|
// layer point of snapshot(if it's enabled). Make sure the rewound point
|
||||||
// rewound point is lower than disk layer.
|
// is lower than disk layer.
|
||||||
|
//
|
||||||
|
// Note it's unnecessary in path mode which always keep trie data and
|
||||||
|
// state data consistent.
|
||||||
var diskRoot common.Hash
|
var diskRoot common.Hash
|
||||||
if bc.cacheConfig.SnapshotLimit > 0 {
|
if bc.cacheConfig.SnapshotLimit > 0 && bc.cacheConfig.StateScheme == rawdb.HashScheme {
|
||||||
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
|
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
|
||||||
}
|
}
|
||||||
if diskRoot != (common.Hash{}) {
|
if diskRoot != (common.Hash{}) {
|
||||||
|
|
@ -454,7 +465,32 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
|
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bc.setupSnapshot()
|
||||||
|
|
||||||
|
// Rewind the chain in case of an incompatible config upgrade.
|
||||||
|
if compatErr != nil {
|
||||||
|
log.Warn("Rewinding chain to upgrade configuration", "err", compatErr)
|
||||||
|
if compatErr.RewindToTime > 0 {
|
||||||
|
bc.SetHeadWithTimestamp(compatErr.RewindToTime)
|
||||||
|
} else {
|
||||||
|
bc.SetHead(compatErr.RewindToBlock)
|
||||||
|
}
|
||||||
|
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start tx indexer if it's enabled.
|
||||||
|
if txLookupLimit != nil {
|
||||||
|
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
|
||||||
|
}
|
||||||
|
return bc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *BlockChain) setupSnapshot() {
|
||||||
|
// Short circuit if the chain is established with path scheme, as the
|
||||||
|
// state snapshot has been integrated into path database natively.
|
||||||
|
if bc.cacheConfig.StateScheme == rawdb.PathScheme {
|
||||||
|
return
|
||||||
|
}
|
||||||
// Load any existing snapshot, regenerating it if loading failed
|
// Load any existing snapshot, regenerating it if loading failed
|
||||||
if bc.cacheConfig.SnapshotLimit > 0 {
|
if bc.cacheConfig.SnapshotLimit > 0 {
|
||||||
// If the chain was rewound past the snapshot persistent layer (causing
|
// If the chain was rewound past the snapshot persistent layer (causing
|
||||||
|
|
@ -462,7 +498,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
// in recovery mode and in that case, don't invalidate the snapshot on a
|
// in recovery mode and in that case, don't invalidate the snapshot on a
|
||||||
// head mismatch.
|
// head mismatch.
|
||||||
var recover bool
|
var recover bool
|
||||||
|
|
||||||
head := bc.CurrentBlock()
|
head := bc.CurrentBlock()
|
||||||
if layer := rawdb.ReadSnapshotRecoveryNumber(bc.db); layer != nil && *layer >= head.Number.Uint64() {
|
if layer := rawdb.ReadSnapshotRecoveryNumber(bc.db); layer != nil && *layer >= head.Number.Uint64() {
|
||||||
log.Warn("Enabling snapshot recovery", "chainhead", head.Number, "diskbase", *layer)
|
log.Warn("Enabling snapshot recovery", "chainhead", head.Number, "diskbase", *layer)
|
||||||
|
|
@ -479,22 +514,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
// Re-initialize the state database with snapshot
|
// Re-initialize the state database with snapshot
|
||||||
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
|
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewind the chain in case of an incompatible config upgrade.
|
|
||||||
if compatErr != nil {
|
|
||||||
log.Warn("Rewinding chain to upgrade configuration", "err", compatErr)
|
|
||||||
if compatErr.RewindToTime > 0 {
|
|
||||||
bc.SetHeadWithTimestamp(compatErr.RewindToTime)
|
|
||||||
} else {
|
|
||||||
bc.SetHead(compatErr.RewindToBlock)
|
|
||||||
}
|
|
||||||
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
|
|
||||||
}
|
|
||||||
// Start tx indexer if it's enabled.
|
|
||||||
if txLookupLimit != nil {
|
|
||||||
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
|
|
||||||
}
|
|
||||||
return bc, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty returns an indicator whether the blockchain is empty.
|
// empty returns an indicator whether the blockchain is empty.
|
||||||
|
|
@ -1290,12 +1309,11 @@ const (
|
||||||
//
|
//
|
||||||
// The optional ancientLimit can also be specified and chain segment before that
|
// The optional ancientLimit can also be specified and chain segment before that
|
||||||
// will be directly stored in the ancient, getting rid of the chain migration.
|
// will be directly stored in the ancient, getting rid of the chain migration.
|
||||||
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts, ancientLimit uint64) (int, error) {
|
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []rlp.RawValue, ancientLimit uint64) (int, error) {
|
||||||
// Verify the supplied headers before insertion without lock
|
// Verify the supplied headers before insertion without lock
|
||||||
var headers []*types.Header
|
var headers []*types.Header
|
||||||
for _, block := range blockChain {
|
for _, block := range blockChain {
|
||||||
headers = append(headers, block.Header())
|
headers = append(headers, block.Header())
|
||||||
|
|
||||||
// Here we also validate that blob transactions in the block do not
|
// Here we also validate that blob transactions in the block do not
|
||||||
// contain a sidecar. While the sidecar does not affect the block hash
|
// contain a sidecar. While the sidecar does not affect the block hash
|
||||||
// or tx hash, sending blobs within a block is not allowed.
|
// or tx hash, sending blobs within a block is not allowed.
|
||||||
|
|
@ -1338,11 +1356,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
//
|
//
|
||||||
// this function only accepts canonical chain data. All side chain will be reverted
|
// this function only accepts canonical chain data. All side chain will be reverted
|
||||||
// eventually.
|
// eventually.
|
||||||
writeAncient := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
writeAncient := func(blockChain types.Blocks, receiptChain []rlp.RawValue) (int, error) {
|
||||||
// Ensure genesis is in the ancient store
|
// Ensure genesis is in the ancient store
|
||||||
if blockChain[0].NumberU64() == 1 {
|
if blockChain[0].NumberU64() == 1 {
|
||||||
if frozen, _ := bc.db.Ancients(); frozen == 0 {
|
if frozen, _ := bc.db.Ancients(); frozen == 0 {
|
||||||
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil})
|
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error writing genesis to ancients", "err", err)
|
log.Error("Error writing genesis to ancients", "err", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -1385,7 +1403,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
// existing local chain segments (reorg around the chain tip). The reorganized part
|
// existing local chain segments (reorg around the chain tip). The reorganized part
|
||||||
// will be included in the provided chain segment, and stale canonical markers will be
|
// will be included in the provided chain segment, and stale canonical markers will be
|
||||||
// silently rewritten. Therefore, no explicit reorg logic is needed.
|
// silently rewritten. Therefore, no explicit reorg logic is needed.
|
||||||
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
writeLive := func(blockChain types.Blocks, receiptChain []rlp.RawValue) (int, error) {
|
||||||
var (
|
var (
|
||||||
skipPresenceCheck = false
|
skipPresenceCheck = false
|
||||||
batch = bc.db.NewBatch()
|
batch = bc.db.NewBatch()
|
||||||
|
|
@ -1410,7 +1428,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
// Write all the data out into the database
|
// Write all the data out into the database
|
||||||
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
|
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
|
||||||
rawdb.WriteBlock(batch, block)
|
rawdb.WriteBlock(batch, block)
|
||||||
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
rawdb.WriteRawReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
||||||
|
|
||||||
// Write everything belongs to the blocks into the database. So that
|
// Write everything belongs to the blocks into the database. So that
|
||||||
// we can ensure all components of body is completed(body, receipts)
|
// we can ensure all components of body is completed(body, receipts)
|
||||||
|
|
@ -1758,18 +1776,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||||
bc.reportBlock(block, nil, err)
|
bc.reportBlock(block, nil, err)
|
||||||
return nil, it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
// No validation errors for the first block (or chain prefix skipped)
|
|
||||||
var activeState *state.StateDB
|
|
||||||
defer func() {
|
|
||||||
// The chain importer is starting and stopping trie prefetchers. If a bad
|
|
||||||
// block or other error is hit however, an early return may not properly
|
|
||||||
// terminate the background threads. This defer ensures that we clean up
|
|
||||||
// and dangling prefetcher, without deferring each and holding on live refs.
|
|
||||||
if activeState != nil {
|
|
||||||
activeState.StopPrefetcher()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Track the singleton witness from this chain insertion (if any)
|
// Track the singleton witness from this chain insertion (if any)
|
||||||
var witness *stateless.Witness
|
var witness *stateless.Witness
|
||||||
|
|
||||||
|
|
@ -1825,63 +1831,20 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Retrieve the parent block and it's state to execute on top
|
// Retrieve the parent block and it's state to execute on top
|
||||||
start := time.Now()
|
|
||||||
parent := it.previous()
|
parent := it.previous()
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||||
}
|
}
|
||||||
statedb, err := state.New(parent.Root, bc.statedb)
|
|
||||||
if err != nil {
|
|
||||||
return nil, it.index, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
|
||||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
|
||||||
// useless due to the intermediate root hashing after each transaction.
|
|
||||||
if bc.chainConfig.IsByzantium(block.Number()) {
|
|
||||||
// Generate witnesses either if we're self-testing, or if it's the
|
|
||||||
// only block being inserted. A bit crude, but witnesses are huge,
|
|
||||||
// so we refuse to make an entire chain of them.
|
|
||||||
if bc.vmConfig.StatelessSelfValidation || (makeWitness && len(chain) == 1) {
|
|
||||||
witness, err = stateless.NewWitness(block.Header(), bc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, it.index, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statedb.StartPrefetcher("chain", witness)
|
|
||||||
}
|
|
||||||
activeState = statedb
|
|
||||||
|
|
||||||
// If we have a followup block, run that against the current state to pre-cache
|
|
||||||
// transactions and probabilistically some of the account/storage trie nodes.
|
|
||||||
var followupInterrupt atomic.Bool
|
|
||||||
if !bc.cacheConfig.TrieCleanNoPrefetch {
|
|
||||||
if followup, err := it.peek(); followup != nil && err == nil {
|
|
||||||
throwaway, _ := state.New(parent.Root, bc.statedb)
|
|
||||||
|
|
||||||
go func(start time.Time, followup *types.Block, throwaway *state.StateDB) {
|
|
||||||
// Disable tracing for prefetcher executions.
|
|
||||||
vmCfg := bc.vmConfig
|
|
||||||
vmCfg.Tracer = nil
|
|
||||||
bc.prefetcher.Prefetch(followup, throwaway, vmCfg, &followupInterrupt)
|
|
||||||
|
|
||||||
blockPrefetchExecuteTimer.Update(time.Since(start))
|
|
||||||
if followupInterrupt.Load() {
|
|
||||||
blockPrefetchInterruptMeter.Mark(1)
|
|
||||||
}
|
|
||||||
}(time.Now(), followup, throwaway)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The traced section of block import.
|
// The traced section of block import.
|
||||||
res, err := bc.processBlock(block, statedb, start, setHead)
|
start := time.Now()
|
||||||
followupInterrupt.Store(true)
|
res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
// Report the import stats before returning the various results
|
// Report the import stats before returning the various results
|
||||||
stats.processed++
|
stats.processed++
|
||||||
stats.usedGas += res.usedGas
|
stats.usedGas += res.usedGas
|
||||||
|
witness = res.witness
|
||||||
|
|
||||||
var snapDiffItems, snapBufItems common.StorageSize
|
var snapDiffItems, snapBufItems common.StorageSize
|
||||||
if bc.snaps != nil {
|
if bc.snaps != nil {
|
||||||
|
|
@ -1937,11 +1900,74 @@ type blockProcessingResult struct {
|
||||||
usedGas uint64
|
usedGas uint64
|
||||||
procTime time.Duration
|
procTime time.Duration
|
||||||
status WriteStatus
|
status WriteStatus
|
||||||
|
witness *stateless.Witness
|
||||||
}
|
}
|
||||||
|
|
||||||
// processBlock executes and validates the given block. If there was no error
|
// processBlock executes and validates the given block. If there was no error
|
||||||
// it writes the block and associated state to database.
|
// it writes the block and associated state to database.
|
||||||
func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) {
|
func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
startTime = time.Now()
|
||||||
|
statedb *state.StateDB
|
||||||
|
interrupt atomic.Bool
|
||||||
|
)
|
||||||
|
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||||
|
|
||||||
|
if bc.cacheConfig.TrieCleanNoPrefetch {
|
||||||
|
statedb, err = state.New(parentRoot, bc.statedb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 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.
|
||||||
|
reader, err := bc.statedb.ReaderWithCache(parentRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
throwaway, err := state.NewWithReader(parentRoot, bc.statedb, reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
statedb, err = state.NewWithReader(parentRoot, bc.statedb, reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go func(start time.Time, throwaway *state.StateDB, block *types.Block) {
|
||||||
|
// Disable tracing for prefetcher executions.
|
||||||
|
vmCfg := bc.vmConfig
|
||||||
|
vmCfg.Tracer = nil
|
||||||
|
bc.prefetcher.Prefetch(block, throwaway, vmCfg, &interrupt)
|
||||||
|
|
||||||
|
blockPrefetchExecuteTimer.Update(time.Since(start))
|
||||||
|
if interrupt.Load() {
|
||||||
|
blockPrefetchInterruptMeter.Mark(1)
|
||||||
|
}
|
||||||
|
}(time.Now(), throwaway, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||||
|
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||||
|
// useless due to the intermediate root hashing after each transaction.
|
||||||
|
var witness *stateless.Witness
|
||||||
|
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||||
|
// Generate witnesses either if we're self-testing, or if it's the
|
||||||
|
// only block being inserted. A bit crude, but witnesses are huge,
|
||||||
|
// so we refuse to make an entire chain of them.
|
||||||
|
if bc.vmConfig.StatelessSelfValidation || makeWitness {
|
||||||
|
witness, err = stateless.NewWitness(block.Header(), bc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statedb.StartPrefetcher("chain", witness)
|
||||||
|
defer statedb.StopPrefetcher()
|
||||||
|
}
|
||||||
|
|
||||||
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
||||||
bc.logger.OnBlockStart(tracing.BlockEvent{
|
bc.logger.OnBlockStart(tracing.BlockEvent{
|
||||||
Block: block,
|
Block: block,
|
||||||
|
|
@ -2000,7 +2026,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xvtime := time.Since(xvstart)
|
xvtime := time.Since(xvstart)
|
||||||
proctime := time.Since(start) // processing + validation + cross validation
|
proctime := time.Since(startTime) // processing + validation + cross validation
|
||||||
|
|
||||||
// Update the metrics touched during block processing and validation
|
// Update the metrics touched during block processing and validation
|
||||||
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
|
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
|
||||||
|
|
@ -2041,9 +2067,14 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||||
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
|
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
|
||||||
|
|
||||||
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
||||||
blockInsertTimer.UpdateSince(start)
|
blockInsertTimer.UpdateSince(startTime)
|
||||||
|
|
||||||
return &blockProcessingResult{usedGas: res.GasUsed, procTime: proctime, status: status}, nil
|
return &blockProcessingResult{
|
||||||
|
usedGas: res.GasUsed,
|
||||||
|
procTime: proctime,
|
||||||
|
status: status,
|
||||||
|
witness: witness,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
||||||
|
|
@ -2527,14 +2558,22 @@ func (bc *BlockChain) reportBlock(block *types.Block, res *ProcessResult, err er
|
||||||
// logForkReadiness will write a log when a future fork is scheduled, but not
|
// logForkReadiness will write a log when a future fork is scheduled, but not
|
||||||
// active. This is useful so operators know their client is ready for the fork.
|
// active. This is useful so operators know their client is ready for the fork.
|
||||||
func (bc *BlockChain) logForkReadiness(block *types.Block) {
|
func (bc *BlockChain) logForkReadiness(block *types.Block) {
|
||||||
c := bc.Config()
|
config := bc.Config()
|
||||||
current, last := c.LatestFork(block.Time()), c.LatestFork(math.MaxUint64)
|
current, last := config.LatestFork(block.Time()), config.LatestFork(math.MaxUint64)
|
||||||
t := c.Timestamp(last)
|
|
||||||
if t == nil {
|
// Short circuit if the timestamp of the last fork is undefined,
|
||||||
|
// or if the network has already passed the last configured fork.
|
||||||
|
t := config.Timestamp(last)
|
||||||
|
if t == nil || current >= last {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
at := time.Unix(int64(*t), 0)
|
at := time.Unix(int64(*t), 0)
|
||||||
if current < last && time.Now().After(bc.lastForkReadyAlert.Add(forkReadyInterval)) {
|
|
||||||
|
// Only log if:
|
||||||
|
// - Current time is before the fork activation time
|
||||||
|
// - Enough time has passed since last alert
|
||||||
|
now := time.Now()
|
||||||
|
if now.Before(at) && now.After(bc.lastForkReadyAlert.Add(forkReadyInterval)) {
|
||||||
log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822),
|
log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822),
|
||||||
"remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix())
|
"remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix())
|
||||||
bc.lastForkReadyAlert = time.Now()
|
bc.lastForkReadyAlert = time.Now()
|
||||||
|
|
@ -2610,7 +2649,7 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
|
||||||
first = headers[0].Number.Uint64()
|
first = headers[0].Number.Uint64()
|
||||||
)
|
)
|
||||||
if first == 1 && frozen == 0 {
|
if first == 1 && frozen == 0 {
|
||||||
_, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil})
|
_, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error writing genesis to ancients", "err", err)
|
log.Error("Error writing genesis to ancients", "err", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,10 @@ import (
|
||||||
|
|
||||||
// insertStats tracks and reports on block insertion.
|
// insertStats tracks and reports on block insertion.
|
||||||
type insertStats struct {
|
type insertStats struct {
|
||||||
queued, processed, ignored int
|
processed, ignored int
|
||||||
usedGas uint64
|
usedGas uint64
|
||||||
lastIndex int
|
lastIndex int
|
||||||
startTime mclock.AbsTime
|
startTime mclock.AbsTime
|
||||||
}
|
}
|
||||||
|
|
||||||
// statsReportLimit is the time limit during import and export after which we
|
// statsReportLimit is the time limit during import and export after which we
|
||||||
|
|
@ -43,8 +43,12 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
||||||
// Fetch the timings for the batch
|
// Fetch the timings for the batch
|
||||||
var (
|
var (
|
||||||
now = mclock.Now()
|
now = mclock.Now()
|
||||||
elapsed = now.Sub(st.startTime)
|
elapsed = now.Sub(st.startTime) + 1 // prevent zero division
|
||||||
|
mgasps = float64(st.usedGas) * 1000 / float64(elapsed)
|
||||||
)
|
)
|
||||||
|
// Update the Mgas per second gauge
|
||||||
|
chainMgaspsGauge.Update(int64(mgasps))
|
||||||
|
|
||||||
// If we're at the last block of the batch or report period reached, log
|
// If we're at the last block of the batch or report period reached, log
|
||||||
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
||||||
// Count the number of transactions in this segment
|
// Count the number of transactions in this segment
|
||||||
|
|
@ -58,7 +62,7 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
||||||
context := []interface{}{
|
context := []interface{}{
|
||||||
"number", end.Number(), "hash", end.Hash(),
|
"number", end.Number(), "hash", end.Hash(),
|
||||||
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
|
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
|
||||||
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
|
"elapsed", common.PrettyDuration(elapsed), "mgasps", mgasps,
|
||||||
}
|
}
|
||||||
if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute {
|
if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute {
|
||||||
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
|
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
|
||||||
|
|
@ -74,9 +78,6 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
||||||
}
|
}
|
||||||
context = append(context, []interface{}{"triedirty", triebufNodes}...)
|
context = append(context, []interface{}{"triedirty", triebufNodes}...)
|
||||||
|
|
||||||
if st.queued > 0 {
|
|
||||||
context = append(context, []interface{}{"queued", st.queued}...)
|
|
||||||
}
|
|
||||||
if st.ignored > 0 {
|
if st.ignored > 0 {
|
||||||
context = append(context, []interface{}{"ignored", st.ignored}...)
|
context = append(context, []interface{}{"ignored", st.ignored}...)
|
||||||
}
|
}
|
||||||
|
|
@ -138,6 +139,7 @@ func (it *insertIterator) next() (*types.Block, error) {
|
||||||
//
|
//
|
||||||
// Both header and body validation errors (nil too) is cached into the iterator
|
// Both header and body validation errors (nil too) is cached into the iterator
|
||||||
// to avoid duplicating work on the following next() call.
|
// to avoid duplicating work on the following next() call.
|
||||||
|
// nolint:unused
|
||||||
func (it *insertIterator) peek() (*types.Block, error) {
|
func (it *insertIterator) peek() (*types.Block, error) {
|
||||||
// If we reached the end of the chain, abort
|
// If we reached the end of the chain, abort
|
||||||
if it.index+1 >= len(it.chain) {
|
if it.index+1 >= len(it.chain) {
|
||||||
|
|
|
||||||
|
|
@ -234,12 +234,22 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
return receipts
|
return receipts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) GetRawReceiptsByHash(hash common.Hash) types.Receipts {
|
// GetRawReceipts retrieves the receipts for all transactions in a given block
|
||||||
|
// without deriving the internal fields and the Bloom.
|
||||||
|
func (bc *BlockChain) GetRawReceipts(hash common.Hash, number uint64) types.Receipts {
|
||||||
|
if receipts, ok := bc.receiptsCache.Get(hash); ok {
|
||||||
|
return receipts
|
||||||
|
}
|
||||||
|
return rawdb.ReadRawReceipts(bc.db, hash, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReceiptsRLP retrieves the receipts of a block.
|
||||||
|
func (bc *BlockChain) GetReceiptsRLP(hash common.Hash) rlp.RawValue {
|
||||||
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
||||||
if number == nil {
|
if number == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return rawdb.ReadRawReceipts(bc.db, hash, *number)
|
return rawdb.ReadReceiptsRLP(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
|
||||||
|
|
|
||||||
|
|
@ -1769,7 +1769,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false)
|
db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1791,7 +1791,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
defer engine.Close()
|
defer engine.Close()
|
||||||
if snapshots {
|
if snapshots && scheme == rawdb.HashScheme {
|
||||||
config.SnapshotLimit = 256
|
config.SnapshotLimit = 256
|
||||||
config.SnapshotWait = true
|
config.SnapshotWait = true
|
||||||
}
|
}
|
||||||
|
|
@ -1820,7 +1820,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
if err := chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false); err != nil {
|
if err := chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false); err != nil {
|
||||||
t.Fatalf("Failed to flush trie state: %v", err)
|
t.Fatalf("Failed to flush trie state: %v", err)
|
||||||
}
|
}
|
||||||
if snapshots {
|
if snapshots && scheme == rawdb.HashScheme {
|
||||||
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
||||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1854,7 +1854,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
db, err = rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false)
|
db, err = rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to reopen persistent freezer database: %v", err)
|
t.Fatalf("Failed to reopen persistent freezer database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1919,7 +1919,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false)
|
db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1952,8 +1952,10 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
if _, err := chain.InsertChain(blocks[1:2]); err != nil {
|
if _, err := chain.InsertChain(blocks[1:2]); err != nil {
|
||||||
t.Fatalf("Failed to import canonical chain start: %v", err)
|
t.Fatalf("Failed to import canonical chain start: %v", err)
|
||||||
}
|
}
|
||||||
if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
|
if scheme == rawdb.HashScheme {
|
||||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
|
||||||
|
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert block B3 and commit the state into disk
|
// Insert block B3 and commit the state into disk
|
||||||
|
|
@ -1977,7 +1979,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
db, err = rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false)
|
db, err = rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to reopen persistent freezer database: %v", err)
|
t.Fatalf("Failed to reopen persistent freezer database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1997,15 +1999,23 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
}
|
}
|
||||||
expHead := uint64(1)
|
expHead := uint64(1)
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
expHead = uint64(2)
|
// The pathdb database makes sure that snapshot and trie are consistent,
|
||||||
|
// so only the last block is reverted in case of a crash.
|
||||||
|
expHead = uint64(3)
|
||||||
}
|
}
|
||||||
if head := chain.CurrentBlock(); head.Number.Uint64() != expHead {
|
if head := chain.CurrentBlock(); head.Number.Uint64() != expHead {
|
||||||
t.Errorf("Head block mismatch: have %d, want %d", head.Number, expHead)
|
t.Errorf("Head block mismatch: have %d, want %d", head.Number, expHead)
|
||||||
}
|
}
|
||||||
|
if scheme == rawdb.PathScheme {
|
||||||
// Reinsert B2-B4
|
// Reinsert B4
|
||||||
if _, err := chain.InsertChain(blocks[1:]); err != nil {
|
if _, err := chain.InsertChain(blocks[3:]); err != nil {
|
||||||
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Reinsert B2-B4
|
||||||
|
if _, err := chain.InsertChain(blocks[1:]); err != nil {
|
||||||
|
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
|
if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
|
||||||
t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
|
t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
|
||||||
|
|
@ -2016,7 +2026,9 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
if head := chain.CurrentBlock(); head.Number.Uint64() != uint64(4) {
|
if head := chain.CurrentBlock(); head.Number.Uint64() != uint64(4) {
|
||||||
t.Errorf("Head block mismatch: have %d, want %d", head.Number, uint64(4))
|
t.Errorf("Head block mismatch: have %d, want %d", head.Number, uint64(4))
|
||||||
}
|
}
|
||||||
if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
|
if scheme == rawdb.HashScheme {
|
||||||
t.Error("Failed to regenerate the snapshot of known state")
|
if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
|
||||||
|
t.Error("Failed to regenerate the snapshot of known state")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1973,7 +1973,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false)
|
db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2023,7 +2023,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||||
}
|
}
|
||||||
if tt.commitBlock > 0 {
|
if tt.commitBlock > 0 {
|
||||||
chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false)
|
chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false)
|
||||||
if snapshots {
|
if snapshots && scheme == rawdb.HashScheme {
|
||||||
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
||||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false)
|
db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +105,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
|
||||||
if basic.commitBlock > 0 && basic.commitBlock == point {
|
if basic.commitBlock > 0 && basic.commitBlock == point {
|
||||||
chain.TrieDB().Commit(blocks[point-1].Root(), false)
|
chain.TrieDB().Commit(blocks[point-1].Root(), false)
|
||||||
}
|
}
|
||||||
if basic.snapshotBlock > 0 && basic.snapshotBlock == point {
|
if basic.snapshotBlock > 0 && basic.snapshotBlock == point && basic.scheme == rawdb.HashScheme {
|
||||||
// Flushing the entire snap tree into the disk, the
|
// Flushing the entire snap tree into the disk, the
|
||||||
// relevant (a) snapshot root and (b) snapshot generator
|
// relevant (a) snapshot root and (b) snapshot generator
|
||||||
// will be persisted atomically.
|
// will be persisted atomically.
|
||||||
|
|
@ -149,13 +149,17 @@ func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks [
|
||||||
block := chain.GetBlockByNumber(basic.expSnapshotBottom)
|
block := chain.GetBlockByNumber(basic.expSnapshotBottom)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
t.Errorf("The corresponding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom)
|
t.Errorf("The corresponding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom)
|
||||||
} else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
|
} else if basic.scheme == rawdb.HashScheme {
|
||||||
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
|
if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
|
||||||
|
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the snapshot, ensure it's integrated
|
// Check the snapshot, ensure it's integrated
|
||||||
if err := chain.snaps.Verify(block.Root()); err != nil {
|
if basic.scheme == rawdb.HashScheme {
|
||||||
t.Errorf("The disk layer is not integrated %v", err)
|
if err := chain.snaps.Verify(block.Root()); err != nil {
|
||||||
|
t.Errorf("The disk layer is not integrated %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,7 +265,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
newdb, err := rawdb.NewDatabaseWithFreezer(pdb, snaptest.ancient, "", false)
|
newdb, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: snaptest.ancient})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -565,12 +569,14 @@ func TestHighCommitCrashWithNewSnapshot(t *testing.T) {
|
||||||
//
|
//
|
||||||
// Expected head header : C8
|
// Expected head header : C8
|
||||||
// Expected head fast block: C8
|
// Expected head fast block: C8
|
||||||
// Expected head block : G
|
// Expected head block : G (Hash mode), C6 (Hash mode)
|
||||||
// Expected snapshot disk : C4
|
// Expected snapshot disk : C4 (Hash mode)
|
||||||
for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
|
for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
|
||||||
expHead := uint64(0)
|
expHead := uint64(0)
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
expHead = uint64(4)
|
// The pathdb database makes sure that snapshot and trie are consistent,
|
||||||
|
// so only the last two blocks are reverted in case of a crash.
|
||||||
|
expHead = uint64(6)
|
||||||
}
|
}
|
||||||
test := &crashSnapshotTest{
|
test := &crashSnapshotTest{
|
||||||
snapshotTestBasic{
|
snapshotTestBasic{
|
||||||
|
|
|
||||||
|
|
@ -734,11 +734,11 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer fast.Stop()
|
defer fast.Stop()
|
||||||
|
|
||||||
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
if n, err := fast.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
// Freezer style fast import the chain.
|
// Freezer style fast import the chain.
|
||||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
ancientDb, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -747,7 +747,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer ancient.Stop()
|
defer ancient.Stop()
|
||||||
|
|
||||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil {
|
if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(len(blocks)/2)); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -824,7 +824,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||||
|
|
||||||
// makeDb creates a db instance for testing.
|
// makeDb creates a db instance for testing.
|
||||||
makeDb := func() ethdb.Database {
|
makeDb := func() ethdb.Database {
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -871,7 +871,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer fast.Stop()
|
defer fast.Stop()
|
||||||
|
|
||||||
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
if n, err := fast.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
assert(t, "fast", fast, height, height, 0)
|
assert(t, "fast", fast, height, height, 0)
|
||||||
|
|
@ -884,7 +884,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer ancient.Stop()
|
defer ancient.Stop()
|
||||||
|
|
||||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(3*len(blocks)/4)); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
assert(t, "ancient", ancient, height, height, 0)
|
assert(t, "ancient", ancient, height, height, 0)
|
||||||
|
|
@ -1623,7 +1623,7 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) {
|
||||||
competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*state.TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*state.TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
||||||
|
|
||||||
// Import the shared chain and the original canonical one
|
// Import the shared chain and the original canonical one
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
||||||
|
|
@ -1689,14 +1689,14 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
||||||
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil)
|
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil)
|
||||||
|
|
||||||
// Import the chain as a ancient-first node and ensure all pointers are updated
|
// Import the chain as a ancient-first node and ensure all pointers are updated
|
||||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
|
ancientDb, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{Ancient: t.TempDir()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
defer ancientDb.Close()
|
defer ancientDb.Close()
|
||||||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
|
|
||||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(3*len(blocks)/4)); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
rawdb.WriteLastPivotNumber(ancientDb, blocks[len(blocks)-1].NumberU64()) // Force fast sync behavior
|
rawdb.WriteLastPivotNumber(ancientDb, blocks[len(blocks)-1].NumberU64()) // Force fast sync behavior
|
||||||
|
|
@ -1747,7 +1747,7 @@ func testLowDiffLongChain(t *testing.T, scheme string) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Import the canonical chain
|
// Import the canonical chain
|
||||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
diskdb, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
defer diskdb.Close()
|
defer diskdb.Close()
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
||||||
|
|
@ -1959,7 +1959,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
||||||
b.OffsetTime(-9) // A higher difficulty
|
b.OffsetTime(-9) // A higher difficulty
|
||||||
})
|
})
|
||||||
// Import the shared chain and the original canonical one
|
// Import the shared chain and the original canonical one
|
||||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
chaindb, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1991,7 +1991,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
||||||
}
|
}
|
||||||
} else if typ == "receipts" {
|
} else if typ == "receipts" {
|
||||||
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
||||||
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
_, err = chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
asserter = func(t *testing.T, block *types.Block) {
|
asserter = func(t *testing.T, block *types.Block) {
|
||||||
|
|
@ -2122,7 +2122,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Import the shared chain and the original canonical one
|
// Import the shared chain and the original canonical one
|
||||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
chaindb, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2157,7 +2157,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
||||||
}
|
}
|
||||||
} else if typ == "receipts" {
|
} else if typ == "receipts" {
|
||||||
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
||||||
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
_, err = chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
asserter = func(t *testing.T, block *types.Block) {
|
asserter = func(t *testing.T, block *types.Block) {
|
||||||
|
|
@ -2496,7 +2496,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false)
|
db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -3403,7 +3403,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
||||||
}
|
}
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
})
|
})
|
||||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
diskdb, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
defer diskdb.Close()
|
defer diskdb.Close()
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
||||||
|
|
@ -4199,16 +4199,16 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) {
|
||||||
gen.SetCoinbase(common.Address{0: byte(0xb), 19: byte(i)})
|
gen.SetCoinbase(common.Address{0: byte(0xb), 19: byte(i)})
|
||||||
})
|
})
|
||||||
|
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil)
|
chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
if n, err := chain.InsertReceiptChain(blocks, receipts, ancientLimit); err != nil {
|
if n, err := chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), ancientLimit); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
if n, err := chain.InsertReceiptChain(chainA, receiptsA, ancientLimit); err != nil {
|
if n, err := chain.InsertReceiptChain(chainA, types.EncodeBlockReceiptLists(receiptsA), ancientLimit); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
// If the common ancestor is below the ancient limit, rewind the chain head.
|
// If the common ancestor is below the ancient limit, rewind the chain head.
|
||||||
|
|
@ -4218,7 +4218,7 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) {
|
||||||
rawdb.WriteLastPivotNumber(db, ancestor)
|
rawdb.WriteLastPivotNumber(db, ancestor)
|
||||||
chain.SetHead(ancestor)
|
chain.SetHead(ancestor)
|
||||||
}
|
}
|
||||||
if n, err := chain.InsertReceiptChain(chainB, receiptsB, ancientLimit); err != nil {
|
if n, err := chain.InsertReceiptChain(chainB, types.EncodeBlockReceiptLists(receiptsB), ancientLimit); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
head := chain.CurrentSnapBlock()
|
head := chain.CurrentSnapBlock()
|
||||||
|
|
@ -4315,7 +4315,7 @@ func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64,
|
||||||
config := DefaultCacheConfigWithScheme(rawdb.PathScheme)
|
config := DefaultCacheConfigWithScheme(rawdb.PathScheme)
|
||||||
config.ChainHistoryMode = history.KeepPostMerge
|
config.ChainHistoryMode = history.KeepPostMerge
|
||||||
|
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), genesis, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil)
|
chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), genesis, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
@ -4336,7 +4336,7 @@ func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64,
|
||||||
if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil {
|
if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil {
|
||||||
t.Fatalf("failed to insert headers before cutoff %d: %v", n, err)
|
t.Fatalf("failed to insert headers before cutoff %d: %v", n, err)
|
||||||
}
|
}
|
||||||
if n, err := chain.InsertReceiptChain(blocksAfter, receiptsAfter, ancientLimit); err != nil {
|
if n, err := chain.InsertReceiptChain(blocksAfter, types.EncodeBlockReceiptLists(receiptsAfter), ancientLimit); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
headSnap := chain.CurrentSnapBlock()
|
headSnap := chain.CurrentSnapBlock()
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ type blockchain interface {
|
||||||
GetHeader(hash common.Hash, number uint64) *types.Header
|
GetHeader(hash common.Hash, number uint64) *types.Header
|
||||||
GetCanonicalHash(number uint64) common.Hash
|
GetCanonicalHash(number uint64) common.Hash
|
||||||
GetReceiptsByHash(hash common.Hash) types.Receipts
|
GetReceiptsByHash(hash common.Hash) types.Receipts
|
||||||
GetRawReceiptsByHash(hash common.Hash) types.Receipts
|
GetRawReceipts(hash common.Hash, number uint64) types.Receipts
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainView represents an immutable view of a chain with a block id and a set
|
// ChainView represents an immutable view of a chain with a block id and a set
|
||||||
|
|
@ -117,7 +117,7 @@ func (cv *ChainView) RawReceipts(number uint64) types.Receipts {
|
||||||
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
|
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return cv.chain.GetRawReceiptsByHash(blockHash)
|
return cv.chain.GetRawReceipts(blockHash, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SharedRange returns the block range shared by two chain views.
|
// SharedRange returns the block range shared by two chain views.
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ const (
|
||||||
databaseVersion = 2 // reindexed if database version does not match
|
databaseVersion = 2 // reindexed if database version does not match
|
||||||
cachedLastBlocks = 1000 // last block of map pointers
|
cachedLastBlocks = 1000 // last block of map pointers
|
||||||
cachedLvPointers = 1000 // first log value pointer of block pointers
|
cachedLvPointers = 1000 // first log value pointer of block pointers
|
||||||
cachedBaseRows = 100 // groups of base layer filter row data
|
|
||||||
cachedFilterMaps = 3 // complete filter maps (cached by map renderer)
|
cachedFilterMaps = 3 // complete filter maps (cached by map renderer)
|
||||||
cachedRenderSnapshots = 8 // saved map renderer data at block boundaries
|
cachedRenderSnapshots = 8 // saved map renderer data at block boundaries
|
||||||
)
|
)
|
||||||
|
|
@ -101,7 +100,6 @@ type FilterMaps struct {
|
||||||
filterMapCache *lru.Cache[uint32, filterMap]
|
filterMapCache *lru.Cache[uint32, filterMap]
|
||||||
lastBlockCache *lru.Cache[uint32, lastBlockOfMap]
|
lastBlockCache *lru.Cache[uint32, lastBlockOfMap]
|
||||||
lvPointerCache *lru.Cache[uint64, uint64]
|
lvPointerCache *lru.Cache[uint64, uint64]
|
||||||
baseRowsCache *lru.Cache[uint64, [][]uint32]
|
|
||||||
|
|
||||||
// the matchers set and the fields of FilterMapsMatcherBackend instances are
|
// the matchers set and the fields of FilterMapsMatcherBackend instances are
|
||||||
// read and written both by exported functions and the indexer.
|
// read and written both by exported functions and the indexer.
|
||||||
|
|
@ -227,7 +225,7 @@ type Config struct {
|
||||||
// NewFilterMaps creates a new FilterMaps and starts the indexer.
|
// NewFilterMaps creates a new FilterMaps and starts the indexer.
|
||||||
func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, finalBlock uint64, params Params, config Config) *FilterMaps {
|
func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, finalBlock uint64, params Params, config Config) *FilterMaps {
|
||||||
rs, initialized, err := rawdb.ReadFilterMapsRange(db)
|
rs, initialized, err := rawdb.ReadFilterMapsRange(db)
|
||||||
if err != nil || rs.Version != databaseVersion {
|
if err != nil || (initialized && rs.Version != databaseVersion) {
|
||||||
rs, initialized = rawdb.FilterMapsRange{}, false
|
rs, initialized = rawdb.FilterMapsRange{}, false
|
||||||
log.Warn("Invalid log index database version; resetting log index")
|
log.Warn("Invalid log index database version; resetting log index")
|
||||||
}
|
}
|
||||||
|
|
@ -264,7 +262,6 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
|
||||||
filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps),
|
filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps),
|
||||||
lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks),
|
lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks),
|
||||||
lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers),
|
lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers),
|
||||||
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
|
|
||||||
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
|
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
|
||||||
}
|
}
|
||||||
f.checkRevertRange() // revert maps that are inconsistent with the current chain view
|
f.checkRevertRange() // revert maps that are inconsistent with the current chain view
|
||||||
|
|
@ -348,7 +345,6 @@ func (f *FilterMaps) reset() {
|
||||||
f.renderSnapshots.Purge()
|
f.renderSnapshots.Purge()
|
||||||
f.lastBlockCache.Purge()
|
f.lastBlockCache.Purge()
|
||||||
f.lvPointerCache.Purge()
|
f.lvPointerCache.Purge()
|
||||||
f.baseRowsCache.Purge()
|
|
||||||
f.indexLock.Unlock()
|
f.indexLock.Unlock()
|
||||||
// deleting the range first ensures that resetDb will be called again at next
|
// deleting the range first ensures that resetDb will be called again at next
|
||||||
// startup and any leftover data will be removed even if it cannot finish now.
|
// startup and any leftover data will be removed even if it cannot finish now.
|
||||||
|
|
@ -565,47 +561,69 @@ func (f *FilterMaps) getFilterMap(mapIndex uint32) (filterMap, error) {
|
||||||
}
|
}
|
||||||
fm := make(filterMap, f.mapHeight)
|
fm := make(filterMap, f.mapHeight)
|
||||||
for rowIndex := range fm {
|
for rowIndex := range fm {
|
||||||
var err error
|
rows, err := f.getFilterMapRows([]uint32{mapIndex}, uint32(rowIndex), false)
|
||||||
fm[rowIndex], err = f.getFilterMapRow(mapIndex, uint32(rowIndex), false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to load filter map %d from database: %v", mapIndex, err)
|
return nil, fmt.Errorf("failed to load filter map %d from database: %v", mapIndex, err)
|
||||||
}
|
}
|
||||||
|
fm[rowIndex] = rows[0]
|
||||||
}
|
}
|
||||||
f.filterMapCache.Add(mapIndex, fm)
|
f.filterMapCache.Add(mapIndex, fm)
|
||||||
return fm, nil
|
return fm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFilterMapRow fetches the given filter map row. If baseLayerOnly is true
|
// getFilterMapRows fetches a set of filter map rows at the corresponding map
|
||||||
// then only the first baseRowLength entries are returned.
|
// indices and a shared row index. If baseLayerOnly is true then only the first
|
||||||
func (f *FilterMaps) getFilterMapRow(mapIndex, rowIndex uint32, baseLayerOnly bool) (FilterRow, error) {
|
// baseRowLength entries are returned.
|
||||||
baseMapRowIndex := f.mapRowIndex(mapIndex&-f.baseRowGroupLength, rowIndex)
|
func (f *FilterMaps) getFilterMapRows(mapIndices []uint32, rowIndex uint32, baseLayerOnly bool) ([]FilterRow, error) {
|
||||||
baseRows, ok := f.baseRowsCache.Get(baseMapRowIndex)
|
rows := make([]FilterRow, len(mapIndices))
|
||||||
if !ok {
|
var ptr int
|
||||||
var err error
|
for len(mapIndices) > ptr {
|
||||||
baseRows, err = rawdb.ReadFilterMapBaseRows(f.db, baseMapRowIndex, f.baseRowGroupLength, f.logMapWidth)
|
baseRowGroup := mapIndices[ptr] / f.baseRowGroupLength
|
||||||
if err != nil {
|
groupLength := 1
|
||||||
return nil, fmt.Errorf("failed to retrieve filter map %d base rows %d: %v", mapIndex, rowIndex, err)
|
for ptr+groupLength < len(mapIndices) && mapIndices[ptr+groupLength]/f.baseRowGroupLength == baseRowGroup {
|
||||||
|
groupLength++
|
||||||
}
|
}
|
||||||
f.baseRowsCache.Add(baseMapRowIndex, baseRows)
|
if err := f.getFilterMapRowsOfGroup(rows[ptr:ptr+groupLength], mapIndices[ptr:ptr+groupLength], rowIndex, baseLayerOnly); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ptr += groupLength
|
||||||
}
|
}
|
||||||
baseRow := slices.Clone(baseRows[mapIndex&(f.baseRowGroupLength-1)])
|
return rows, nil
|
||||||
if baseLayerOnly {
|
}
|
||||||
return baseRow, nil
|
|
||||||
}
|
// getFilterMapRowsOfGroup fetches a set of filter map rows at map indices
|
||||||
extRow, err := rawdb.ReadFilterMapExtRow(f.db, f.mapRowIndex(mapIndex, rowIndex), f.logMapWidth)
|
// belonging to the same base row group.
|
||||||
|
func (f *FilterMaps) getFilterMapRowsOfGroup(target []FilterRow, mapIndices []uint32, rowIndex uint32, baseLayerOnly bool) error {
|
||||||
|
baseRowGroup := mapIndices[0] / f.baseRowGroupLength
|
||||||
|
baseMapRowIndex := f.mapRowIndex(baseRowGroup*f.baseRowGroupLength, rowIndex)
|
||||||
|
baseRows, err := rawdb.ReadFilterMapBaseRows(f.db, baseMapRowIndex, f.baseRowGroupLength, f.logMapWidth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve filter map %d extended row %d: %v", mapIndex, rowIndex, err)
|
return fmt.Errorf("failed to retrieve base row group %d of row %d: %v", baseRowGroup, rowIndex, err)
|
||||||
}
|
}
|
||||||
return FilterRow(append(baseRow, extRow...)), nil
|
for i, mapIndex := range mapIndices {
|
||||||
|
if mapIndex/f.baseRowGroupLength != baseRowGroup {
|
||||||
|
panic("mapIndices are not in the same base row group")
|
||||||
|
}
|
||||||
|
row := baseRows[mapIndex&(f.baseRowGroupLength-1)]
|
||||||
|
if !baseLayerOnly {
|
||||||
|
extRow, err := rawdb.ReadFilterMapExtRow(f.db, f.mapRowIndex(mapIndex, rowIndex), f.logMapWidth)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to retrieve filter map %d extended row %d: %v", mapIndex, rowIndex, err)
|
||||||
|
}
|
||||||
|
row = append(row, extRow...)
|
||||||
|
}
|
||||||
|
target[i] = row
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// storeFilterMapRows stores a set of filter map rows at the corresponding map
|
// storeFilterMapRows stores a set of filter map rows at the corresponding map
|
||||||
// indices and a shared row index.
|
// indices and a shared row index.
|
||||||
func (f *FilterMaps) storeFilterMapRows(batch ethdb.Batch, mapIndices []uint32, rowIndex uint32, rows []FilterRow) error {
|
func (f *FilterMaps) storeFilterMapRows(batch ethdb.Batch, mapIndices []uint32, rowIndex uint32, rows []FilterRow) error {
|
||||||
for len(mapIndices) > 0 {
|
for len(mapIndices) > 0 {
|
||||||
baseMapIndex := mapIndices[0] & -f.baseRowGroupLength
|
baseRowGroup := mapIndices[0] / f.baseRowGroupLength
|
||||||
groupLength := 1
|
groupLength := 1
|
||||||
for groupLength < len(mapIndices) && mapIndices[groupLength]&-f.baseRowGroupLength == baseMapIndex {
|
for groupLength < len(mapIndices) && mapIndices[groupLength]/f.baseRowGroupLength == baseRowGroup {
|
||||||
groupLength++
|
groupLength++
|
||||||
}
|
}
|
||||||
if err := f.storeFilterMapRowsOfGroup(batch, mapIndices[:groupLength], rowIndex, rows[:groupLength]); err != nil {
|
if err := f.storeFilterMapRowsOfGroup(batch, mapIndices[:groupLength], rowIndex, rows[:groupLength]); err != nil {
|
||||||
|
|
@ -619,26 +637,20 @@ func (f *FilterMaps) storeFilterMapRows(batch ethdb.Batch, mapIndices []uint32,
|
||||||
// storeFilterMapRowsOfGroup stores a set of filter map rows at map indices
|
// storeFilterMapRowsOfGroup stores a set of filter map rows at map indices
|
||||||
// belonging to the same base row group.
|
// belonging to the same base row group.
|
||||||
func (f *FilterMaps) storeFilterMapRowsOfGroup(batch ethdb.Batch, mapIndices []uint32, rowIndex uint32, rows []FilterRow) error {
|
func (f *FilterMaps) storeFilterMapRowsOfGroup(batch ethdb.Batch, mapIndices []uint32, rowIndex uint32, rows []FilterRow) error {
|
||||||
baseMapIndex := mapIndices[0] & -f.baseRowGroupLength
|
baseRowGroup := mapIndices[0] / f.baseRowGroupLength
|
||||||
baseMapRowIndex := f.mapRowIndex(baseMapIndex, rowIndex)
|
baseMapRowIndex := f.mapRowIndex(baseRowGroup*f.baseRowGroupLength, rowIndex)
|
||||||
var baseRows [][]uint32
|
var baseRows [][]uint32
|
||||||
if uint32(len(mapIndices)) != f.baseRowGroupLength { // skip base rows read if all rows are replaced
|
if uint32(len(mapIndices)) != f.baseRowGroupLength { // skip base rows read if all rows are replaced
|
||||||
var ok bool
|
var err error
|
||||||
baseRows, ok = f.baseRowsCache.Get(baseMapRowIndex)
|
baseRows, err = rawdb.ReadFilterMapBaseRows(f.db, baseMapRowIndex, f.baseRowGroupLength, f.logMapWidth)
|
||||||
if ok {
|
if err != nil {
|
||||||
baseRows = slices.Clone(baseRows)
|
return fmt.Errorf("failed to retrieve base row group %d of row %d for modification: %v", baseRowGroup, rowIndex, err)
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
baseRows, err = rawdb.ReadFilterMapBaseRows(f.db, baseMapRowIndex, f.baseRowGroupLength, f.logMapWidth)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to retrieve filter map %d base rows %d for modification: %v", mapIndices[0]&-f.baseRowGroupLength, rowIndex, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
baseRows = make([][]uint32, f.baseRowGroupLength)
|
baseRows = make([][]uint32, f.baseRowGroupLength)
|
||||||
}
|
}
|
||||||
for i, mapIndex := range mapIndices {
|
for i, mapIndex := range mapIndices {
|
||||||
if mapIndex&-f.baseRowGroupLength != baseMapIndex {
|
if mapIndex/f.baseRowGroupLength != baseRowGroup {
|
||||||
panic("mapIndices are not in the same base row group")
|
panic("mapIndices are not in the same base row group")
|
||||||
}
|
}
|
||||||
baseRow := []uint32(rows[i])
|
baseRow := []uint32(rows[i])
|
||||||
|
|
@ -650,7 +662,6 @@ func (f *FilterMaps) storeFilterMapRowsOfGroup(batch ethdb.Batch, mapIndices []u
|
||||||
baseRows[mapIndex&(f.baseRowGroupLength-1)] = baseRow
|
baseRows[mapIndex&(f.baseRowGroupLength-1)] = baseRow
|
||||||
rawdb.WriteFilterMapExtRow(batch, f.mapRowIndex(mapIndex, rowIndex), extRow, f.logMapWidth)
|
rawdb.WriteFilterMapExtRow(batch, f.mapRowIndex(mapIndex, rowIndex), extRow, f.logMapWidth)
|
||||||
}
|
}
|
||||||
f.baseRowsCache.Add(baseMapRowIndex, baseRows)
|
|
||||||
rawdb.WriteFilterMapBaseRows(batch, baseMapRowIndex, baseRows, f.logMapWidth)
|
rawdb.WriteFilterMapBaseRows(batch, baseMapRowIndex, baseRows, f.logMapWidth)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -428,10 +428,12 @@ func (ts *testSetup) matcherViewHash() common.Hash {
|
||||||
binary.LittleEndian.PutUint32(enc[:4], r)
|
binary.LittleEndian.PutUint32(enc[:4], r)
|
||||||
for m := uint32(0); m <= headMap; m++ {
|
for m := uint32(0); m <= headMap; m++ {
|
||||||
binary.LittleEndian.PutUint32(enc[4:8], m)
|
binary.LittleEndian.PutUint32(enc[4:8], m)
|
||||||
row, _ := mb.GetFilterMapRow(ctx, m, r, false)
|
rows, _ := mb.GetFilterMapRows(ctx, []uint32{m}, r, false)
|
||||||
for _, v := range row {
|
for _, row := range rows {
|
||||||
binary.LittleEndian.PutUint32(enc[8:], v)
|
for _, v := range row {
|
||||||
hasher.Write(enc[:])
|
binary.LittleEndian.PutUint32(enc[8:], v)
|
||||||
|
hasher.Write(enc[:])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -515,7 +517,7 @@ func (tc *testChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
return tc.receipts[hash]
|
return tc.receipts[hash]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *testChain) GetRawReceiptsByHash(hash common.Hash) types.Receipts {
|
func (tc *testChain) GetRawReceipts(hash common.Hash, number uint64) types.Receipts {
|
||||||
tc.lock.RLock()
|
tc.lock.RLock()
|
||||||
defer tc.lock.RUnlock()
|
defer tc.lock.RUnlock()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -45,7 +44,7 @@ var ErrMatchAll = errors.New("match all patterns not supported")
|
||||||
type MatcherBackend interface {
|
type MatcherBackend interface {
|
||||||
GetParams() *Params
|
GetParams() *Params
|
||||||
GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error)
|
GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error)
|
||||||
GetFilterMapRow(ctx context.Context, mapIndex, rowIndex uint32, baseLayerOnly bool) (FilterRow, error)
|
GetFilterMapRows(ctx context.Context, mapIndices []uint32, rowIndex uint32, baseLayerOnly bool) ([]FilterRow, error)
|
||||||
GetLogByLvIndex(ctx context.Context, lvIndex uint64) (*types.Log, error)
|
GetLogByLvIndex(ctx context.Context, lvIndex uint64) (*types.Log, error)
|
||||||
SyncLogIndex(ctx context.Context) (SyncRange, error)
|
SyncLogIndex(ctx context.Context) (SyncRange, error)
|
||||||
Close()
|
Close()
|
||||||
|
|
@ -365,50 +364,57 @@ func (m *singleMatcherInstance) getMatchesForLayer(ctx context.Context, layerInd
|
||||||
var st int
|
var st int
|
||||||
m.stats.setState(&st, stOther)
|
m.stats.setState(&st, stOther)
|
||||||
params := m.backend.GetParams()
|
params := m.backend.GetParams()
|
||||||
maskedMapIndex, rowIndex := uint32(math.MaxUint32), uint32(0)
|
var ptr int
|
||||||
for _, mapIndex := range m.mapIndices {
|
for len(m.mapIndices) > ptr {
|
||||||
filterRows, ok := m.filterRows[mapIndex]
|
// find next group of map indices mapped onto the same row
|
||||||
if !ok {
|
maskedMapIndex := params.maskedMapIndex(m.mapIndices[ptr], layerIndex)
|
||||||
continue
|
rowIndex := params.rowIndex(m.mapIndices[ptr], layerIndex, m.value)
|
||||||
}
|
groupLength := 1
|
||||||
if mm := params.maskedMapIndex(mapIndex, layerIndex); mm != maskedMapIndex {
|
for ptr+groupLength < len(m.mapIndices) && params.maskedMapIndex(m.mapIndices[ptr+groupLength], layerIndex) == maskedMapIndex {
|
||||||
// only recalculate rowIndex when necessary
|
groupLength++
|
||||||
maskedMapIndex = mm
|
|
||||||
rowIndex = params.rowIndex(mapIndex, layerIndex, m.value)
|
|
||||||
}
|
}
|
||||||
if layerIndex == 0 {
|
if layerIndex == 0 {
|
||||||
m.stats.setState(&st, stFetchFirst)
|
m.stats.setState(&st, stFetchFirst)
|
||||||
} else {
|
} else {
|
||||||
m.stats.setState(&st, stFetchMore)
|
m.stats.setState(&st, stFetchMore)
|
||||||
}
|
}
|
||||||
filterRow, err := m.backend.GetFilterMapRow(ctx, mapIndex, rowIndex, layerIndex == 0)
|
groupRows, err := m.backend.GetFilterMapRows(ctx, m.mapIndices[ptr:ptr+groupLength], rowIndex, layerIndex == 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.stats.setState(&st, stNone)
|
m.stats.setState(&st, stNone)
|
||||||
return nil, fmt.Errorf("failed to retrieve filter map %d row %d: %v", mapIndex, rowIndex, err)
|
return nil, fmt.Errorf("failed to retrieve filter map %d row %d: %v", m.mapIndices[ptr], rowIndex, err)
|
||||||
}
|
}
|
||||||
if layerIndex == 0 {
|
|
||||||
matchBaseRowAccessMeter.Mark(1)
|
|
||||||
matchBaseRowSizeMeter.Mark(int64(len(filterRow)))
|
|
||||||
} else {
|
|
||||||
matchExtRowAccessMeter.Mark(1)
|
|
||||||
matchExtRowSizeMeter.Mark(int64(len(filterRow)))
|
|
||||||
}
|
|
||||||
m.stats.addAmount(st, int64(len(filterRow)))
|
|
||||||
m.stats.setState(&st, stOther)
|
m.stats.setState(&st, stOther)
|
||||||
filterRows = append(filterRows, filterRow)
|
for i := range groupLength {
|
||||||
if uint32(len(filterRow)) < params.maxRowLength(layerIndex) {
|
mapIndex := m.mapIndices[ptr+i]
|
||||||
m.stats.setState(&st, stProcess)
|
filterRow := groupRows[i]
|
||||||
matches := params.potentialMatches(filterRows, mapIndex, m.value)
|
filterRows, ok := m.filterRows[mapIndex]
|
||||||
m.stats.addAmount(st, int64(len(matches)))
|
if !ok {
|
||||||
results = append(results, matcherResult{
|
panic("dropped map in mapIndices")
|
||||||
mapIndex: mapIndex,
|
}
|
||||||
matches: matches,
|
if layerIndex == 0 {
|
||||||
})
|
matchBaseRowAccessMeter.Mark(1)
|
||||||
m.stats.setState(&st, stOther)
|
matchBaseRowSizeMeter.Mark(int64(len(filterRow)))
|
||||||
delete(m.filterRows, mapIndex)
|
} else {
|
||||||
} else {
|
matchExtRowAccessMeter.Mark(1)
|
||||||
m.filterRows[mapIndex] = filterRows
|
matchExtRowSizeMeter.Mark(int64(len(filterRow)))
|
||||||
|
}
|
||||||
|
m.stats.addAmount(st, int64(len(filterRow)))
|
||||||
|
filterRows = append(filterRows, filterRow)
|
||||||
|
if uint32(len(filterRow)) < params.maxRowLength(layerIndex) {
|
||||||
|
m.stats.setState(&st, stProcess)
|
||||||
|
matches := params.potentialMatches(filterRows, mapIndex, m.value)
|
||||||
|
m.stats.addAmount(st, int64(len(matches)))
|
||||||
|
results = append(results, matcherResult{
|
||||||
|
mapIndex: mapIndex,
|
||||||
|
matches: matches,
|
||||||
|
})
|
||||||
|
m.stats.setState(&st, stOther)
|
||||||
|
delete(m.filterRows, mapIndex)
|
||||||
|
} else {
|
||||||
|
m.filterRows[mapIndex] = filterRows
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ptr += groupLength
|
||||||
}
|
}
|
||||||
m.cleanMapIndices()
|
m.cleanMapIndices()
|
||||||
m.stats.setState(&st, stNone)
|
m.stats.setState(&st, stNone)
|
||||||
|
|
|
||||||
|
|
@ -67,18 +67,15 @@ func (fm *FilterMapsMatcherBackend) Close() {
|
||||||
delete(fm.f.matchers, fm)
|
delete(fm.f.matchers, fm)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFilterMapRow returns the given row of the given map. If the row is empty
|
// GetFilterMapRows returns the given row of the given map. If the row is empty
|
||||||
// then a non-nil zero length row is returned. If baseLayerOnly is true then
|
// then a non-nil zero length row is returned. If baseLayerOnly is true then
|
||||||
// only the first baseRowLength entries of the row are guaranteed to be
|
// only the first baseRowLength entries of the row are guaranteed to be
|
||||||
// returned.
|
// returned.
|
||||||
// Note that the returned slices should not be modified, they should be copied
|
// Note that the returned slices should not be modified, they should be copied
|
||||||
// on write.
|
// on write.
|
||||||
// GetFilterMapRow implements MatcherBackend.
|
// GetFilterMapRows implements MatcherBackend.
|
||||||
func (fm *FilterMapsMatcherBackend) GetFilterMapRow(ctx context.Context, mapIndex, rowIndex uint32, baseLayerOnly bool) (FilterRow, error) {
|
func (fm *FilterMapsMatcherBackend) GetFilterMapRows(ctx context.Context, mapIndices []uint32, rowIndex uint32, baseLayerOnly bool) ([]FilterRow, error) {
|
||||||
fm.f.indexLock.RLock()
|
return fm.f.getFilterMapRows(mapIndices, rowIndex, baseLayerOnly)
|
||||||
defer fm.f.indexLock.RUnlock()
|
|
||||||
|
|
||||||
return fm.f.getFilterMapRow(mapIndex, rowIndex, baseLayerOnly)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockLvPointer returns the starting log value index where the log values
|
// GetBlockLvPointer returns the starting log value index where the log values
|
||||||
|
|
|
||||||
|
|
@ -545,8 +545,8 @@ func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawVa
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
|
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
|
||||||
// The receipt metadata fields are not guaranteed to be populated, so they
|
// The receipt metadata fields and the Bloom are not guaranteed to be populated,
|
||||||
// should not be used. Use ReadReceipts instead if the metadata is needed.
|
// so they should not be used. Use ReadReceipts instead if the metadata is needed.
|
||||||
func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts {
|
func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts {
|
||||||
// Retrieve the flattened receipt slice
|
// Retrieve the flattened receipt slice
|
||||||
data := ReadReceiptsRLP(db, hash, number)
|
data := ReadReceiptsRLP(db, hash, number)
|
||||||
|
|
@ -621,6 +621,14 @@ func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteRawReceipts stores all the transaction receipts belonging to a block.
|
||||||
|
func WriteRawReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, receipts rlp.RawValue) {
|
||||||
|
// Store the flattened receipt slice
|
||||||
|
if err := db.Put(blockReceiptsKey(number, hash), receipts); err != nil {
|
||||||
|
log.Crit("Failed to store block receipts", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteReceipts removes all receipt data associated with a block hash.
|
// DeleteReceipts removes all receipt data associated with a block hash.
|
||||||
func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||||
if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
|
if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
|
||||||
|
|
@ -701,18 +709,11 @@ func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts) (int64, error) {
|
func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []rlp.RawValue) (int64, error) {
|
||||||
var stReceipts []*types.ReceiptForStorage
|
|
||||||
|
|
||||||
return db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
return db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
// Convert receipts to storage format and sum up total difficulty.
|
|
||||||
stReceipts = stReceipts[:0]
|
|
||||||
for _, receipt := range receipts[i] {
|
|
||||||
stReceipts = append(stReceipts, (*types.ReceiptForStorage)(receipt))
|
|
||||||
}
|
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
if err := writeAncientBlock(op, block, header, stReceipts); err != nil {
|
if err := writeAncientBlock(op, block, header, receipts[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -720,7 +721,7 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage) error {
|
func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts rlp.RawValue) error {
|
||||||
num := block.NumberU64()
|
num := block.NumberU64()
|
||||||
if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
|
if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
|
||||||
return fmt.Errorf("can't add block %d hash: %v", num, err)
|
return fmt.Errorf("can't add block %d hash: %v", num, err)
|
||||||
|
|
|
||||||
|
|
@ -377,7 +377,11 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||||
t.Fatalf("receipts returned when body was deleted: %v", rs)
|
t.Fatalf("receipts returned when body was deleted: %v", rs)
|
||||||
}
|
}
|
||||||
// Ensure that receipts without metadata can be returned without the block body too
|
// Ensure that receipts without metadata can be returned without the block body too
|
||||||
if err := checkReceiptsRLP(ReadRawReceipts(db, hash, 0), receipts); err != nil {
|
raw := ReadRawReceipts(db, hash, 0)
|
||||||
|
for _, r := range raw {
|
||||||
|
r.Bloom = types.CreateBloom(r)
|
||||||
|
}
|
||||||
|
if err := checkReceiptsRLP(raw, receipts); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Sanity check that body alone without the receipt is a full purge
|
// Sanity check that body alone without the receipt is a full purge
|
||||||
|
|
@ -412,7 +416,7 @@ func checkReceiptsRLP(have, want types.Receipts) error {
|
||||||
func TestAncientStorage(t *testing.T) {
|
func TestAncientStorage(t *testing.T) {
|
||||||
// Freezer style fast import the chain.
|
// Freezer style fast import the chain.
|
||||||
frdir := t.TempDir()
|
frdir := t.TempDir()
|
||||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false)
|
db, err := Open(NewMemoryDatabase(), OpenOptions{Ancient: frdir})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create database with ancient backend")
|
t.Fatalf("failed to create database with ancient backend")
|
||||||
}
|
}
|
||||||
|
|
@ -439,7 +443,7 @@ func TestAncientStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write and verify the header in the database
|
// Write and verify the header in the database
|
||||||
WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil})
|
WriteAncientBlocks(db, []*types.Block{block}, types.EncodeBlockReceiptLists([]types.Receipts{nil}))
|
||||||
|
|
||||||
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
|
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
|
||||||
t.Fatalf("no header returned")
|
t.Fatalf("no header returned")
|
||||||
|
|
@ -465,7 +469,7 @@ func TestAncientStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteAncientHeaderChain(t *testing.T) {
|
func TestWriteAncientHeaderChain(t *testing.T) {
|
||||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), t.TempDir(), "", false)
|
db, err := Open(NewMemoryDatabase(), OpenOptions{Ancient: t.TempDir()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create database with ancient backend")
|
t.Fatalf("failed to create database with ancient backend")
|
||||||
}
|
}
|
||||||
|
|
@ -582,7 +586,7 @@ func TestHashesInRange(t *testing.T) {
|
||||||
func BenchmarkWriteAncientBlocks(b *testing.B) {
|
func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||||
// Open freezer database.
|
// Open freezer database.
|
||||||
frdir := b.TempDir()
|
frdir := b.TempDir()
|
||||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false)
|
db, err := Open(NewMemoryDatabase(), OpenOptions{Ancient: frdir})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to create database with ancient backend")
|
b.Fatalf("failed to create database with ancient backend")
|
||||||
}
|
}
|
||||||
|
|
@ -609,7 +613,7 @@ func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||||
|
|
||||||
blocks := allBlocks[i : i+length]
|
blocks := allBlocks[i : i+length]
|
||||||
receipts := batchReceipts[:length]
|
receipts := batchReceipts[:length]
|
||||||
writeSize, err := WriteAncientBlocks(db, blocks, receipts)
|
writeSize, err := WriteAncientBlocks(db, blocks, types.EncodeBlockReceiptLists(receipts))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -886,7 +890,7 @@ func TestHeadersRLPStorage(t *testing.T) {
|
||||||
// Have N headers in the freezer
|
// Have N headers in the freezer
|
||||||
frdir := t.TempDir()
|
frdir := t.TempDir()
|
||||||
|
|
||||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false)
|
db, err := Open(NewMemoryDatabase(), OpenOptions{Ancient: frdir})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create database with ancient backend")
|
t.Fatalf("failed to create database with ancient backend")
|
||||||
}
|
}
|
||||||
|
|
@ -909,7 +913,7 @@ func TestHeadersRLPStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
receipts := make([]types.Receipts, 100)
|
receipts := make([]types.Receipts, 100)
|
||||||
// Write first half to ancients
|
// Write first half to ancients
|
||||||
WriteAncientBlocks(db, chain[:50], receipts[:50])
|
WriteAncientBlocks(db, chain[:50], types.EncodeBlockReceiptLists(receipts[:50]))
|
||||||
// Write second half to db
|
// Write second half to db
|
||||||
for i := 50; i < 100; i++ {
|
for i := 50; i < 100; i++ {
|
||||||
WriteCanonicalHash(db, chain[i].Hash(), chain[i].NumberU64())
|
WriteCanonicalHash(db, chain[i].Hash(), chain[i].NumberU64())
|
||||||
|
|
|
||||||
|
|
@ -446,7 +446,7 @@ type FilterMapsRange struct {
|
||||||
// database entry is not present, that is interpreted as a valid non-initialized
|
// database entry is not present, that is interpreted as a valid non-initialized
|
||||||
// state and returns a blank range structure and no error.
|
// state and returns a blank range structure and no error.
|
||||||
func ReadFilterMapsRange(db ethdb.KeyValueReader) (FilterMapsRange, bool, error) {
|
func ReadFilterMapsRange(db ethdb.KeyValueReader) (FilterMapsRange, bool, error) {
|
||||||
if has, err := db.Has(filterMapsRangeKey); !has || err != nil {
|
if has, err := db.Has(filterMapsRangeKey); err != nil || !has {
|
||||||
return FilterMapsRange{}, false, err
|
return FilterMapsRange{}, false, err
|
||||||
}
|
}
|
||||||
encRange, err := db.Get(filterMapsRangeKey)
|
encRange, err := db.Get(filterMapsRangeKey)
|
||||||
|
|
@ -457,7 +457,8 @@ func ReadFilterMapsRange(db ethdb.KeyValueReader) (FilterMapsRange, bool, error)
|
||||||
if err := rlp.DecodeBytes(encRange, &fmRange); err != nil {
|
if err := rlp.DecodeBytes(encRange, &fmRange); err != nil {
|
||||||
return FilterMapsRange{}, false, err
|
return FilterMapsRange{}, false, err
|
||||||
}
|
}
|
||||||
return fmRange, true, err
|
|
||||||
|
return fmRange, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteFilterMapsRange stores the filter maps range data.
|
// WriteFilterMapsRange stores the filter maps range data.
|
||||||
|
|
|
||||||
|
|
@ -258,13 +258,21 @@ func ReadStateHistory(db ethdb.AncientReaderOp, id uint64) ([]byte, []byte, []by
|
||||||
// WriteStateHistory writes the provided state history to database. Compute the
|
// WriteStateHistory writes the provided state history to database. Compute the
|
||||||
// position of state history in freezer by minus one since the id of first state
|
// position of state history in freezer by minus one since the id of first state
|
||||||
// history starts from one(zero for initial state).
|
// history starts from one(zero for initial state).
|
||||||
func WriteStateHistory(db ethdb.AncientWriter, id uint64, meta []byte, accountIndex []byte, storageIndex []byte, accounts []byte, storages []byte) {
|
func WriteStateHistory(db ethdb.AncientWriter, id uint64, meta []byte, accountIndex []byte, storageIndex []byte, accounts []byte, storages []byte) error {
|
||||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
_, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||||
op.AppendRaw(stateHistoryMeta, id-1, meta)
|
if err := op.AppendRaw(stateHistoryMeta, id-1, meta); err != nil {
|
||||||
op.AppendRaw(stateHistoryAccountIndex, id-1, accountIndex)
|
return err
|
||||||
op.AppendRaw(stateHistoryStorageIndex, id-1, storageIndex)
|
}
|
||||||
op.AppendRaw(stateHistoryAccountData, id-1, accounts)
|
if err := op.AppendRaw(stateHistoryAccountIndex, id-1, accountIndex); err != nil {
|
||||||
op.AppendRaw(stateHistoryStorageData, id-1, storages)
|
return err
|
||||||
return nil
|
}
|
||||||
|
if err := op.AppendRaw(stateHistoryStorageIndex, id-1, storageIndex); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := op.AppendRaw(stateHistoryAccountData, id-1, accounts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return op.AppendRaw(stateHistoryStorageData, id-1, storages)
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package rawdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -45,25 +44,6 @@ const HashScheme = "hash"
|
||||||
// on extra state diffs to survive deep reorg.
|
// on extra state diffs to survive deep reorg.
|
||||||
const PathScheme = "path"
|
const PathScheme = "path"
|
||||||
|
|
||||||
// hasher is used to compute the sha256 hash of the provided data.
|
|
||||||
type hasher struct{ sha crypto.KeccakState }
|
|
||||||
|
|
||||||
var hasherPool = sync.Pool{
|
|
||||||
New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} },
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHasher() *hasher {
|
|
||||||
return hasherPool.Get().(*hasher)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *hasher) hash(data []byte) common.Hash {
|
|
||||||
return crypto.HashData(h.sha, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *hasher) release() {
|
|
||||||
hasherPool.Put(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadAccountTrieNode retrieves the account trie node with the specified node path.
|
// ReadAccountTrieNode retrieves the account trie node with the specified node path.
|
||||||
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte {
|
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte {
|
||||||
data, _ := db.Get(accountTrieNodeKey(path))
|
data, _ := db.Get(accountTrieNodeKey(path))
|
||||||
|
|
@ -170,9 +150,7 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c
|
||||||
if len(blob) == 0 {
|
if len(blob) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
h := newHasher()
|
return crypto.Keccak256Hash(blob) == hash // exists but not match
|
||||||
defer h.release()
|
|
||||||
return h.hash(blob) == hash // exists but not match
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("Unknown scheme %v", scheme))
|
panic(fmt.Sprintf("Unknown scheme %v", scheme))
|
||||||
}
|
}
|
||||||
|
|
@ -194,9 +172,7 @@ func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash
|
||||||
if len(blob) == 0 {
|
if len(blob) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
h := newHasher()
|
if crypto.Keccak256Hash(blob) != hash {
|
||||||
defer h.release()
|
|
||||||
if h.hash(blob) != hash {
|
|
||||||
return nil // exists but not match
|
return nil // exists but not match
|
||||||
}
|
}
|
||||||
return blob
|
return blob
|
||||||
|
|
|
||||||
|
|
@ -77,13 +77,6 @@ func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
for i := c.start; i < c.limit; i++ {
|
for i := c.start; i < c.limit; i++ {
|
||||||
exist, err := db.HasAncient("a", uint64(i))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to check presence, %v", err)
|
|
||||||
}
|
|
||||||
if exist {
|
|
||||||
t.Fatalf("Item %d is already truncated", uint64(i))
|
|
||||||
}
|
|
||||||
_, err = db.Ancient("a", uint64(i))
|
_, err = db.Ancient("a", uint64(i))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Error is expected for non-existent item")
|
t.Fatal("Error is expected for non-existent item")
|
||||||
|
|
@ -93,13 +86,6 @@ func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||||
|
|
||||||
// Test the items in range should be reachable
|
// Test the items in range should be reachable
|
||||||
for i := 10; i < 90; i++ {
|
for i := 10; i < 90; i++ {
|
||||||
exist, err := db.HasAncient("a", uint64(i))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to check presence, %v", err)
|
|
||||||
}
|
|
||||||
if !exist {
|
|
||||||
t.Fatalf("Item %d is missing", uint64(i))
|
|
||||||
}
|
|
||||||
blob, err := db.Ancient("a", uint64(i))
|
blob, err := db.Ancient("a", uint64(i))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to retrieve item, %v", err)
|
t.Fatalf("Failed to retrieve item, %v", err)
|
||||||
|
|
@ -110,13 +96,6 @@ func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the items in unknown table shouldn't be reachable
|
// Test the items in unknown table shouldn't be reachable
|
||||||
exist, err := db.HasAncient("b", uint64(0))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to check presence, %v", err)
|
|
||||||
}
|
|
||||||
if exist {
|
|
||||||
t.Fatal("Item in unknown table shouldn't be found")
|
|
||||||
}
|
|
||||||
_, err = db.Ancient("b", uint64(0))
|
_, err = db.Ancient("b", uint64(0))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Error is expected for unknown table")
|
t.Fatal("Error is expected for unknown table")
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb/eradb"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -43,7 +44,10 @@ const (
|
||||||
// feature. The background thread will keep moving ancient chain segments from
|
// feature. The background thread will keep moving ancient chain segments from
|
||||||
// key-value database to flat files for saving space on live database.
|
// key-value database to flat files for saving space on live database.
|
||||||
type chainFreezer struct {
|
type chainFreezer struct {
|
||||||
ethdb.AncientStore // Ancient store for storing cold chain segment
|
ancients ethdb.AncientStore // Ancient store for storing cold chain segment
|
||||||
|
|
||||||
|
// Optional Era database used as a backup for the pruned chain.
|
||||||
|
eradb *eradb.Store
|
||||||
|
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
@ -56,23 +60,27 @@ type chainFreezer struct {
|
||||||
// state freezer (e.g. dev mode).
|
// state freezer (e.g. dev mode).
|
||||||
// - if non-empty directory is given, initializes the regular file-based
|
// - if non-empty directory is given, initializes the regular file-based
|
||||||
// state freezer.
|
// state freezer.
|
||||||
func newChainFreezer(datadir string, namespace string, readonly bool) (*chainFreezer, error) {
|
func newChainFreezer(datadir string, eraDir string, namespace string, readonly bool) (*chainFreezer, error) {
|
||||||
var (
|
|
||||||
err error
|
|
||||||
freezer ethdb.AncientStore
|
|
||||||
)
|
|
||||||
if datadir == "" {
|
if datadir == "" {
|
||||||
freezer = NewMemoryFreezer(readonly, chainFreezerTableConfigs)
|
return &chainFreezer{
|
||||||
} else {
|
ancients: NewMemoryFreezer(readonly, chainFreezerTableConfigs),
|
||||||
freezer, err = NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerTableConfigs)
|
quit: make(chan struct{}),
|
||||||
|
trigger: make(chan chan struct{}),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
freezer, err := NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerTableConfigs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
edb, err := eradb.New(resolveChainEraDir(datadir, eraDir))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &chainFreezer{
|
return &chainFreezer{
|
||||||
AncientStore: freezer,
|
ancients: freezer,
|
||||||
quit: make(chan struct{}),
|
eradb: edb,
|
||||||
trigger: make(chan chan struct{}),
|
quit: make(chan struct{}),
|
||||||
|
trigger: make(chan chan struct{}),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +92,11 @@ func (f *chainFreezer) Close() error {
|
||||||
close(f.quit)
|
close(f.quit)
|
||||||
}
|
}
|
||||||
f.wg.Wait()
|
f.wg.Wait()
|
||||||
return f.AncientStore.Close()
|
|
||||||
|
if f.eradb != nil {
|
||||||
|
f.eradb.Close()
|
||||||
|
}
|
||||||
|
return f.ancients.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// readHeadNumber returns the number of chain head block. 0 is returned if the
|
// readHeadNumber returns the number of chain head block. 0 is returned if the
|
||||||
|
|
@ -334,3 +346,75 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||||
})
|
})
|
||||||
return hashes, err
|
return hashes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ancient retrieves an ancient binary blob from the append-only immutable files.
|
||||||
|
func (f *chainFreezer) Ancient(kind string, number uint64) ([]byte, error) {
|
||||||
|
// Lookup the entry in the underlying ancient store, assuming that
|
||||||
|
// headers and hashes are always available.
|
||||||
|
if kind == ChainFreezerHeaderTable || kind == ChainFreezerHashTable {
|
||||||
|
return f.ancients.Ancient(kind, number)
|
||||||
|
}
|
||||||
|
tail, err := f.ancients.Tail()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Lookup the entry in the underlying ancient store if it's not pruned
|
||||||
|
if number >= tail {
|
||||||
|
return f.ancients.Ancient(kind, number)
|
||||||
|
}
|
||||||
|
// Lookup the entry in the optional era backend
|
||||||
|
if f.eradb == nil {
|
||||||
|
return nil, errOutOfBounds
|
||||||
|
}
|
||||||
|
switch kind {
|
||||||
|
case ChainFreezerBodiesTable:
|
||||||
|
return f.eradb.GetRawBody(number)
|
||||||
|
case ChainFreezerReceiptTable:
|
||||||
|
return f.eradb.GetRawReceipts(number)
|
||||||
|
}
|
||||||
|
return nil, errUnknownTable
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAncients executes an operation while preventing mutations to the freezer,
|
||||||
|
// i.e. if fn performs multiple reads, they will be consistent with each other.
|
||||||
|
func (f *chainFreezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) {
|
||||||
|
if store, ok := f.ancients.(*Freezer); ok {
|
||||||
|
store.writeLock.Lock()
|
||||||
|
defer store.writeLock.Unlock()
|
||||||
|
}
|
||||||
|
return fn(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods below are just pass-through to the underlying ancient store.
|
||||||
|
|
||||||
|
func (f *chainFreezer) Ancients() (uint64, error) {
|
||||||
|
return f.ancients.Ancients()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *chainFreezer) Tail() (uint64, error) {
|
||||||
|
return f.ancients.Tail()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *chainFreezer) AncientSize(kind string) (uint64, error) {
|
||||||
|
return f.ancients.AncientSize(kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *chainFreezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
|
||||||
|
return f.ancients.AncientRange(kind, start, count, maxBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *chainFreezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (int64, error) {
|
||||||
|
return f.ancients.ModifyAncients(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *chainFreezer) TruncateHead(items uint64) (uint64, error) {
|
||||||
|
return f.ancients.TruncateHead(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *chainFreezer) TruncateTail(items uint64) (uint64, error) {
|
||||||
|
return f.ancients.TruncateTail(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *chainFreezer) SyncAncient() error {
|
||||||
|
return f.ancients.SyncAncient()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,11 +86,6 @@ type nofreezedb struct {
|
||||||
ethdb.KeyValueStore
|
ethdb.KeyValueStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAncient returns an error as we don't have a backing chain freezer.
|
|
||||||
func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) {
|
|
||||||
return false, errNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ancient returns an error as we don't have a backing chain freezer.
|
// Ancient returns an error as we don't have a backing chain freezer.
|
||||||
func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
|
func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
|
||||||
return nil, errNotSupported
|
return nil, errNotSupported
|
||||||
|
|
@ -186,19 +181,49 @@ func resolveChainFreezerDir(ancient string) string {
|
||||||
return freezer
|
return freezer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabaseWithFreezer creates a high level database on top of a given key-
|
// resolveChainEraDir is a helper function which resolves the absolute path of era database.
|
||||||
// value data store with a freezer moving immutable chain segments into cold
|
func resolveChainEraDir(chainFreezerDir string, era string) string {
|
||||||
// storage. The passed ancient indicates the path of root ancient directory
|
switch {
|
||||||
// where the chain freezer can be opened.
|
case era == "":
|
||||||
|
return filepath.Join(chainFreezerDir, "era")
|
||||||
|
case !filepath.IsAbs(era):
|
||||||
|
return filepath.Join(chainFreezerDir, era)
|
||||||
|
default:
|
||||||
|
return era
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDatabaseWithFreezer creates a high level database on top of a given key-value store.
|
||||||
|
// The passed ancient indicates the path of root ancient directory where the chain freezer
|
||||||
|
// can be opened.
|
||||||
|
//
|
||||||
|
// Deprecated: use Open.
|
||||||
func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly bool) (ethdb.Database, error) {
|
func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly bool) (ethdb.Database, error) {
|
||||||
|
return Open(db, OpenOptions{
|
||||||
|
Ancient: ancient,
|
||||||
|
MetricsNamespace: namespace,
|
||||||
|
ReadOnly: readonly,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenOptions specifies options for opening the database.
|
||||||
|
type OpenOptions struct {
|
||||||
|
Ancient string // ancients directory
|
||||||
|
Era string // era files directory
|
||||||
|
MetricsNamespace string // prefix added to freezer metric names
|
||||||
|
ReadOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open creates a high-level database wrapper for the given key-value store.
|
||||||
|
func Open(db ethdb.KeyValueStore, opts OpenOptions) (ethdb.Database, error) {
|
||||||
// Create the idle freezer instance. If the given ancient directory is empty,
|
// Create the idle freezer instance. If the given ancient directory is empty,
|
||||||
// in-memory chain freezer is used (e.g. dev mode); otherwise the regular
|
// in-memory chain freezer is used (e.g. dev mode); otherwise the regular
|
||||||
// file-based freezer is created.
|
// file-based freezer is created.
|
||||||
chainFreezerDir := ancient
|
chainFreezerDir := opts.Ancient
|
||||||
if chainFreezerDir != "" {
|
if chainFreezerDir != "" {
|
||||||
chainFreezerDir = resolveChainFreezerDir(chainFreezerDir)
|
chainFreezerDir = resolveChainFreezerDir(chainFreezerDir)
|
||||||
}
|
}
|
||||||
frdb, err := newChainFreezer(chainFreezerDir, namespace, readonly)
|
frdb, err := newChainFreezer(chainFreezerDir, opts.Era, opts.MetricsNamespace, opts.ReadOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printChainMetadata(db)
|
printChainMetadata(db)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -282,7 +307,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Freezer is consistent with the key-value database, permit combining the two
|
// Freezer is consistent with the key-value database, permit combining the two
|
||||||
if !readonly {
|
if !opts.ReadOnly {
|
||||||
frdb.wg.Add(1)
|
frdb.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
frdb.freeze(db)
|
frdb.freeze(db)
|
||||||
|
|
@ -290,7 +315,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
return &freezerdb{
|
return &freezerdb{
|
||||||
ancientRoot: ancient,
|
ancientRoot: opts.Ancient,
|
||||||
KeyValueStore: db,
|
KeyValueStore: db,
|
||||||
chainFreezer: frdb,
|
chainFreezer: frdb,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
||||||
345
core/rawdb/eradb/eradb.go
Normal file
345
core/rawdb/eradb/eradb.go
Normal file
|
|
@ -0,0 +1,345 @@
|
||||||
|
// Copyright 2025 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 eradb implements a history backend using era1 files.
|
||||||
|
package eradb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/era"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const openFileLimit = 64
|
||||||
|
|
||||||
|
var errClosed = errors.New("era store is closed")
|
||||||
|
|
||||||
|
// Store manages read access to a directory of era1 files.
|
||||||
|
// The getter methods are thread-safe.
|
||||||
|
type Store struct {
|
||||||
|
datadir string
|
||||||
|
|
||||||
|
// The mutex protects all remaining fields.
|
||||||
|
mu sync.Mutex
|
||||||
|
cond *sync.Cond
|
||||||
|
lru lru.BasicLRU[uint64, *fileCacheEntry]
|
||||||
|
opening map[uint64]*fileCacheEntry
|
||||||
|
closing bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileCacheEntry struct {
|
||||||
|
refcount int // reference count. This is protected by Store.mu!
|
||||||
|
opened chan struct{} // signals opening of file has completed
|
||||||
|
file *era.Era // the file
|
||||||
|
err error // error from opening the file
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileCacheStatus byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
storeClosing fileCacheStatus = iota
|
||||||
|
fileIsNew
|
||||||
|
fileIsOpening
|
||||||
|
fileIsCached
|
||||||
|
)
|
||||||
|
|
||||||
|
// New opens the store directory.
|
||||||
|
func New(datadir string) (*Store, error) {
|
||||||
|
db := &Store{
|
||||||
|
datadir: datadir,
|
||||||
|
lru: lru.NewBasicLRU[uint64, *fileCacheEntry](openFileLimit),
|
||||||
|
opening: make(map[uint64]*fileCacheEntry),
|
||||||
|
}
|
||||||
|
db.cond = sync.NewCond(&db.mu)
|
||||||
|
log.Info("Opened Era store", "datadir", datadir)
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes all open era1 files in the cache.
|
||||||
|
func (db *Store) Close() {
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
|
||||||
|
// Prevent new cache additions.
|
||||||
|
db.closing = true
|
||||||
|
|
||||||
|
// Deref all active files. Since inactive files have a refcount of one, they will be
|
||||||
|
// closed right here and now after decrementing. Files which are currently being used
|
||||||
|
// have a refcount > 1 and will hit zero when their access finishes.
|
||||||
|
for _, epoch := range db.lru.Keys() {
|
||||||
|
entry, _ := db.lru.Peek(epoch)
|
||||||
|
if entry.derefAndClose(epoch) {
|
||||||
|
db.lru.Remove(epoch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all store access to finish.
|
||||||
|
for db.lru.Len() > 0 || len(db.opening) > 0 {
|
||||||
|
db.cond.Wait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawBody returns the raw body for a given block number.
|
||||||
|
func (db *Store) GetRawBody(number uint64) ([]byte, error) {
|
||||||
|
epoch := number / uint64(era.MaxEra1Size)
|
||||||
|
entry := db.getEraByEpoch(epoch)
|
||||||
|
if entry.err != nil {
|
||||||
|
if errors.Is(entry.err, fs.ErrNotExist) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, entry.err
|
||||||
|
}
|
||||||
|
defer db.doneWithFile(epoch, entry)
|
||||||
|
|
||||||
|
return entry.file.GetRawBodyByNumber(number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawReceipts returns the raw receipts for a given block number.
|
||||||
|
func (db *Store) GetRawReceipts(number uint64) ([]byte, error) {
|
||||||
|
epoch := number / uint64(era.MaxEra1Size)
|
||||||
|
entry := db.getEraByEpoch(epoch)
|
||||||
|
if entry.err != nil {
|
||||||
|
if errors.Is(entry.err, fs.ErrNotExist) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, entry.err
|
||||||
|
}
|
||||||
|
defer db.doneWithFile(epoch, entry)
|
||||||
|
|
||||||
|
data, err := entry.file.GetRawReceiptsByNumber(number)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertReceipts(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertReceipts transforms an encoded block receipts list from the format
|
||||||
|
// used by era1 into the 'storage' format used by the go-ethereum ancients database.
|
||||||
|
func convertReceipts(input []byte) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
out bytes.Buffer
|
||||||
|
enc = rlp.NewEncoderBuffer(&out)
|
||||||
|
)
|
||||||
|
blockListIter, err := rlp.NewListIterator(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid block receipts list: %v", err)
|
||||||
|
}
|
||||||
|
outerList := enc.List()
|
||||||
|
for i := 0; blockListIter.Next(); i++ {
|
||||||
|
kind, content, _, err := rlp.Split(blockListIter.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("receipt %d invalid: %v", i, err)
|
||||||
|
}
|
||||||
|
var receiptData []byte
|
||||||
|
switch kind {
|
||||||
|
case rlp.Byte:
|
||||||
|
return nil, fmt.Errorf("receipt %d is single byte", i)
|
||||||
|
case rlp.String:
|
||||||
|
// Typed receipt - skip type.
|
||||||
|
receiptData = content[1:]
|
||||||
|
case rlp.List:
|
||||||
|
// Legacy receipt
|
||||||
|
receiptData = blockListIter.Value()
|
||||||
|
}
|
||||||
|
// Convert data list.
|
||||||
|
// Input is [status, gas-used, bloom, logs]
|
||||||
|
// Output is [status, gas-used, logs], i.e. we need to skip the bloom.
|
||||||
|
dataIter, err := rlp.NewListIterator(receiptData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("receipt %d has invalid data: %v", i, err)
|
||||||
|
}
|
||||||
|
innerList := enc.List()
|
||||||
|
for field := 0; dataIter.Next(); field++ {
|
||||||
|
if field == 2 {
|
||||||
|
continue // skip bloom
|
||||||
|
}
|
||||||
|
enc.Write(dataIter.Value())
|
||||||
|
}
|
||||||
|
enc.ListEnd(innerList)
|
||||||
|
if dataIter.Err() != nil {
|
||||||
|
return nil, fmt.Errorf("receipt %d iterator error: %v", i, dataIter.Err())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enc.ListEnd(outerList)
|
||||||
|
if blockListIter.Err() != nil {
|
||||||
|
return nil, fmt.Errorf("block receipt list iterator error: %v", blockListIter.Err())
|
||||||
|
}
|
||||||
|
enc.Flush()
|
||||||
|
return out.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEraByEpoch opens an era file or gets it from the cache.
|
||||||
|
// The caller can freely access the returned entry's .file and .err
|
||||||
|
// db.doneWithFile must be called when it is done reading the file.
|
||||||
|
func (db *Store) getEraByEpoch(epoch uint64) *fileCacheEntry {
|
||||||
|
stat, entry := db.getCacheEntry(epoch)
|
||||||
|
|
||||||
|
switch stat {
|
||||||
|
case storeClosing:
|
||||||
|
return &fileCacheEntry{err: errClosed}
|
||||||
|
|
||||||
|
case fileIsNew:
|
||||||
|
// Open the file and put it into the cache.
|
||||||
|
e, err := db.openEraFile(epoch)
|
||||||
|
if err != nil {
|
||||||
|
db.fileFailedToOpen(epoch, entry, err)
|
||||||
|
} else {
|
||||||
|
db.fileOpened(epoch, entry, e)
|
||||||
|
}
|
||||||
|
close(entry.opened)
|
||||||
|
|
||||||
|
case fileIsOpening:
|
||||||
|
// Wait for open to finish.
|
||||||
|
<-entry.opened
|
||||||
|
|
||||||
|
case fileIsCached:
|
||||||
|
// Nothing to do.
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("invalid file state %d", stat))
|
||||||
|
}
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCacheEntry gets an open era file from the cache.
|
||||||
|
func (db *Store) getCacheEntry(epoch uint64) (stat fileCacheStatus, entry *fileCacheEntry) {
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
|
||||||
|
if db.closing {
|
||||||
|
return storeClosing, nil
|
||||||
|
}
|
||||||
|
if entry = db.opening[epoch]; entry != nil {
|
||||||
|
stat = fileIsOpening
|
||||||
|
} else if entry, _ = db.lru.Get(epoch); entry != nil {
|
||||||
|
stat = fileIsCached
|
||||||
|
} else {
|
||||||
|
// It's a new file, create an entry in the opening table. Note the entry is
|
||||||
|
// created with an initial refcount of one. We increment the count once more
|
||||||
|
// before returning, but the count will return to one when the file has been
|
||||||
|
// accessed. When the store is closed or the file gets evicted from the cache,
|
||||||
|
// refcount will be decreased by one, thus allowing it to hit zero.
|
||||||
|
entry = &fileCacheEntry{refcount: 1, opened: make(chan struct{})}
|
||||||
|
db.opening[epoch] = entry
|
||||||
|
stat = fileIsNew
|
||||||
|
}
|
||||||
|
entry.refcount++
|
||||||
|
return stat, entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileOpened is called after an era file has been successfully opened.
|
||||||
|
func (db *Store) fileOpened(epoch uint64, entry *fileCacheEntry, file *era.Era) {
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
|
||||||
|
delete(db.opening, epoch)
|
||||||
|
db.cond.Signal() // db.opening was modified
|
||||||
|
|
||||||
|
// The database may have been closed while opening the file. When that happens, we
|
||||||
|
// need to close the file here, since it isn't tracked by the LRU yet.
|
||||||
|
if db.closing {
|
||||||
|
entry.err = errClosed
|
||||||
|
file.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add it to the LRU. This may evict an existing item, which we have to close.
|
||||||
|
entry.file = file
|
||||||
|
evictedEpoch, evictedEntry, _ := db.lru.Add3(epoch, entry)
|
||||||
|
if evictedEntry != nil {
|
||||||
|
evictedEntry.derefAndClose(evictedEpoch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileFailedToOpen is called when an era file could not be opened.
|
||||||
|
func (db *Store) fileFailedToOpen(epoch uint64, entry *fileCacheEntry, err error) {
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
|
||||||
|
delete(db.opening, epoch)
|
||||||
|
db.cond.Signal() // db.opening was modified
|
||||||
|
entry.err = err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Store) openEraFile(epoch uint64) (*era.Era, error) {
|
||||||
|
// File name scheme is <network>-<epoch>-<root>.
|
||||||
|
glob := fmt.Sprintf("*-%05d-*.era1", epoch)
|
||||||
|
matches, err := filepath.Glob(filepath.Join(db.datadir, glob))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(matches) > 1 {
|
||||||
|
return nil, fmt.Errorf("multiple era1 files found for epoch %d", epoch)
|
||||||
|
}
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return nil, fs.ErrNotExist
|
||||||
|
}
|
||||||
|
filename := matches[0]
|
||||||
|
|
||||||
|
e, err := era.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Sanity-check start block.
|
||||||
|
if e.Start()%uint64(era.MaxEra1Size) != 0 {
|
||||||
|
return nil, fmt.Errorf("pre-merge era1 file has invalid boundary. %d %% %d != 0", e.Start(), era.MaxEra1Size)
|
||||||
|
}
|
||||||
|
log.Debug("Opened era1 file", "epoch", epoch)
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// doneWithFile signals that the caller has finished using a file.
|
||||||
|
// This decrements the refcount and ensures the file is closed by the last user.
|
||||||
|
func (db *Store) doneWithFile(epoch uint64, entry *fileCacheEntry) {
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
|
||||||
|
if entry.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if entry.derefAndClose(epoch) {
|
||||||
|
// Delete closed entry from LRU if it is still present.
|
||||||
|
if e, _ := db.lru.Peek(epoch); e == entry {
|
||||||
|
db.lru.Remove(epoch)
|
||||||
|
db.cond.Signal() // db.lru was modified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// derefAndClose decrements the reference counter and closes the file
|
||||||
|
// when it hits zero.
|
||||||
|
func (entry *fileCacheEntry) derefAndClose(epoch uint64) (closed bool) {
|
||||||
|
entry.refcount--
|
||||||
|
if entry.refcount > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr := entry.file.Close()
|
||||||
|
if closeErr == nil {
|
||||||
|
log.Debug("Closed era1 file", "epoch", epoch)
|
||||||
|
} else {
|
||||||
|
log.Warn("Error closing era1 file", "epoch", epoch, "err", closeErr)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
103
core/rawdb/eradb/eradb_test.go
Normal file
103
core/rawdb/eradb/eradb_test.go
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright 2025 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 eradb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEraDatabase(t *testing.T) {
|
||||||
|
db, err := New("testdata")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
r, err := db.GetRawBody(175881)
|
||||||
|
require.NoError(t, err)
|
||||||
|
var body *types.Body
|
||||||
|
err = rlp.DecodeBytes(r, &body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, body, "block body not found")
|
||||||
|
assert.Equal(t, 3, len(body.Transactions))
|
||||||
|
|
||||||
|
r, err = db.GetRawReceipts(175881)
|
||||||
|
require.NoError(t, err)
|
||||||
|
var receipts []*types.ReceiptForStorage
|
||||||
|
err = rlp.DecodeBytes(r, &receipts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, receipts, "receipts not found")
|
||||||
|
assert.Equal(t, 3, len(receipts), "receipts length mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEraDatabaseConcurrentOpen(t *testing.T) {
|
||||||
|
db, err := New("testdata")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
const N = 25
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(N)
|
||||||
|
for range N {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
r, err := db.GetRawBody(1024)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("err:", err)
|
||||||
|
}
|
||||||
|
if len(r) == 0 {
|
||||||
|
t.Error("empty body")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEraDatabaseConcurrentOpenClose(t *testing.T) {
|
||||||
|
db, err := New("testdata")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
const N = 10
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(N)
|
||||||
|
for range N {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
r, err := db.GetRawBody(1024)
|
||||||
|
if err == errClosed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Error("err:", err)
|
||||||
|
}
|
||||||
|
if len(r) == 0 {
|
||||||
|
t.Error("empty body")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
db.Close()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
BIN
core/rawdb/eradb/testdata/sepolia-00000-643a00f7.era1
vendored
Normal file
BIN
core/rawdb/eradb/testdata/sepolia-00000-643a00f7.era1
vendored
Normal file
Binary file not shown.
BIN
core/rawdb/eradb/testdata/sepolia-00021-b8814b14.era1
vendored
Normal file
BIN
core/rawdb/eradb/testdata/sepolia-00021-b8814b14.era1
vendored
Normal file
Binary file not shown.
|
|
@ -172,10 +172,7 @@ func (f *Freezer) Close() error {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if errs != nil {
|
return errors.Join(errs...)
|
||||||
return fmt.Errorf("%v", errs)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AncientDatadir returns the path of the ancient store.
|
// AncientDatadir returns the path of the ancient store.
|
||||||
|
|
@ -183,15 +180,6 @@ func (f *Freezer) AncientDatadir() (string, error) {
|
||||||
return f.datadir, nil
|
return f.datadir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAncient returns an indicator whether the specified ancient data exists
|
|
||||||
// in the freezer.
|
|
||||||
func (f *Freezer) HasAncient(kind string, number uint64) (bool, error) {
|
|
||||||
if table := f.tables[kind]; table != nil {
|
|
||||||
return table.has(number), nil
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ancient retrieves an ancient binary blob from the append-only immutable files.
|
// Ancient retrieves an ancient binary blob from the append-only immutable files.
|
||||||
func (f *Freezer) Ancient(kind string, number uint64) ([]byte, error) {
|
func (f *Freezer) Ancient(kind string, number uint64) ([]byte, error) {
|
||||||
if table := f.tables[kind]; table != nil {
|
if table := f.tables[kind]; table != nil {
|
||||||
|
|
|
||||||
|
|
@ -45,14 +45,6 @@ func newMemoryTable(name string, config freezerTableConfig) *memoryTable {
|
||||||
return &memoryTable{name: name, config: config}
|
return &memoryTable{name: name, config: config}
|
||||||
}
|
}
|
||||||
|
|
||||||
// has returns an indicator whether the specified data exists.
|
|
||||||
func (t *memoryTable) has(number uint64) bool {
|
|
||||||
t.lock.RLock()
|
|
||||||
defer t.lock.RUnlock()
|
|
||||||
|
|
||||||
return number >= t.offset && number < t.items
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve retrieves multiple items in sequence, starting from the index 'start'.
|
// retrieve retrieves multiple items in sequence, starting from the index 'start'.
|
||||||
// It will return:
|
// It will return:
|
||||||
// - at most 'count' items,
|
// - at most 'count' items,
|
||||||
|
|
@ -232,17 +224,6 @@ func NewMemoryFreezer(readonly bool, tableName map[string]freezerTableConfig) *M
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAncient returns an indicator whether the specified data exists.
|
|
||||||
func (f *MemoryFreezer) HasAncient(kind string, number uint64) (bool, error) {
|
|
||||||
f.lock.RLock()
|
|
||||||
defer f.lock.RUnlock()
|
|
||||||
|
|
||||||
if table := f.tables[kind]; table != nil {
|
|
||||||
return table.has(number), nil
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ancient retrieves an ancient binary blob from the in-memory freezer.
|
// Ancient retrieves an ancient binary blob from the in-memory freezer.
|
||||||
func (f *MemoryFreezer) Ancient(kind string, number uint64) ([]byte, error) {
|
func (f *MemoryFreezer) Ancient(kind string, number uint64) ([]byte, error) {
|
||||||
f.lock.RLock()
|
f.lock.RLock()
|
||||||
|
|
|
||||||
|
|
@ -105,15 +105,6 @@ func (f *resettableFreezer) Close() error {
|
||||||
return f.freezer.Close()
|
return f.freezer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAncient returns an indicator whether the specified ancient data exists
|
|
||||||
// in the freezer
|
|
||||||
func (f *resettableFreezer) HasAncient(kind string, number uint64) (bool, error) {
|
|
||||||
f.lock.RLock()
|
|
||||||
defer f.lock.RUnlock()
|
|
||||||
|
|
||||||
return f.freezer.HasAncient(kind, number)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ancient retrieves an ancient binary blob from the append-only immutable files.
|
// Ancient retrieves an ancient binary blob from the append-only immutable files.
|
||||||
func (f *resettableFreezer) Ancient(kind string, number uint64) ([]byte, error) {
|
func (f *resettableFreezer) Ancient(kind string, number uint64) ([]byte, error) {
|
||||||
f.lock.RLock()
|
f.lock.RLock()
|
||||||
|
|
|
||||||
|
|
@ -1106,12 +1106,6 @@ func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []i
|
||||||
return output, sizes, nil
|
return output, sizes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// has returns an indicator whether the specified number data is still accessible
|
|
||||||
// in the freezer table.
|
|
||||||
func (t *freezerTable) has(number uint64) bool {
|
|
||||||
return t.items.Load() > number && t.itemHidden.Load() <= number
|
|
||||||
}
|
|
||||||
|
|
||||||
// size returns the total data size in the freezer table.
|
// size returns the total data size in the freezer table.
|
||||||
func (t *freezerTable) size() (uint64, error) {
|
func (t *freezerTable) size() (uint64, error) {
|
||||||
t.lock.RLock()
|
t.lock.RLock()
|
||||||
|
|
|
||||||
|
|
@ -357,9 +357,6 @@ func checkAncientCount(t *testing.T, f *Freezer, kind string, n uint64) {
|
||||||
// Check at index n-1.
|
// Check at index n-1.
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
index := n - 1
|
index := n - 1
|
||||||
if ok, _ := f.HasAncient(kind, index); !ok {
|
|
||||||
t.Errorf("HasAncient(%q, %d) returned false unexpectedly", kind, index)
|
|
||||||
}
|
|
||||||
if _, err := f.Ancient(kind, index); err != nil {
|
if _, err := f.Ancient(kind, index); err != nil {
|
||||||
t.Errorf("Ancient(%q, %d) returned unexpected error %q", kind, index, err)
|
t.Errorf("Ancient(%q, %d) returned unexpected error %q", kind, index, err)
|
||||||
}
|
}
|
||||||
|
|
@ -367,9 +364,6 @@ func checkAncientCount(t *testing.T, f *Freezer, kind string, n uint64) {
|
||||||
|
|
||||||
// Check at index n.
|
// Check at index n.
|
||||||
index := n
|
index := n
|
||||||
if ok, _ := f.HasAncient(kind, index); ok {
|
|
||||||
t.Errorf("HasAncient(%q, %d) returned true unexpectedly", kind, index)
|
|
||||||
}
|
|
||||||
if _, err := f.Ancient(kind, index); err == nil {
|
if _, err := f.Ancient(kind, index); err == nil {
|
||||||
t.Errorf("Ancient(%q, %d) didn't return expected error", kind, index)
|
t.Errorf("Ancient(%q, %d) didn't return expected error", kind, index)
|
||||||
} else if err != errOutOfBounds {
|
} else if err != errOutOfBounds {
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,6 @@ func (t *table) Get(key []byte) ([]byte, error) {
|
||||||
return t.db.Get(append([]byte(t.prefix), key...))
|
return t.db.Get(append([]byte(t.prefix), key...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAncient is a noop passthrough that just forwards the request to the underlying
|
|
||||||
// database.
|
|
||||||
func (t *table) HasAncient(kind string, number uint64) (bool, error) {
|
|
||||||
return t.db.HasAncient(kind, number)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ancient is a noop passthrough that just forwards the request to the underlying
|
// Ancient is a noop passthrough that just forwards the request to the underlying
|
||||||
// database.
|
// database.
|
||||||
func (t *table) Ancient(kind string, number uint64) ([]byte, error) {
|
func (t *table) Ancient(kind string, number uint64) ([]byte, error) {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"maps"
|
"maps"
|
||||||
|
gomath "math"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
@ -92,97 +93,94 @@ func (ae *AccessEvents) Copy() *AccessEvents {
|
||||||
|
|
||||||
// AddAccount returns the gas to be charged for each of the currently cold
|
// AddAccount returns the gas to be charged for each of the currently cold
|
||||||
// member fields of an account.
|
// member fields of an account.
|
||||||
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
var gas uint64 // accumulate the consumed gas
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
consumed, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
if consumed < expected {
|
||||||
|
return expected
|
||||||
|
}
|
||||||
|
gas += consumed
|
||||||
|
consumed, expected = ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, availableGas-consumed)
|
||||||
|
if consumed < expected {
|
||||||
|
return expected + gas
|
||||||
|
}
|
||||||
|
gas += expected
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageCallGas returns the gas to be charged for each of the currently
|
// MessageCallGas returns the gas to be charged for each of the currently
|
||||||
// cold member fields of an account, that need to be touched when making a message
|
// cold member fields of an account, that need to be touched when making a message
|
||||||
// call to that account.
|
// call to that account.
|
||||||
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
func (ae *AccessEvents) MessageCallGas(destination common.Address, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
_, expected := ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
if expected == 0 {
|
||||||
return gas
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValueTransferGas returns the gas to be charged for each of the currently
|
// ValueTransferGas returns the gas to be charged for each of the currently
|
||||||
// cold balance member fields of the caller and the callee accounts.
|
// cold balance member fields of the caller and the callee accounts.
|
||||||
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
|
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
_, expected1 := ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
if expected1 > availableGas {
|
||||||
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
return expected1
|
||||||
return gas
|
}
|
||||||
|
_, expected2 := ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas-expected1)
|
||||||
|
if expected1+expected2 == 0 {
|
||||||
|
return params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected1 + expected2
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCreatePreCheckGas charges access costs before
|
// ContractCreatePreCheckGas charges access costs before
|
||||||
// a contract creation is initiated. It is just reads, because the
|
// a contract creation is initiated. It is just reads, because the
|
||||||
// address collision is done before the transfer, and so no write
|
// address collision is done before the transfer, and so no write
|
||||||
// are guaranteed to happen at this point.
|
// are guaranteed to happen at this point.
|
||||||
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address) uint64 {
|
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
consumed, expected1 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
_, expected2 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, availableGas-consumed)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
return expected1 + expected2
|
||||||
return gas
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCreateInitGas returns the access gas costs for the initialization of
|
// ContractCreateInitGas returns the access gas costs for the initialization of
|
||||||
// a contract creation.
|
// a contract creation.
|
||||||
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address) uint64 {
|
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, availableGas uint64) (uint64, uint64) {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
consumed, expected1 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true)
|
gas += consumed
|
||||||
return gas
|
consumed, expected2 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true, availableGas-consumed)
|
||||||
|
gas += consumed
|
||||||
|
return gas, expected1 + expected2
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
||||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, gomath.MaxUint64)
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, gomath.MaxUint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTxDestination adds the member fields of the sender account to the access event list,
|
// AddTxDestination adds the member fields of the sender account to the access event list,
|
||||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
|
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue, doesntExist bool) {
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue)
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, gomath.MaxUint64)
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, doesntExist, gomath.MaxUint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
||||||
func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool) uint64 {
|
func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||||
treeIndex, subIndex := utils.StorageIndex(slot.Bytes())
|
treeIndex, subIndex := utils.StorageIndex(slot.Bytes())
|
||||||
return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite)
|
_, expected := ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, availableGas)
|
||||||
|
if expected == 0 && chargeWarmCosts {
|
||||||
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
||||||
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold
|
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the
|
||||||
// access cost to be charged, if need be.
|
// consumed and required gas.
|
||||||
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 {
|
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool, availableGas uint64) (uint64, uint64) {
|
||||||
stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite)
|
|
||||||
|
|
||||||
var gas uint64
|
|
||||||
if stemRead {
|
|
||||||
gas += params.WitnessBranchReadCost
|
|
||||||
}
|
|
||||||
if selectorRead {
|
|
||||||
gas += params.WitnessChunkReadCost
|
|
||||||
}
|
|
||||||
if stemWrite {
|
|
||||||
gas += params.WitnessBranchWriteCost
|
|
||||||
}
|
|
||||||
if selectorWrite {
|
|
||||||
gas += params.WitnessChunkWriteCost
|
|
||||||
}
|
|
||||||
if selectorFill {
|
|
||||||
gas += params.WitnessChunkFillCost
|
|
||||||
}
|
|
||||||
return gas
|
|
||||||
}
|
|
||||||
|
|
||||||
// touchAddress adds any missing access event to the access event list.
|
|
||||||
func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) {
|
|
||||||
branchKey := newBranchAccessKey(addr, treeIndex)
|
branchKey := newBranchAccessKey(addr, treeIndex)
|
||||||
chunkKey := newChunkAccessKey(branchKey, subIndex)
|
chunkKey := newChunkAccessKey(branchKey, subIndex)
|
||||||
|
|
||||||
|
|
@ -190,11 +188,9 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int,
|
||||||
var branchRead, chunkRead bool
|
var branchRead, chunkRead bool
|
||||||
if _, hasStem := ae.branches[branchKey]; !hasStem {
|
if _, hasStem := ae.branches[branchKey]; !hasStem {
|
||||||
branchRead = true
|
branchRead = true
|
||||||
ae.branches[branchKey] = AccessWitnessReadFlag
|
|
||||||
}
|
}
|
||||||
if _, hasSelector := ae.chunks[chunkKey]; !hasSelector {
|
if _, hasSelector := ae.chunks[chunkKey]; !hasSelector {
|
||||||
chunkRead = true
|
chunkRead = true
|
||||||
ae.chunks[chunkKey] = AccessWitnessReadFlag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write access.
|
// Write access.
|
||||||
|
|
@ -202,17 +198,51 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int,
|
||||||
if isWrite {
|
if isWrite {
|
||||||
if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
|
if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
|
||||||
branchWrite = true
|
branchWrite = true
|
||||||
ae.branches[branchKey] |= AccessWitnessWriteFlag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkValue := ae.chunks[chunkKey]
|
chunkValue := ae.chunks[chunkKey]
|
||||||
if (chunkValue & AccessWitnessWriteFlag) == 0 {
|
if (chunkValue & AccessWitnessWriteFlag) == 0 {
|
||||||
chunkWrite = true
|
chunkWrite = true
|
||||||
ae.chunks[chunkKey] |= AccessWitnessWriteFlag
|
|
||||||
}
|
}
|
||||||
// TODO: charge chunk filling costs if the leaf was previously empty in the state
|
|
||||||
}
|
}
|
||||||
return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill
|
|
||||||
|
var gas uint64
|
||||||
|
if branchRead {
|
||||||
|
gas += params.WitnessBranchReadCost
|
||||||
|
}
|
||||||
|
if chunkRead {
|
||||||
|
gas += params.WitnessChunkReadCost
|
||||||
|
}
|
||||||
|
if branchWrite {
|
||||||
|
gas += params.WitnessBranchWriteCost
|
||||||
|
}
|
||||||
|
if chunkWrite {
|
||||||
|
gas += params.WitnessChunkWriteCost
|
||||||
|
}
|
||||||
|
if chunkFill {
|
||||||
|
gas += params.WitnessChunkFillCost
|
||||||
|
}
|
||||||
|
|
||||||
|
if availableGas < gas {
|
||||||
|
// consumed != expected
|
||||||
|
return availableGas, gas
|
||||||
|
}
|
||||||
|
|
||||||
|
if branchRead {
|
||||||
|
ae.branches[branchKey] = AccessWitnessReadFlag
|
||||||
|
}
|
||||||
|
if branchWrite {
|
||||||
|
ae.branches[branchKey] |= AccessWitnessWriteFlag
|
||||||
|
}
|
||||||
|
if chunkRead {
|
||||||
|
ae.chunks[chunkKey] = AccessWitnessReadFlag
|
||||||
|
}
|
||||||
|
if chunkWrite {
|
||||||
|
ae.chunks[chunkKey] |= AccessWitnessWriteFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumed == expected
|
||||||
|
return gas, gas
|
||||||
}
|
}
|
||||||
|
|
||||||
type branchAccessKey struct {
|
type branchAccessKey struct {
|
||||||
|
|
@ -240,7 +270,7 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
|
// CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
|
||||||
func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool) uint64 {
|
func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool, availableGas uint64) (uint64, uint64) {
|
||||||
// note that in the case where the copied code is outside the range of the
|
// note that in the case where the copied code is outside the range of the
|
||||||
// contract code but touches the last leaf with contract code in it,
|
// contract code but touches the last leaf with contract code in it,
|
||||||
// we don't include the last leaf of code in the AccessWitness. The
|
// we don't include the last leaf of code in the AccessWitness. The
|
||||||
|
|
@ -248,7 +278,7 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
||||||
// is already in the AccessWitness so a stateless verifier can see that
|
// is already in the AccessWitness so a stateless verifier can see that
|
||||||
// the code from the last leaf is not needed.
|
// the code from the last leaf is not needed.
|
||||||
if (codeLen == 0 && size == 0) || startPC > codeLen {
|
if (codeLen == 0 && size == 0) || startPC > codeLen {
|
||||||
return 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
endPC := startPC + size
|
endPC := startPC + size
|
||||||
|
|
@ -263,22 +293,34 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
||||||
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
|
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
|
||||||
treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
|
treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
|
||||||
subIndex := byte((chunkNumber + 128) % 256)
|
subIndex := byte((chunkNumber + 128) % 256)
|
||||||
gas := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite)
|
consumed, expected := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, availableGas)
|
||||||
|
// did we OOG ?
|
||||||
|
if expected > consumed {
|
||||||
|
return statelessGasCharged + consumed, statelessGasCharged + expected
|
||||||
|
}
|
||||||
var overflow bool
|
var overflow bool
|
||||||
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
|
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, consumed)
|
||||||
if overflow {
|
if overflow {
|
||||||
panic("overflow when adding gas")
|
panic("overflow when adding gas")
|
||||||
}
|
}
|
||||||
|
availableGas -= consumed
|
||||||
}
|
}
|
||||||
return statelessGasCharged
|
return statelessGasCharged, statelessGasCharged
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicDataGas adds the account's basic data to the accessed data, and returns the
|
// BasicDataGas adds the account's basic data to the accessed data, and returns the
|
||||||
// amount of gas that it costs.
|
// amount of gas that it costs.
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an
|
// Note that an access in write mode implies an access in read mode, whereas an
|
||||||
// access in read mode does not imply an access in write mode.
|
// access in read mode does not imply an access in write mode.
|
||||||
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
_, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, availableGas)
|
||||||
|
if expected == 0 && chargeWarmCosts {
|
||||||
|
if availableGas < params.WarmStorageReadCostEIP2929 {
|
||||||
|
return availableGas
|
||||||
|
}
|
||||||
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
||||||
|
|
@ -286,6 +328,13 @@ func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
||||||
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||||
// read mode does not imply an access in write mode.
|
// read mode does not imply an access in write mode.
|
||||||
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
_, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, availableGas)
|
||||||
|
if expected == 0 && chargeWarmCosts {
|
||||||
|
if availableGas < params.WarmStorageReadCostEIP2929 {
|
||||||
|
return availableGas
|
||||||
|
}
|
||||||
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -40,50 +41,50 @@ func TestAccountHeaderGas(t *testing.T) {
|
||||||
ae := NewAccessEvents(utils.NewPointCache(1024))
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
// Check cold read cost
|
// Check cold read cost
|
||||||
gas := ae.BasicDataGas(testAddr, false)
|
gas := ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.BasicDataGas(testAddr, false)
|
gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold read costs in the same group no longer incur the branch read cost
|
// Check cold read costs in the same group no longer incur the branch read cost
|
||||||
gas = ae.CodeHashGas(testAddr, false)
|
gas = ae.CodeHashGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold write cost
|
// Check cold write cost
|
||||||
gas = ae.BasicDataGas(testAddr, true)
|
gas = ae.BasicDataGas(testAddr, true, math.MaxUint64, false)
|
||||||
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm write cost
|
// Check warm write cost
|
||||||
gas = ae.BasicDataGas(testAddr, true)
|
gas = ae.BasicDataGas(testAddr, true, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check a write without a read charges both read and write costs
|
// Check a write without a read charges both read and write costs
|
||||||
gas = ae.BasicDataGas(testAddr2, true)
|
gas = ae.BasicDataGas(testAddr2, true, math.MaxUint64, false)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that a write followed by a read charges nothing
|
// Check that a write followed by a read charges nothing
|
||||||
gas = ae.BasicDataGas(testAddr2, false)
|
gas = ae.BasicDataGas(testAddr2, false, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that reading a slot from the account header only charges the
|
// Check that reading a slot from the account header only charges the
|
||||||
// chunk read cost.
|
// chunk read cost.
|
||||||
gas = ae.SlotGas(testAddr, common.Hash{}, false)
|
gas = ae.SlotGas(testAddr, common.Hash{}, false, math.MaxUint64, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
}
|
}
|
||||||
|
|
@ -100,13 +101,13 @@ func TestContractCreateInitGas(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold read cost, without a value
|
// Check cold read cost, without a value
|
||||||
gas := ae.ContractCreateInitGas(testAddr)
|
gas, _ := ae.ContractCreateInitGas(testAddr, math.MaxUint64)
|
||||||
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + 2*params.WitnessChunkWriteCost + 2*params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + 2*params.WitnessChunkWriteCost + 2*params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.ContractCreateInitGas(testAddr)
|
gas, _ = ae.ContractCreateInitGas(testAddr, math.MaxUint64)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
@ -118,24 +119,24 @@ func TestMessageCallGas(t *testing.T) {
|
||||||
ae := NewAccessEvents(utils.NewPointCache(1024))
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
// Check cold read cost, without a value
|
// Check cold read cost, without a value
|
||||||
gas := ae.MessageCallGas(testAddr)
|
gas := ae.MessageCallGas(testAddr, math.MaxUint64)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that reading the basic data and code hash of the same account does not incur the branch read cost
|
// Check that reading the basic data and code hash of the same account does not incur the branch read cost
|
||||||
gas = ae.BasicDataGas(testAddr, false)
|
gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
gas = ae.CodeHashGas(testAddr, false)
|
gas = ae.CodeHashGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.MessageCallGas(testAddr)
|
gas = ae.MessageCallGas(testAddr, math.MaxUint64)
|
||||||
if gas != 0 {
|
if gas != params.WarmStorageReadCostEIP2929 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WarmStorageReadCostEIP2929)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Number of codehash->size associations to keep.
|
// Number of codehash->size associations to keep.
|
||||||
codeSizeCacheSize = 100000
|
codeSizeCacheSize = 1_000_000 // 4 megabytes in total
|
||||||
|
|
||||||
// Cache size granted for caching clean code.
|
// Cache size granted for caching clean code.
|
||||||
codeCacheSize = 64 * 1024 * 1024
|
codeCacheSize = 256 * 1024 * 1024
|
||||||
|
|
||||||
// Number of address->curve point associations to keep.
|
// Number of address->curve point associations to keep.
|
||||||
pointCacheSize = 4096
|
pointCacheSize = 4096
|
||||||
|
|
@ -175,26 +175,27 @@ func NewDatabaseForTesting() *CachingDB {
|
||||||
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||||
var readers []StateReader
|
var readers []StateReader
|
||||||
|
|
||||||
// Set up the state snapshot reader if available. This feature
|
// Configure the state reader using the standalone snapshot in hash mode.
|
||||||
// is optional and may be partially useful if it's not fully
|
// This reader offers improved performance but is optional and only
|
||||||
// generated.
|
// partially useful if the snapshot is not fully generated.
|
||||||
if db.snap != nil {
|
if db.TrieDB().Scheme() == rawdb.HashScheme && db.snap != nil {
|
||||||
// If standalone state snapshot is available (hash scheme),
|
|
||||||
// then construct the legacy snap reader.
|
|
||||||
snap := db.snap.Snapshot(stateRoot)
|
snap := db.snap.Snapshot(stateRoot)
|
||||||
if snap != nil {
|
if snap != nil {
|
||||||
readers = append(readers, newFlatReader(snap))
|
readers = append(readers, newFlatReader(snap))
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// If standalone state snapshot is not available, try to construct
|
// Configure the state reader using the path database in path mode.
|
||||||
// the state reader with database.
|
// 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)
|
reader, err := db.triedb.StateReader(stateRoot)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
readers = append(readers, newFlatReader(reader)) // state reader is optional
|
readers = append(readers, newFlatReader(reader))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set up the trie reader, which is expected to always be available
|
// Configure the trie reader, which is expected to be available as the
|
||||||
// as the gatekeeper unless the state is corrupted.
|
// gatekeeper unless the state is corrupted.
|
||||||
tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache)
|
tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -208,6 +209,15 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||||
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil
|
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReaderWithCache creates a state reader with internal local cache.
|
||||||
|
func (db *CachingDB) ReaderWithCache(stateRoot common.Hash) (Reader, error) {
|
||||||
|
reader, err := db.Reader(stateRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newReaderWithCache(reader), nil
|
||||||
|
}
|
||||||
|
|
||||||
// OpenTrie opens the main account trie at a specific root hash.
|
// OpenTrie opens the main account trie at a specific root hash.
|
||||||
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||||
if db.triedb.IsVerkle() {
|
if db.triedb.IsVerkle() {
|
||||||
|
|
|
||||||
|
|
@ -408,6 +408,7 @@ func (ch storageChange) copy() journalEntry {
|
||||||
account: ch.account,
|
account: ch.account,
|
||||||
key: ch.key,
|
key: ch.key,
|
||||||
prevvalue: ch.prevvalue,
|
prevvalue: ch.prevvalue,
|
||||||
|
origvalue: ch.origvalue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/lru"
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
|
|
@ -51,6 +52,9 @@ type ContractCodeReader interface {
|
||||||
|
|
||||||
// StateReader defines the interface for accessing accounts and storage slots
|
// StateReader defines the interface for accessing accounts and storage slots
|
||||||
// associated with a specific state.
|
// associated with a specific state.
|
||||||
|
//
|
||||||
|
// StateReader is assumed to be thread-safe and implementation must take care
|
||||||
|
// of the concurrency issue by themselves.
|
||||||
type StateReader interface {
|
type StateReader interface {
|
||||||
// Account retrieves the account associated with a particular address.
|
// Account retrieves the account associated with a particular address.
|
||||||
//
|
//
|
||||||
|
|
@ -70,6 +74,9 @@ type StateReader interface {
|
||||||
|
|
||||||
// Reader defines the interface for accessing accounts, storage slots and contract
|
// Reader defines the interface for accessing accounts, storage slots and contract
|
||||||
// code associated with a specific state.
|
// code associated with a specific state.
|
||||||
|
//
|
||||||
|
// Reader is assumed to be thread-safe and implementation must take care of the
|
||||||
|
// concurrency issue by themselves.
|
||||||
type Reader interface {
|
type Reader interface {
|
||||||
ContractCodeReader
|
ContractCodeReader
|
||||||
StateReader
|
StateReader
|
||||||
|
|
@ -77,6 +84,8 @@ type Reader interface {
|
||||||
|
|
||||||
// cachingCodeReader implements ContractCodeReader, accessing contract code either in
|
// cachingCodeReader implements ContractCodeReader, accessing contract code either in
|
||||||
// local key-value store or the shared code cache.
|
// local key-value store or the shared code cache.
|
||||||
|
//
|
||||||
|
// cachingCodeReader is safe for concurrent access.
|
||||||
type cachingCodeReader struct {
|
type cachingCodeReader struct {
|
||||||
db ethdb.KeyValueReader
|
db ethdb.KeyValueReader
|
||||||
|
|
||||||
|
|
@ -123,18 +132,14 @@ func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash)
|
||||||
return len(code), nil
|
return len(code), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// flatReader wraps a database state reader.
|
// flatReader wraps a database state reader and is safe for concurrent access.
|
||||||
type flatReader struct {
|
type flatReader struct {
|
||||||
reader database.StateReader
|
reader database.StateReader
|
||||||
buff crypto.KeccakState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFlatReader constructs a state reader with on the given state root.
|
// newFlatReader constructs a state reader with on the given state root.
|
||||||
func newFlatReader(reader database.StateReader) *flatReader {
|
func newFlatReader(reader database.StateReader) *flatReader {
|
||||||
return &flatReader{
|
return &flatReader{reader: reader}
|
||||||
reader: reader,
|
|
||||||
buff: crypto.NewKeccakState(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account implements StateReader, retrieving the account specified by the address.
|
// Account implements StateReader, retrieving the account specified by the address.
|
||||||
|
|
@ -144,7 +149,7 @@ func newFlatReader(reader database.StateReader) *flatReader {
|
||||||
//
|
//
|
||||||
// The returned account might be nil if it's not existent.
|
// The returned account might be nil if it's not existent.
|
||||||
func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
account, err := r.reader.Account(crypto.HashData(r.buff, addr.Bytes()))
|
account, err := r.reader.Account(crypto.Keccak256Hash(addr.Bytes()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -174,8 +179,8 @@ func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
//
|
//
|
||||||
// The returned storage slot might be empty if it's not existent.
|
// The returned storage slot might be empty if it's not existent.
|
||||||
func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||||
addrHash := crypto.HashData(r.buff, addr.Bytes())
|
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||||
slotHash := crypto.HashData(r.buff, key.Bytes())
|
slotHash := crypto.Keccak256Hash(key.Bytes())
|
||||||
ret, err := r.reader.Storage(addrHash, slotHash)
|
ret, err := r.reader.Storage(addrHash, slotHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
|
|
@ -196,13 +201,20 @@ func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
||||||
|
|
||||||
// trieReader implements the StateReader interface, providing functions to access
|
// trieReader implements the StateReader interface, providing functions to access
|
||||||
// state from the referenced trie.
|
// state from the referenced trie.
|
||||||
|
//
|
||||||
|
// trieReader is safe for concurrent read.
|
||||||
type trieReader struct {
|
type trieReader struct {
|
||||||
root common.Hash // State root which uniquely represent a state
|
root common.Hash // State root which uniquely represent a state
|
||||||
db *triedb.Database // Database for loading trie
|
db *triedb.Database // Database for loading trie
|
||||||
buff crypto.KeccakState // Buffer for keccak256 hashing
|
buff crypto.KeccakState // Buffer for keccak256 hashing
|
||||||
mainTrie Trie // Main trie, resolved in constructor
|
|
||||||
|
// Main trie, resolved in constructor. Note either the Merkle-Patricia-tree
|
||||||
|
// or Verkle-tree is not safe for concurrent read.
|
||||||
|
mainTrie Trie
|
||||||
|
|
||||||
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved
|
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved
|
||||||
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved
|
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved
|
||||||
|
lock sync.Mutex // Lock for protecting concurrent read
|
||||||
}
|
}
|
||||||
|
|
||||||
// trieReader constructs a trie reader of the specific state. An error will be
|
// trieReader constructs a trie reader of the specific state. An error will be
|
||||||
|
|
@ -230,11 +242,8 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account implements StateReader, retrieving the account specified by the address.
|
// account is the inner version of Account and assumes the r.lock is already held.
|
||||||
//
|
func (r *trieReader) account(addr common.Address) (*types.StateAccount, error) {
|
||||||
// An error will be returned if the trie state is corrupted. An nil account
|
|
||||||
// will be returned if it's not existent in the trie.
|
|
||||||
func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
|
||||||
account, err := r.mainTrie.GetAccount(addr)
|
account, err := r.mainTrie.GetAccount(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -247,12 +256,26 @@ func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Account implements StateReader, retrieving the account specified by the address.
|
||||||
|
//
|
||||||
|
// An error will be returned if the trie state is corrupted. An nil account
|
||||||
|
// will be returned if it's not existent in the trie.
|
||||||
|
func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
|
||||||
|
return r.account(addr)
|
||||||
|
}
|
||||||
|
|
||||||
// Storage implements StateReader, retrieving the storage slot specified by the
|
// Storage implements StateReader, retrieving the storage slot specified by the
|
||||||
// address and slot key.
|
// address and slot key.
|
||||||
//
|
//
|
||||||
// An error will be returned if the trie state is corrupted. An empty storage
|
// An error will be returned if the trie state is corrupted. An empty storage
|
||||||
// slot will be returned if it's not existent in the trie.
|
// slot will be returned if it's not existent in the trie.
|
||||||
func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tr Trie
|
tr Trie
|
||||||
found bool
|
found bool
|
||||||
|
|
@ -268,7 +291,7 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
||||||
// The storage slot is accessed without account caching. It's unexpected
|
// The storage slot is accessed without account caching. It's unexpected
|
||||||
// behavior but try to resolve the account first anyway.
|
// behavior but try to resolve the account first anyway.
|
||||||
if !ok {
|
if !ok {
|
||||||
_, err := r.Account(addr)
|
_, err := r.account(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -293,6 +316,9 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
||||||
// multiStateReader is the aggregation of a list of StateReader interface,
|
// multiStateReader is the aggregation of a list of StateReader interface,
|
||||||
// providing state access by leveraging all readers. The checking priority
|
// providing state access by leveraging all readers. The checking priority
|
||||||
// is determined by the position in the reader list.
|
// is determined by the position in the reader list.
|
||||||
|
//
|
||||||
|
// multiStateReader is safe for concurrent read and assumes all underlying
|
||||||
|
// readers are thread-safe as well.
|
||||||
type multiStateReader struct {
|
type multiStateReader struct {
|
||||||
readers []StateReader // List of state readers, sorted by checking priority
|
readers []StateReader // List of state readers, sorted by checking priority
|
||||||
}
|
}
|
||||||
|
|
@ -358,3 +384,95 @@ func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader {
|
||||||
StateReader: stateReader,
|
StateReader: stateReader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readerWithCache is a wrapper around Reader that maintains additional state caches
|
||||||
|
// to support concurrent state access.
|
||||||
|
type readerWithCache struct {
|
||||||
|
Reader // safe for concurrent read
|
||||||
|
|
||||||
|
// Previously resolved state entries.
|
||||||
|
accounts map[common.Address]*types.StateAccount
|
||||||
|
accountLock sync.RWMutex
|
||||||
|
|
||||||
|
// List of storage buckets, each of which is thread-safe.
|
||||||
|
// This reader is typically used in scenarios requiring concurrent
|
||||||
|
// access to storage. Using multiple buckets helps mitigate
|
||||||
|
// the overhead caused by locking.
|
||||||
|
storageBuckets [16]struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
storages map[common.Address]map[common.Hash]common.Hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newReaderWithCache constructs the reader with local cache.
|
||||||
|
func newReaderWithCache(reader Reader) *readerWithCache {
|
||||||
|
r := &readerWithCache{
|
||||||
|
Reader: reader,
|
||||||
|
accounts: make(map[common.Address]*types.StateAccount),
|
||||||
|
}
|
||||||
|
for i := range r.storageBuckets {
|
||||||
|
r.storageBuckets[i].storages = make(map[common.Address]map[common.Hash]common.Hash)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account implements StateReader, retrieving the account specified by the address.
|
||||||
|
// The returned account might be nil if it's not existent.
|
||||||
|
//
|
||||||
|
// An error will be returned if the state is corrupted in the underlying reader.
|
||||||
|
func (r *readerWithCache) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
|
// Try to resolve the requested account in the local cache
|
||||||
|
r.accountLock.RLock()
|
||||||
|
acct, ok := r.accounts[addr]
|
||||||
|
r.accountLock.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return acct, nil
|
||||||
|
}
|
||||||
|
// Try to resolve the requested account from the underlying reader
|
||||||
|
acct, err := r.Reader.Account(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r.accountLock.Lock()
|
||||||
|
r.accounts[addr] = acct
|
||||||
|
r.accountLock.Unlock()
|
||||||
|
return acct, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage implements StateReader, retrieving the storage slot specified by the
|
||||||
|
// address and slot key. The returned storage slot might be empty if it's not
|
||||||
|
// existent.
|
||||||
|
//
|
||||||
|
// An error will be returned if the state is corrupted in the underlying reader.
|
||||||
|
func (r *readerWithCache) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||||
|
var (
|
||||||
|
value common.Hash
|
||||||
|
ok bool
|
||||||
|
bucket = &r.storageBuckets[addr[0]&0x0f]
|
||||||
|
)
|
||||||
|
// Try to resolve the requested storage slot in the local cache
|
||||||
|
bucket.lock.RLock()
|
||||||
|
slots, ok := bucket.storages[addr]
|
||||||
|
if ok {
|
||||||
|
value, ok = slots[slot]
|
||||||
|
}
|
||||||
|
bucket.lock.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
// Try to resolve the requested storage slot from the underlying reader
|
||||||
|
value, err := r.Reader.Storage(addr, slot)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
bucket.lock.Lock()
|
||||||
|
slots, ok = bucket.storages[addr]
|
||||||
|
if !ok {
|
||||||
|
slots = make(map[common.Hash]common.Hash)
|
||||||
|
bucket.storages[addr] = slots
|
||||||
|
}
|
||||||
|
slots[slot] = value
|
||||||
|
bucket.lock.Unlock()
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,9 @@ func newHelper(scheme string) *testHelper {
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
config := &triedb.Config{}
|
config := &triedb.Config{}
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
config.PathDB = &pathdb.Config{} // disable caching
|
config.PathDB = &pathdb.Config{
|
||||||
|
SnapshotNoBuild: true,
|
||||||
|
} // disable caching
|
||||||
} else {
|
} else {
|
||||||
config.HashDB = &hashdb.Config{} // disable caching
|
config.HashDB = &hashdb.Config{} // disable caching
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -159,11 +159,17 @@ type StateDB struct {
|
||||||
|
|
||||||
// New creates a new state from a given trie.
|
// New creates a new state from a given trie.
|
||||||
func New(root common.Hash, db Database) (*StateDB, error) {
|
func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
tr, err := db.OpenTrie(root)
|
reader, err := db.Reader(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
reader, err := db.Reader(root)
|
return NewWithReader(root, db, reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithReader creates a new state for the specified state root. Unlike New,
|
||||||
|
// this function accepts an additional Reader which is bound to the given root.
|
||||||
|
func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, error) {
|
||||||
|
tr, err := db.OpenTrie(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -292,7 +298,7 @@ func (s *StateDB) SubRefund(gas uint64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exist reports whether the given account address exists in the state.
|
// Exist reports whether the given account address exists in the state.
|
||||||
// Notably this also returns true for self-destructed accounts.
|
// Notably this also returns true for self-destructed accounts within the current transaction.
|
||||||
func (s *StateDB) Exist(addr common.Address) bool {
|
func (s *StateDB) Exist(addr common.Address) bool {
|
||||||
return s.getStateObject(addr) != nil
|
return s.getStateObject(addr) != nil
|
||||||
}
|
}
|
||||||
|
|
@ -392,6 +398,12 @@ func (s *StateDB) Database() Database {
|
||||||
return s.db
|
return s.db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reader retrieves the low level database reader supporting the
|
||||||
|
// lower level operations.
|
||||||
|
func (s *StateDB) Reader() Reader {
|
||||||
|
return s.reader
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
|
func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
|
||||||
stateObject := s.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
|
@ -550,9 +562,8 @@ func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common
|
||||||
// updateStateObject writes the given object to the trie.
|
// updateStateObject writes the given object to the trie.
|
||||||
func (s *StateDB) updateStateObject(obj *stateObject) {
|
func (s *StateDB) updateStateObject(obj *stateObject) {
|
||||||
// Encode the account and update the account trie
|
// Encode the account and update the account trie
|
||||||
addr := obj.Address()
|
if err := s.trie.UpdateAccount(obj.Address(), &obj.data, len(obj.code)); err != nil {
|
||||||
if err := s.trie.UpdateAccount(addr, &obj.data, len(obj.code)); err != nil {
|
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", obj.Address(), err))
|
||||||
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err))
|
|
||||||
}
|
}
|
||||||
if obj.dirtyCode {
|
if obj.dirtyCode {
|
||||||
s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code)
|
s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code)
|
||||||
|
|
@ -650,11 +661,10 @@ func (s *StateDB) CreateContract(addr common.Address) {
|
||||||
// Snapshots of the copied state cannot be applied to the copy.
|
// Snapshots of the copied state cannot be applied to the copy.
|
||||||
func (s *StateDB) Copy() *StateDB {
|
func (s *StateDB) Copy() *StateDB {
|
||||||
// Copy all the basic fields, initialize the memory ones
|
// Copy all the basic fields, initialize the memory ones
|
||||||
reader, _ := s.db.Reader(s.originalRoot) // impossible to fail
|
|
||||||
state := &StateDB{
|
state := &StateDB{
|
||||||
db: s.db,
|
db: s.db,
|
||||||
trie: mustCopyTrie(s.trie),
|
trie: mustCopyTrie(s.trie),
|
||||||
reader: reader,
|
reader: s.reader,
|
||||||
originalRoot: s.originalRoot,
|
originalRoot: s.originalRoot,
|
||||||
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
||||||
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
||||||
|
|
|
||||||
|
|
@ -979,7 +979,8 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
|
||||||
)
|
)
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{
|
tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{
|
||||||
CleanCacheSize: 0,
|
TrieCleanSize: 0,
|
||||||
|
StateCleanSize: 0,
|
||||||
WriteBufferSize: 0,
|
WriteBufferSize: 0,
|
||||||
}}) // disable caching
|
}}) // disable caching
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,22 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"runtime"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// statePrefetcher is a basic Prefetcher, which blindly executes a block on top
|
// statePrefetcher is a basic Prefetcher that executes transactions from a block
|
||||||
// of an arbitrary state with the goal of prefetching potentially useful state
|
// on top of the parent state, aiming to prefetch potentially useful state data
|
||||||
// data from disk before the main block processor start executing.
|
// from disk. Transactions are executed in parallel to fully leverage the
|
||||||
|
// SSD's read performance.
|
||||||
type statePrefetcher struct {
|
type statePrefetcher struct {
|
||||||
config *params.ChainConfig // Chain configuration options
|
config *params.ChainConfig // Chain configuration options
|
||||||
chain *HeaderChain // Canonical block chain
|
chain *HeaderChain // Canonical block chain
|
||||||
|
|
@ -43,41 +48,81 @@ func newStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePr
|
||||||
|
|
||||||
// Prefetch processes the state changes according to the Ethereum rules by running
|
// Prefetch processes the state changes according to the Ethereum rules by running
|
||||||
// the transaction messages using the statedb, but any changes are discarded. The
|
// the transaction messages using the statedb, but any changes are discarded. The
|
||||||
// only goal is to pre-cache transaction signatures and state trie nodes.
|
// only goal is to warm the state caches.
|
||||||
func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) {
|
func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) {
|
||||||
var (
|
var (
|
||||||
header = block.Header()
|
fails atomic.Int64
|
||||||
gaspool = new(GasPool).AddGas(block.GasLimit())
|
header = block.Header()
|
||||||
blockContext = NewEVMBlockContext(header, p.chain, nil)
|
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||||
evm = vm.NewEVM(blockContext, statedb, p.config, cfg)
|
workers errgroup.Group
|
||||||
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
reader = statedb.Reader()
|
||||||
)
|
)
|
||||||
// Iterate over and process the individual transactions
|
workers.SetLimit(runtime.NumCPU() / 2)
|
||||||
byzantium := p.config.IsByzantium(block.Number())
|
|
||||||
for i, tx := range block.Transactions() {
|
|
||||||
// If block precaching was interrupted, abort
|
|
||||||
if interrupt != nil && interrupt.Load() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Convert the transaction into an executable message and pre-cache its sender
|
|
||||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
|
||||||
if err != nil {
|
|
||||||
return // Also invalid block, bail out
|
|
||||||
}
|
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
|
||||||
|
|
||||||
// We attempt to apply a transaction. The goal is not to execute
|
// Iterate over and process the individual transactions
|
||||||
// the transaction successfully, rather to warm up touched data slots.
|
for i, tx := range block.Transactions() {
|
||||||
if _, err := ApplyMessage(evm, msg, gaspool); err != nil {
|
stateCpy := statedb.Copy() // closure
|
||||||
return // Ugh, something went horribly wrong, bail out
|
workers.Go(func() error {
|
||||||
}
|
// If block precaching was interrupted, abort
|
||||||
// If we're pre-byzantium, pre-load trie nodes for the intermediate root
|
if interrupt != nil && interrupt.Load() {
|
||||||
if !byzantium {
|
return nil
|
||||||
statedb.IntermediateRoot(true)
|
}
|
||||||
}
|
// Preload the touched accounts and storage slots in advance
|
||||||
}
|
sender, err := types.Sender(signer, tx)
|
||||||
// If were post-byzantium, pre-load trie nodes for the final root hash
|
if err != nil {
|
||||||
if byzantium {
|
fails.Add(1)
|
||||||
statedb.IntermediateRoot(true)
|
return nil
|
||||||
|
}
|
||||||
|
reader.Account(sender)
|
||||||
|
|
||||||
|
if tx.To() != nil {
|
||||||
|
account, _ := reader.Account(*tx.To())
|
||||||
|
|
||||||
|
// Preload the contract code if the destination has non-empty code
|
||||||
|
if account != nil && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||||
|
reader.Code(*tx.To(), common.BytesToHash(account.CodeHash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, list := range tx.AccessList() {
|
||||||
|
reader.Account(list.Address)
|
||||||
|
if len(list.StorageKeys) > 0 {
|
||||||
|
for _, slot := range list.StorageKeys {
|
||||||
|
reader.Storage(list.Address, slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Execute the message to preload the implicit touched states
|
||||||
|
evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), stateCpy, p.config, cfg)
|
||||||
|
|
||||||
|
// Convert the transaction into an executable message and pre-cache its sender
|
||||||
|
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||||
|
if err != nil {
|
||||||
|
fails.Add(1)
|
||||||
|
return nil // Also invalid block, bail out
|
||||||
|
}
|
||||||
|
// Disable the nonce check
|
||||||
|
msg.SkipNonceChecks = true
|
||||||
|
|
||||||
|
stateCpy.SetTxContext(tx.Hash(), i)
|
||||||
|
|
||||||
|
// We attempt to apply a transaction. The goal is not to execute
|
||||||
|
// the transaction successfully, rather to warm up touched data slots.
|
||||||
|
if _, err := ApplyMessage(evm, msg, new(GasPool).AddGas(block.GasLimit())); err != nil {
|
||||||
|
fails.Add(1)
|
||||||
|
return nil // Ugh, something went horribly wrong, bail out
|
||||||
|
}
|
||||||
|
// Pre-load trie nodes for the intermediate root.
|
||||||
|
//
|
||||||
|
// This operation incurs significant memory allocations due to
|
||||||
|
// trie hashing and node decoding. TODO(rjl493456442): investigate
|
||||||
|
// ways to mitigate this overhead.
|
||||||
|
stateCpy.IntermediateRoot(true)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
workers.Wait()
|
||||||
|
|
||||||
|
blockPrefetchTxsValidMeter.Mark(int64(len(block.Transactions())) - fails.Load())
|
||||||
|
blockPrefetchTxsInvalidMeter.Mark(fails.Load())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,9 @@ type Message struct {
|
||||||
|
|
||||||
// When SkipNonceChecks is true, the message nonce is not checked against the
|
// When SkipNonceChecks is true, the message nonce is not checked against the
|
||||||
// account nonce in state.
|
// account nonce in state.
|
||||||
// This field will be set to true for operations like RPC eth_call.
|
//
|
||||||
|
// This field will be set to true for operations like RPC eth_call
|
||||||
|
// or the state prefetching.
|
||||||
SkipNonceChecks bool
|
SkipNonceChecks bool
|
||||||
|
|
||||||
// When SkipFromEOACheck is true, the message sender is not checked to be an EOA.
|
// When SkipFromEOACheck is true, the message sender is not checked to be an EOA.
|
||||||
|
|
@ -455,7 +457,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
||||||
|
|
||||||
if targetAddr := msg.To; targetAddr != nil {
|
if targetAddr := msg.To; targetAddr != nil {
|
||||||
st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0)
|
st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0, !st.state.Exist(*targetAddr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -550,7 +552,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
|
|
||||||
// add the coinbase to the witness iff the fee is greater than 0
|
// add the coinbase to the witness iff the fee is greater than 0
|
||||||
if rules.IsEIP4762 && fee.Sign() != 0 {
|
if rules.IsEIP4762 && fee.Sign() != 0 {
|
||||||
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true)
|
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true, math.MaxUint64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,8 @@ func TestTxIndexer(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...))
|
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...)))
|
||||||
|
|
||||||
// Index the initial blocks from ancient store
|
// Index the initial blocks from ancient store
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
|
|
@ -235,8 +235,9 @@ func TestTxIndexerRepair(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...))
|
encReceipts := types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...))
|
||||||
|
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), encReceipts)
|
||||||
|
|
||||||
// Index the initial blocks from ancient store
|
// Index the initial blocks from ancient store
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
|
|
@ -425,8 +426,9 @@ func TestTxIndexerReport(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...))
|
encReceipts := types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...))
|
||||||
|
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), encReceipts)
|
||||||
|
|
||||||
// Index the initial blocks from ancient store
|
// Index the initial blocks from ancient store
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,12 @@ const (
|
||||||
// limit can never hurt.
|
// limit can never hurt.
|
||||||
txMaxSize = 1024 * 1024
|
txMaxSize = 1024 * 1024
|
||||||
|
|
||||||
|
// maxBlobsPerTx is the maximum number of blobs that a single transaction can
|
||||||
|
// carry. We choose a smaller limit than the protocol-permitted MaxBlobsPerBlock
|
||||||
|
// in order to ensure network and txpool stability.
|
||||||
|
// Note: if you increase this, validation will fail on txMaxSize.
|
||||||
|
maxBlobsPerTx = 7
|
||||||
|
|
||||||
// maxTxsPerAccount is the maximum number of blob transactions admitted from
|
// maxTxsPerAccount is the maximum number of blob transactions admitted from
|
||||||
// a single account. The limit is enforced to minimize the DoS potential of
|
// a single account. The limit is enforced to minimize the DoS potential of
|
||||||
// a private tx cancelling publicly propagated blobs.
|
// a private tx cancelling publicly propagated blobs.
|
||||||
|
|
@ -1095,10 +1101,11 @@ func (p *BlobPool) SetGasTip(tip *big.Int) {
|
||||||
// and does not require the pool mutex to be held.
|
// and does not require the pool mutex to be held.
|
||||||
func (p *BlobPool) ValidateTxBasics(tx *types.Transaction) error {
|
func (p *BlobPool) ValidateTxBasics(tx *types.Transaction) error {
|
||||||
opts := &txpool.ValidationOptions{
|
opts := &txpool.ValidationOptions{
|
||||||
Config: p.chain.Config(),
|
Config: p.chain.Config(),
|
||||||
Accept: 1 << types.BlobTxType,
|
Accept: 1 << types.BlobTxType,
|
||||||
MaxSize: txMaxSize,
|
MaxSize: txMaxSize,
|
||||||
MinTip: p.gasTip.ToBig(),
|
MinTip: p.gasTip.ToBig(),
|
||||||
|
MaxBlobCount: maxBlobsPerTx,
|
||||||
}
|
}
|
||||||
return txpool.ValidateTransaction(tx, p.head, p.signer, opts)
|
return txpool.ValidateTransaction(tx, p.head, p.signer, opts)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1142,6 +1142,65 @@ func TestChangingSlotterSize(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBlobCountLimit tests the blobpool enforced limits on the max blob count.
|
||||||
|
func TestBlobCountLimit(t *testing.T) {
|
||||||
|
var (
|
||||||
|
key1, _ = crypto.GenerateKey()
|
||||||
|
key2, _ = crypto.GenerateKey()
|
||||||
|
|
||||||
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||||
|
)
|
||||||
|
|
||||||
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
|
statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
|
||||||
|
statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
|
||||||
|
statedb.Commit(0, true, false)
|
||||||
|
|
||||||
|
// Make Prague-enabled custom chain config.
|
||||||
|
cancunTime := uint64(0)
|
||||||
|
pragueTime := uint64(0)
|
||||||
|
config := ¶ms.ChainConfig{
|
||||||
|
ChainID: big.NewInt(1),
|
||||||
|
LondonBlock: big.NewInt(0),
|
||||||
|
BerlinBlock: big.NewInt(0),
|
||||||
|
CancunTime: &cancunTime,
|
||||||
|
PragueTime: &pragueTime,
|
||||||
|
BlobScheduleConfig: ¶ms.BlobScheduleConfig{
|
||||||
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chain := &testBlockChain{
|
||||||
|
config: config,
|
||||||
|
basefee: uint256.NewInt(1050),
|
||||||
|
blobfee: uint256.NewInt(105),
|
||||||
|
statedb: statedb,
|
||||||
|
}
|
||||||
|
pool := New(Config{Datadir: t.TempDir()}, chain, nil)
|
||||||
|
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
|
||||||
|
t.Fatalf("failed to create blob pool: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to add transactions.
|
||||||
|
var (
|
||||||
|
tx1 = makeMultiBlobTx(0, 1, 1000, 100, 7, key1)
|
||||||
|
tx2 = makeMultiBlobTx(0, 1, 800, 70, 8, key2)
|
||||||
|
)
|
||||||
|
errs := pool.Add([]*types.Transaction{tx1, tx2}, true)
|
||||||
|
|
||||||
|
// Check that first succeeds second fails.
|
||||||
|
if errs[0] != nil {
|
||||||
|
t.Fatalf("expected tx with 7 blobs to succeed")
|
||||||
|
}
|
||||||
|
if !errors.Is(errs[1], txpool.ErrTxBlobLimitExceeded) {
|
||||||
|
t.Fatalf("expected tx with 8 blobs to fail, got: %v", errs[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyPoolInternals(t, pool)
|
||||||
|
pool.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that adding transaction will correctly store it in the persistent store
|
// Tests that adding transaction will correctly store it in the persistent store
|
||||||
// and update all the indices.
|
// and update all the indices.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,10 @@ var (
|
||||||
// making the transaction invalid, rather a DOS protection.
|
// making the transaction invalid, rather a DOS protection.
|
||||||
ErrOversizedData = errors.New("oversized data")
|
ErrOversizedData = errors.New("oversized data")
|
||||||
|
|
||||||
|
// ErrTxBlobLimitExceeded is returned if a transaction would exceed the number
|
||||||
|
// of blobs allowed by blobpool.
|
||||||
|
ErrTxBlobLimitExceeded = errors.New("transaction blob limit exceeded")
|
||||||
|
|
||||||
// ErrAlreadyReserved is returned if the sender address has a pending transaction
|
// ErrAlreadyReserved is returned if the sender address has a pending transaction
|
||||||
// in a different subpool. For example, this error is returned in response to any
|
// in a different subpool. For example, this error is returned in response to any
|
||||||
// input transaction of non-blob type when a blob transaction from this sender
|
// input transaction of non-blob type when a blob transaction from this sender
|
||||||
|
|
|
||||||
|
|
@ -108,11 +108,33 @@ func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ec
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey, bytes uint64) *types.Transaction {
|
// pricedDataTransaction generates a signed transaction with fixed-size data,
|
||||||
data := make([]byte, bytes)
|
// and ensures that the resulting signature components (r and s) are exactly 32 bytes each,
|
||||||
crand.Read(data)
|
// producing transactions with deterministic size.
|
||||||
|
//
|
||||||
|
// This avoids variability in transaction size caused by leading zeros being omitted in
|
||||||
|
// RLP encoding of r/s. Since r and s are derived from ECDSA, they occasionally have leading
|
||||||
|
// zeros and thus can be shorter than 32 bytes.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// r: 0 leading zeros, bytesSize: 32, bytes: [221 ... 101]
|
||||||
|
// s: 1 leading zeros, bytesSize: 31, bytes: [0 75 ... 47]
|
||||||
|
func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey, dataBytes uint64) *types.Transaction {
|
||||||
|
var tx *types.Transaction
|
||||||
|
|
||||||
tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gaslimit, gasprice, data), types.HomesteadSigner{}, key)
|
// 10 attempts is statistically sufficient since leading zeros in ECDSA signatures are rare and randomly distributed.
|
||||||
|
var retryTimes = 10
|
||||||
|
for i := 0; i < retryTimes; i++ {
|
||||||
|
data := make([]byte, dataBytes)
|
||||||
|
crand.Read(data)
|
||||||
|
|
||||||
|
tx, _ = types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gaslimit, gasprice, data), types.HomesteadSigner{}, key)
|
||||||
|
_, r, s := tx.RawSignatureValues()
|
||||||
|
if len(r.Bytes()) == 32 && len(s.Bytes()) == 32 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1239,7 +1261,7 @@ func TestAllowedTxSize(t *testing.T) {
|
||||||
const largeDataLength = txMaxSize - 200 // enough to have a 5 bytes RLP encoding of the data length number
|
const largeDataLength = txMaxSize - 200 // enough to have a 5 bytes RLP encoding of the data length number
|
||||||
txWithLargeData := pricedDataTransaction(0, pool.currentHead.Load().GasLimit, big.NewInt(1), key, largeDataLength)
|
txWithLargeData := pricedDataTransaction(0, pool.currentHead.Load().GasLimit, big.NewInt(1), key, largeDataLength)
|
||||||
maxTxLengthWithoutData := txWithLargeData.Size() - largeDataLength // 103 bytes
|
maxTxLengthWithoutData := txWithLargeData.Size() - largeDataLength // 103 bytes
|
||||||
maxTxDataLength := txMaxSize - maxTxLengthWithoutData // 131072 - 103 = 130953 bytes
|
maxTxDataLength := txMaxSize - maxTxLengthWithoutData // 131072 - 103 = 130969 bytes
|
||||||
|
|
||||||
// Try adding a transaction with maximal allowed size
|
// Try adding a transaction with maximal allowed size
|
||||||
tx := pricedDataTransaction(0, pool.currentHead.Load().GasLimit, big.NewInt(1), key, maxTxDataLength)
|
tx := pricedDataTransaction(0, pool.currentHead.Load().GasLimit, big.NewInt(1), key, maxTxDataLength)
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,10 @@ var (
|
||||||
type ValidationOptions struct {
|
type ValidationOptions struct {
|
||||||
Config *params.ChainConfig // Chain configuration to selectively validate based on current fork rules
|
Config *params.ChainConfig // Chain configuration to selectively validate based on current fork rules
|
||||||
|
|
||||||
Accept uint8 // Bitmap of transaction types that should be accepted for the calling pool
|
Accept uint8 // Bitmap of transaction types that should be accepted for the calling pool
|
||||||
MaxSize uint64 // Maximum size of a transaction that the caller can meaningfully handle
|
MaxSize uint64 // Maximum size of a transaction that the caller can meaningfully handle
|
||||||
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
|
MaxBlobCount int // Maximum number of blobs allowed per transaction
|
||||||
|
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidationFunction is an method type which the pools use to perform the tx-validations which do not
|
// ValidationFunction is an method type which the pools use to perform the tx-validations which do not
|
||||||
|
|
@ -63,6 +64,9 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||||
if opts.Accept&(1<<tx.Type()) == 0 {
|
if opts.Accept&(1<<tx.Type()) == 0 {
|
||||||
return fmt.Errorf("%w: tx type %v not supported by this pool", core.ErrTxTypeNotSupported, tx.Type())
|
return fmt.Errorf("%w: tx type %v not supported by this pool", core.ErrTxTypeNotSupported, tx.Type())
|
||||||
}
|
}
|
||||||
|
if blobCount := len(tx.BlobHashes()); blobCount > opts.MaxBlobCount {
|
||||||
|
return fmt.Errorf("%w: blob count %v, limit %v", ErrTxBlobLimitExceeded, blobCount, opts.MaxBlobCount)
|
||||||
|
}
|
||||||
// Before performing any expensive validations, sanity check that the tx is
|
// Before performing any expensive validations, sanity check that the tx is
|
||||||
// smaller than the maximum limit the pool can meaningfully handle
|
// smaller than the maximum limit the pool can meaningfully handle
|
||||||
if tx.Size() > opts.MaxSize {
|
if tx.Size() > opts.MaxSize {
|
||||||
|
|
|
||||||
|
|
@ -38,17 +38,13 @@ type Account struct {
|
||||||
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
|
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
|
||||||
Balance *big.Int `json:"balance" gencodec:"required"`
|
Balance *big.Int `json:"balance" gencodec:"required"`
|
||||||
Nonce uint64 `json:"nonce,omitempty"`
|
Nonce uint64 `json:"nonce,omitempty"`
|
||||||
|
|
||||||
// used in tests
|
|
||||||
PrivateKey []byte `json:"secretKey,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type accountMarshaling struct {
|
type accountMarshaling struct {
|
||||||
Code hexutil.Bytes
|
Code hexutil.Bytes
|
||||||
Balance *math.HexOrDecimal256
|
Balance *math.HexOrDecimal256
|
||||||
Nonce math.HexOrDecimal64
|
Nonce math.HexOrDecimal64
|
||||||
Storage map[storageJSON]storageJSON
|
Storage map[storageJSON]storageJSON
|
||||||
PrivateKey hexutil.Bytes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
|
// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
|
||||||
|
|
|
||||||
|
|
@ -59,11 +59,12 @@ func (b *Bloom) SetBytes(d []byte) {
|
||||||
|
|
||||||
// Add adds d to the filter. Future calls of Test(d) will return true.
|
// Add adds d to the filter. Future calls of Test(d) will return true.
|
||||||
func (b *Bloom) Add(d []byte) {
|
func (b *Bloom) Add(d []byte) {
|
||||||
b.add(d, make([]byte, 6))
|
var buf [6]byte
|
||||||
|
b.AddWithBuffer(d, &buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// add is internal version of Add, which takes a scratch buffer for reuse (needs to be at least 6 bytes)
|
// add is internal version of Add, which takes a scratch buffer for reuse (needs to be at least 6 bytes)
|
||||||
func (b *Bloom) add(d []byte, buf []byte) {
|
func (b *Bloom) AddWithBuffer(d []byte, buf *[6]byte) {
|
||||||
i1, v1, i2, v2, i3, v3 := bloomValues(d, buf)
|
i1, v1, i2, v2, i3, v3 := bloomValues(d, buf)
|
||||||
b[i1] |= v1
|
b[i1] |= v1
|
||||||
b[i2] |= v2
|
b[i2] |= v2
|
||||||
|
|
@ -84,7 +85,8 @@ func (b Bloom) Bytes() []byte {
|
||||||
|
|
||||||
// Test checks if the given topic is present in the bloom filter
|
// Test checks if the given topic is present in the bloom filter
|
||||||
func (b Bloom) Test(topic []byte) bool {
|
func (b Bloom) Test(topic []byte) bool {
|
||||||
i1, v1, i2, v2, i3, v3 := bloomValues(topic, make([]byte, 6))
|
var buf [6]byte
|
||||||
|
i1, v1, i2, v2, i3, v3 := bloomValues(topic, &buf)
|
||||||
return v1 == v1&b[i1] &&
|
return v1 == v1&b[i1] &&
|
||||||
v2 == v2&b[i2] &&
|
v2 == v2&b[i2] &&
|
||||||
v3 == v3&b[i3]
|
v3 == v3&b[i3]
|
||||||
|
|
@ -104,12 +106,12 @@ func (b *Bloom) UnmarshalText(input []byte) error {
|
||||||
func CreateBloom(receipt *Receipt) Bloom {
|
func CreateBloom(receipt *Receipt) Bloom {
|
||||||
var (
|
var (
|
||||||
bin Bloom
|
bin Bloom
|
||||||
buf = make([]byte, 6)
|
buf [6]byte
|
||||||
)
|
)
|
||||||
for _, log := range receipt.Logs {
|
for _, log := range receipt.Logs {
|
||||||
bin.add(log.Address.Bytes(), buf)
|
bin.AddWithBuffer(log.Address.Bytes(), &buf)
|
||||||
for _, b := range log.Topics {
|
for _, b := range log.Topics {
|
||||||
bin.add(b[:], buf)
|
bin.AddWithBuffer(b[:], &buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bin
|
return bin
|
||||||
|
|
@ -139,21 +141,20 @@ func Bloom9(data []byte) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// bloomValues returns the bytes (index-value pairs) to set for the given data
|
// bloomValues returns the bytes (index-value pairs) to set for the given data
|
||||||
func bloomValues(data []byte, hashbuf []byte) (uint, byte, uint, byte, uint, byte) {
|
func bloomValues(data []byte, hashbuf *[6]byte) (uint, byte, uint, byte, uint, byte) {
|
||||||
sha := hasherPool.Get().(crypto.KeccakState)
|
sha := hasherPool.Get().(crypto.KeccakState)
|
||||||
sha.Reset()
|
sha.Reset()
|
||||||
sha.Write(data)
|
sha.Write(data)
|
||||||
sha.Read(hashbuf)
|
sha.Read(hashbuf[:])
|
||||||
hasherPool.Put(sha)
|
hasherPool.Put(sha)
|
||||||
// The actual bits to flip
|
// The actual bits to flip
|
||||||
v1 := byte(1 << (hashbuf[1] & 0x7))
|
v1 := byte(1 << (hashbuf[1] & 0x7))
|
||||||
v2 := byte(1 << (hashbuf[3] & 0x7))
|
v2 := byte(1 << (hashbuf[3] & 0x7))
|
||||||
v3 := byte(1 << (hashbuf[5] & 0x7))
|
v3 := byte(1 << (hashbuf[5] & 0x7))
|
||||||
// The indices for the bytes to OR in
|
// The indices for the bytes to OR in
|
||||||
i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf)&0x7ff)>>3) - 1
|
i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[0:])&0x7ff)>>3) - 1
|
||||||
i2 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[2:])&0x7ff)>>3) - 1
|
i2 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[2:])&0x7ff)>>3) - 1
|
||||||
i3 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[4:])&0x7ff)>>3) - 1
|
i3 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[4:])&0x7ff)>>3) - 1
|
||||||
|
|
||||||
return i1, v1, i2, v2, i3, v3
|
return i1, v1, i2, v2, i3, v3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,10 @@ var _ = (*accountMarshaling)(nil)
|
||||||
// MarshalJSON marshals as JSON.
|
// MarshalJSON marshals as JSON.
|
||||||
func (a Account) MarshalJSON() ([]byte, error) {
|
func (a Account) MarshalJSON() ([]byte, error) {
|
||||||
type Account struct {
|
type Account struct {
|
||||||
Code hexutil.Bytes `json:"code,omitempty"`
|
Code hexutil.Bytes `json:"code,omitempty"`
|
||||||
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
||||||
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
||||||
Nonce math.HexOrDecimal64 `json:"nonce,omitempty"`
|
Nonce math.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||||
PrivateKey hexutil.Bytes `json:"secretKey,omitempty"`
|
|
||||||
}
|
}
|
||||||
var enc Account
|
var enc Account
|
||||||
enc.Code = a.Code
|
enc.Code = a.Code
|
||||||
|
|
@ -33,18 +32,16 @@ func (a Account) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
enc.Balance = (*math.HexOrDecimal256)(a.Balance)
|
enc.Balance = (*math.HexOrDecimal256)(a.Balance)
|
||||||
enc.Nonce = math.HexOrDecimal64(a.Nonce)
|
enc.Nonce = math.HexOrDecimal64(a.Nonce)
|
||||||
enc.PrivateKey = a.PrivateKey
|
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (a *Account) UnmarshalJSON(input []byte) error {
|
func (a *Account) UnmarshalJSON(input []byte) error {
|
||||||
type Account struct {
|
type Account struct {
|
||||||
Code *hexutil.Bytes `json:"code,omitempty"`
|
Code *hexutil.Bytes `json:"code,omitempty"`
|
||||||
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
||||||
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
||||||
Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"`
|
Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||||
PrivateKey *hexutil.Bytes `json:"secretKey,omitempty"`
|
|
||||||
}
|
}
|
||||||
var dec Account
|
var dec Account
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -66,8 +63,5 @@ func (a *Account) UnmarshalJSON(input []byte) error {
|
||||||
if dec.Nonce != nil {
|
if dec.Nonce != nil {
|
||||||
a.Nonce = uint64(*dec.Nonce)
|
a.Nonce = uint64(*dec.Nonce)
|
||||||
}
|
}
|
||||||
if dec.PrivateKey != nil {
|
|
||||||
a.PrivateKey = *dec.PrivateKey
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,11 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
|
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
|
||||||
var hasherPool = sync.Pool{
|
var hasherPool = sync.Pool{
|
||||||
New: func() interface{} { return sha3.NewLegacyKeccak256() },
|
New: func() interface{} { return crypto.NewKeccakState() },
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
|
@ -258,7 +259,7 @@ func (r *Receipt) Size() common.StorageSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReceiptForStorage is a wrapper around a Receipt with RLP serialization
|
// ReceiptForStorage is a wrapper around a Receipt with RLP serialization
|
||||||
// that omits the Bloom field and deserialization that re-computes it.
|
// that omits the Bloom field. The Bloom field is recomputed by DeriveFields.
|
||||||
type ReceiptForStorage Receipt
|
type ReceiptForStorage Receipt
|
||||||
|
|
||||||
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
||||||
|
|
@ -291,7 +292,6 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||||
}
|
}
|
||||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||||
r.Logs = stored.Logs
|
r.Logs = stored.Logs
|
||||||
r.Bloom = CreateBloom((*Receipt)(r))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -372,6 +372,26 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
|
||||||
rs[i].Logs[j].Index = logIndex
|
rs[i].Logs[j].Index = logIndex
|
||||||
logIndex++
|
logIndex++
|
||||||
}
|
}
|
||||||
|
// also derive the Bloom if not derived yet
|
||||||
|
rs[i].Bloom = CreateBloom(rs[i])
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeBlockReceiptLists encodes a list of block receipt lists into RLP.
|
||||||
|
func EncodeBlockReceiptLists(receipts []Receipts) []rlp.RawValue {
|
||||||
|
var storageReceipts []*ReceiptForStorage
|
||||||
|
result := make([]rlp.RawValue, len(receipts))
|
||||||
|
for i, receipt := range receipts {
|
||||||
|
storageReceipts = storageReceipts[:0]
|
||||||
|
for _, r := range receipt {
|
||||||
|
storageReceipts = append(storageReceipts, (*ReceiptForStorage)(r))
|
||||||
|
}
|
||||||
|
bytes, err := rlp.EncodeToBytes(storageReceipts)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Failed to encode block receipts", "err", err)
|
||||||
|
}
|
||||||
|
result[i] = bytes
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -154,148 +155,161 @@ var (
|
||||||
blockNumber = big.NewInt(1)
|
blockNumber = big.NewInt(1)
|
||||||
blockTime = uint64(2)
|
blockTime = uint64(2)
|
||||||
blockHash = common.BytesToHash([]byte{0x03, 0x14})
|
blockHash = common.BytesToHash([]byte{0x03, 0x14})
|
||||||
|
|
||||||
// Create the corresponding receipts
|
|
||||||
receipts = Receipts{
|
|
||||||
&Receipt{
|
|
||||||
Status: ReceiptStatusFailed,
|
|
||||||
CumulativeGasUsed: 1,
|
|
||||||
Logs: []*Log{
|
|
||||||
{
|
|
||||||
Address: common.BytesToAddress([]byte{0x11}),
|
|
||||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
|
||||||
// derived fields:
|
|
||||||
BlockNumber: blockNumber.Uint64(),
|
|
||||||
TxHash: txs[0].Hash(),
|
|
||||||
TxIndex: 0,
|
|
||||||
BlockHash: blockHash,
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
|
||||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
|
||||||
// derived fields:
|
|
||||||
BlockNumber: blockNumber.Uint64(),
|
|
||||||
TxHash: txs[0].Hash(),
|
|
||||||
TxIndex: 0,
|
|
||||||
BlockHash: blockHash,
|
|
||||||
Index: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[0].Hash(),
|
|
||||||
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
|
|
||||||
GasUsed: 1,
|
|
||||||
EffectiveGasPrice: big.NewInt(11),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 0,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
PostState: common.Hash{2}.Bytes(),
|
|
||||||
CumulativeGasUsed: 3,
|
|
||||||
Logs: []*Log{
|
|
||||||
{
|
|
||||||
Address: common.BytesToAddress([]byte{0x22}),
|
|
||||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
|
||||||
// derived fields:
|
|
||||||
BlockNumber: blockNumber.Uint64(),
|
|
||||||
TxHash: txs[1].Hash(),
|
|
||||||
TxIndex: 1,
|
|
||||||
BlockHash: blockHash,
|
|
||||||
Index: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Address: common.BytesToAddress([]byte{0x02, 0x22}),
|
|
||||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
|
||||||
// derived fields:
|
|
||||||
BlockNumber: blockNumber.Uint64(),
|
|
||||||
TxHash: txs[1].Hash(),
|
|
||||||
TxIndex: 1,
|
|
||||||
BlockHash: blockHash,
|
|
||||||
Index: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[1].Hash(),
|
|
||||||
GasUsed: 2,
|
|
||||||
EffectiveGasPrice: big.NewInt(22),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 1,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
Type: AccessListTxType,
|
|
||||||
PostState: common.Hash{3}.Bytes(),
|
|
||||||
CumulativeGasUsed: 6,
|
|
||||||
Logs: []*Log{},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[2].Hash(),
|
|
||||||
GasUsed: 3,
|
|
||||||
EffectiveGasPrice: big.NewInt(33),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 2,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
Type: DynamicFeeTxType,
|
|
||||||
PostState: common.Hash{4}.Bytes(),
|
|
||||||
CumulativeGasUsed: 10,
|
|
||||||
Logs: []*Log{},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[3].Hash(),
|
|
||||||
GasUsed: 4,
|
|
||||||
EffectiveGasPrice: big.NewInt(1044),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 3,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
Type: DynamicFeeTxType,
|
|
||||||
PostState: common.Hash{5}.Bytes(),
|
|
||||||
CumulativeGasUsed: 15,
|
|
||||||
Logs: []*Log{},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[4].Hash(),
|
|
||||||
GasUsed: 5,
|
|
||||||
EffectiveGasPrice: big.NewInt(1055),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 4,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
Type: BlobTxType,
|
|
||||||
PostState: common.Hash{6}.Bytes(),
|
|
||||||
CumulativeGasUsed: 21,
|
|
||||||
Logs: []*Log{},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[5].Hash(),
|
|
||||||
GasUsed: 6,
|
|
||||||
EffectiveGasPrice: big.NewInt(1066),
|
|
||||||
BlobGasUsed: params.BlobTxBlobGasPerBlob,
|
|
||||||
BlobGasPrice: big.NewInt(920),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 5,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
Type: BlobTxType,
|
|
||||||
PostState: common.Hash{7}.Bytes(),
|
|
||||||
CumulativeGasUsed: 28,
|
|
||||||
Logs: []*Log{},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[6].Hash(),
|
|
||||||
GasUsed: 7,
|
|
||||||
EffectiveGasPrice: big.NewInt(1077),
|
|
||||||
BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob,
|
|
||||||
BlobGasPrice: big.NewInt(920),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 6,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var receiptsOnce sync.Once
|
||||||
|
var testReceipts Receipts
|
||||||
|
|
||||||
|
func getTestReceipts() Receipts {
|
||||||
|
// Compute the blooms only once
|
||||||
|
receiptsOnce.Do(func() {
|
||||||
|
// Create the corresponding receipts
|
||||||
|
r := Receipts{
|
||||||
|
&Receipt{
|
||||||
|
Status: ReceiptStatusFailed,
|
||||||
|
CumulativeGasUsed: 1,
|
||||||
|
Logs: []*Log{
|
||||||
|
{
|
||||||
|
Address: common.BytesToAddress([]byte{0x11}),
|
||||||
|
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||||
|
// derived fields:
|
||||||
|
BlockNumber: blockNumber.Uint64(),
|
||||||
|
TxHash: txs[0].Hash(),
|
||||||
|
TxIndex: 0,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
Index: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||||
|
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||||
|
// derived fields:
|
||||||
|
BlockNumber: blockNumber.Uint64(),
|
||||||
|
TxHash: txs[0].Hash(),
|
||||||
|
TxIndex: 0,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
Index: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[0].Hash(),
|
||||||
|
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
|
||||||
|
GasUsed: 1,
|
||||||
|
EffectiveGasPrice: big.NewInt(11),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 0,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
PostState: common.Hash{2}.Bytes(),
|
||||||
|
CumulativeGasUsed: 3,
|
||||||
|
Logs: []*Log{
|
||||||
|
{
|
||||||
|
Address: common.BytesToAddress([]byte{0x22}),
|
||||||
|
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||||
|
// derived fields:
|
||||||
|
BlockNumber: blockNumber.Uint64(),
|
||||||
|
TxHash: txs[1].Hash(),
|
||||||
|
TxIndex: 1,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
Index: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: common.BytesToAddress([]byte{0x02, 0x22}),
|
||||||
|
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||||
|
// derived fields:
|
||||||
|
BlockNumber: blockNumber.Uint64(),
|
||||||
|
TxHash: txs[1].Hash(),
|
||||||
|
TxIndex: 1,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
Index: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[1].Hash(),
|
||||||
|
GasUsed: 2,
|
||||||
|
EffectiveGasPrice: big.NewInt(22),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 1,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: AccessListTxType,
|
||||||
|
PostState: common.Hash{3}.Bytes(),
|
||||||
|
CumulativeGasUsed: 6,
|
||||||
|
Logs: []*Log{},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[2].Hash(),
|
||||||
|
GasUsed: 3,
|
||||||
|
EffectiveGasPrice: big.NewInt(33),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 2,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: DynamicFeeTxType,
|
||||||
|
PostState: common.Hash{4}.Bytes(),
|
||||||
|
CumulativeGasUsed: 10,
|
||||||
|
Logs: []*Log{},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[3].Hash(),
|
||||||
|
GasUsed: 4,
|
||||||
|
EffectiveGasPrice: big.NewInt(1044),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 3,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: DynamicFeeTxType,
|
||||||
|
PostState: common.Hash{5}.Bytes(),
|
||||||
|
CumulativeGasUsed: 15,
|
||||||
|
Logs: []*Log{},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[4].Hash(),
|
||||||
|
GasUsed: 5,
|
||||||
|
EffectiveGasPrice: big.NewInt(1055),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 4,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: BlobTxType,
|
||||||
|
PostState: common.Hash{6}.Bytes(),
|
||||||
|
CumulativeGasUsed: 21,
|
||||||
|
Logs: []*Log{},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[5].Hash(),
|
||||||
|
GasUsed: 6,
|
||||||
|
EffectiveGasPrice: big.NewInt(1066),
|
||||||
|
BlobGasUsed: params.BlobTxBlobGasPerBlob,
|
||||||
|
BlobGasPrice: big.NewInt(920),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 5,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: BlobTxType,
|
||||||
|
PostState: common.Hash{7}.Bytes(),
|
||||||
|
CumulativeGasUsed: 28,
|
||||||
|
Logs: []*Log{},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[6].Hash(),
|
||||||
|
GasUsed: 7,
|
||||||
|
EffectiveGasPrice: big.NewInt(1077),
|
||||||
|
BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob,
|
||||||
|
BlobGasPrice: big.NewInt(920),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 6,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, receipt := range r {
|
||||||
|
receipt.Bloom = CreateBloom(receipt)
|
||||||
|
}
|
||||||
|
testReceipts = r
|
||||||
|
})
|
||||||
|
return testReceipts
|
||||||
|
}
|
||||||
|
|
||||||
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||||
input := []byte{0x80}
|
input := []byte{0x80}
|
||||||
var r Receipt
|
var r Receipt
|
||||||
|
|
@ -310,6 +324,7 @@ func TestDeriveFields(t *testing.T) {
|
||||||
// Re-derive receipts.
|
// Re-derive receipts.
|
||||||
basefee := big.NewInt(1000)
|
basefee := big.NewInt(1000)
|
||||||
blobGasPrice := big.NewInt(920)
|
blobGasPrice := big.NewInt(920)
|
||||||
|
receipts := getTestReceipts()
|
||||||
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
|
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
|
||||||
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs)
|
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -335,6 +350,7 @@ func TestDeriveFields(t *testing.T) {
|
||||||
// Test that we can marshal/unmarshal receipts to/from json without errors.
|
// Test that we can marshal/unmarshal receipts to/from json without errors.
|
||||||
// This also confirms that our test receipts contain all the required fields.
|
// This also confirms that our test receipts contain all the required fields.
|
||||||
func TestReceiptJSON(t *testing.T) {
|
func TestReceiptJSON(t *testing.T) {
|
||||||
|
receipts := getTestReceipts()
|
||||||
for i := range receipts {
|
for i := range receipts {
|
||||||
b, err := receipts[i].MarshalJSON()
|
b, err := receipts[i].MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -351,6 +367,7 @@ func TestReceiptJSON(t *testing.T) {
|
||||||
// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even
|
// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even
|
||||||
// though it is required per the spec.
|
// though it is required per the spec.
|
||||||
func TestEffectiveGasPriceNotRequired(t *testing.T) {
|
func TestEffectiveGasPriceNotRequired(t *testing.T) {
|
||||||
|
receipts := getTestReceipts()
|
||||||
r := *receipts[0]
|
r := *receipts[0]
|
||||||
r.EffectiveGasPrice = nil
|
r.EffectiveGasPrice = nil
|
||||||
b, err := r.MarshalJSON()
|
b, err := r.MarshalJSON()
|
||||||
|
|
@ -511,6 +528,7 @@ func clearComputedFieldsOnReceipt(receipt *Receipt) *Receipt {
|
||||||
cpy.EffectiveGasPrice = big.NewInt(0)
|
cpy.EffectiveGasPrice = big.NewInt(0)
|
||||||
cpy.BlobGasUsed = 0
|
cpy.BlobGasUsed = 0
|
||||||
cpy.BlobGasPrice = nil
|
cpy.BlobGasPrice = nil
|
||||||
|
cpy.Bloom = CreateBloom(&cpy)
|
||||||
return &cpy
|
return &cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -355,28 +355,31 @@ func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int {
|
||||||
// Note: if the effective gasTipCap is negative, this method returns both error
|
// Note: if the effective gasTipCap is negative, this method returns both error
|
||||||
// the actual negative value, _and_ ErrGasFeeCapTooLow
|
// the actual negative value, _and_ ErrGasFeeCapTooLow
|
||||||
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
|
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
|
||||||
|
dst := new(big.Int)
|
||||||
|
err := tx.calcEffectiveGasTip(dst, baseFee)
|
||||||
|
return dst, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// calcEffectiveGasTip calculates the effective gas tip of the transaction and
|
||||||
|
// saves the result to dst.
|
||||||
|
func (tx *Transaction) calcEffectiveGasTip(dst *big.Int, baseFee *big.Int) error {
|
||||||
if baseFee == nil {
|
if baseFee == nil {
|
||||||
return tx.GasTipCap(), nil
|
dst.Set(tx.inner.gasTipCap())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
gasFeeCap := tx.GasFeeCap()
|
gasFeeCap := tx.inner.gasFeeCap()
|
||||||
if gasFeeCap.Cmp(baseFee) < 0 {
|
if gasFeeCap.Cmp(baseFee) < 0 {
|
||||||
err = ErrGasFeeCapTooLow
|
err = ErrGasFeeCapTooLow
|
||||||
}
|
}
|
||||||
gasFeeCap = gasFeeCap.Sub(gasFeeCap, baseFee)
|
|
||||||
|
|
||||||
gasTipCap := tx.GasTipCap()
|
dst.Sub(gasFeeCap, baseFee)
|
||||||
if gasTipCap.Cmp(gasFeeCap) < 0 {
|
gasTipCap := tx.inner.gasTipCap()
|
||||||
return gasTipCap, err
|
if gasTipCap.Cmp(dst) < 0 {
|
||||||
|
dst.Set(gasTipCap)
|
||||||
}
|
}
|
||||||
return gasFeeCap, err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
// EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an
|
|
||||||
// error in case the effective gasTipCap is negative
|
|
||||||
func (tx *Transaction) EffectiveGasTipValue(baseFee *big.Int) *big.Int {
|
|
||||||
effectiveTip, _ := tx.EffectiveGasTip(baseFee)
|
|
||||||
return effectiveTip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
|
// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
|
||||||
|
|
@ -384,7 +387,11 @@ func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int)
|
||||||
if baseFee == nil {
|
if baseFee == nil {
|
||||||
return tx.GasTipCapCmp(other)
|
return tx.GasTipCapCmp(other)
|
||||||
}
|
}
|
||||||
return tx.EffectiveGasTipValue(baseFee).Cmp(other.EffectiveGasTipValue(baseFee))
|
// Use more efficient internal method.
|
||||||
|
txTip, otherTip := new(big.Int), new(big.Int)
|
||||||
|
tx.calcEffectiveGasTip(txTip, baseFee)
|
||||||
|
other.calcEffectiveGasTip(otherTip, baseFee)
|
||||||
|
return txTip.Cmp(otherTip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
|
// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
|
||||||
|
|
@ -392,7 +399,9 @@ func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) i
|
||||||
if baseFee == nil {
|
if baseFee == nil {
|
||||||
return tx.GasTipCapIntCmp(other)
|
return tx.GasTipCapIntCmp(other)
|
||||||
}
|
}
|
||||||
return tx.EffectiveGasTipValue(baseFee).Cmp(other)
|
txTip := new(big.Int)
|
||||||
|
tx.calcEffectiveGasTip(txTip, baseFee)
|
||||||
|
return txTip.Cmp(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlobGas returns the blob gas limit of the transaction for blob transactions, 0 otherwise.
|
// BlobGas returns the blob gas limit of the transaction for blob transactions, 0 otherwise.
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -593,3 +594,101 @@ func BenchmarkHash(b *testing.B) {
|
||||||
signer.Hash(tx)
|
signer.Hash(tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkEffectiveGasTip(b *testing.B) {
|
||||||
|
signer := LatestSigner(params.TestChainConfig)
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
txdata := &DynamicFeeTx{
|
||||||
|
ChainID: big.NewInt(1),
|
||||||
|
Nonce: 0,
|
||||||
|
GasTipCap: big.NewInt(2000000000),
|
||||||
|
GasFeeCap: big.NewInt(3000000000),
|
||||||
|
Gas: 21000,
|
||||||
|
To: &common.Address{},
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Data: nil,
|
||||||
|
}
|
||||||
|
tx, _ := SignNewTx(key, signer, txdata)
|
||||||
|
baseFee := big.NewInt(1000000000) // 1 gwei
|
||||||
|
|
||||||
|
b.Run("Original", func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err := tx.EffectiveGasTip(baseFee)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("IntoMethod", func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
dst := new(big.Int)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := tx.calcEffectiveGasTip(dst, baseFee)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEffectiveGasTipInto(t *testing.T) {
|
||||||
|
signer := LatestSigner(params.TestChainConfig)
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
tipCap int64
|
||||||
|
feeCap int64
|
||||||
|
baseFee *int64
|
||||||
|
}{
|
||||||
|
{tipCap: 1, feeCap: 100, baseFee: intPtr(50)},
|
||||||
|
{tipCap: 10, feeCap: 100, baseFee: intPtr(50)},
|
||||||
|
{tipCap: 50, feeCap: 100, baseFee: intPtr(50)},
|
||||||
|
{tipCap: 100, feeCap: 100, baseFee: intPtr(50)},
|
||||||
|
{tipCap: 1, feeCap: 50, baseFee: intPtr(50)},
|
||||||
|
{tipCap: 1, feeCap: 20, baseFee: intPtr(50)}, // Base fee higher than fee cap
|
||||||
|
{tipCap: 50, feeCap: 100, baseFee: intPtr(0)},
|
||||||
|
{tipCap: 50, feeCap: 100, baseFee: nil}, // nil base fee
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testCases {
|
||||||
|
txdata := &DynamicFeeTx{
|
||||||
|
ChainID: big.NewInt(1),
|
||||||
|
Nonce: 0,
|
||||||
|
GasTipCap: big.NewInt(tc.tipCap),
|
||||||
|
GasFeeCap: big.NewInt(tc.feeCap),
|
||||||
|
Gas: 21000,
|
||||||
|
To: &common.Address{},
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Data: nil,
|
||||||
|
}
|
||||||
|
tx, _ := SignNewTx(key, signer, txdata)
|
||||||
|
|
||||||
|
var baseFee *big.Int
|
||||||
|
if tc.baseFee != nil {
|
||||||
|
baseFee = big.NewInt(*tc.baseFee)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get result from original method
|
||||||
|
orig, origErr := tx.EffectiveGasTip(baseFee)
|
||||||
|
|
||||||
|
// Get result from new method
|
||||||
|
dst := new(big.Int)
|
||||||
|
newErr := tx.calcEffectiveGasTip(dst, baseFee)
|
||||||
|
|
||||||
|
// Compare results
|
||||||
|
if (origErr != nil) != (newErr != nil) {
|
||||||
|
t.Fatalf("case %d: error mismatch: orig %v, new %v", i, origErr, newErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if orig.Cmp(dst) != 0 {
|
||||||
|
t.Fatalf("case %d: result mismatch: orig %v, new %v", i, orig, dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create integer pointer
|
||||||
|
func intPtr(i int64) *int64 {
|
||||||
|
return &i
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ var PrecompiledContractsPrague = PrecompiledContracts{
|
||||||
|
|
||||||
var PrecompiledContractsBLS = PrecompiledContractsPrague
|
var PrecompiledContractsBLS = PrecompiledContractsPrague
|
||||||
|
|
||||||
var PrecompiledContractsVerkle = PrecompiledContractsPrague
|
var PrecompiledContractsVerkle = PrecompiledContractsBerlin
|
||||||
|
|
||||||
var (
|
var (
|
||||||
PrecompiledAddressesPrague []common.Address
|
PrecompiledAddressesPrague []common.Address
|
||||||
|
|
|
||||||
|
|
@ -370,7 +370,7 @@ func BenchmarkPrecompiledBLS12381G1MultiExpWorstCase(b *testing.B) {
|
||||||
Name: "WorstCaseG1",
|
Name: "WorstCaseG1",
|
||||||
NoBenchmark: false,
|
NoBenchmark: false,
|
||||||
}
|
}
|
||||||
benchmarkPrecompiled("f0c", testcase, b)
|
benchmarkPrecompiled("f0b", testcase, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BenchmarkPrecompiledBLS12381G2MultiExpWorstCase benchmarks the worst case we could find that still fits a gaslimit of 10MGas.
|
// BenchmarkPrecompiledBLS12381G2MultiExpWorstCase benchmarks the worst case we could find that still fits a gaslimit of 10MGas.
|
||||||
|
|
@ -391,5 +391,5 @@ func BenchmarkPrecompiledBLS12381G2MultiExpWorstCase(b *testing.B) {
|
||||||
Name: "WorstCaseG2",
|
Name: "WorstCaseG2",
|
||||||
NoBenchmark: false,
|
NoBenchmark: false,
|
||||||
}
|
}
|
||||||
benchmarkPrecompiled("f0f", testcase, b)
|
benchmarkPrecompiled("f0d", testcase, b)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -339,12 +339,10 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC
|
||||||
addr := common.Address(a.Bytes20())
|
addr := common.Address(a.Bytes20())
|
||||||
code := interpreter.evm.StateDB.GetCode(addr)
|
code := interpreter.evm.StateDB.GetCode(addr)
|
||||||
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
||||||
if !scope.Contract.IsSystemCall {
|
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas)
|
||||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false)
|
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
if consumed < wanted {
|
||||||
scope.Contract.Gas = 0
|
return nil, ErrOutOfGas
|
||||||
return nil, ErrOutOfGas
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy)
|
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy)
|
||||||
|
|
||||||
|
|
@ -367,9 +365,9 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||||
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
||||||
// advanced past this boundary.
|
// advanced past this boundary.
|
||||||
contractAddr := scope.Contract.Address()
|
contractAddr := scope.Contract.Address()
|
||||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false)
|
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
scope.Contract.UseGas(wanted, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
scope.Contract.Gas = 0
|
if consumed < wanted {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -395,9 +393,9 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
||||||
|
|
||||||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
||||||
contractAddr := scope.Contract.Address()
|
contractAddr := scope.Contract.Address()
|
||||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false)
|
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
scope.Contract.Gas = 0
|
if consumed < wanted {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,8 +206,14 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
|
|
||||||
if !evm.StateDB.Exist(addr) {
|
if !evm.StateDB.Exist(addr) {
|
||||||
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
||||||
// add proof of absence to witness
|
// Add proof of absence to witness
|
||||||
wgas := evm.AccessEvents.AddAccount(addr, false)
|
// At this point, the read costs have already been charged, either because this
|
||||||
|
// is a direct tx call, in which case it's covered by the intrinsic gas, or because
|
||||||
|
// of a CALL instruction, in which case BASIC_DATA has been added to the access
|
||||||
|
// list in write mode. If there is enough gas paying for the addition of the code
|
||||||
|
// hash leaf to the access list, then account creation will proceed unimpaired.
|
||||||
|
// Thus, only pay for the creation of the code hash leaf here.
|
||||||
|
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas, false)
|
||||||
if gas < wgas {
|
if gas < wgas {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
return nil, 0, ErrOutOfGas
|
return nil, 0, ErrOutOfGas
|
||||||
|
|
@ -433,7 +439,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
|
|
||||||
// Charge the contract creation init gas in verkle mode
|
// Charge the contract creation init gas in verkle mode
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address)
|
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas)
|
||||||
if statelessGas > gas {
|
if statelessGas > gas {
|
||||||
return nil, common.Address{}, 0, ErrOutOfGas
|
return nil, common.Address{}, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
@ -481,14 +487,14 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
}
|
}
|
||||||
// Charge the contract creation init gas in verkle mode
|
// Charge the contract creation init gas in verkle mode
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
statelessGas := evm.AccessEvents.ContractCreateInitGas(address)
|
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas)
|
||||||
if statelessGas > gas {
|
if consumed < wanted {
|
||||||
return nil, common.Address{}, 0, ErrOutOfGas
|
return nil, common.Address{}, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, gas-statelessGas, tracing.GasChangeWitnessContractInit)
|
evm.Config.Tracer.OnGasChange(gas, gas-consumed, tracing.GasChangeWitnessContractInit)
|
||||||
}
|
}
|
||||||
gas = gas - statelessGas
|
gas = gas - consumed
|
||||||
}
|
}
|
||||||
evm.Context.Transfer(evm.StateDB, caller, address, value)
|
evm.Context.Transfer(evm.StateDB, caller, address, value)
|
||||||
|
|
||||||
|
|
@ -535,7 +541,9 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
||||||
return ret, ErrCodeStoreOutOfGas
|
return ret, ErrCodeStoreOutOfGas
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) {
|
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas)
|
||||||
|
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||||
|
if len(ret) > 0 && (consumed < wanted) {
|
||||||
return ret, ErrCodeStoreOutOfGas
|
return ret, ErrCodeStoreOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -555,7 +563,8 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
||||||
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
||||||
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||||
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), crypto.Keccak256(code))
|
inithash := crypto.HashData(evm.interpreter.hasher, code)
|
||||||
|
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
|
||||||
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
|
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -394,14 +394,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
|
|
||||||
if transfersValue {
|
|
||||||
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
|
|
||||||
if overflow {
|
|
||||||
return 0, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -428,16 +421,6 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
|
|
||||||
address := common.Address(stack.Back(1).Bytes20())
|
|
||||||
transfersValue := !stack.Back(2).IsZero()
|
|
||||||
if transfersValue {
|
|
||||||
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
|
|
||||||
if overflow {
|
|
||||||
return 0, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
@ -234,11 +233,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
||||||
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||||
|
|
||||||
if interpreter.hasher == nil {
|
interpreter.hasher.Reset()
|
||||||
interpreter.hasher = crypto.NewKeccakState()
|
|
||||||
} else {
|
|
||||||
interpreter.hasher.Reset()
|
|
||||||
}
|
|
||||||
interpreter.hasher.Write(data)
|
interpreter.hasher.Write(data)
|
||||||
interpreter.hasher.Read(interpreter.hasherBuf[:])
|
interpreter.hasher.Read(interpreter.hasherBuf[:])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ type StateDB interface {
|
||||||
SelfDestruct6780(common.Address) (uint256.Int, bool)
|
SelfDestruct6780(common.Address) (uint256.Int, bool)
|
||||||
|
|
||||||
// Exist reports whether the given account exists in state.
|
// Exist reports whether the given account exists in state.
|
||||||
// Notably this should also return true for self-destructed accounts.
|
// Notably this also returns true for self-destructed accounts within the current transaction.
|
||||||
Exist(common.Address) bool
|
Exist(common.Address) bool
|
||||||
// Empty returns whether the given account is empty. Empty
|
// Empty returns whether the given account is empty. Empty
|
||||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
evm.Config.ExtraEips = extraEips
|
evm.Config.ExtraEips = extraEips
|
||||||
return &EVMInterpreter{evm: evm, table: table}
|
return &EVMInterpreter{evm: evm, table: table, hasher: crypto.NewKeccakState()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run loops and evaluates the contract's code with the given input data and returns
|
// Run loops and evaluates the contract's code with the given input data and returns
|
||||||
|
|
@ -237,7 +237,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
// if the PC ends up in a new "chunk" of verkleized code, charge the
|
// if the PC ends up in a new "chunk" of verkleized code, charge the
|
||||||
// associated costs.
|
// associated costs.
|
||||||
contractAddr := contract.Address()
|
contractAddr := contract.Address()
|
||||||
contract.Gas -= in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false)
|
consumed, wanted := in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas)
|
||||||
|
contract.UseGas(consumed, in.evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||||
|
if consumed < wanted {
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the operation from the jump table and validate the stack to ensure there are
|
// Get the operation from the jump table and validate the stack to ensure there are
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ func validate(jt JumpTable) JumpTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newVerkleInstructionSet() JumpTable {
|
func newVerkleInstructionSet() JumpTable {
|
||||||
instructionSet := newCancunInstructionSet()
|
instructionSet := newShanghaiInstructionSet()
|
||||||
enable4762(&instructionSet)
|
enable4762(&instructionSet)
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,31 +25,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true)
|
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false)
|
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
if contract.IsSystemCall {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
address := stack.peek().Bytes20()
|
address := stack.peek().Bytes20()
|
||||||
gas := evm.AccessEvents.BasicDataGas(address, false)
|
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
|
@ -57,56 +42,69 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
if contract.IsSystemCall {
|
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
gas := evm.AccessEvents.BasicDataGas(address, false)
|
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
if contract.IsSystemCall {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
address := stack.peek().Bytes20()
|
address := stack.peek().Bytes20()
|
||||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
gas := evm.AccessEvents.CodeHashGas(address, false)
|
return evm.AccessEvents.CodeHashGas(address, false, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc {
|
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
var (
|
||||||
if err != nil {
|
target = common.Address(stack.Back(1).Bytes20())
|
||||||
return 0, err
|
witnessGas uint64
|
||||||
}
|
_, isPrecompile = evm.precompile(target)
|
||||||
if contract.IsSystemCall {
|
isSystemContract = target == params.HistoryStorageAddress
|
||||||
return gas, nil
|
)
|
||||||
}
|
|
||||||
if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile {
|
// If value is transferred, it is charged before 1/64th
|
||||||
return gas, nil
|
// is subtracted from the available gas pool.
|
||||||
}
|
if withTransferCosts && !stack.Back(2).IsZero() {
|
||||||
witnessGas := evm.AccessEvents.MessageCallGas(contract.Address())
|
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas)
|
||||||
if witnessGas == 0 {
|
if wantedValueTransferWitnessGas > contract.Gas {
|
||||||
|
return wantedValueTransferWitnessGas, nil
|
||||||
|
}
|
||||||
|
witnessGas = wantedValueTransferWitnessGas
|
||||||
|
} else if isPrecompile || isSystemContract {
|
||||||
witnessGas = params.WarmStorageReadCostEIP2929
|
witnessGas = params.WarmStorageReadCostEIP2929
|
||||||
|
} else {
|
||||||
|
// The charging for the value transfer is done BEFORE subtracting
|
||||||
|
// the 1/64th gas, as this is considered part of the CALL instruction.
|
||||||
|
// (so before we get to this point)
|
||||||
|
// But the message call is part of the subcall, for which only 63/64th
|
||||||
|
// of the gas should be available.
|
||||||
|
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas-witnessGas)
|
||||||
|
var overflow bool
|
||||||
|
if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
if witnessGas > contract.Gas {
|
||||||
|
return witnessGas, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return witnessGas + gas, nil
|
|
||||||
|
contract.Gas -= witnessGas
|
||||||
|
// if the operation fails, adds witness gas to the gas before returning the error
|
||||||
|
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||||
|
contract.Gas += witnessGas // restore witness gas so that it can be charged at the callsite
|
||||||
|
var overflow bool
|
||||||
|
if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
return gas, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall)
|
gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall, true)
|
||||||
gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode)
|
gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode, false)
|
||||||
gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall)
|
gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall, false)
|
||||||
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall)
|
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
|
@ -118,15 +116,44 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
contractAddr := contract.Address()
|
contractAddr := contract.Address()
|
||||||
statelessGas := evm.AccessEvents.BasicDataGas(contractAddr, false)
|
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas, false)
|
||||||
|
if wanted > contract.Gas {
|
||||||
|
return wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas := wanted
|
||||||
|
balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0
|
||||||
|
_, isPrecompile := evm.precompile(beneficiaryAddr)
|
||||||
|
isSystemContract := beneficiaryAddr == params.HistoryStorageAddress
|
||||||
|
|
||||||
|
if (isPrecompile || isSystemContract) && balanceIsZero {
|
||||||
|
return statelessGas, nil
|
||||||
|
}
|
||||||
|
|
||||||
if contractAddr != beneficiaryAddr {
|
if contractAddr != beneficiaryAddr {
|
||||||
statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, false)
|
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas-statelessGas, false)
|
||||||
|
if wanted > contract.Gas-statelessGas {
|
||||||
|
return statelessGas + wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas += wanted
|
||||||
}
|
}
|
||||||
// Charge write costs if it transfers value
|
// Charge write costs if it transfers value
|
||||||
if evm.StateDB.GetBalance(contractAddr).Sign() != 0 {
|
if !balanceIsZero {
|
||||||
statelessGas += evm.AccessEvents.BasicDataGas(contractAddr, true)
|
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas-statelessGas, false)
|
||||||
|
if wanted > contract.Gas-statelessGas {
|
||||||
|
return statelessGas + wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas += wanted
|
||||||
|
|
||||||
if contractAddr != beneficiaryAddr {
|
if contractAddr != beneficiaryAddr {
|
||||||
statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, true)
|
if evm.StateDB.Exist(beneficiaryAddr) {
|
||||||
|
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas-statelessGas, false)
|
||||||
|
} else {
|
||||||
|
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas-statelessGas)
|
||||||
|
}
|
||||||
|
if wanted > contract.Gas-statelessGas {
|
||||||
|
return statelessGas + wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas += wanted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return statelessGas, nil
|
return statelessGas, nil
|
||||||
|
|
@ -137,17 +164,19 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
var (
|
|
||||||
codeOffset = stack.Back(1)
|
|
||||||
length = stack.Back(2)
|
|
||||||
)
|
|
||||||
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
|
||||||
if overflow {
|
|
||||||
uint64CodeOffset = gomath.MaxUint64
|
|
||||||
}
|
|
||||||
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
|
||||||
if !contract.IsDeployment && !contract.IsSystemCall {
|
if !contract.IsDeployment && !contract.IsSystemCall {
|
||||||
gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
|
var (
|
||||||
|
codeOffset = stack.Back(1)
|
||||||
|
length = stack.Back(2)
|
||||||
|
)
|
||||||
|
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||||
|
if overflow {
|
||||||
|
uint64CodeOffset = gomath.MaxUint64
|
||||||
|
}
|
||||||
|
|
||||||
|
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
||||||
|
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas-gas)
|
||||||
|
gas += wanted
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -158,16 +187,17 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if contract.IsSystemCall {
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
|
_, isPrecompile := evm.precompile(addr)
|
||||||
|
if isPrecompile || addr == params.HistoryStorageAddress {
|
||||||
|
var overflow bool
|
||||||
|
if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
addr := common.Address(stack.peek().Bytes20())
|
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas-gas, true)
|
||||||
wgas := evm.AccessEvents.BasicDataGas(addr, false)
|
|
||||||
if wgas == 0 {
|
|
||||||
wgas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
var overflow bool
|
var overflow bool
|
||||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
|
||||||
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
|
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
@ -73,6 +74,12 @@ func NewKeccakState() KeccakState {
|
||||||
return sha3.NewLegacyKeccak256().(KeccakState)
|
return sha3.NewLegacyKeccak256().(KeccakState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasherPool = sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
return sha3.NewLegacyKeccak256().(KeccakState)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
|
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
|
||||||
func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
||||||
kh.Reset()
|
kh.Reset()
|
||||||
|
|
@ -84,22 +91,26 @@ func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
||||||
// Keccak256 calculates and returns the Keccak256 hash of the input data.
|
// Keccak256 calculates and returns the Keccak256 hash of the input data.
|
||||||
func Keccak256(data ...[]byte) []byte {
|
func Keccak256(data ...[]byte) []byte {
|
||||||
b := make([]byte, 32)
|
b := make([]byte, 32)
|
||||||
d := NewKeccakState()
|
d := hasherPool.Get().(KeccakState)
|
||||||
|
d.Reset()
|
||||||
for _, b := range data {
|
for _, b := range data {
|
||||||
d.Write(b)
|
d.Write(b)
|
||||||
}
|
}
|
||||||
d.Read(b)
|
d.Read(b)
|
||||||
|
hasherPool.Put(d)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
|
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
|
||||||
// converting it to an internal Hash data structure.
|
// converting it to an internal Hash data structure.
|
||||||
func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
||||||
d := NewKeccakState()
|
d := hasherPool.Get().(KeccakState)
|
||||||
|
d.Reset()
|
||||||
for _, b := range data {
|
for _, b := range data {
|
||||||
d.Write(b)
|
d.Write(b)
|
||||||
}
|
}
|
||||||
d.Read(h[:])
|
d.Read(h[:])
|
||||||
|
hasherPool.Put(d)
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func randFieldElement() [32]byte {
|
func randFieldElement() [32]byte {
|
||||||
|
|
|
||||||
|
|
@ -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 !nacl && !js && !wasip1 && cgo && !gofuzz
|
//go:build !nacl && !js && !wasip1 && cgo && !gofuzz && !tinygo
|
||||||
// +build !nacl,!js,!wasip1,cgo,!gofuzz
|
// +build !nacl,!js,!wasip1,cgo,!gofuzz,!tinygo
|
||||||
|
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 nacl || js || wasip1 || !cgo || gofuzz
|
//go:build nacl || js || wasip1 || !cgo || gofuzz || tinygo
|
||||||
// +build nacl js wasip1 !cgo gofuzz
|
// +build nacl js wasip1 !cgo gofuzz tinygo
|
||||||
|
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,9 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r
|
||||||
if hash, ok := blockNrOrHash.Hash(); ok {
|
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||||
header := b.eth.blockchain.GetHeaderByHash(hash)
|
header := b.eth.blockchain.GetHeaderByHash(hash)
|
||||||
if header == nil {
|
if header == nil {
|
||||||
return nil, errors.New("header for hash not found")
|
// Return 'null' and no error if block is not found.
|
||||||
|
// This behavior is required by RPC spec.
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
return nil, errors.New("hash is not currently canonical")
|
return nil, errors.New("hash is not currently canonical")
|
||||||
|
|
|
||||||
|
|
@ -134,13 +134,17 @@ func TestSendTx(t *testing.T) {
|
||||||
func testSendTx(t *testing.T, withLocal bool) {
|
func testSendTx(t *testing.T, withLocal bool) {
|
||||||
b := initBackend(withLocal)
|
b := initBackend(withLocal)
|
||||||
|
|
||||||
txA := pricedSetCodeTx(0, 250000, uint256.NewInt(params.GWei), uint256.NewInt(params.GWei), key, []unsignedAuth{
|
txA := pricedSetCodeTx(0, 250000, uint256.NewInt(params.GWei), uint256.NewInt(params.GWei), key, []unsignedAuth{{nonce: 0, key: key}})
|
||||||
{
|
if err := b.SendTx(context.Background(), txA); err != nil {
|
||||||
nonce: 0,
|
t.Fatalf("Failed to submit tx: %v", err)
|
||||||
key: key,
|
}
|
||||||
},
|
for {
|
||||||
})
|
pending, _ := b.TxPool().ContentFrom(address)
|
||||||
b.SendTx(context.Background(), txA)
|
if len(pending) == 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
txB := makeTx(1, nil, nil, key)
|
txB := makeTx(1, nil, nil, key)
|
||||||
err := b.SendTx(context.Background(), txB)
|
err := b.SendTx(context.Background(), txB)
|
||||||
|
|
|
||||||
|
|
@ -271,26 +271,26 @@ func storageRangeAt(statedb *state.StateDB, root common.Hash, address common.Add
|
||||||
//
|
//
|
||||||
// With one parameter, returns the list of accounts modified in the specified block.
|
// With one parameter, returns the list of accounts modified in the specified block.
|
||||||
func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
|
func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
|
||||||
var startBlock, endBlock *types.Block
|
var startHeader, endHeader *types.Header
|
||||||
|
|
||||||
startBlock = api.eth.blockchain.GetBlockByNumber(startNum)
|
startHeader = api.eth.blockchain.GetHeaderByNumber(startNum)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("start block %x not found", startNum)
|
return nil, fmt.Errorf("start block %x not found", startNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
if endNum == nil {
|
if endNum == nil {
|
||||||
endBlock = startBlock
|
endHeader = startHeader
|
||||||
startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
|
startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
|
return nil, fmt.Errorf("block %x has no parent", endHeader.Number)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
endBlock = api.eth.blockchain.GetBlockByNumber(*endNum)
|
endHeader = api.eth.blockchain.GetHeaderByNumber(*endNum)
|
||||||
if endBlock == nil {
|
if endHeader == nil {
|
||||||
return nil, fmt.Errorf("end block %d not found", *endNum)
|
return nil, fmt.Errorf("end block %d not found", *endNum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.getModifiedAccounts(startBlock, endBlock)
|
return api.getModifiedAccounts(startHeader, endHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetModifiedAccountsByHash returns all accounts that have changed between the
|
// GetModifiedAccountsByHash returns all accounts that have changed between the
|
||||||
|
|
@ -299,38 +299,38 @@ func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64
|
||||||
//
|
//
|
||||||
// With one parameter, returns the list of accounts modified in the specified block.
|
// With one parameter, returns the list of accounts modified in the specified block.
|
||||||
func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
|
func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
|
||||||
var startBlock, endBlock *types.Block
|
var startHeader, endHeader *types.Header
|
||||||
startBlock = api.eth.blockchain.GetBlockByHash(startHash)
|
startHeader = api.eth.blockchain.GetHeaderByHash(startHash)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("start block %x not found", startHash)
|
return nil, fmt.Errorf("start block %x not found", startHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
if endHash == nil {
|
if endHash == nil {
|
||||||
endBlock = startBlock
|
endHeader = startHeader
|
||||||
startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
|
startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
|
return nil, fmt.Errorf("block %x has no parent", endHeader.Number)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
endBlock = api.eth.blockchain.GetBlockByHash(*endHash)
|
endHeader = api.eth.blockchain.GetHeaderByHash(*endHash)
|
||||||
if endBlock == nil {
|
if endHeader == nil {
|
||||||
return nil, fmt.Errorf("end block %x not found", *endHash)
|
return nil, fmt.Errorf("end block %x not found", *endHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.getModifiedAccounts(startBlock, endBlock)
|
return api.getModifiedAccounts(startHeader, endHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) {
|
func (api *DebugAPI) getModifiedAccounts(startHeader, endHeader *types.Header) ([]common.Address, error) {
|
||||||
if startBlock.Number().Uint64() >= endBlock.Number().Uint64() {
|
if startHeader.Number.Uint64() >= endHeader.Number.Uint64() {
|
||||||
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64())
|
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startHeader.Number.Uint64(), endHeader.Number.Uint64())
|
||||||
}
|
}
|
||||||
triedb := api.eth.BlockChain().TrieDB()
|
triedb := api.eth.BlockChain().TrieDB()
|
||||||
|
|
||||||
oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startBlock.Root()), triedb)
|
oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startHeader.Root), triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newTrie, err := trie.NewStateTrie(trie.StateTrieID(endBlock.Root()), triedb)
|
newTrie, err := trie.NewStateTrie(trie.StateTrieID(endHeader.Root), triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,25 +18,74 @@ package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"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/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/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var dumper = spew.ConfigState{Indent: " "}
|
var dumper = spew.ConfigState{Indent: " "}
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
addr common.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAccounts(n int) (accounts []Account) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
accounts = append(accounts, Account{key: key, addr: addr})
|
||||||
|
}
|
||||||
|
slices.SortFunc(accounts, func(a, b Account) int { return a.addr.Cmp(b.addr) })
|
||||||
|
return accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTestBlockChain creates a new test blockchain. OBS: After test is done, teardown must be
|
||||||
|
// invoked in order to release associated resources.
|
||||||
|
func newTestBlockChain(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *core.BlockChain {
|
||||||
|
engine := ethash.NewFaker()
|
||||||
|
// Generate blocks for testing
|
||||||
|
_, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, generator)
|
||||||
|
|
||||||
|
// Import the canonical chain
|
||||||
|
cacheConfig := &core.CacheConfig{
|
||||||
|
TrieCleanLimit: 256,
|
||||||
|
TrieDirtyLimit: 256,
|
||||||
|
TrieTimeLimit: 5 * time.Minute,
|
||||||
|
SnapshotLimit: 0,
|
||||||
|
Preimages: true,
|
||||||
|
TrieDirtyDisabled: true, // Archive mode
|
||||||
|
}
|
||||||
|
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), cacheConfig, gspec, nil, engine, vm.Config{}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
|
}
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
||||||
func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.Dump {
|
func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.Dump {
|
||||||
result := statedb.RawDump(&state.DumpConfig{
|
result := statedb.RawDump(&state.DumpConfig{
|
||||||
SkipCode: true,
|
SkipCode: true,
|
||||||
|
|
@ -224,3 +273,66 @@ func TestStorageRangeAt(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetModifiedAccounts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Initialize test accounts
|
||||||
|
accounts := newAccounts(4)
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[3].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genBlocks := 1
|
||||||
|
signer := types.HomesteadSigner{}
|
||||||
|
blockChain := newTestBlockChain(t, genBlocks, genesis, func(_ int, b *core.BlockGen) {
|
||||||
|
// Transfer from account[0] to account[1]
|
||||||
|
// value: 1000 wei
|
||||||
|
// fee: 0 wei
|
||||||
|
for _, account := range accounts[:3] {
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: 0,
|
||||||
|
To: &accounts[3].addr,
|
||||||
|
Value: big.NewInt(1000),
|
||||||
|
Gas: params.TxGas,
|
||||||
|
GasPrice: b.BaseFee(),
|
||||||
|
Data: nil}),
|
||||||
|
signer, account.key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer blockChain.Stop()
|
||||||
|
|
||||||
|
// Create a debug API instance.
|
||||||
|
api := NewDebugAPI(&Ethereum{blockchain: blockChain})
|
||||||
|
|
||||||
|
// Test GetModifiedAccountsByNumber
|
||||||
|
t.Run("GetModifiedAccountsByNumber", func(t *testing.T) {
|
||||||
|
addrs, err := api.GetModifiedAccountsByNumber(uint64(genBlocks), nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase
|
||||||
|
for _, account := range accounts {
|
||||||
|
if !slices.Contains(addrs, account.addr) {
|
||||||
|
t.Fatalf("account %s not found in modified accounts", account.addr.Hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test GetModifiedAccountsByHash
|
||||||
|
t.Run("GetModifiedAccountsByHash", func(t *testing.T) {
|
||||||
|
header := blockChain.GetHeaderByNumber(uint64(genBlocks))
|
||||||
|
addrs, err := api.GetModifiedAccountsByHash(header.Hash(), nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase
|
||||||
|
for _, account := range accounts {
|
||||||
|
if !slices.Contains(addrs, account.addr) {
|
||||||
|
t.Fatalf("account %s not found in modified accounts", account.addr.Hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,14 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
}
|
}
|
||||||
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
|
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
|
||||||
|
|
||||||
chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/", false)
|
dbOptions := node.DatabaseOptions{
|
||||||
|
Cache: config.DatabaseCache,
|
||||||
|
Handles: config.DatabaseHandles,
|
||||||
|
AncientsDirectory: config.DatabaseFreezer,
|
||||||
|
EraDirectory: config.DatabaseEra,
|
||||||
|
MetricsNamespace: "eth/db/chaindata/",
|
||||||
|
}
|
||||||
|
chainDb, err := stack.OpenDatabaseWithOptions("chaindata", dbOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -337,9 +344,6 @@ func makeExtraData(extra []byte) []byte {
|
||||||
func (s *Ethereum) APIs() []rpc.API {
|
func (s *Ethereum) APIs() []rpc.API {
|
||||||
apis := ethapi.GetAPIs(s.APIBackend)
|
apis := ethapi.GetAPIs(s.APIBackend)
|
||||||
|
|
||||||
// Append any APIs exposed explicitly by the consensus engine
|
|
||||||
apis = append(apis, s.engine.APIs(s.BlockChain())...)
|
|
||||||
|
|
||||||
// Append all the local APIs and return
|
// Append all the local APIs and return
|
||||||
return append(apis, []rpc.API{
|
return append(apis, []rpc.API{
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue