Merge branch 'main' into update-main

This commit is contained in:
Cal Bera 2025-08-15 10:03:27 -07:00
commit 21d7bcb7cb
134 changed files with 3283 additions and 843 deletions

View file

@ -0,0 +1,10 @@
{
"permissions": {
"allow": [
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git push:*)"
],
"deny": []
}
}

View file

@ -1,27 +0,0 @@
on:
workflow_dispatch:
### Note we cannot use cron-triggered builds right now, Gitea seems to have
### a few bugs in that area. So this workflow is scheduled using an external
### triggering mechanism and workflow_dispatch.
#
# schedule:
# - cron: '0 15 * * *'
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 }}

View file

@ -1,46 +0,0 @@
on:
push:
tags:
- "v*"
workflow_dispatch:
### Note we cannot use cron-triggered builds right now, Gitea seems to have
### a few bugs in that area. So this workflow is scheduled using an external
### triggering mechanism and workflow_dispatch.
#
# schedule:
# - cron: '0 16 * * *'
jobs:
ppa:
name: PPA Upload
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Show environment
run: |
env
- 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 }}

View file

@ -1,179 +0,0 @@
on:
push:
branches:
- "master"
tags:
- "v*"
workflow_dispatch:
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 -static -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 -static -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 -static -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 -static -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 -static -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 -static -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 }}
windows:
name: Windows Build
runs-on: "win-11"
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.24
cache: false
# Note: gcc.exe only works properly if the corresponding bin/ directory is
# contained in PATH.
- name: "Build (amd64)"
shell: cmd
run: |
set PATH=%GETH_MINGW%\bin;%PATH%
go run build/ci.go install -dlgo -arch amd64 -cc %GETH_MINGW%\bin\gcc.exe
env:
GETH_MINGW: 'C:\msys64\mingw64'
- name: "Build (386)"
shell: cmd
run: |
set PATH=%GETH_MINGW%\bin;%PATH%
go run build/ci.go install -dlgo -arch 386 -cc %GETH_MINGW%\bin\gcc.exe
env:
GETH_MINGW: 'C:\msys64\mingw32'
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

31
.github/CODEOWNERS vendored
View file

@ -1,33 +1,4 @@
# Lines starting with '#' are comments. # Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners. # Each line is a file pattern followed by one or more owners.
accounts/usbwallet/ @gballet * @calbera
accounts/scwallet/ @gballet
accounts/abi/ @gballet @MariusVanDerWijden
beacon/engine/ @MariusVanDerWijden @lightclient @fjl
beacon/light/ @zsfelfoldi
beacon/merkle/ @zsfelfoldi
beacon/types/ @zsfelfoldi @fjl
beacon/params/ @zsfelfoldi @fjl
cmd/evm/ @MariusVanDerWijden @lightclient
core/state/ @rjl493456442
crypto/ @gballet @jwasinger @fjl
core/ @rjl493456442
eth/ @rjl493456442
eth/catalyst/ @MariusVanDerWijden @lightclient @fjl @jwasinger
eth/tracers/ @s1na
ethclient/ @fjl
ethdb/ @rjl493456442
event/ @fjl
trie/ @rjl493456442
triedb/ @rjl493456442
core/tracing/ @s1na
graphql/ @s1na
internal/ethapi/ @fjl @s1na @lightclient
internal/era/ @lightclient
miner/ @MariusVanDerWijden @fjl @rjl493456442
node/ @fjl
p2p/ @fjl @zsfelfoldi
rlp/ @fjl
params/ @fjl @gballet @rjl493456442 @zsfelfoldi
rpc/ @fjl

View file

@ -21,7 +21,7 @@ Please make sure your contributions adhere to our coding guidelines:
(i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
* Code must be documented adhering to the official Go * Code must be documented adhering to the official Go
[commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
* Pull requests need to be based on and opened against the `master` branch. * Pull requests need to be based on and opened against the `main` branch.
* Commit messages should be prefixed with the package(s) they modify. * Commit messages should be prefixed with the package(s) they modify.
* E.g. "eth, rpc: make trace configs optional" * E.g. "eth, rpc: make trace configs optional"

11
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

BIN
.github/meta/bera-geth.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,5 +1,5 @@
# Number of days of inactivity before an Issue is closed for lack of response # Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 30 daysUntilClose: 366
# Label requiring a response # Label requiring a response
responseRequiredLabel: "need:more-information" responseRequiredLabel: "need:more-information"
# Comment to post when closing an Issue for lack of response. Set to `false` to disable # Comment to post when closing an Issue for lack of response. Set to `false` to disable

106
.github/workflows/BACKPORT_README.md vendored Normal file
View file

@ -0,0 +1,106 @@
# Backport Workflow Documentation
## Overview
This repository uses an automated backport workflow to cherry-pick merged pull requests from the `main` branch to release branches. The workflow is powered by the [korthout/backport-action](https://github.com/marketplace/actions/backport-merged-pull-requests-to-selected-branches).
## How It Works
### Automatic Backporting via Labels
1. **Add a backport label** to your pull request before or after merging:
- Label format: `backport/{version}`
- Examples: `backport/v1.0.0`, `backport/v2.3.1`, `backport/1.0`
2. **When the PR is merged to main**, the workflow automatically:
- Detects all `backport/*` labels
- Transforms them to target `release/{version}` branches
- Creates new pull requests with cherry-picked commits to each target branch
3. **Target branches** must exist:
- For label `backport/v1.0.0` → targets branch `release/v1.0.0`
- For label `backport/2.0` → targets branch `release/2.0`
- The release branch must already exist in the repository
### Manual Backporting via Comments
You can also trigger backports manually by commenting on a merged PR:
- Comment `/backport` on any merged pull request
- The workflow will look for backport labels and create the appropriate PRs
## Example Workflow
1. Create a pull request to `main` with your changes
2. Add label: `backport/v1.2.3`
3. Get the PR reviewed and merged
4. The bot automatically:
- Creates a new branch: `backport-{PR-number}-to-release-v1.2.3`
- Cherry-picks the commits from your merged PR
- Opens a new PR from that branch to `release/v1.2.3`
- Adds labels: `backport`, `automated`
- Assigns the original PR author
## Handling Multiple Backports
You can backport to multiple release branches by adding multiple labels:
- `backport/v1.0.0` - backports to `release/v1.0.0`
- `backport/v2.0.0` - backports to `release/v2.0.0`
- `backport/v3.0.0` - backports to `release/v3.0.0`
Each label will create a separate backport PR.
## Conflict Resolution
When cherry-pick conflicts occur:
1. The bot creates a **draft PR** with the conflicts committed
2. The draft PR includes instructions on how to resolve the conflicts
3. You need to:
- Check out the backport branch locally
- Resolve the conflicts
- Push the resolved changes
- Mark the PR as ready for review
## Backport PR Format
Backport PRs are created with:
- **Title**: `[Backport release/{version}] {original PR title}`
- **Description**: Includes original PR information, author, and description
- **Labels**: `backport`, `automated`
- **Assignee**: Original PR author
## Prerequisites
For the workflow to function correctly:
1. Release branches must follow the naming pattern: `release/{version}`
2. The workflow must have write permissions (already configured)
3. Release branches must exist before attempting to backport
## Troubleshooting
### Backport not triggering
- Ensure the PR is merged to `main` (not just closed)
- Verify the label format is exactly `backport/{version}`
- Check that the target `release/{version}` branch exists
### Conflicts in backport
- The bot will create a draft PR with conflicts
- Follow the instructions in the PR to resolve conflicts locally
### Multiple commits or merge commits
- The workflow is configured to skip merge commits
- Only non-merge commits will be cherry-picked
- If you need merge commits, update the `merge_commits` setting in the workflow
## Configuration
The workflow is defined in `.github/workflows/backport.yml`. Key settings:
- **Label pattern**: `backport/{version}`
- **Target branch pattern**: `release/{version}`
- **Conflict strategy**: Create draft PRs with conflicts
- **Merge commits**: Skipped (only cherry-picks non-merge commits)
## Security
- The workflow uses `GITHUB_TOKEN` for authentication
- Runs on `pull_request_target` to handle forks securely
- Only triggers on merged PRs to prevent abuse

140
.github/workflows/RELEASE_SIGNING.md vendored Normal file
View file

@ -0,0 +1,140 @@
# Release Signing Setup
This document explains how release signing is implemented for bera-geth, including PGP signing for binaries and Cosign signing for Docker images.
## Overview
The bera-geth release process includes cryptographic signing for all release artifacts:
- **Binary releases**: Signed with PGP/GPG
- **Docker images**: Signed with Cosign (keyless signing via OIDC)
## Configuration
### Prerequisites for Release Managers
#### PGP Signing Setup
1. **Create the `LINUX_SIGNING_KEY` secret in GitHub**:
```bash
# Export your PGP private key
gpg --export-secret-keys --armor YOUR_KEY_ID > private.key
# Base64 encode it
cat private.key | base64 -w 0
# Copy the output and add it as the LINUX_SIGNING_KEY secret in GitHub repository settings
```
2. **Ensure the public key matches**: The public key at `.github/workflows/release.asc` should correspond to the private key used for signing.
#### Cosign Setup
Cosign uses keyless signing (no secret required). The workflow uses GitHub's OIDC provider to sign images, which means:
- No private keys to manage
- Signatures are tied to the GitHub Actions workflow identity
- Full transparency via Rekor transparency log
## Release Process
### Triggering a Release
Releases are triggered by:
- Pushing a tag matching `v1.*` (e.g., `v1.0.0`, `v1.0.0-rc1`)
- The workflow will automatically create a draft release with all signed artifacts
### What Gets Signed
1. **Binary Archives**:
- `bera-geth-linux-amd64-*.tar.gz``bera-geth-linux-amd64-*.tar.gz.asc`
- `bera-geth-linux-arm64-*.tar.gz``bera-geth-linux-arm64-*.tar.gz.asc`
- `bera-geth-alltools-linux-amd64-*.tar.gz``bera-geth-alltools-linux-amd64-*.tar.gz.asc`
- `bera-geth-alltools-linux-arm64-*.tar.gz``bera-geth-alltools-linux-arm64-*.tar.gz.asc`
2. **Docker Images**:
- Multi-arch images at `ghcr.io/berachain/bera-geth:VERSION`
- Signed with Cosign using keyless signing
## Verification Instructions
### Verifying Binary Signatures
Users can verify the PGP signatures of release binaries:
```bash
# Download and import the public key
curl -sSL https://raw.githubusercontent.com/berachain/bera-geth/main/.github/workflows/release.asc | gpg --import
# Download a release archive and its signature
wget https://github.com/berachain/bera-geth/releases/download/v1.0.0/bera-geth-linux-amd64-v1.0.0.tar.gz
wget https://github.com/berachain/bera-geth/releases/download/v1.0.0/bera-geth-linux-amd64-v1.0.0.tar.gz.asc
# Verify the signature
gpg --verify bera-geth-linux-amd64-v1.0.0.tar.gz.asc bera-geth-linux-amd64-v1.0.0.tar.gz
```
Expected output:
```
gpg: Signature made [date] using RSA key ID [key-id]
gpg: Good signature from "bera-geth-linux-signing-key"
```
### Verifying Docker Images
Docker images are signed with Cosign and can be verified:
```bash
# Install cosign if not already installed
brew install cosign # macOS
# or see https://docs.sigstore.dev/cosign/installation/
# Verify a specific version
cosign verify ghcr.io/berachain/bera-geth:v1.0.0
# Verify the latest image
cosign verify ghcr.io/berachain/bera-geth:latest
```
The verification will show:
- The GitHub Actions workflow that created the image
- The commit SHA
- The OIDC issuer (GitHub)
## Security Considerations
1. **PGP Key Security**:
- The PGP private key should be kept secure and only accessible to authorized release managers
- Regularly rotate keys and update the public key in the repository
- Use a strong passphrase for the private key
2. **Cosign Keyless Signing**:
- Signatures are tied to the GitHub Actions workflow identity
- Verification includes checking the workflow that signed the image
- All signatures are recorded in the Rekor transparency log
3. **Best Practices**:
- Always verify signatures before using release artifacts in production
- Check that the signing workflow matches the official repository
- Monitor the repository for any changes to signing keys or workflows
## Troubleshooting
### PGP Signing Issues
If PGP signing fails:
1. Check that the `LINUX_SIGNING_KEY` secret is properly set
2. Verify the key hasn't expired: `gpg --list-secret-keys`
3. Ensure the base64 encoding was done correctly
### Cosign Signing Issues
If Cosign signing fails:
1. Ensure the workflow has `id-token: write` permission
2. Check that the Docker image was successfully pushed before signing
3. Verify the image tag/digest is correct
### Release Draft Issues
If the release draft fails:
1. Ensure all artifacts were successfully uploaded
2. Check that the tag follows the correct format (`v1.*`)
3. Verify the workflow has `contents: write` permission

137
.github/workflows/backport.yml vendored Normal file
View file

@ -0,0 +1,137 @@
name: Backport merged pull request
on:
pull_request_target:
types: [closed]
issue_comment:
types: [created]
permissions:
contents: write # Required to push branches
pull-requests: write # Required to create pull requests
jobs:
backport:
name: Backport pull request
runs-on: ubuntu-latest
# Only run when:
# - A pull request to main is merged (not just closed)
# - OR a comment starting with '/backport' is created by someone other than the bot
if: >
(
github.event_name == 'pull_request_target' &&
github.event.pull_request.merged &&
github.event.pull_request.base.ref == 'main'
) || (
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
github.event.comment.user.id != 97796249 &&
startsWith(github.event.comment.body, '/backport')
)
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# Fetch all history for all branches
fetch-depth: 0
- name: Extract backport targets from labels
id: extract-targets
uses: actions/github-script@v7
with:
script: |
const labels = context.payload.pull_request?.labels || [];
const backportLabels = labels
.map(label => label.name)
.filter(name => name.startsWith('backport/'))
.map(name => {
// Extract version from "backport/{version}"
const version = name.replace('backport/', '');
// Transform to "release/{version}"
return `release/${version}`;
});
if (backportLabels.length > 0) {
console.log(`Found backport targets: ${backportLabels.join(', ')}`);
// Join with spaces as the backport action expects
core.setOutput('targets', backportLabels.join(' '));
core.setOutput('has-targets', 'true');
} else {
console.log('No backport labels found');
core.setOutput('has-targets', 'false');
}
// Also output the original labels for the action
const originalLabels = labels
.map(label => label.name)
.filter(name => name.startsWith('backport/'))
.map(name => {
// Transform "backport/{version}" to "backport release/{version}"
const version = name.replace('backport/', '');
return `backport release/${version}`;
})
.join(',');
core.setOutput('transformed-labels', originalLabels);
- name: Create backport pull requests
if: steps.extract-targets.outputs.has-targets == 'true'
uses: korthout/backport-action@v3
with:
# GitHub token for authentication
github_token: ${{ secrets.GITHUB_TOKEN }}
# Directly specify target branches extracted from labels
target_branches: ${{ steps.extract-targets.outputs.targets }}
# Disable label pattern matching since we're using target_branches
label_pattern: ""
# Branch name template for the backport branch
# This creates branches like: backport-123-to-release-v1.0.0
branch_name: "backport-${pull_number}-to-${target_branch}"
# Title template for the backport PR
pull_title: "[Backport ${target_branch}] ${pull_title}"
# Description template for the backport PR
pull_description: |
# Backport to ${target_branch}
This is an automatic backport of pull request #${pull_number} to `${target_branch}`.
## Original PR Information
- **Title**: ${pull_title}
- **Author**: @${pull_author}
- **Original PR**: #${pull_number}
## Original Description
${pull_description}
## Related Issues
${issue_refs}
---
*This pull request was created automatically by the backport action.*
# Labels to add to the backport PR
add_labels: "automated"
# Add original PR author as assignee
add_author_as_assignee: true
# Enable experimental features (includes conflict handling)
experimental: |
{
"detect_merge_method": true
}
# Cherry-picking strategy
# Options: "auto", "recursive", "ort"
cherry_picking: "auto"
# How to handle merge commits
# Options: "fail" or "skip"
# Using skip to handle PRs that were kept in sync with base via merge commits
merge_commits: "skip"

View file

@ -1,16 +1,16 @@
name: CI
on: on:
push: push:
branches: branches: [ main, "release/*" ]
- master
pull_request: pull_request:
branches: branches: [ main ]
- master
workflow_dispatch: workflow_dispatch:
jobs: jobs:
lint: lint:
name: Lint name: Lint
runs-on: self-hosted-ghr runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@ -34,15 +34,9 @@ jobs:
go run build/ci.go check_generate go run build/ci.go check_generate
go run build/ci.go check_baddeps go run build/ci.go check_baddeps
test: tests:
name: Test name: Tests
needs: lint runs-on: ubuntu-latest
runs-on: self-hosted-ghr
strategy:
matrix:
go:
- '1.25'
- '1.24'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@ -51,8 +45,8 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ${{ matrix.go }} go-version: 1.24.0
cache: false cache: false
- name: Run tests - name: Run tests
run: go test ./... run: go test -short ./...

118
.github/workflows/docker.yml vendored Normal file
View file

@ -0,0 +1,118 @@
name: Docker
on:
push:
tags:
- "v1.*"
branches:
- "main"
workflow_dispatch:
jobs:
docker-build:
name: Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
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: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push multi-arch images to GHCR
run: |
go run build/ci.go dockerx -platform linux/amd64,linux/arm64 -hub ghcr.io/berachain/bera-geth -upload
- name: Get image digest and version
id: image_info
run: |
# Extract version information
VERSION=""
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
VERSION="${{ github.ref_name }}"
elif [[ "${{ github.ref }}" == refs/heads/main ]]; then
VERSION="latest"
fi
if [ -z "$VERSION" ]; then
echo "Error: Could not determine version from ref '${{ github.ref }}'"
exit 1
fi
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
# Get the digest of the pushed manifest list (top-level manifest list digest)
# Avoid relying on template fields (which vary across buildx versions) and avoid -e exit on failure
INSPECT_OUTPUT=$(docker buildx imagetools inspect ghcr.io/berachain/bera-geth:${VERSION} || true)
DIGEST=$(printf "%s\n" "$INSPECT_OUTPUT" | awk '/^Digest:/ {print $2; exit}')
if [ -z "$DIGEST" ]; then
echo "Error: Could not determine image digest for ghcr.io/berachain/bera-geth:${VERSION}"
exit 1
fi
echo "DIGEST=${DIGEST}" >> $GITHUB_OUTPUT
outputs:
VERSION: ${{ steps.image_info.outputs.VERSION }}
DIGEST: ${{ steps.image_info.outputs.DIGEST }}
docker-signatures:
name: Docker Signatures
needs: [docker-build]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.docker-build.outputs.VERSION }}
DIGEST: ${{ needs.docker-build.outputs.DIGEST }}
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Sign Docker images with Cosign
env:
COSIGN_EXPERIMENTAL: true
COSIGN_YES: "true"
run: |
# Sign the tagged version if it exists
if [ -n "${{ env.VERSION }}" ]; then
cosign sign --yes ghcr.io/berachain/bera-geth:${{ env.VERSION }}
fi
# Sign the multi-arch manifest by digest
if [ -n "${{ env.DIGEST }}" ]; then
cosign sign --yes ghcr.io/berachain/bera-geth@${{ env.DIGEST }}
else
echo "Error: DIGEST is empty. Skipping digest signing."
exit 1
fi

52
.github/workflows/release.asc vendored Normal file
View file

@ -0,0 +1,52 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGiFS/oBEAC8b3nWhb1bFjOJhz4tkFN8ku5J6ElWj0wCzM6l2WZPhcU/VBMd
0/iIDC2ZtFp2a11N9xpT1+ct5pYaUwLUor4Ms9p+mi7Ct3wGKgcOngGgfAqTdm/1
V04e9Q6ATPWimaC6vHEMHXYR7NmQuqazluwIAai6RfKm6gXVfgVHdOFC+Chr7Pk6
QRn3fXXnFHnLakQwFz12q0bq6UemKYfPEHl28ObKFXJ0fG8bVKlbghxdJ6UZWbF4
n+gzeFHxEdgvChqa/GBt9Rqpw55b/lrEZ4hp9q9RYcvTZ+q5RJjA+KM7zQMehwan
8I3Uyo9cNI+6NDp/UBQRSXP5dqMp4CjMxlBTuDHyaB1SSuxNThYkMmsEZIRppRPu
g7znwLtErD4Pptzzo1Am7i614t2UtKf4FAVrUPJOeaShu600W7BFeqZvkrKJMbpu
+k/Z+EAQR4hQS0FqpGuB4xlIoR3HuEdOaL7aQ04ZjDlEzS12KlZCmKT3rtCWz7Zg
kAw89iPkDjzGoO+pNtvU2WA8VroCiGg4BJbuSeIi89KU286IkrSUiB0QZcLicbdX
k8ro6Ns+prf0XeWlXn3g5NLpRc1UfVvHB3gRo7L7dCCxHdnPZke0SYLlkY/B6MIL
FE4+SnJV5sDCAoSlcYVqAChqN3Vh1hCnsXpK/2HUrX6HDPWOZNSVYuqTXwARAQAB
tCpiZXJhLWdldGggcmVsZWFzZXIgPGNhbGJlcmFAYmVyYWNoYWluLmNvbT6JAlEE
EwEIADsWIQTN/ECePFkfXNo+9Agx95ZXOzmsaAUCaIVL+gIbAwULCQgHAgIiAgYV
CgkICwIEFgIDAQIeBwIXgAAKCRAx95ZXOzmsaKKWEACd+pD8proofzZgwL9b+3ql
rwwuDfjkORkKW0+ve5Y10ydu/VsWAqGRQIPBaCyaCFlziFqPSnWQA5NhK7rfytVt
2pOTD2KsbBfYtk7wWrgzG78xgOa9SCsO8p908QytsunWUgBK/zFgHH2sVYNjWlPt
nU92z7nBdq7NYiW8S2WrsdZgXvVyVitymaVyLUnnyXCezYqZ81+AuIUmKpcz9aLv
VQBL3Z2CNt8IWR1xRg0dkVFEqI7C+KoA+lQXIij0gapUOf4039YUIj7XgvIrbDXr
juzEhmvWw8kPLC5ZX60whX+QYewYnBCckKeYK8HwUJgcxN6OQ5RsPbt60PK9+Hdb
E1D8/Sl2srud6erV4Ji4/TFDBx6o08KqBiHbU1PvEov7qfnQLQ79lhlOiIaNTAzy
qIRyiM0NSM4Meq+bVT+2bYn83fgMzI9YOa8kqoCY0wZcbK48/S8YJ3mq3zShoQEm
tSD/8EwNjRuoaddwTQdcRnhu45QxkQmg1blwnDr9wp4pGI6xeWSUEzoyn9HePkN9
HjQS7Ooyh81ahqrenTO3Sy0t65/XRD1VZfFZ0FYxI0VJr32b1bdyJnlh3+2CRjTB
jTC7eRnM+rFSuK1Whv6WPgWK9nKEQa89KDJqRciGddbkjobgKfTGfhwSlCdSPkvr
Ffkkzngq5/GwzgLXosTA5rkCDQRohUv6ARAAqxG/ZODPfDXxgunrohuXiL6kdGq6
ZgI9ouOgeKSHXEeEeBE5NDmLne/kzmlo/GIDcNucieHoJ1/8rVjL7v/9/veroz3q
hHT3E5TrlkSnDR6b2588SqsxVX/OLASw7es2/aOM6Jz4Gd4IgcemZtZuLiopFGBS
CmrKqqzMA+WRkGDAkrcji1TZNNZlRlZfhF60LtsIxopKKfJ3wRy38geW84JMM2Ol
ZD8lEJr965ikHqeJdsqJkhRLp/Qp1NjM0NEbwh/24KSd2gNcrrMDoOAZSUpv3vV/
kXY4ZXJUgBY6L9g5/zbdKwiCtSw3WTJVUBlh+y2Cmgz9NG9ipnqiRXEyeuQsWfVh
mmv1XDBxPNGHzEQYGuf09fbzOomdOv8aSma2BZT+UT/ajMLN3EIO1dBioR2CwX2P
3zlxNL33aNgFNg0QPyLPlkbjYxty91fj29Cz6UKEJW8XoHJ8LWmeYIwGNc2+B7ty
NVpqG7GoWp/yxbbhXT9ISXzI/ZbHwb71wLOyG9c7ZQSaBkMLbgqLdMbdJ4sfhyrI
WYfi8HayshWwR0tVlshs2lOaNgL6UtpiC2Ni87MOGO4TeKb3R9QoCiti9hpkSmoW
SDqwMWa3VLUMZUMZUvDx3iKAZ23nA1xeB/PPLlsv9+GJDpS7j8CCzlwYKSdEIAL7
l7zvu7+eos4gJkEAEQEAAYkCNgQYAQgAIBYhBM38QJ48WR9c2j70CDH3llc7Oaxo
BQJohUv6AhsMAAoJEDH3llc7OaxoLmsQAIXXQiD5xTEcu+0JAWIfyr6bHxg1CeeQ
luWfxraTxomCsKCHu6uloMCbfA/vkw3ArkAxK4ZsLojzkwkqZIt/SJDV35tMobpv
LE32DupyjAPFkuFTCbPFKPtRP5e3aCb8n1bHq7OqaAoMhBqLFyDKeh9hduKNpQie
508IOtrdnoq3oN5z1TWNwUY88pcd3sEhpcxekWBftghVhO+EVyVUJ3fUxouH+v/M
YX33zxREIYx+OHBMlgQFEM6UWFHwn7ILWxxQaG0/CXYDadnC1IymzP0bWlhEZdpU
QG1ZE22Ymkyb2ZuyHAJqUlLMKeYiiv3eY4gXQZCbDUzU/egAXooS/OrO9QVDXswE
FlJgGuvf5QIw/aicicc6+uT5jytDewteS/fS7Jic4dPm9vAOZjhEBfnSpEjj3/jw
d5+bUu5fVSo5PfMEbwxHv9g+uR505ymqVbkK0pXhpSiZyfb3qXI02aMoegMwY/8h
sRSkt25iQ5ofk12pbzHB51pKCK1U8oHYDMB/89hpBUc1amL5M1gP1asZj34TC1y9
qhg8Qo9H0AyJY2QAc9OqBIDFhotf+q/st6YSnTkXoY0IaJgA28I5ACEJRsH6/unz
51wTAaWKaLFz3OL39GFLMkuIV/2rq5geP+u89m8dgyrZOMB/ZgpG6VA1dY9ZAf3/
3uG6ibaGc3mn
=/kse
-----END PGP PUBLIC KEY BLOCK-----

270
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,270 @@
name: Release
on:
push:
tags:
- "v1.*"
workflow_dispatch:
jobs:
extract-version:
name: Extract Version
runs-on: ubuntu-latest
steps:
- name: Extract version
run: echo "VERSION=$(echo ${GITHUB_REF#refs/tags/})" >> $GITHUB_OUTPUT
id: extract_version
outputs:
VERSION: ${{ steps.extract_version.outputs.VERSION }}
linux-intel:
name: Linux Build (intel)
needs: [extract-version]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.extract-version.outputs.VERSION }}
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: |
sudo apt-get update
sudo apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib
- name: Build (amd64)
run: |
go run build/ci.go install -static -arch amd64 -dlgo -git-tag="${{ env.VERSION }}"
- name: Create archive (amd64)
run: |
go run build/ci.go archive -arch amd64 -type tar -signer LINUX_SIGNING_KEY
env:
LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }}
- name: Upload artifacts (amd64)
uses: actions/upload-artifact@v4
with:
name: bera-geth-linux-amd64-archives
path: |
bera-geth-linux-amd64-*.tar.gz
bera-geth-linux-amd64-*.tar.gz.asc
bera-geth-alltools-linux-amd64-*.tar.gz
bera-geth-alltools-linux-amd64-*.tar.gz.asc
- name: Cleanup bin
run: rm -f build/bin/*
linux-arm:
name: Linux Build (arm)
needs: [extract-version]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.extract-version.outputs.VERSION }}
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: |
sudo apt-get update
sudo 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
sudo ln -s /usr/include/asm-generic /usr/include/asm
- name: Build (arm64)
run: |
go run build/ci.go install -static -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc -git-tag="${{ env.VERSION }}"
- name: Create archive (arm64)
run: |
go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY
env:
LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }}
- name: Upload artifacts (arm64)
uses: actions/upload-artifact@v4
with:
name: bera-geth-linux-arm64-archives
path: |
bera-geth-linux-arm64-*.tar.gz
bera-geth-linux-arm64-*.tar.gz.asc
bera-geth-alltools-linux-arm64-*.tar.gz
bera-geth-alltools-linux-arm64-*.tar.gz.asc
- name: Cleanup bin
run: rm -fr build/bin/*
draft-release:
name: Draft Release
needs: [extract-version, linux-intel, linux-arm]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.extract-version.outputs.VERSION }}
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # This is necessary for generating the changelog
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
- name: Generate full changelog
id: changelog
run: |
echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
echo "$(git log --pretty=format:"- %s" $(git describe --tags --abbrev=0 ${{ env.VERSION }}^)..${{ env.VERSION }})" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create release draft
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Debug: Show what was downloaded
echo "Contents of artifacts directory:"
ls -la ./artifacts/ || echo "Artifacts directory not found"
# Move all artifacts to current directory
if [ -d "./artifacts" ]; then
find ./artifacts -name "*.tar.gz" -exec mv {} . \;
find ./artifacts -name "*.tar.gz.asc" -exec mv {} . \;
else
echo "Warning: No artifacts directory found"
fi
# List available files for debugging
echo "Available files:"
ls -la *.tar.gz *.tar.gz.asc 2>/dev/null || echo "No archive files found"
# Get actual archive names
GETH_AMD64=$(ls bera-geth-linux-amd64-*.tar.gz 2>/dev/null | grep -v alltools | head -1)
GETH_ARM64=$(ls bera-geth-linux-arm64-*.tar.gz 2>/dev/null | grep -v alltools | head -1)
GETH_ALLTOOLS_AMD64=$(ls bera-geth-alltools-linux-amd64-*.tar.gz 2>/dev/null | head -1)
GETH_ALLTOOLS_ARM64=$(ls bera-geth-alltools-linux-arm64-*.tar.gz 2>/dev/null | head -1)
echo "Found archives:"
echo " AMD64: ${GETH_AMD64:-NOT FOUND}"
echo " ARM64: ${GETH_ARM64:-NOT FOUND}"
echo " Alltools AMD64: ${GETH_ALLTOOLS_AMD64:-NOT FOUND}"
echo " Alltools ARM64: ${GETH_ALLTOOLS_ARM64:-NOT FOUND}"
# For tag builds, use the actual tag
RELEASE_TAG="${{ env.VERSION }}"
echo "Release tag: $RELEASE_TAG"
echo "GitHub ref: ${{ github.ref }}"
echo "GitHub SHA: ${{ github.sha }}"
# The tag should already exist since it triggered this workflow
# We just need to ensure gh release uses it correctly
body=$(cat <<- 'ENDBODY'
![image](https://raw.githubusercontent.com/berachain/bera-geth/main/.github/meta/bera-geth.png)
## Summary
Add a summary, including:
- Critical bug fixes
- New features
- Any breaking changes (and what to expect)
## Update Priority
This table provides priorities for which classes of users should update particular components.
| User Class | Priority |
|----------------------|-----------------|
| Payload Builders | <TODO> |
| Non-Payload Builders | <TODO> |
## All Changes
${{ steps.changelog.outputs.CHANGELOG }}
## Binaries
| System | Architecture | Binary | PGP Signature | Notes |
|:---:|:---:|:---:|:---:|:---|
| <img src="https://simpleicons.org/icons/linux.svg" style="width: 32px;"/> | amd64 | [GETH_AMD64_PLACEHOLDER](https://github.com/${{ github.repository }}/releases/download/RELEASE_TAG_PLACEHOLDER/GETH_AMD64_PLACEHOLDER) | [Signature](https://github.com/${{ github.repository }}/releases/download/RELEASE_TAG_PLACEHOLDER/GETH_AMD64_PLACEHOLDER.asc) | Most Linux systems |
| <img src="https://simpleicons.org/icons/linux.svg" style="width: 32px;"/> | arm64 | [GETH_ARM64_PLACEHOLDER](https://github.com/${{ github.repository }}/releases/download/RELEASE_TAG_PLACEHOLDER/GETH_ARM64_PLACEHOLDER) | [Signature](https://github.com/${{ github.repository }}/releases/download/RELEASE_TAG_PLACEHOLDER/GETH_ARM64_PLACEHOLDER.asc) | 64-bit ARM systems |
| <img src="https://simpleicons.org/icons/linux.svg" style="width: 32px;"/> | amd64 | [GETH_ALLTOOLS_AMD64_PLACEHOLDER](https://github.com/${{ github.repository }}/releases/download/RELEASE_TAG_PLACEHOLDER/GETH_ALLTOOLS_AMD64_PLACEHOLDER) | [Signature](https://github.com/${{ github.repository }}/releases/download/RELEASE_TAG_PLACEHOLDER/GETH_ALLTOOLS_AMD64_PLACEHOLDER.asc) | All tools bundle (amd64) |
| <img src="https://simpleicons.org/icons/linux.svg" style="width: 32px;"/> | arm64 | [GETH_ALLTOOLS_ARM64_PLACEHOLDER](https://github.com/${{ github.repository }}/releases/download/RELEASE_TAG_PLACEHOLDER/GETH_ALLTOOLS_ARM64_PLACEHOLDER) | [Signature](https://github.com/${{ github.repository }}/releases/download/RELEASE_TAG_PLACEHOLDER/GETH_ALLTOOLS_ARM64_PLACEHOLDER.asc) | All tools bundle (arm64) |
| **System** | **Option** | - | **Resource** |
| <img src="https://simpleicons.org/icons/docker.svg" style="width: 32px;"/> | Docker | | [ghcr.io/berachain/bera-geth](https://ghcr.io/berachain/bera-geth) |
### Verifying Binary Signatures
All release binaries are signed with PGP. To verify:
1. Download the [public key](https://raw.githubusercontent.com/${{ github.repository }}/main/.github/workflows/release.asc)
2. Import the key: `gpg --import release.asc`
3. Verify the signature: `gpg --verify <filename>.asc <filename>`
### Docker Images
Docker images are available at `ghcr.io/berachain/bera-geth` and are signed with [Cosign](https://github.com/sigstore/cosign) using keyless signing from GitHub Actions OIDC.
To verify a specific release image (recommended: by digest):
```bash
cosign verify \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--certificate-identity-regexp "^https://github.com/berachain/bera-geth/.github/workflows/docker.yml@.+" \
ghcr.io/berachain/bera-geth@$(docker buildx imagetools inspect ghcr.io/berachain/bera-geth:RELEASE_TAG_PLACEHOLDER | awk '/^Digest:/ {print $2; exit}')
```
To verify the `latest` unstable image instead (not recommended; tags can move):
```bash
cosign verify \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--certificate-identity-regexp "^https://github.com/berachain/bera-geth/.github/workflows/docker.yml@.+" \
ghcr.io/berachain/bera-geth@$(docker buildx imagetools inspect ghcr.io/berachain/bera-geth:latest | awk '/^Digest:/ {print $2; exit}')
```
### Installation
The archives contain the geth binary and license file. Extract and run:
```bash
tar -xzf GETH_AMD64_PLACEHOLDER
./geth
```
The **alltools** archives additionally contain:
- `abigen` - Source code generator for Ethereum contracts
- `evm` - Developer utility for the Ethereum Virtual Machine
- `rlpdump` - Developer utility for RLP data
- `clef` - Ethereum account management tool
ENDBODY
)
# Replace placeholders with actual filenames and release tag
body="${body//GETH_AMD64_PLACEHOLDER/$GETH_AMD64}"
body="${body//GETH_ARM64_PLACEHOLDER/$GETH_ARM64}"
body="${body//GETH_ALLTOOLS_AMD64_PLACEHOLDER/$GETH_ALLTOOLS_AMD64}"
body="${body//GETH_ALLTOOLS_ARM64_PLACEHOLDER/$GETH_ALLTOOLS_ARM64}"
body="${body//RELEASE_TAG_PLACEHOLDER/$RELEASE_TAG}"
# Create assets array for gh release
assets=()
for asset in bera-geth-*.tar.gz bera-geth-*.tar.gz.asc; do
if [ -f "$asset" ]; then
assets+=("$asset")
fi
done
# Create the release with the actual tag
# Using --target with GITHUB_SHA ensures we create the release at the right commit
echo "$body" | gh release create "$RELEASE_TAG" \
--draft \
--title "Bera Geth $RELEASE_TAG" \
-F - \
"${assets[@]}"

8
.gitignore vendored
View file

@ -55,4 +55,10 @@ cmd/ethkey/ethkey
cmd/evm/evm cmd/evm/evm
cmd/geth/geth cmd/geth/geth
cmd/rlpdump/rlpdump cmd/rlpdump/rlpdump
cmd/workload/workload cmd/workload/workload
# claude
.claude
# for testing docker builds
.github/workflows/docker-test.yml

22
CHANGELOG.md Normal file
View file

@ -0,0 +1,22 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.TBD.0]
### Added
- Add support for the Berachain mainnet (`--berachain`) and Bepolia networks
(`--bepolia`)
- Add support for configuring the Prague1 fork on Berachain networks.
### Changed
- BRIP-2: Allowed configuration of the `MinimumBaseFeeWei`. For Berachain, this will be set as 1 gwei. (https://github.com/berachain/bera-geth/pull/2)
- BRIP-2: Allowed configuration of the `BaseFeeChangeDenominator`. For Berachain, this will be set as 48 (a 6x increase corresponding to 6x faster block times). (https://github.com/berachain/bera-geth/pull/2)
- [WIP] Updated release GH workflow to publish tarballs and push built images to GHCR.
- Removed support for appveyor, gitea, travis, circleci.

145
CLAUDE.md Normal file
View file

@ -0,0 +1,145 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is **bera-geth**, Berachain's fork of go-ethereum that implements the Berachain blockchain network. Key differences from upstream:
- **Custom base fee mechanism**: 1 gwei minimum with 6x faster adjustments via Prague1 fork
- **Networks**: Berachain mainnet (Chain ID 80094) and Bepolia testnet (Chain ID 80069)
- **Optimized for faster block times** while maintaining Ethereum compatibility
## Development Commands
### Building
```bash
make geth # Build main geth binary
make all # Build all executables
make evm # Build EVM utility
```
### Testing
```bash
make test # Run all tests
go test ./path/to/package # Run specific package tests
go test -run TestName # Run specific test
```
### Code Quality
```bash
make lint # Run linters
make fmt # Format code with gofmt
```
### Development Tools
```bash
make devtools # Install development dependencies
```
### Cleaning
```bash
make clean # Clean build artifacts
```
## Codebase Architecture
### Core Components
**Core Blockchain Logic** (`/core/`)
- `blockchain.go` - Main blockchain state management
- `state_processor.go` - Transaction execution
- `types/` - Core data structures (blocks, transactions, receipts)
- `vm/` - Ethereum Virtual Machine implementation
- `txpool/` - Transaction pool management
**Consensus Layer** (`/consensus/`)
- `beacon/` - Proof-of-stake consensus (primary)
- `misc/eip1559/` - Base fee calculation with Berachain modifications
- `clique/` - Proof-of-authority for testing
**Network Protocol** (`/eth/`)
- `backend.go` - Main Ethereum service
- `downloader/` - Block/state synchronization
- `protocols/` - Network protocol handlers
**P2P Networking** (`/p2p/`)
- `discover/` - Node discovery protocols
- `rlpx/` - Encrypted communication
### Key Entry Points
**Main Binary**: `/cmd/geth/main.go`
- CLI application setup
- Network selection logic
- Node initialization
**Other Executables**: `/cmd/`
- `clef/` - Account management
- `abigen/` - ABI binding generator
- `evm/` - Standalone EVM executor
### Configuration Management
**Chain Configs**: `/params/config.go`
- Network parameters and fork schedules
- Berachain/Bepolia network definitions
- Prague1 fork configuration
**Network Discovery**: `/params/bootnodes.go`
- Bootstrap nodes for peer discovery
### Berachain-Specific Modifications
**Base Fee Changes** (`/consensus/misc/eip1559/eip1559.go:116-121`)
- Prague1 fork enforces 1 gwei minimum base fee
- BaseFeeChangeDenominator set to 48 (6x faster adjustment)
**Network Configurations** (`/params/config.go:77-99, 107-129`)
- BerachainChainConfig with Chain ID 80094
- BepoliaChainConfig with Chain ID 80069
- Custom Prague1 fork activation parameters
### Test Organization
Tests follow Go conventions with `*_test.go` files alongside source code:
- Unit tests in package directories
- Integration tests in `/tests/`
- Consensus tests for Ethereum specification compliance
- EVM operation tests in `/core/vm/testdata/`
### Build System
**Primary Build**: `/build/ci.go`
- Cross-platform compilation
- Docker image building
- Release packaging
**Simple Builds**: `/Makefile`
- Development-focused targets
- Requires Go 1.23+ and C compiler
## Development Workflow
1. **Architecture Understanding**: The codebase follows Ethereum's layered architecture (consensus → state → networking → application)
2. **Testing Strategy**: Always run tests after changes with `make test`
3. **Code Style**: Use `make fmt` and `make lint` before committing
4. **Network Testing**: Use `--bepolia` flag for testnet development
5. **Debugging**: Use `./build/bin/geth --help` for comprehensive CLI options
## Network-Specific Usage
### Berachain Mainnet
```bash
./build/bin/geth --berachain
```
### Bepolia Testnet
```bash
./build/bin/geth --bepolia
```
### Development Mode
```bash
./build/bin/geth --dev
```

View file

@ -6,24 +6,36 @@ ARG BUILDNUM=""
# Build Geth in a stock Go builder container # Build Geth in a stock Go builder container
FROM golang:1.24-alpine AS builder FROM golang:1.24-alpine AS builder
# These build arguments need to be redeclared in the builder stage
ARG COMMIT
ARG VERSION
ARG BUILDNUM
RUN apk add --no-cache gcc musl-dev linux-headers git RUN apk add --no-cache gcc musl-dev linux-headers git
# Get dependencies - will also be cached if we won't change go.mod/go.sum # Get dependencies - will also be cached if we won't change go.mod/go.sum
COPY go.mod /go-ethereum/ COPY go.mod /bera-geth/
COPY go.sum /go-ethereum/ COPY go.sum /bera-geth/
RUN cd /go-ethereum && go mod download RUN cd /bera-geth && go mod download
ADD . /go-ethereum ADD . /bera-geth
RUN cd /go-ethereum && go run build/ci.go install -static ./cmd/geth # Pass git information to the build process
# When VERSION is provided (e.g., from CI), use it as the git tag
RUN cd /bera-geth && \
if [ -n "$VERSION" ]; then \
go run build/ci.go install -static -git-tag="$VERSION" -git-commit="$COMMIT" ./cmd/bera-geth; \
else \
go run build/ci.go install -static ./cmd/bera-geth; \
fi
# Pull Geth into a second stage deploy alpine container # Pull Geth into a second stage deploy alpine container
FROM alpine:latest FROM alpine:latest
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates
COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/ COPY --from=builder /bera-geth/build/bin/bera-geth /usr/local/bin/
EXPOSE 8545 8546 30303 30303/udp EXPOSE 8545 8546 30303 30303/udp
ENTRYPOINT ["geth"] ENTRYPOINT ["bera-geth"]
# Add some metadata labels to help programmatic image consumption # Add some metadata labels to help programmatic image consumption
ARG COMMIT="" ARG COMMIT=""

View file

@ -6,28 +6,44 @@ ARG BUILDNUM=""
# Build Geth in a stock Go builder container # Build Geth in a stock Go builder container
FROM golang:1.24-alpine AS builder FROM golang:1.24-alpine AS builder
# These build arguments need to be redeclared in the builder stage
ARG COMMIT
ARG VERSION
ARG BUILDNUM
RUN apk add --no-cache gcc musl-dev linux-headers git RUN apk add --no-cache gcc musl-dev linux-headers git
# Get dependencies - will also be cached if we won't change go.mod/go.sum # Get dependencies - will also be cached if we won't change go.mod/go.sum
COPY go.mod /go-ethereum/ COPY go.mod /bera-geth/
COPY go.sum /go-ethereum/ COPY go.sum /bera-geth/
RUN cd /go-ethereum && go mod download RUN cd /bera-geth && go mod download
ADD . /go-ethereum ADD . /bera-geth
# This is not strictly necessary, but it matches the "Dockerfile" steps, thus # This is not strictly necessary, but it matches the "Dockerfile" steps, thus
# makes it so that under certain circumstances, the docker layer can be cached, # makes it so that under certain circumstances, the docker layer can be cached,
# and the builder can jump to the next (build all) command, with the go cache fully loaded. # and the builder can jump to the next (build all) command, with the go cache fully loaded.
# #
RUN cd /go-ethereum && go run build/ci.go install -static ./cmd/geth # Pass git information to the build process when provided
RUN cd /bera-geth && \
if [ -n "$VERSION" ]; then \
go run build/ci.go install -static -git-tag="$VERSION" -git-commit="$COMMIT" ./cmd/bera-geth; \
else \
go run build/ci.go install -static ./cmd/bera-geth; \
fi
RUN cd /go-ethereum && go run build/ci.go install -static RUN cd /bera-geth && \
if [ -n "$VERSION" ]; then \
go run build/ci.go install -static -git-tag="$VERSION" -git-commit="$COMMIT"; \
else \
go run build/ci.go install -static; \
fi
# Pull all binaries into a second stage deploy alpine container # Pull all binaries into a second stage deploy alpine container
FROM alpine:latest FROM alpine:latest
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/ COPY --from=builder /bera-geth/build/bin/* /usr/local/bin/
EXPOSE 8545 8546 30303 30303/udp EXPOSE 8545 8546 30303 30303/udp

View file

@ -2,17 +2,17 @@
# with Go source code. If you know what GOPATH is then you probably # with Go source code. If you know what GOPATH is then you probably
# don't need to bother with make. # don't need to bother with make.
.PHONY: geth evm all test lint fmt clean devtools help .PHONY: bera-geth evm all test lint fmt clean devtools help
GOBIN = ./build/bin GOBIN = ./build/bin
GO ?= latest GO ?= latest
GORUN = go run GORUN = go run
#? geth: Build geth. #? bera-geth: Build bera-geth.
geth: bera-geth:
$(GORUN) build/ci.go install ./cmd/geth $(GORUN) build/ci.go install ./cmd/bera-geth
@echo "Done building." @echo "Done building."
@echo "Run \"$(GOBIN)/geth\" to launch geth." @echo "Run \"$(GOBIN)/bera-geth\" to launch bera-geth."
#? evm: Build evm. #? evm: Build evm.
evm: evm:

View file

@ -16,11 +16,11 @@ archives are published at https://geth.ethereum.org/downloads/.
For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/getting-started/installing-geth). For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/getting-started/installing-geth).
Building `geth` requires both a Go (version 1.23 or later) and a C compiler. You can install Building `bera-geth` requires both a Go (version 1.23 or later) and a C compiler. You can install
them using your favourite package manager. Once the dependencies are installed, run them using your favourite package manager. Once the dependencies are installed, run
```shell ```shell
make geth make bera-geth
``` ```
or, to build the full suite of utilities: or, to build the full suite of utilities:
@ -36,19 +36,19 @@ directory.
| Command | Description | | Command | Description |
| :--------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | :--------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI page](https://geth.ethereum.org/docs/fundamentals/command-line-options) for command line options. | | **`bera-geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `bera-geth --help` and the [CLI page](https://geth.ethereum.org/docs/fundamentals/command-line-options) for command line options. |
| `clef` | Stand-alone signing tool, which can be used as a backend signer for `geth`. | | `clef` | Stand-alone signing tool, which can be used as a backend signer for `bera-geth`. |
| `devp2p` | Utilities to interact with nodes on the networking layer, without running a full blockchain. | | `devp2p` | Utilities to interact with nodes on the networking layer, without running a full blockchain. |
| `abigen` | Source code generator to convert Ethereum contract definitions into easy-to-use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://geth.ethereum.org/docs/developers/dapp-developer/native-bindings) page for details. | | `abigen` | Source code generator to convert Ethereum contract definitions into easy-to-use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://geth.ethereum.org/docs/developers/dapp-developer/native-bindings) page for details. |
| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). | | `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). |
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). | | `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
## Running `geth` ## Running `bera-geth`
Going through all the possible command line flags is out of scope here (please consult our Going through all the possible command line flags is out of scope here (please consult our
[CLI Wiki page](https://geth.ethereum.org/docs/fundamentals/command-line-options)), [CLI Wiki page](https://geth.ethereum.org/docs/fundamentals/command-line-options)),
but we've enumerated a few common parameter combos to get you up to speed quickly but we've enumerated a few common parameter combos to get you up to speed quickly
on how you can run your own `geth` instance. on how you can run your own `bera-geth` instance.
### Hardware Requirements ### Hardware Requirements
@ -74,19 +74,19 @@ particular use case, the user doesn't care about years-old historical data, so w
sync quickly to the current state of the network. To do so: sync quickly to the current state of the network. To do so:
```shell ```shell
$ geth console $ bera-geth console
``` ```
This command will: This command will:
* Start `geth` in snap sync mode (default, can be changed with the `--syncmode` flag), * Start `bera-geth` in snap sync mode (default, can be changed with the `--syncmode` flag),
causing it to download more data in exchange for avoiding processing the entire history causing it to download more data in exchange for avoiding processing the entire history
of the Ethereum network, which is very CPU intensive. of the Ethereum network, which is very CPU intensive.
* Start the built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interacting-with-geth/javascript-console), * Start the built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interacting-with-geth/javascript-console),
(via the trailing `console` subcommand) through which you can interact using [`web3` methods](https://github.com/ChainSafe/web3.js/blob/0.20.7/DOCUMENTATION.md) (via the trailing `console` subcommand) through which you can interact using [`web3` methods](https://github.com/ChainSafe/web3.js/blob/0.20.7/DOCUMENTATION.md)
(note: the `web3` version bundled within `geth` is very old, and not up to date with official docs), (note: the `web3` version bundled within `bera-geth` is very old, and not up to date with official docs),
as well as `geth`'s own [management APIs](https://geth.ethereum.org/docs/interacting-with-geth/rpc). as well as `bera-geth`'s own [management APIs](https://geth.ethereum.org/docs/interacting-with-geth/rpc).
This tool is optional and if you leave it out you can always attach it to an already running This tool is optional and if you leave it out you can always attach it to an already running
`geth` instance with `geth attach`. `bera-geth` instance with `bera-geth attach`.
### A Full node on the Holesky test network ### A Full node on the Holesky test network
@ -97,45 +97,45 @@ network, you want to join the **test** network with your node, which is fully eq
the main network, but with play-Ether only. the main network, but with play-Ether only.
```shell ```shell
$ geth --holesky console $ bera-geth --holesky console
``` ```
The `console` subcommand has the same meaning as above and is equally The `console` subcommand has the same meaning as above and is equally
useful on the testnet too. useful on the testnet too.
Specifying the `--holesky` flag, however, will reconfigure your `geth` instance a bit: Specifying the `--holesky` flag, however, will reconfigure your `bera-geth` instance a bit:
* Instead of connecting to the main Ethereum network, the client will connect to the Holesky * Instead of connecting to the main Ethereum network, the client will connect to the Holesky
test network, which uses different P2P bootnodes, different network IDs and genesis test network, which uses different P2P bootnodes, different network IDs and genesis
states. states.
* Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth` * Instead of using the default data directory (`~/.ethereum` on Linux for example), `bera-geth`
will nest itself one level deeper into a `holesky` subfolder (`~/.ethereum/holesky` on will nest itself one level deeper into a `holesky` subfolder (`~/.ethereum/holesky` on
Linux). Note, on OSX and Linux this also means that attaching to a running testnet node Linux). Note, on OSX and Linux this also means that attaching to a running testnet node
requires the use of a custom endpoint since `geth attach` will try to attach to a requires the use of a custom endpoint since `bera-geth attach` will try to attach to a
production node endpoint by default, e.g., production node endpoint by default, e.g.,
`geth attach <datadir>/holesky/geth.ipc`. Windows users are not affected by `bera-geth attach <datadir>/holesky/geth.ipc`. Windows users are not affected by
this. this.
*Note: Although some internal protective measures prevent transactions from *Note: Although some internal protective measures prevent transactions from
crossing over between the main network and test network, you should always crossing over between the main network and test network, you should always
use separate accounts for play and real money. Unless you manually move use separate accounts for play and real money. Unless you manually move
accounts, `geth` will by default correctly separate the two networks and will not make any accounts, `bera-geth` will by default correctly separate the two networks and will not make any
accounts available between them.* accounts available between them.*
### Configuration ### Configuration
As an alternative to passing the numerous flags to the `geth` binary, you can also pass a As an alternative to passing the numerous flags to the `bera-geth` binary, you can also pass a
configuration file via: configuration file via:
```shell ```shell
$ geth --config /path/to/your_config.toml $ bera-geth --config /path/to/your_config.toml
``` ```
To get an idea of how the file should look like you can use the `dumpconfig` subcommand to To get an idea of how the file should look like you can use the `dumpconfig` subcommand to
export your existing configuration: export your existing configuration:
```shell ```shell
$ geth --your-favourite-flags dumpconfig $ bera-geth --your-favourite-flags dumpconfig
``` ```
#### Docker quick start #### Docker quick start
@ -149,25 +149,25 @@ docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \
ethereum/client-go ethereum/client-go
``` ```
This will start `geth` in snap-sync mode with a DB memory allowance of 1GB, as the This will start `bera-geth` in snap-sync mode with a DB memory allowance of 1GB, as the
above command does. It will also create a persistent volume in your home directory for above command does. It will also create a persistent volume in your home directory for
saving your blockchain as well as map the default ports. There is also an `alpine` tag saving your blockchain as well as map the default ports. There is also an `alpine` tag
available for a slim version of the image. available for a slim version of the image.
Do not forget `--http.addr 0.0.0.0`, if you want to access RPC from other containers Do not forget `--http.addr 0.0.0.0`, if you want to access RPC from other containers
and/or hosts. By default, `geth` binds to the local interface and RPC endpoints are not and/or hosts. By default, `bera-geth` binds to the local interface and RPC endpoints are not
accessible from the outside. accessible from the outside.
### Programmatically interfacing `geth` nodes ### Programmatically interfacing `bera-geth` nodes
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 `bera-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.org/en/developers/docs/apis/json-rpc/) this, `bera-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 [`bera-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).
The IPC interface is enabled by default and exposes all the APIs supported by `geth`, The IPC interface is enabled by default and exposes all the APIs supported by `bera-geth`,
whereas the HTTP and WS interfaces need to manually be enabled and only expose a whereas the HTTP and WS interfaces need to manually be enabled and only expose a
subset of APIs due to security reasons. These can be turned on/off and configured as subset of APIs due to security reasons. These can be turned on/off and configured as
you'd expect. you'd expect.
@ -188,7 +188,7 @@ HTTP based JSON-RPC API options:
* `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it) * `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
You'll need to use your own programming environments' capabilities (libraries, tools, etc) to You'll need to use your own programming environments' capabilities (libraries, tools, etc) to
connect via HTTP, WS or IPC to a `geth` node configured with the above flags and you'll connect via HTTP, WS or IPC to a `bera-geth` node configured with the above flags and you'll
need to speak [JSON-RPC](https://www.jsonrpc.org/specification) on all transports. You need to speak [JSON-RPC](https://www.jsonrpc.org/specification) on all transports. You
can reuse the same connection for multiple requests! can reuse the same connection for multiple requests!
@ -204,7 +204,7 @@ Maintaining your own private network is more involved as a lot of configurations
granted in the official networks need to be manually set up. granted in the official networks need to be manually set up.
Unfortunately since [the Merge](https://ethereum.org/en/roadmap/merge/) it is no longer possible Unfortunately since [the Merge](https://ethereum.org/en/roadmap/merge/) it is no longer possible
to easily set up a network of geth nodes without also setting up a corresponding beacon chain. to easily set up a network of bera-geth nodes without also setting up a corresponding beacon chain.
There are three different solutions depending on your use case: There are three different solutions depending on your use case:

View file

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

View file

@ -101,6 +101,17 @@ func (ec *engineClient) callNewPayload(fork string, event types.ChainHeadEvent)
params = []any{execData} params = []any{execData}
) )
switch fork { switch fork {
case "electra1":
// TODO(BRIP-4): Add ParentProposerPubkey to the chainHeadEvent to add to the newPayload call.
method = "engine_newPayloadV4P11"
parentBeaconRoot := event.BeaconHead.ParentRoot
blobHashes := collectBlobHashes(event.Block)
hexRequests := make([]hexutil.Bytes, len(event.ExecRequests))
for i := range event.ExecRequests {
hexRequests[i] = hexutil.Bytes(event.ExecRequests[i])
}
parentProposerPubkey := &common.Pubkey{}
params = append(params, blobHashes, parentBeaconRoot, hexRequests, parentProposerPubkey)
case "electra": case "electra":
method = "engine_newPayloadV4" method = "engine_newPayloadV4"
parentBeaconRoot := event.BeaconHead.ParentRoot parentBeaconRoot := event.BeaconHead.ParentRoot
@ -145,6 +156,8 @@ func (ec *engineClient) callForkchoiceUpdated(fork string, event types.ChainHead
var method string var method string
switch fork { switch fork {
case "electra1":
method = "engine_forkchoiceUpdatedV3P11"
case "deneb", "electra": case "deneb", "electra":
method = "engine_forkchoiceUpdatedV3" method = "engine_forkchoiceUpdatedV3"
case "capella": case "capella":

View file

@ -21,6 +21,7 @@ func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"` BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
ProposerPubkey *common.Pubkey `json:"parentProposerPubkey"`
} }
var enc PayloadAttributes var enc PayloadAttributes
enc.Timestamp = hexutil.Uint64(p.Timestamp) enc.Timestamp = hexutil.Uint64(p.Timestamp)
@ -28,6 +29,7 @@ func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient
enc.Withdrawals = p.Withdrawals enc.Withdrawals = p.Withdrawals
enc.BeaconRoot = p.BeaconRoot enc.BeaconRoot = p.BeaconRoot
enc.ProposerPubkey = p.ProposerPubkey
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -39,6 +41,7 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"` BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
ProposerPubkey *common.Pubkey `json:"parentProposerPubkey"`
} }
var dec PayloadAttributes var dec PayloadAttributes
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -62,5 +65,8 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
if dec.BeaconRoot != nil { if dec.BeaconRoot != nil {
p.BeaconRoot = dec.BeaconRoot p.BeaconRoot = dec.BeaconRoot
} }
if dec.ProposerPubkey != nil {
p.ProposerPubkey = dec.ProposerPubkey
}
return nil return nil
} }

View file

@ -33,9 +33,10 @@ import (
type PayloadVersion byte type PayloadVersion byte
var ( var (
PayloadV1 PayloadVersion = 0x1 PayloadV1 PayloadVersion = 0x1
PayloadV2 PayloadVersion = 0x2 PayloadV2 PayloadVersion = 0x2
PayloadV3 PayloadVersion = 0x3 PayloadV3 PayloadVersion = 0x3
PayloadV3P11 PayloadVersion = 0x31
) )
//go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go //go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go
@ -48,6 +49,7 @@ type PayloadAttributes struct {
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"` BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
ProposerPubkey *common.Pubkey `json:"parentProposerPubkey"` // Berachain: only used prague1 and onwards
} }
// JSON type overrides for PayloadAttributes. // JSON type overrides for PayloadAttributes.
@ -218,8 +220,8 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
// and that the blockhash of the constructed block matches the parameters. Nil // and that the blockhash of the constructed block matches the parameters. Nil
// Withdrawals value will propagate through the returned block. Empty // Withdrawals value will propagate through the returned block. Empty
// Withdrawals value must be passed via non-nil, length 0 value in data. // Withdrawals value must be passed via non-nil, length 0 value in data.
func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, proposerPubkey *common.Pubkey) (*types.Block, error) {
block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requests) block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requests, proposerPubkey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -232,7 +234,7 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b
// ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used // ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used
// for stateless execution, so it skips checking if the executable data hashes to // for stateless execution, so it skips checking if the executable data hashes to
// the requested hash (stateless has to *compute* the root hash, it's not given). // the requested hash (stateless has to *compute* the root hash, it's not given).
func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, proposerPubkey *common.Pubkey) (*types.Block, error) {
txs, err := decodeTransactions(data.Transactions) txs, err := decodeTransactions(data.Transactions)
if err != nil { if err != nil {
return nil, err return nil, err
@ -275,26 +277,27 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
} }
header := &types.Header{ header := &types.Header{
ParentHash: data.ParentHash, ParentHash: data.ParentHash,
UncleHash: types.EmptyUncleHash, UncleHash: types.EmptyUncleHash,
Coinbase: data.FeeRecipient, Coinbase: data.FeeRecipient,
Root: data.StateRoot, Root: data.StateRoot,
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
ReceiptHash: data.ReceiptsRoot, ReceiptHash: data.ReceiptsRoot,
Bloom: types.BytesToBloom(data.LogsBloom), Bloom: types.BytesToBloom(data.LogsBloom),
Difficulty: common.Big0, Difficulty: common.Big0,
Number: new(big.Int).SetUint64(data.Number), Number: new(big.Int).SetUint64(data.Number),
GasLimit: data.GasLimit, GasLimit: data.GasLimit,
GasUsed: data.GasUsed, GasUsed: data.GasUsed,
Time: data.Timestamp, Time: data.Timestamp,
BaseFee: data.BaseFeePerGas, BaseFee: data.BaseFeePerGas,
Extra: data.ExtraData, Extra: data.ExtraData,
MixDigest: data.Random, MixDigest: data.Random,
WithdrawalsHash: withdrawalsRoot, WithdrawalsHash: withdrawalsRoot,
ExcessBlobGas: data.ExcessBlobGas, ExcessBlobGas: data.ExcessBlobGas,
BlobGasUsed: data.BlobGasUsed, BlobGasUsed: data.BlobGasUsed,
ParentBeaconRoot: beaconRoot, ParentBeaconRoot: beaconRoot,
RequestsHash: requestsHash, RequestsHash: requestsHash,
ParentProposerPubkey: proposerPubkey,
} }
return types.NewBlockWithHeader(header). return types.NewBlockWithHeader(header).
WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}). WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}).
@ -359,8 +362,8 @@ type ExecutionPayloadBody struct {
// Client identifiers to support ClientVersionV1. // Client identifiers to support ClientVersionV1.
const ( const (
ClientCode = "GE" ClientCode = "BG"
ClientName = "go-ethereum" ClientName = "bera-geth"
) )
// ClientVersionV1 contains information which identifies a client implementation. // ClientVersionV1 contains information which identifies a client implementation.

View file

@ -64,18 +64,18 @@ import (
) )
var ( var (
// Files that end up in the geth*.zip archive. // Files that end up in the bera-geth*.zip archive.
gethArchiveFiles = []string{ gethArchiveFiles = []string{
"COPYING", "COPYING",
executablePath("geth"), executablePath("bera-geth"),
} }
// Files that end up in the geth-alltools*.zip archive. // Files that end up in the bera-geth-alltools*.zip archive.
allToolsArchiveFiles = []string{ allToolsArchiveFiles = []string{
"COPYING", "COPYING",
executablePath("abigen"), executablePath("abigen"),
executablePath("evm"), executablePath("evm"),
executablePath("geth"), executablePath("bera-geth"),
executablePath("rlpdump"), executablePath("rlpdump"),
executablePath("clef"), executablePath("clef"),
} }
@ -91,7 +91,7 @@ var (
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.", Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",
}, },
{ {
BinaryName: "geth", BinaryName: "bera-geth",
Description: "Ethereum CLI client.", Description: "Ethereum CLI client.",
}, },
{ {
@ -237,6 +237,7 @@ func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (
if env.Commit != "" { if env.Commit != "" {
ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitCommit="+env.Commit) ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitCommit="+env.Commit)
ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitDate="+env.Date) ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitDate="+env.Date)
ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitTag="+env.Tag)
} }
// Strip DWARF on darwin. This used to be required for certain things, // Strip DWARF on darwin. This used to be required for certain things,
// and there is no downside to this, so we just keep doing it. // and there is no downside to this, so we just keep doing it.
@ -553,7 +554,7 @@ func doArchive(cmdline []string) {
atype = flag.String("type", "zip", "Type of archive to write (zip|tar)") atype = flag.String("type", "zip", "Type of archive to write (zip|tar)")
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`) signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`)
signify = flag.String("signify", "", `Environment variable holding the signify key (e.g. LINUX_SIGNIFY_KEY)`) signify = flag.String("signify", "", `Environment variable holding the signify key (e.g. LINUX_SIGNIFY_KEY)`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) upload = flag.String("upload", "", `Destination to upload the archives (usually "berachain/bera-geth")`)
ext string ext string
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
@ -568,9 +569,9 @@ func doArchive(cmdline []string) {
var ( var (
env = build.Env() env = build.Env()
basegeth = archiveBasename(*arch, version.Archive(env.Commit)) basegeth = archiveBasename(*arch, version.Archive(env.Tag, env.Commit))
geth = "geth-" + basegeth + ext geth = "bera-geth-" + basegeth + ext
alltools = "geth-alltools-" + basegeth + ext alltools = "bera-geth-alltools-" + basegeth + ext
) )
maybeSkipArchive(env) maybeSkipArchive(env)
if err := build.WriteArchive(geth, gethArchiveFiles); err != nil { if err := build.WriteArchive(geth, gethArchiveFiles); err != nil {
@ -610,7 +611,7 @@ func archiveUpload(archive string, blobstore string, signer string, signifyVar s
} }
if signifyVar != "" { if signifyVar != "" {
key := os.Getenv(signifyVar) key := os.Getenv(signifyVar)
untrustedComment := "verify with geth-release.pub" untrustedComment := "verify with bera-geth-release.pub"
trustedComment := fmt.Sprintf("%s (%s)", archive, time.Now().UTC().Format(time.RFC1123)) trustedComment := fmt.Sprintf("%s (%s)", archive, time.Now().UTC().Format(time.RFC1123))
if err := signify.SignFile(archive, archive+".sig", key, untrustedComment, trustedComment); err != nil { if err := signify.SignFile(archive, archive+".sig", key, untrustedComment, trustedComment); err != nil {
return err return err
@ -646,17 +647,17 @@ func maybeSkipArchive(env build.Environment) {
log.Printf("skipping archive creation because this is a PR build") log.Printf("skipping archive creation because this is a PR build")
os.Exit(0) os.Exit(0)
} }
if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") { if env.Branch != "main" && !strings.HasPrefix(env.Tag, "v1.") {
log.Printf("skipping archive creation because branch %q, tag %q is not on the inclusion list", env.Branch, env.Tag) log.Printf("skipping archive creation because branch %q, tag %q is not on the inclusion list", env.Branch, env.Tag)
os.Exit(0) os.Exit(0)
} }
} }
// Builds the docker images and optionally uploads them to Docker Hub. // Builds the docker images and optionally uploads them to GHCR.
func doDockerBuildx(cmdline []string) { func doDockerBuildx(cmdline []string) {
var ( var (
platform = flag.String("platform", "", `Push a multi-arch docker image for the specified architectures (usually "linux/amd64,linux/arm64")`) platform = flag.String("platform", "", `Push a multi-arch docker image for the specified architectures (usually "linux/amd64,linux/arm64")`)
hubImage = flag.String("hub", "ethereum/client-go", `Where to upload the docker image`) hubImage = flag.String("hub", "ghcr.io/berachain/bera-geth", `Where to upload the docker image`)
upload = flag.Bool("upload", false, `Whether to trigger upload`) upload = flag.Bool("upload", false, `Whether to trigger upload`)
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
@ -675,21 +676,16 @@ func doDockerBuildx(cmdline []string) {
build.MustRun(auther) build.MustRun(auther)
} }
// Retrieve the version infos to build and push to the following paths: // Retrieve the version infos to build and push to the following paths:
// - ethereum/client-go:latest - Pushes to the master branch, Geth only // - ethereum/client-go:latest - Pushes to the main branch, Geth only
// - ethereum/client-go:stable - Version tag publish on GitHub, Geth only // - ethereum/client-go:alltools-latest - Pushes to the main branch, Geth & tools
// - ethereum/client-go:alltools-latest - Pushes to the master branch, Geth & tools
// - ethereum/client-go:alltools-stable - Version tag publish on GitHub, Geth & tools
// - ethereum/client-go:release-<major>.<minor> - Version tag publish on GitHub, Geth only
// - ethereum/client-go:alltools-release-<major>.<minor> - Version tag publish on GitHub, Geth & tools
// - ethereum/client-go:v<major>.<minor>.<patch> - Version tag publish on GitHub, Geth only // - ethereum/client-go:v<major>.<minor>.<patch> - Version tag publish on GitHub, Geth only
// - ethereum/client-go:alltools-v<major>.<minor>.<patch> - Version tag publish on GitHub, Geth & tools // - ethereum/client-go:alltools-v<major>.<minor>.<patch> - Version tag publish on GitHub, Geth & tools
var tags []string var tag string
switch { switch {
case env.Branch == "master": case env.Branch == "main":
tags = []string{"latest"} tag = "latest"
case strings.HasPrefix(env.Tag, "v1."): case strings.HasPrefix(env.Tag, "v1."):
tags = []string{"stable", fmt.Sprintf("release-%v", version.Family), "v" + version.Semantic} tag = env.Tag
} }
// Need to create a mult-arch builder // Need to create a mult-arch builder
check := exec.Command("docker", "buildx", "inspect", "multi-arch-builder") check := exec.Command("docker", "buildx", "inspect", "multi-arch-builder")
@ -704,22 +700,25 @@ func doDockerBuildx(cmdline []string) {
{file: "Dockerfile", base: fmt.Sprintf("%s:", *hubImage)}, {file: "Dockerfile", base: fmt.Sprintf("%s:", *hubImage)},
{file: "Dockerfile.alltools", base: fmt.Sprintf("%s:alltools-", *hubImage)}, {file: "Dockerfile.alltools", base: fmt.Sprintf("%s:alltools-", *hubImage)},
} { } {
for _, tag := range tags { // latest, stable etc gethImage := fmt.Sprintf("%s%s", spec.base, tag)
gethImage := fmt.Sprintf("%s%s", spec.base, tag) // Prefer tag when present for VERSION
cmd := exec.Command("docker", "buildx", "build", dockerVersion := version.WithMeta
"--build-arg", "COMMIT="+env.Commit, if env.Tag != "" {
"--build-arg", "VERSION="+version.WithMeta, dockerVersion = env.Tag
"--build-arg", "BUILDNUM="+env.Buildnum,
"--tag", gethImage,
"--platform", *platform,
"--file", spec.file,
)
if *upload {
cmd.Args = append(cmd.Args, "--push")
}
cmd.Args = append(cmd.Args, ".")
build.MustRun(cmd)
} }
cmd := exec.Command("docker", "buildx", "build",
"--build-arg", "COMMIT="+env.Commit,
"--build-arg", "VERSION="+dockerVersion,
"--build-arg", "BUILDNUM="+env.Buildnum,
"--tag", gethImage,
"--platform", *platform,
"--file", spec.file,
)
if *upload {
cmd.Args = append(cmd.Args, "--push")
}
cmd.Args = append(cmd.Args, ".")
build.MustRun(cmd)
} }
} }
@ -729,7 +728,7 @@ func doDebianSource(cmdline []string) {
cachedir = flag.String("cachedir", "./build/cache", `Filesystem path to cache the downloaded Go bundles at`) cachedir = flag.String("cachedir", "./build/cache", `Filesystem path to cache the downloaded Go bundles at`)
signer = flag.String("signer", "", `Signing key name, also used as package author`) signer = flag.String("signer", "", `Signing key name, also used as package author`)
upload = flag.String("upload", "", `Where to upload the source package (usually "ethereum/ethereum")`) upload = flag.String("upload", "", `Where to upload the source package (usually "ethereum/ethereum")`)
sshUser = flag.String("sftp-user", "", `Username for SFTP upload (usually "geth-ci")`) sshUser = flag.String("sftp-user", "", `Username for SFTP upload (usually "bera-geth-ci")`)
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`) workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
now = time.Now() now = time.Now()
) )
@ -888,7 +887,7 @@ func makeWorkdir(wdflag string) string {
if wdflag != "" { if wdflag != "" {
err = os.MkdirAll(wdflag, 0744) err = os.MkdirAll(wdflag, 0744)
} else { } else {
wdflag, err = os.MkdirTemp("", "geth-build-") wdflag, err = os.MkdirTemp("", "bera-geth-build-")
} }
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -1042,7 +1041,7 @@ func doWindowsInstaller(cmdline []string) {
arch = flag.String("arch", runtime.GOARCH, "Architecture for cross build packaging") arch = flag.String("arch", runtime.GOARCH, "Architecture for cross build packaging")
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. WINDOWS_SIGNING_KEY)`) signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. WINDOWS_SIGNING_KEY)`)
signify = flag.String("signify key", "", `Environment variable holding the signify signing key (e.g. WINDOWS_SIGNIFY_KEY)`) signify = flag.String("signify key", "", `Environment variable holding the signify signing key (e.g. WINDOWS_SIGNIFY_KEY)`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) upload = flag.String("upload", "", `Destination to upload the archives (usually "berachain/bera-geth")`)
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`) workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
@ -1061,7 +1060,7 @@ func doWindowsInstaller(cmdline []string) {
continue continue
} }
allTools = append(allTools, filepath.Base(file)) allTools = append(allTools, filepath.Base(file))
if filepath.Base(file) == "geth.exe" { if filepath.Base(file) == "bera-geth.exe" {
gethTool = file gethTool = file
} else { } else {
devTools = append(devTools, file) devTools = append(devTools, file)
@ -1075,7 +1074,7 @@ func doWindowsInstaller(cmdline []string) {
"Geth": gethTool, "Geth": gethTool,
"DevTools": devTools, "DevTools": devTools,
} }
build.Render("build/nsis.geth.nsi", filepath.Join(*workdir, "geth.nsi"), 0644, nil) build.Render("build/nsis.geth.nsi", filepath.Join(*workdir, "bera-geth.nsi"), 0644, nil)
build.Render("build/nsis.install.nsh", filepath.Join(*workdir, "install.nsh"), 0644, templateData) build.Render("build/nsis.install.nsh", filepath.Join(*workdir, "install.nsh"), 0644, templateData)
build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools) build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools)
build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil) build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil)
@ -1093,7 +1092,7 @@ func doWindowsInstaller(cmdline []string) {
if env.Commit != "" { if env.Commit != "" {
ver[2] += "-" + env.Commit[:8] ver[2] += "-" + env.Commit[:8]
} }
installer, err := filepath.Abs("geth-" + archiveBasename(*arch, version.Archive(env.Commit)) + ".exe") installer, err := filepath.Abs("bera-geth-" + archiveBasename(*arch, version.Archive(env.Tag, env.Commit)) + ".exe")
if err != nil { if err != nil {
log.Fatalf("Failed to convert installer file path: %v", err) log.Fatalf("Failed to convert installer file path: %v", err)
} }
@ -1103,7 +1102,7 @@ func doWindowsInstaller(cmdline []string) {
"/DMINORVERSION="+ver[1], "/DMINORVERSION="+ver[1],
"/DBUILDVERSION="+ver[2], "/DBUILDVERSION="+ver[2],
"/DARCH="+*arch, "/DARCH="+*arch,
filepath.Join(*workdir, "geth.nsi"), filepath.Join(*workdir, "bera-geth.nsi"),
) )
// Sign and publish installer. // Sign and publish installer.
if err := archiveUpload(installer, *upload, *signer, *signify); err != nil { if err := archiveUpload(installer, *upload, *signer, *signify); err != nil {
@ -1115,7 +1114,7 @@ func doWindowsInstaller(cmdline []string) {
func doPurge(cmdline []string) { func doPurge(cmdline []string) {
var ( var (
store = flag.String("store", "", `Destination from where to purge archives (usually "gethstore/builds")`) store = flag.String("store", "", `Destination from where to purge archives (usually "berachain/bera-geth")`)
limit = flag.Int("days", 30, `Age threshold above which to delete unstable archives`) limit = flag.Int("days", 30, `Age threshold above which to delete unstable archives`)
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)

View file

@ -1,32 +0,0 @@
machine:
services:
- docker
dependencies:
cache_directories:
- "~/.ethash" # Cache the ethash DAG generated by hive for consecutive builds
- "~/.docker" # Cache all docker images manually to avoid lengthy rebuilds
override:
# Restore all previously cached docker images
- mkdir -p ~/.docker
- for img in `ls ~/.docker`; do docker load -i ~/.docker/$img; done
# Pull in and hive, restore cached ethash DAGs and do a dry run
- go get -u github.com/karalabe/hive
- (cd ~/.go_workspace/src/github.com/karalabe/hive && mkdir -p workspace/ethash/ ~/.ethash)
- (cd ~/.go_workspace/src/github.com/karalabe/hive && cp -r ~/.ethash/. workspace/ethash/)
- (cd ~/.go_workspace/src/github.com/karalabe/hive && hive --docker-noshell --client=NONE --test=. --sim=. --loglevel=6)
# Cache all the docker images and the ethash DAGs
- for img in `docker images | grep -v "^<none>" | tail -n +2 | awk '{print $1}'`; do docker save $img > ~/.docker/`echo $img | tr '/' ':'`.tar; done
- cp -r ~/.go_workspace/src/github.com/karalabe/hive/workspace/ethash/. ~/.ethash
test:
override:
# Build Geth and move into a known folder
- make geth
- cp ./build/bin/geth $HOME/geth
# Run hive and move all generated logs into the public artifacts folder
- (cd ~/.go_workspace/src/github.com/karalabe/hive && hive --docker-noshell --client=go-ethereum:local --override=$HOME/geth --test=. --sim=.)
- cp -r ~/.go_workspace/src/github.com/karalabe/hive/workspace/logs/* $CIRCLE_ARTIFACTS

View file

@ -47,6 +47,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/triedb"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -281,8 +282,19 @@ func initGenesis(ctx *cli.Context) error {
chaindb := utils.MakeChainDatabase(ctx, stack, false) chaindb := utils.MakeChainDatabase(ctx, stack, false)
defer chaindb.Close() defer chaindb.Close()
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle()) // Berachain: Check if genesis already exists for PBSS databases BEFORE opening trie database.
defer triedb.Close() // This prevents the state history truncation that happens during trie database initialization.
var triedb *triedb.Database
if rawdb.ReadStateScheme(chaindb) == rawdb.PathScheme &&
rawdb.ReadChainConfig(chaindb, rawdb.ReadCanonicalHash(chaindb, 0)) != nil {
log.Info("PBSS db already initialized with genesis, skipping trie db initialization")
} else {
// Only create triedb if we're not using PBSS or genesis is empty on disk. Refer to
// https://github.com/ethereum/go-ethereum/pull/25523 for why the triedb cannot be
// re-created when using PBSS.
triedb = utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
defer triedb.Close()
}
_, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides) _, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
if err != nil { if err != nil {
@ -479,6 +491,10 @@ func importHistory(ctx *cli.Context) error {
network = "holesky" network = "holesky"
case ctx.Bool(utils.HoodiFlag.Name): case ctx.Bool(utils.HoodiFlag.Name):
network = "hoodi" network = "hoodi"
case ctx.Bool(utils.BerachainFlag.Name):
network = "berachain"
case ctx.Bool(utils.BepoliaFlag.Name):
network = "bepolia"
} }
} else { } else {
// No network flag set, try to determine network based on files // No network flag set, try to determine network based on files
@ -715,6 +731,10 @@ func downloadEra(ctx *cli.Context) error {
case ctx.IsSet(utils.MainnetFlag.Name): case ctx.IsSet(utils.MainnetFlag.Name):
case ctx.IsSet(utils.SepoliaFlag.Name): case ctx.IsSet(utils.SepoliaFlag.Name):
network = "sepolia" network = "sepolia"
case ctx.IsSet(utils.BerachainFlag.Name):
network = "berachain"
case ctx.IsSet(utils.BepoliaFlag.Name):
network = "bepolia"
default: default:
return errors.New("unsupported network, no known era1 checksums") return errors.New("unsupported network, no known era1 checksums")
} }

View file

@ -131,7 +131,13 @@ func defaultNodeConfig() node.Config {
git, _ := version.VCS() git, _ := version.VCS()
cfg := node.DefaultConfig cfg := node.DefaultConfig
cfg.Name = clientIdentifier cfg.Name = clientIdentifier
cfg.Version = version.WithCommit(git.Commit, git.Date) // Prefer git tag for the advertised version (used in web3_clientVersion),
// trimming any leading 'v' because NodeName adds the 'v' prefix itself.
ver := version.WithMeta
if git.Tag != "" {
ver = strings.TrimPrefix(git.Tag, "v")
}
cfg.Version = ver
cfg.HTTPModules = append(cfg.HTTPModules, "eth") cfg.HTTPModules = append(cfg.HTTPModules, "eth")
cfg.WSModules = append(cfg.WSModules, "eth") cfg.WSModules = append(cfg.WSModules, "eth")
cfg.IPCPath = clientIdentifier + ".ipc" cfg.IPCPath = clientIdentifier + ".ipc"

View file

@ -69,7 +69,7 @@ func TestConsoleWelcome(t *testing.T) {
geth.Expect(` geth.Expect(`
Welcome to the Geth JavaScript console! Welcome to the Geth JavaScript console!
instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}} instance: BeraGeth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}}
at block: 0 ({{niltime}}) at block: 0 ({{niltime}})
datadir: {{.Datadir}} datadir: {{.Datadir}}
modules: {{apis}} modules: {{apis}}
@ -89,9 +89,9 @@ func TestAttachWelcome(t *testing.T) {
) )
// Configure the instance for IPC attachment // Configure the instance for IPC attachment
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
ipc = `\\.\pipe\geth` + strconv.Itoa(trulyRandInt(100000, 999999)) ipc = `\\.\pipe\bera-geth` + strconv.Itoa(trulyRandInt(100000, 999999))
} else { } else {
ipc = filepath.Join(t.TempDir(), "geth.ipc") ipc = filepath.Join(t.TempDir(), "bera-geth.ipc")
} }
// And HTTP + WS attachment // And HTTP + WS attachment
p := trulyRandInt(1024, 65533) // Yeah, sometimes this will fail, sorry :P p := trulyRandInt(1024, 65533) // Yeah, sometimes this will fail, sorry :P
@ -140,7 +140,7 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
attach.Expect(` attach.Expect(`
Welcome to the Geth JavaScript console! Welcome to the Geth JavaScript console!
instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}} instance: BeraGeth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}}
at block: 0 ({{niltime}}){{if ipc}} at block: 0 ({{niltime}}){{if ipc}}
datadir: {{datadir}}{{end}} datadir: {{datadir}}{{end}}
modules: {{apis}} modules: {{apis}}

View file

@ -21,6 +21,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings"
"testing" "testing"
) )
@ -97,6 +98,142 @@ func TestCustomGenesis(t *testing.T) {
} }
} }
// TestPBSSRepeatInitNoTrieOpen verifies that running init twice on a PBSS (path-scheme)
// database does not re-initialize the trie database and safely short-circuits.
func TestPBSSRepeatInitNoTrieOpen(t *testing.T) {
t.Parallel()
// Minimal genesis with TTD=0 to keep execution simple
genesis := `{
"alloc": {"0x0000000000000000000000000000000000000001": {"balance": "0x1"}},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x1",
"gasLimit": "0x2fefd8",
"nonce": "0x0000000000000000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00",
"config": { "terminalTotalDifficulty": 0 }
}`
datadir := t.TempDir()
jsonPath := filepath.Join(datadir, "genesis.json")
if err := os.WriteFile(jsonPath, []byte(genesis), 0600); err != nil {
t.Fatalf("failed to write genesis file: %v", err)
}
// First init should perform normal initialization
gethInit1 := runGeth(t, "--datadir", datadir, "init", jsonPath)
gethInit1.WaitExit()
if gethInit1.Err != nil {
t.Fatalf("first init failed: %v", gethInit1.Err)
}
// Second init should skip trie db initialization and not truncate freezer
gethInit2 := runGeth(t, "--datadir", datadir, "init", jsonPath)
gethInit2.WaitExit()
if gethInit2.Err != nil {
t.Fatalf("second init failed: %v", gethInit2.Err)
}
stderr := gethInit2.StderrText()
if !strings.Contains(stderr, "PBSS db already initialized with genesis, skipping trie db initialization") {
t.Fatalf("expected skip-triedb log not found in stderr. got:\n%s", stderr)
}
}
// TestPBSSConfigUpdate verifies that a repeat init on PBSS writes updated chain
// configuration without touching the trie database when the genesis block hash
// is unchanged.
func TestPBSSConfigUpdate(t *testing.T) {
t.Parallel()
// Base genesis (no Prague1 yet). Include all prior timestamp forks explicitly
// with timestamp 0 to satisfy fork ordering: Shanghai -> Cancun -> Prague -> Prague1.
baseGenesis := `{
"alloc": {"0x0000000000000000000000000000000000000001": {"balance": "0x1"}},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x1",
"gasLimit": "0x2fefd8",
"nonce": "0x0000000000000000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00",
"config": {
"terminalTotalDifficulty": 0,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"shanghaiTime": 0
}
}`
// Updated config: enable Berachain Prague1 while keeping genesis state identical.
// Include all prior timestamp forks to maintain correct ordering.
updatedGenesis := `{
"alloc": {"0x0000000000000000000000000000000000000001": {"balance": "0x1"}},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x1",
"gasLimit": "0x2fefd8",
"nonce": "0x0000000000000000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00",
"config": {
"terminalTotalDifficulty": 0,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"shanghaiTime": 0,
"berachain": {
"prague1": {
"time": 1000,
"baseFeeChangeDenominator": 48,
"poLDistributorAddress": "0x1111111111111111111111111111111111111111"
}
}
}
}`
datadir := t.TempDir()
jsonPath := filepath.Join(datadir, "genesis.json")
if err := os.WriteFile(jsonPath, []byte(baseGenesis), 0600); err != nil {
t.Fatalf("failed to write base genesis file: %v", err)
}
// First init with base config
runGeth(t, "--datadir", datadir, "init", jsonPath).WaitExit()
// Overwrite with updated config (same genesis state, different chain config)
if err := os.WriteFile(jsonPath, []byte(updatedGenesis), 0600); err != nil {
t.Fatalf("failed to write updated genesis file: %v", err)
}
// Second init should skip trie db init and write the new chain config
geth := runGeth(t, "--datadir", datadir, "init", jsonPath)
geth.WaitExit()
stderr := geth.StderrText()
if !strings.Contains(stderr, "PBSS db already initialized with genesis, skipping trie db initialization") {
t.Fatalf("expected PBSS skip log not found in stderr. got:\n%s", stderr)
}
if !strings.Contains(stderr, "Writing new chain config") {
t.Fatalf("expected config-update log not found in stderr. got:\n%s", stderr)
}
}
// TestCustomBackend that the backend selection and detection (leveldb vs pebble) works properly. // TestCustomBackend that the backend selection and detection (leveldb vs pebble) works properly.
func TestCustomBackend(t *testing.T) { func TestCustomBackend(t *testing.T) {
t.Parallel() t.Parallel()
@ -186,9 +323,9 @@ func TestCustomBackend(t *testing.T) {
{ // Reject invalid backend choice { // Reject invalid backend choice
initArgs: []string{"--db.engine", "mssql"}, initArgs: []string{"--db.engine", "mssql"},
initExpect: `Fatal: Invalid choice for db.engine 'mssql', allowed 'leveldb' or 'pebble'`, initExpect: `Fatal: Invalid choice for db.engine 'mssql', allowed 'leveldb' or 'pebble'`,
// Since the init fails, this will return the (default) mainnet genesis // Since the init fails, this will return the (default) berachain mainnet genesis
// block nonce // block nonce
execExpect: `0x0000000000000042`, execExpect: `0x0000000000001234`,
}, },
} { } {
if err := testfunc(t, tt); err != nil { if err := testfunc(t, tt); err != nil {

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// geth is a command-line client for Ethereum. // bera-geth is a command-line client for Ethereum.
package main package main
import ( import (
@ -46,7 +46,7 @@ import (
) )
const ( const (
clientIdentifier = "geth" // Client identifier to advertise over the network clientIdentifier = "bera-geth" // Client identifier to advertise over the network
) )
var ( var (
@ -207,7 +207,7 @@ var app = flags.NewApp("the go-ethereum command line interface")
func init() { func init() {
// Initialize the CLI app and start Geth // Initialize the CLI app and start Geth
app.Action = geth app.Action = berageth
app.Commands = []*cli.Command{ app.Commands = []*cli.Command{
// See chaincmd.go: // See chaincmd.go:
initCommand, initCommand,
@ -255,7 +255,7 @@ func init() {
debug.Flags, debug.Flags,
metricsFlags, metricsFlags,
) )
flags.AutoEnvVars(app.Flags, "GETH") flags.AutoEnvVars(app.Flags, "BERA_GETH")
app.Before = func(ctx *cli.Context) error { app.Before = func(ctx *cli.Context) error {
maxprocs.Set() // Automatically set GOMAXPROCS to match Linux container CPU quota. maxprocs.Set() // Automatically set GOMAXPROCS to match Linux container CPU quota.
@ -263,7 +263,7 @@ func init() {
if err := debug.Setup(ctx); err != nil { if err := debug.Setup(ctx); err != nil {
return err return err
} }
flags.CheckEnvVars(ctx, app.Flags, "GETH") flags.CheckEnvVars(ctx, app.Flags, "BERA_GETH")
return nil return nil
} }
app.After = func(ctx *cli.Context) error { app.After = func(ctx *cli.Context) error {
@ -285,24 +285,32 @@ func main() {
func prepare(ctx *cli.Context) { func prepare(ctx *cli.Context) {
// If we're running a known preset, log it for convenience. // If we're running a known preset, log it for convenience.
switch { switch {
case ctx.IsSet(utils.MainnetFlag.Name):
log.Info("Starting bera-geth on Ethereum mainnet...")
case ctx.IsSet(utils.SepoliaFlag.Name): case ctx.IsSet(utils.SepoliaFlag.Name):
log.Info("Starting Geth on Sepolia testnet...") log.Info("Starting bera-geth on Sepolia testnet...")
case ctx.IsSet(utils.HoleskyFlag.Name): case ctx.IsSet(utils.HoleskyFlag.Name):
log.Info("Starting Geth on Holesky testnet...") log.Info("Starting bera-geth on Holesky testnet...")
case ctx.IsSet(utils.HoodiFlag.Name): case ctx.IsSet(utils.HoodiFlag.Name):
log.Info("Starting Geth on Hoodi testnet...") log.Info("Starting bera-geth on Hoodi testnet...")
case !ctx.IsSet(utils.NetworkIdFlag.Name): case ctx.IsSet(utils.BepoliaFlag.Name):
log.Info("Starting Geth on Ethereum mainnet...") log.Info("Starting bera-geth on Bepolia testnet...")
case ctx.IsSet(utils.BerachainFlag.Name):
log.Info("Starting bera-geth on Berachain mainnet...")
} }
// If we're a full node on mainnet without --cache specified, bump default cache allowance
// If we're a full node on Berachain mainnet without --cache specified, bump default cache allowance
if !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) { if !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
// Make sure we're not on any supported preconfigured testnet either // Make sure we're not on any supported preconfigured testnet either
if !ctx.IsSet(utils.HoleskyFlag.Name) && if !ctx.IsSet(utils.HoleskyFlag.Name) &&
!ctx.IsSet(utils.SepoliaFlag.Name) && !ctx.IsSet(utils.SepoliaFlag.Name) &&
!ctx.IsSet(utils.HoodiFlag.Name) && !ctx.IsSet(utils.HoodiFlag.Name) &&
!ctx.IsSet(utils.BepoliaFlag.Name) &&
!ctx.IsSet(utils.DeveloperFlag.Name) { !ctx.IsSet(utils.DeveloperFlag.Name) {
// Nope, we're really on mainnet. Bump that cache up! // Nope, we're really on mainnet. Bump that cache up!
log.Info("Bumping default cache on mainnet", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 4096) log.Info("Bumping default cache on mainnet", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 4096)
@ -311,10 +319,10 @@ func prepare(ctx *cli.Context) {
} }
} }
// geth is the main entry point into the system if no special subcommand is run. // berageth is the main entry point into the system if no special subcommand is run.
// It creates a default node based on the command line arguments and runs it in // It creates a default node based on the command line arguments and runs it in
// blocking mode, waiting for it to be shut down. // blocking mode, waiting for it to be shut down.
func geth(ctx *cli.Context) error { func berageth(ctx *cli.Context) error {
if args := ctx.Args().Slice(); len(args) > 0 { if args := ctx.Args().Slice(); len(args) > 0 {
return fmt.Errorf("invalid command: %q", args[0]) return fmt.Errorf("invalid command: %q", args[0])
} }
@ -342,7 +350,7 @@ func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
events := make(chan accounts.WalletEvent, 16) events := make(chan accounts.WalletEvent, 16)
stack.AccountManager().Subscribe(events) stack.AccountManager().Subscribe(events)
// Create a client to interact with local geth node. // Create a client to interact with local bera-geth node.
rpcClient := stack.Attach() rpcClient := stack.Attach()
ethClient := ethclient.NewClient(rpcClient) ethClient := ethclient.NewClient(rpcClient)

View file

@ -72,7 +72,12 @@ func printVersion(ctx *cli.Context) error {
git, _ := version.VCS() git, _ := version.VCS()
fmt.Println(strings.Title(clientIdentifier)) fmt.Println(strings.Title(clientIdentifier))
fmt.Println("Version:", version.WithMeta) // Use git tag if available, otherwise fall back to hardcoded version
if git.Tag != "" {
fmt.Println("Version:", git.Tag)
} else {
fmt.Println("Version:", version.WithMeta)
}
if git.Commit != "" { if git.Commit != "" {
fmt.Println("Git Commit:", git.Commit) fmt.Println("Git Commit:", git.Commit)
} }

View file

@ -236,6 +236,10 @@ func ethFilter(args []string) (nodeFilter, error) {
filter = forkid.NewStaticFilter(params.HoleskyChainConfig, core.DefaultHoleskyGenesisBlock().ToBlock()) filter = forkid.NewStaticFilter(params.HoleskyChainConfig, core.DefaultHoleskyGenesisBlock().ToBlock())
case "hoodi": case "hoodi":
filter = forkid.NewStaticFilter(params.HoodiChainConfig, core.DefaultHoodiGenesisBlock().ToBlock()) filter = forkid.NewStaticFilter(params.HoodiChainConfig, core.DefaultHoodiGenesisBlock().ToBlock())
case "berachain":
filter = forkid.NewStaticFilter(params.BerachainChainConfig, core.DefaultBerachainGenesisBlock().ToBlock())
case "bepolia":
filter = forkid.NewStaticFilter(params.BepoliaChainConfig, core.DefaultBepoliaGenesisBlock().ToBlock())
default: default:
return nil, fmt.Errorf("unknown network %q", args[0]) return nil, fmt.Errorf("unknown network %q", args[0])
} }

View file

@ -56,6 +56,7 @@ type header struct {
BlobGasUsed *uint64 `json:"blobGasUsed" rlp:"optional"` BlobGasUsed *uint64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"` ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
ParentProposerPubkey *common.Pubkey `json:"parentProposerPubkey" rlp:"optional"`
} }
type headerMarshaling struct { type headerMarshaling struct {
@ -117,25 +118,26 @@ func (c *cliqueInput) UnmarshalJSON(input []byte) error {
// ToBlock converts i into a *types.Block // ToBlock converts i into a *types.Block
func (i *bbInput) ToBlock() *types.Block { func (i *bbInput) ToBlock() *types.Block {
header := &types.Header{ header := &types.Header{
ParentHash: i.Header.ParentHash, ParentHash: i.Header.ParentHash,
UncleHash: types.EmptyUncleHash, UncleHash: types.EmptyUncleHash,
Coinbase: common.Address{}, Coinbase: common.Address{},
Root: i.Header.Root, Root: i.Header.Root,
TxHash: types.EmptyTxsHash, TxHash: types.EmptyTxsHash,
ReceiptHash: types.EmptyReceiptsHash, ReceiptHash: types.EmptyReceiptsHash,
Bloom: i.Header.Bloom, Bloom: i.Header.Bloom,
Difficulty: common.Big0, Difficulty: common.Big0,
Number: i.Header.Number, Number: i.Header.Number,
GasLimit: i.Header.GasLimit, GasLimit: i.Header.GasLimit,
GasUsed: i.Header.GasUsed, GasUsed: i.Header.GasUsed,
Time: i.Header.Time, Time: i.Header.Time,
Extra: i.Header.Extra, Extra: i.Header.Extra,
MixDigest: i.Header.MixDigest, MixDigest: i.Header.MixDigest,
BaseFee: i.Header.BaseFee, BaseFee: i.Header.BaseFee,
WithdrawalsHash: i.Header.WithdrawalsHash, WithdrawalsHash: i.Header.WithdrawalsHash,
BlobGasUsed: i.Header.BlobGasUsed, BlobGasUsed: i.Header.BlobGasUsed,
ExcessBlobGas: i.Header.ExcessBlobGas, ExcessBlobGas: i.Header.ExcessBlobGas,
ParentBeaconRoot: i.Header.ParentBeaconBlockRoot, ParentBeaconRoot: i.Header.ParentBeaconBlockRoot,
ParentProposerPubkey: i.Header.ParentProposerPubkey,
} }
// Fill optional values. // Fill optional values.

View file

@ -101,6 +101,7 @@ type stEnv struct {
ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"` ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"`
ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"` ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
ParentProposerPubkey *common.Pubkey `json:"parentProposerPubkey"`
} }
type stEnvMarshaling struct { type stEnvMarshaling struct {
@ -220,6 +221,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
) )
core.ProcessParentBlockHash(prevHash, evm) core.ProcessParentBlockHash(prevHash, evm)
} }
// TODO(BRIP-4): Validate Prague1 block rules.
for i := 0; txIt.Next(); i++ { for i := 0; txIt.Next(); i++ {
tx, err := txIt.Tx() tx, err := txIt.Tx()
if err != nil { if err != nil {

View file

@ -38,6 +38,7 @@ func (h header) MarshalJSON() ([]byte, error) {
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed" rlp:"optional"` BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas" rlp:"optional"` ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
ParentProposerPubkey *common.Pubkey `json:"parentProposerPubkey" rlp:"optional"`
} }
var enc header var enc header
enc.ParentHash = h.ParentHash enc.ParentHash = h.ParentHash
@ -60,6 +61,7 @@ func (h header) MarshalJSON() ([]byte, error) {
enc.BlobGasUsed = (*math.HexOrDecimal64)(h.BlobGasUsed) enc.BlobGasUsed = (*math.HexOrDecimal64)(h.BlobGasUsed)
enc.ExcessBlobGas = (*math.HexOrDecimal64)(h.ExcessBlobGas) enc.ExcessBlobGas = (*math.HexOrDecimal64)(h.ExcessBlobGas)
enc.ParentBeaconBlockRoot = h.ParentBeaconBlockRoot enc.ParentBeaconBlockRoot = h.ParentBeaconBlockRoot
enc.ParentProposerPubkey = h.ParentProposerPubkey
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -86,6 +88,7 @@ func (h *header) UnmarshalJSON(input []byte) error {
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed" rlp:"optional"` BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas" rlp:"optional"` ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
ParentProposerPubkey *common.Pubkey `json:"parentProposerPubkey" rlp:"optional"`
} }
var dec header var dec header
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -155,5 +158,8 @@ func (h *header) UnmarshalJSON(input []byte) error {
if dec.ParentBeaconBlockRoot != nil { if dec.ParentBeaconBlockRoot != nil {
h.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot h.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot
} }
if dec.ParentProposerPubkey != nil {
h.ParentProposerPubkey = dec.ParentProposerPubkey
}
return nil return nil
} }

View file

@ -37,6 +37,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"`
ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
ParentProposerPubkey *common.Pubkey `json:"parentProposerPubkey"`
} }
var enc stEnv var enc stEnv
enc.Coinbase = common.UnprefixedAddress(s.Coinbase) enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
@ -59,6 +60,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
enc.ParentExcessBlobGas = (*math.HexOrDecimal64)(s.ParentExcessBlobGas) enc.ParentExcessBlobGas = (*math.HexOrDecimal64)(s.ParentExcessBlobGas)
enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed) enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed)
enc.ParentBeaconBlockRoot = s.ParentBeaconBlockRoot enc.ParentBeaconBlockRoot = s.ParentBeaconBlockRoot
enc.ParentProposerPubkey = s.ParentProposerPubkey
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -85,6 +87,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"`
ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
ParentProposerPubkey *common.Pubkey `json:"parentProposerPubkey"`
} }
var dec stEnv var dec stEnv
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -154,5 +157,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
if dec.ParentBeaconBlockRoot != nil { if dec.ParentBeaconBlockRoot != nil {
s.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot s.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot
} }
if dec.ParentProposerPubkey != nil {
s.ParentProposerPubkey = dec.ParentProposerPubkey
}
return nil return nil
} }

View file

@ -150,6 +150,9 @@ func Transition(ctx *cli.Context) error {
if err := applyCancunChecks(&prestate.Env, chainConfig); err != nil { if err := applyCancunChecks(&prestate.Env, chainConfig); err != nil {
return err return err
} }
if err := applyPrague1Checks(&prestate.Env, chainConfig); err != nil {
return err
}
// Configure tracer // Configure tracer
if ctx.IsSet(TraceTracerFlag.Name) { // Custom tracing if ctx.IsSet(TraceTracerFlag.Name) { // Custom tracing
@ -265,6 +268,19 @@ func applyCancunChecks(env *stEnv, chainConfig *params.ChainConfig) error {
return nil return nil
} }
func applyPrague1Checks(env *stEnv, chainConfig *params.ChainConfig) error {
if !chainConfig.IsPrague1(big.NewInt(int64(env.Number)), env.Timestamp) {
env.ParentProposerPubkey = nil // un-set it if it has been set too early
return nil
}
// Post-prague1
// We require BRIP-0004 proposer pubkey to be set in the env
if env.ParentProposerPubkey == nil {
return NewError(ErrorConfig, errors.New("post-prague1 env requires parentProposerPubkey to be set"))
}
return nil
}
type Alloc map[common.Address]types.Account type Alloc map[common.Address]types.Account
func (g Alloc) OnRoot(common.Hash) {} func (g Alloc) OnRoot(common.Hash) {}

View file

@ -139,7 +139,7 @@ var (
} }
NetworkIdFlag = &cli.Uint64Flag{ NetworkIdFlag = &cli.Uint64Flag{
Name: "networkid", Name: "networkid",
Usage: "Explicitly set network id (integer)(For testnets: use --sepolia, --holesky, --hoodi instead)", Usage: "Explicitly set network id (integer)(Testnet: use --bepolia)(For Ethereum: use --mainnet, --sepolia, etc.)",
Value: ethconfig.Defaults.NetworkId, Value: ethconfig.Defaults.NetworkId,
Category: flags.EthCategory, Category: flags.EthCategory,
} }
@ -163,6 +163,17 @@ var (
Usage: "Hoodi network: pre-configured proof-of-stake test network", Usage: "Hoodi network: pre-configured proof-of-stake test network",
Category: flags.EthCategory, Category: flags.EthCategory,
} }
// Berachain
BerachainFlag = &cli.BoolFlag{
Name: "berachain",
Usage: "Berachain mainnet",
Category: flags.EthCategory,
}
BepoliaFlag = &cli.BoolFlag{
Name: "bepolia",
Usage: "Bepolia network: pre-configured proof-of-stake test network",
Category: flags.EthCategory,
}
// Dev mode // Dev mode
DeveloperFlag = &cli.BoolFlag{ DeveloperFlag = &cli.BoolFlag{
Name: "dev", Name: "dev",
@ -449,7 +460,7 @@ var (
// Performance tuning settings // Performance tuning settings
CacheFlag = &cli.IntFlag{ CacheFlag = &cli.IntFlag{
Name: "cache", Name: "cache",
Usage: "Megabytes of memory allocated to internal caching (default = 4096 mainnet full node, 128 light mode)", Usage: "Megabytes of memory allocated to internal caching (default = 4096 berachain mainnet full node, 128 light mode)",
Value: 1024, Value: 1024,
Category: flags.PerfCategory, Category: flags.PerfCategory,
} }
@ -1056,9 +1067,9 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
// 1. --bootnodes flag // 1. --bootnodes flag
// 2. Config file // 2. Config file
// 3. Network preset flags (e.g. --holesky) // 3. Network preset flags (e.g. --holesky)
// 4. default to mainnet nodes // 4. default to Berachain mainnet nodes
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
urls := params.MainnetBootnodes urls := params.BerachainBootnodes
if ctx.IsSet(BootnodesFlag.Name) { if ctx.IsSet(BootnodesFlag.Name) {
urls = SplitAndTrim(ctx.String(BootnodesFlag.Name)) urls = SplitAndTrim(ctx.String(BootnodesFlag.Name))
} else { } else {
@ -1066,12 +1077,16 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
return // Already set by config file, don't apply defaults. return // Already set by config file, don't apply defaults.
} }
switch { switch {
case ctx.Bool(MainnetFlag.Name):
urls = params.MainnetBootnodes
case ctx.Bool(HoleskyFlag.Name): case ctx.Bool(HoleskyFlag.Name):
urls = params.HoleskyBootnodes urls = params.HoleskyBootnodes
case ctx.Bool(SepoliaFlag.Name): case ctx.Bool(SepoliaFlag.Name):
urls = params.SepoliaBootnodes urls = params.SepoliaBootnodes
case ctx.Bool(HoodiFlag.Name): case ctx.Bool(HoodiFlag.Name):
urls = params.HoodiBootnodes urls = params.HoodiBootnodes
case ctx.Bool(BepoliaFlag.Name):
urls = params.BepoliaBootnodes
} }
} }
cfg.BootstrapNodes = mustParseBootnodes(urls) cfg.BootstrapNodes = mustParseBootnodes(urls)
@ -1562,7 +1577,7 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
// SetEthConfig applies eth-related command line flags to the config. // SetEthConfig applies eth-related command line flags to the config.
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// Avoid conflicting network flags, don't allow network id override on preset networks // Avoid conflicting network flags, don't allow network id override on preset networks
flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag, NetworkIdFlag) flags.CheckExclusive(ctx, BerachainFlag, BepoliaFlag, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag, NetworkIdFlag)
flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
// Set configurations from CLI flags // Set configurations from CLI flags

View file

@ -37,6 +37,8 @@ var (
Flags: []cli.Flag{ Flags: []cli.Flag{
testSepoliaFlag, testSepoliaFlag,
testMainnetFlag, testMainnetFlag,
testBepoliaFlag,
testBerachainFlag,
filterQueryFileFlag, filterQueryFileFlag,
filterErrorFileFlag, filterErrorFileFlag,
}, },

View file

@ -85,6 +85,16 @@ var (
Usage: "Use test cases for mainnet network", Usage: "Use test cases for mainnet network",
Category: flags.TestingCategory, Category: flags.TestingCategory,
} }
testBepoliaFlag = &cli.BoolFlag{
Name: "bepolia",
Usage: "Use test cases for bepolia network",
Category: flags.TestingCategory,
}
testBerachainFlag = &cli.BoolFlag{
Name: "berachain",
Usage: "Use test cases for berachain mainnet network",
Category: flags.TestingCategory,
}
) )
// testConfig holds the parameters for testing. // testConfig holds the parameters for testing.
@ -117,10 +127,13 @@ func validateHistoryPruneErr(err error, blockNum uint64, historyPruneBlock *uint
return err return err
} }
// TODO(bera): Add test filter & history queries for Berachain mainnet & Bepolia in queries/ folder
func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) { func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
flags.CheckExclusive(ctx, testMainnetFlag, testSepoliaFlag) flags.CheckExclusive(ctx, testMainnetFlag, testSepoliaFlag, testBerachainFlag, testBepoliaFlag)
if (ctx.IsSet(testMainnetFlag.Name) || ctx.IsSet(testSepoliaFlag.Name)) && ctx.IsSet(filterQueryFileFlag.Name) { if (ctx.IsSet(testMainnetFlag.Name) || ctx.IsSet(testSepoliaFlag.Name) || ctx.IsSet(testBerachainFlag.Name) || ctx.IsSet(testBepoliaFlag.Name)) &&
exit(filterQueryFileFlag.Name + " cannot be used with " + testMainnetFlag.Name + " or " + testSepoliaFlag.Name) ctx.IsSet(filterQueryFileFlag.Name) {
exit(filterQueryFileFlag.Name + " cannot be used with " + testMainnetFlag.Name + " or " +
testSepoliaFlag.Name + "or" + testBerachainFlag.Name + " or " + testBepoliaFlag.Name)
} }
// configure ethclient // configure ethclient
@ -166,6 +179,44 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
} }
cfg.historyPruneBlock = new(uint64) cfg.historyPruneBlock = new(uint64)
*cfg.historyPruneBlock = history.PrunePoints[params.SepoliaGenesisHash].BlockNumber *cfg.historyPruneBlock = history.PrunePoints[params.SepoliaGenesisHash].BlockNumber
case ctx.Bool(testBerachainFlag.Name):
cfg.fsys = builtinTestFiles
if ctx.IsSet(filterQueryFileFlag.Name) {
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)
} else {
cfg.filterQueryFile = "queries/filter_queries_berachain.json"
}
if ctx.IsSet(historyTestFileFlag.Name) {
cfg.historyTestFile = ctx.String(historyTestFileFlag.Name)
} else {
cfg.historyTestFile = "queries/history_berachain.json"
}
if ctx.IsSet(traceTestFileFlag.Name) {
cfg.traceTestFile = ctx.String(traceTestFileFlag.Name)
} else {
cfg.traceTestFile = "queries/trace_berachain.json"
}
cfg.historyPruneBlock = new(uint64)
*cfg.historyPruneBlock = history.PrunePoints[params.BerachainGenesisHash].BlockNumber
case ctx.Bool(testBepoliaFlag.Name):
cfg.fsys = builtinTestFiles
if ctx.IsSet(filterQueryFileFlag.Name) {
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)
} else {
cfg.filterQueryFile = "queries/filter_queries_bepolia.json"
}
if ctx.IsSet(historyTestFileFlag.Name) {
cfg.historyTestFile = ctx.String(historyTestFileFlag.Name)
} else {
cfg.historyTestFile = "queries/history_bepolia.json"
}
if ctx.IsSet(traceTestFileFlag.Name) {
cfg.traceTestFile = ctx.String(traceTestFileFlag.Name)
} else {
cfg.traceTestFile = "queries/trace_bepolia.json"
}
cfg.historyPruneBlock = new(uint64)
*cfg.historyPruneBlock = history.PrunePoints[params.BepoliaGenesisHash].BlockNumber
default: default:
cfg.fsys = os.DirFS(".") cfg.fsys = os.DirFS(".")
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name) cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)

View file

@ -39,11 +39,16 @@ const (
HashLength = 32 HashLength = 32
// AddressLength is the expected length of the address // AddressLength is the expected length of the address
AddressLength = 20 AddressLength = 20
// Berachain: PubkeyLength represents the expected byte length of a BLS12-381 public key
// as used by the beacon chain.
PubkeyLength = 48
) )
var ( var (
hashT = reflect.TypeFor[Hash]() hashT = reflect.TypeOf(Hash{})
addressT = reflect.TypeFor[Address]() addressT = reflect.TypeOf(Address{})
pubkeyT = reflect.TypeOf(Pubkey{})
// MaxAddress represents the maximum possible address value. // MaxAddress represents the maximum possible address value.
MaxAddress = HexToAddress("0xffffffffffffffffffffffffffffffffffffffff") MaxAddress = HexToAddress("0xffffffffffffffffffffffffffffffffffffffff")
@ -486,3 +491,60 @@ func (b PrettyBytes) TerminalString() string {
} }
return fmt.Sprintf("%#x...%x (%dB)", b[:3], b[len(b)-3:], len(b)) return fmt.Sprintf("%#x...%x (%dB)", b[:3], b[len(b)-3:], len(b))
} }
/////////// Berachain: Pubkey
// Pubkey represents a fixed-length 48-byte BLS public key.
// JSON and text serialization use 0x-prefixed hex strings.
type Pubkey [PubkeyLength]byte
// Bytes returns a copy of the underlying byte slice.
func (p Pubkey) Bytes() []byte { return p[:] }
// String returns the hex-encoded string representation of the pubkey.
func (p Pubkey) String() string { return hexutil.Encode(p[:]) }
// Format implements fmt.Formatter.
// Pubkey supports the %v, %s, %q, %x, %X and %d format verbs.
func (p Pubkey) Format(s fmt.State, c rune) {
hexb := make([]byte, 2+len(p)*2)
copy(hexb, "0x")
hex.Encode(hexb[2:], p[:])
switch c {
case 'x', 'X':
if !s.Flag('#') {
hexb = hexb[2:]
}
if c == 'X' {
hexb = bytes.ToUpper(hexb)
}
fallthrough
case 'v', 's':
s.Write(hexb)
case 'q':
q := []byte{'"'}
s.Write(q)
s.Write(hexb)
s.Write(q)
case 'd':
fmt.Fprint(s, ([len(p)]byte)(p))
default:
fmt.Fprintf(s, "%%!%c(pubkey=%x)", c, p)
}
}
// MarshalText encodes the pubkey as a 0x-prefixed hex string.
func (p Pubkey) MarshalText() ([]byte, error) {
return hexutil.Bytes(p[:]).MarshalText()
}
// UnmarshalText decodes a 0x-prefixed hex string into the pubkey.
func (p *Pubkey) UnmarshalText(input []byte) error {
return hexutil.UnmarshalFixedText("Pubkey", input, p[:])
}
// UnmarshalJSON decodes a JSON string containing the 0x-prefixed hex pubkey.
func (p *Pubkey) UnmarshalJSON(input []byte) error {
return hexutil.UnmarshalFixedJSON(pubkeyT, input, p[:])
}

View file

@ -282,6 +282,17 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
return err return err
} }
} }
// Berachain specific: verify the existence of the proposer pubkey.
prague1 := chain.Config().IsPrague1(header.Number, header.Time)
if !prague1 {
if header.ParentProposerPubkey != nil {
return fmt.Errorf("invalid proposer pubkey: have %#x, expected nil", header.ParentProposerPubkey)
}
} else {
if header.ParentProposerPubkey == nil {
return errors.New("header is missing proposer pubkey")
}
}
return nil return nil
} }

View file

@ -54,6 +54,21 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade
// CalcBaseFee calculates the basefee of the header. // CalcBaseFee calculates the basefee of the header.
func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
calculatedBaseFee := calcBaseFee(config, parent)
// Starting at the Prague1 fork, the base fee must be at least the minimum base fee.
if config.IsPrague1(parent.Number, parent.Time) {
minBaseFee := new(big.Int).SetUint64(config.Berachain.Prague1.MinimumBaseFeeWei)
if calculatedBaseFee.Cmp(minBaseFee) < 0 {
calculatedBaseFee = minBaseFee
}
}
return calculatedBaseFee
}
// calcBaseFee calculates the basefee of the header in wei.
func calcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
// If the current block is the first EIP-1559 block, return the InitialBaseFee. // If the current block is the first EIP-1559 block, return the InitialBaseFee.
if !config.IsLondon(parent.Number) { if !config.IsLondon(parent.Number) {
return new(big.Int).SetUint64(params.InitialBaseFee) return new(big.Int).SetUint64(params.InitialBaseFee)
@ -76,7 +91,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
num.SetUint64(parent.GasUsed - parentGasTarget) num.SetUint64(parent.GasUsed - parentGasTarget)
num.Mul(num, parent.BaseFee) num.Mul(num, parent.BaseFee)
num.Div(num, denom.SetUint64(parentGasTarget)) num.Div(num, denom.SetUint64(parentGasTarget))
num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator())) num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator(parent.Number, parent.Time)))
if num.Cmp(common.Big1) < 0 { if num.Cmp(common.Big1) < 0 {
return num.Add(parent.BaseFee, common.Big1) return num.Add(parent.BaseFee, common.Big1)
} }
@ -87,7 +102,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
num.SetUint64(parentGasTarget - parent.GasUsed) num.SetUint64(parentGasTarget - parent.GasUsed)
num.Mul(num, parent.BaseFee) num.Mul(num, parent.BaseFee)
num.Div(num, denom.SetUint64(parentGasTarget)) num.Div(num, denom.SetUint64(parentGasTarget))
num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator())) num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator(parent.Number, parent.Time)))
baseFee := num.Sub(parent.BaseFee, num) baseFee := num.Sub(parent.BaseFee, num)
if baseFee.Cmp(common.Big0) < 0 { if baseFee.Cmp(common.Big0) < 0 {

View file

@ -19,10 +19,13 @@ package core
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"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/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
@ -47,7 +50,7 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain) *Bloc
// ValidateBody validates the given block's uncles and verifies the block // ValidateBody validates the given block's uncles and verifies the block
// header's transaction and uncle roots. The headers are assumed to be already // header's transaction and uncle roots. The headers are assumed to be already
// validated at this point. // validated at this point. Also it the Prague1 block according to BRIP-0004.
func (v *BlockValidator) ValidateBody(block *types.Block) error { func (v *BlockValidator) ValidateBody(block *types.Block) error {
// check EIP 7934 RLP-encoded block size cap // check EIP 7934 RLP-encoded block size cap
if v.config.IsOsaka(block.Number(), block.Time()) && block.Size() > params.MaxBlockSize { if v.config.IsOsaka(block.Number(), block.Time()) && block.Size() > params.MaxBlockSize {
@ -67,7 +70,8 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash) return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash)
} }
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash { txs := block.Transactions()
if hash := types.DeriveSha(txs, trie.NewStackTrie(nil)); hash != header.TxHash {
return fmt.Errorf("transaction root hash mismatch (header value %x, calculated %x)", header.TxHash, hash) return fmt.Errorf("transaction root hash mismatch (header value %x, calculated %x)", header.TxHash, hash)
} }
@ -85,9 +89,48 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
return errors.New("withdrawals present in block body") return errors.New("withdrawals present in block body")
} }
// Berachain: Pre-compute expected PoL tx hash when in Prague1.
isPrague1 := v.config.IsPrague1(block.Number(), block.Time())
var expectedPoLTx *types.Transaction
if isPrague1 {
if block.ProposerPubkey() == nil {
return errors.New("post-prague1 block missing parent proposer pubkey")
}
polTx, err := types.NewPoLTx(
v.config.ChainID,
v.config.Berachain.Prague1.PoLDistributorAddress,
new(big.Int).Sub(block.Number(), big.NewInt(1)),
params.PoLTxGasLimit,
block.BaseFee(),
block.ProposerPubkey(),
)
if err != nil {
return fmt.Errorf("failed to create expected PoL tx: %w", err)
}
expectedPoLTx = polTx
// Validate that the block has at least one tx.
if len(txs) == 0 {
return errors.New("post-prague1 block missing PoL tx")
}
}
// Blob transactions may be present after the Cancun fork. // Blob transactions may be present after the Cancun fork.
var blobs int var blobs int
for i, tx := range block.Transactions() { for i, tx := range txs {
// Berachain: validate the PoL tx is only the first tx in the block.
switch {
case isPrague1 && i == 0:
if tx.Hash() != expectedPoLTx.Hash() {
log.Debug("PoL tx hash mismatch", "have", tx.Hash(), "expected", expectedPoLTx.Hash())
log.Debug("PoL tx contents", "have", spew.Sdump(tx), "expected", spew.Sdump(expectedPoLTx))
return fmt.Errorf("PoL tx hash mismatch: have %v, want %v", tx.Hash(), expectedPoLTx.Hash())
}
case tx.Type() == types.PoLTxType:
return fmt.Errorf("invalid block: tx at index %d is a PoL tx", i)
}
// Count the number of blobs to validate against the header's blobGasUsed // Count the number of blobs to validate against the header's blobGasUsed
blobs += len(tx.BlobHashes()) blobs += len(tx.BlobHashes())

View file

@ -30,8 +30,131 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
) )
// -----------------------------------------------------------------------------
// Berachain:Prague-1 PoL validation tests
// -----------------------------------------------------------------------------
// newPrague1Config returns a ChainConfig where Prague (and Prague-1) are active
// from timestamp 0. The caller can specify the PoL distributor address.
func newPrague1Config(distributor common.Address) *params.ChainConfig {
zero := uint64(0)
cfg := *params.AllDevChainProtocolChanges // copy
cfg.Berachain.Prague1 = params.Prague1Config{
Time: &zero,
BaseFeeChangeDenominator: 48,
MinimumBaseFeeWei: 10000000000,
PoLDistributorAddress: distributor,
}
return &cfg
}
// buildTestChain initialises an in-memory blockchain with the provided config
// and returns the chain and its validator.
func buildTestChain(t *testing.T, cfg *params.ChainConfig) (*BlockChain, Validator) {
t.Helper()
db := rawdb.NewMemoryDatabase()
genesis := &Genesis{Config: cfg}
chain, err := NewBlockChain(db, genesis, ethash.NewFaker(), nil)
if err != nil {
t.Fatalf("failed to create blockchain: %v", err)
}
return chain, chain.Validator()
}
// samplePubkey returns a deterministic 48-byte pubkey for tests.
func samplePubkey() *common.Pubkey {
var pk common.Pubkey
for i := 0; i < common.PubkeyLength; i++ {
pk[i] = byte(i)
}
return &pk
}
// makeBlock builds a child block on top of parent with the supplied txs and timestamp.
func makeBlock(parent *types.Header, txs types.Transactions, timestamp uint64) *types.Block {
header := &types.Header{
ParentHash: parent.Hash(),
Number: new(big.Int).Add(parent.Number, big.NewInt(1)),
Time: timestamp,
GasLimit: params.GenesisGasLimit * 10,
Difficulty: big.NewInt(1),
ParentProposerPubkey: samplePubkey(),
BaseFee: big.NewInt(1000000000),
}
return types.NewBlock(header, &types.Body{Transactions: txs}, nil, trie.NewStackTrie(nil))
}
func TestValidateBody_Prague1_Valid(t *testing.T) {
distributor := common.HexToAddress("0x1111111111111111111111111111111111111111")
cfg := newPrague1Config(distributor)
chain, validator := buildTestChain(t, cfg)
// Build PoL tx + dummy tx.
polTx, err := types.NewPoLTx(cfg.ChainID, distributor, big.NewInt(0), params.PoLTxGasLimit, big.NewInt(1000000000), samplePubkey())
if err != nil {
t.Fatalf("failed to create PoL tx: %v", err)
}
dummyTx := types.NewTx(&types.LegacyTx{Nonce: 1})
block := makeBlock(chain.CurrentHeader(), types.Transactions{polTx, dummyTx}, 1)
if err := validator.ValidateBody(block); err != nil {
t.Fatalf("ValidateBody returned error for valid Prague1 block: %v", err)
}
}
func TestValidateBody_Prague1_InvalidHash(t *testing.T) {
distributor := common.HexToAddress("0x2222222222222222222222222222222222222222")
cfg := newPrague1Config(distributor)
chain, validator := buildTestChain(t, cfg)
// PoL tx with WRONG pubkey (different from header.ParentProposerPubkey).
wrongPk := &common.Pubkey{}
polTx, _ := types.NewPoLTx(cfg.ChainID, distributor, big.NewInt(0), params.PoLTxGasLimit, big.NewInt(1000000000), wrongPk)
block := makeBlock(chain.CurrentHeader(), types.Transactions{polTx}, 1)
if err := validator.ValidateBody(block); err == nil {
t.Fatalf("expected error due to invalid PoL hash, got nil")
}
}
func TestValidateBody_Prague1_MisplacedPoL(t *testing.T) {
distributor := common.HexToAddress("0x3333333333333333333333333333333333333333")
cfg := newPrague1Config(distributor)
chain, validator := buildTestChain(t, cfg)
polTx, _ := types.NewPoLTx(cfg.ChainID, distributor, big.NewInt(0), params.PoLTxGasLimit, big.NewInt(1000000000), samplePubkey())
dummyTx := types.NewTx(&types.LegacyTx{Nonce: 1})
// PoL tx placed second.
block := makeBlock(chain.CurrentHeader(), types.Transactions{dummyTx, polTx}, 1)
if err := validator.ValidateBody(block); err == nil {
t.Fatalf("expected error for PoL tx at index >0, got nil")
}
}
func TestValidateBody_PrePrague1_PoLProhibited(t *testing.T) {
distributor := common.HexToAddress("0x4444444444444444444444444444444444444444")
// Prague time active, but Prague1 *future* at timestamp 1000.
future := uint64(1000)
cfg := *params.AllDevChainProtocolChanges
cfg.Berachain.Prague1.Time = &future
cfg.Berachain.Prague1.BaseFeeChangeDenominator = 48
cfg.Berachain.Prague1.MinimumBaseFeeWei = 10000000000
cfg.Berachain.Prague1.PoLDistributorAddress = distributor
chain, validator := buildTestChain(t, &cfg)
polTx, _ := types.NewPoLTx(cfg.ChainID, distributor, big.NewInt(0), params.PoLTxGasLimit, big.NewInt(1000000000), samplePubkey())
block := makeBlock(chain.CurrentHeader(), types.Transactions{polTx}, 1) // timestamp 1 < future
if err := validator.ValidateBody(block); err == nil {
t.Fatalf("expected error: PoL tx before Prague1 fork should be invalid")
}
}
// Tests that simple header verification works, for both good and bad blocks. // Tests that simple header verification works, for both good and bad blocks.
func TestHeaderVerification(t *testing.T) { func TestHeaderVerification(t *testing.T) {
testHeaderVerification(t, rawdb.HashScheme) testHeaderVerification(t, rawdb.HashScheme)

View file

@ -102,6 +102,12 @@ func (b *BlockGen) SetParentBeaconRoot(root common.Hash) {
ProcessBeaconBlockRoot(root, vm.NewEVM(blockContext, b.statedb, b.cm.config, vm.Config{})) ProcessBeaconBlockRoot(root, vm.NewEVM(blockContext, b.statedb, b.cm.config, vm.Config{}))
} }
// SetParentProposerPubkey sets the parent proposer pubkey field of the generated
// block.
func (b *BlockGen) SetParentProposerPubkey(pubkey common.Pubkey) {
b.header.ParentProposerPubkey = &pubkey
}
// addTx adds a transaction to the generated block. If no coinbase has // addTx adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address. // been set, the block's coinbase is set to the zero address.
// //
@ -116,9 +122,14 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
var ( var (
blockContext = NewEVMBlockContext(b.header, bc, &b.header.Coinbase) blockContext = NewEVMBlockContext(b.header, bc, &b.header.Coinbase)
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig) evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
gasUsed = &b.header.GasUsed
) )
// Berachain: PoL txs do not count towards block gas.
if tx.Type() == types.PoLTxType {
gasUsed = new(uint64)
}
b.statedb.SetTxContext(tx.Hash(), len(b.txs)) b.statedb.SetTxContext(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed) receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, gasUsed)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -619,6 +630,9 @@ func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engi
header.BlobGasUsed = new(uint64) header.BlobGasUsed = new(uint64)
header.ParentBeaconRoot = new(common.Hash) header.ParentBeaconRoot = new(common.Hash)
} }
if cm.config.IsPrague1(header.Number, header.Time) {
header.ParentProposerPubkey = new(common.Pubkey)
}
return header return header
} }

View file

@ -268,6 +268,11 @@ func gatherForks(config *params.ChainConfig, genesis uint64) ([]uint64, []uint64
} }
} }
} }
// Berachain-specific: include nested Prague1 fork time in the fork id set
// so that clients using it (e.g. reth) compute the same fork checksum.
if config != nil && config.Berachain.Prague1.Time != nil {
forksByTime = append(forksByTime, *config.Berachain.Prague1.Time)
}
slices.Sort(forksByBlock) slices.Sort(forksByBlock)
slices.Sort(forksByTime) slices.Sort(forksByTime)

View file

@ -223,6 +223,10 @@ func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.Gene
genesis = DefaultHoleskyGenesisBlock() genesis = DefaultHoleskyGenesisBlock()
case params.HoodiGenesisHash: case params.HoodiGenesisHash:
genesis = DefaultHoodiGenesisBlock() genesis = DefaultHoodiGenesisBlock()
case params.BerachainGenesisHash:
genesis = DefaultBerachainGenesisBlock()
case params.BepoliaGenesisHash:
genesis = DefaultBepoliaGenesisBlock()
} }
if genesis != nil { if genesis != nil {
return genesis.Alloc, nil return genesis.Alloc, nil
@ -303,8 +307,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
ghash := rawdb.ReadCanonicalHash(db, 0) ghash := rawdb.ReadCanonicalHash(db, 0)
if (ghash == common.Hash{}) { if (ghash == common.Hash{}) {
if genesis == nil { if genesis == nil {
log.Info("Writing default main-net genesis block") log.Info("Writing default berachain mainnet genesis block")
genesis = DefaultGenesisBlock() genesis = DefaultBerachainGenesisBlock()
} else { } else {
log.Info("Writing custom genesis block") log.Info("Writing custom genesis block")
} }
@ -328,8 +332,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
// networks must explicitly specify the genesis in the config file, mainnet // networks must explicitly specify the genesis in the config file, mainnet
// genesis will be used as default and the initialization will always fail. // genesis will be used as default and the initialization will always fail.
if genesis == nil { if genesis == nil {
log.Info("Writing default main-net genesis block") log.Info("Writing default berachain mainnet genesis block")
genesis = DefaultGenesisBlock() genesis = DefaultBerachainGenesisBlock()
} else { } else {
log.Info("Writing custom genesis block") log.Info("Writing custom genesis block")
} }
@ -381,9 +385,11 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
return newCfg, ghash, compatErr, nil return newCfg, ghash, compatErr, nil
} }
// Don't overwrite if the old is identical to the new. It's useful // Don't overwrite if the old is identical to the new. It's useful
// for the scenarios that database is opened in the read-only mode. // for the scenarios that database is opened in the read-only mode
// OR if the chain config is updated to include new Berachain fork info.
storedData, _ := json.Marshal(storedCfg) storedData, _ := json.Marshal(storedCfg)
if newData, _ := json.Marshal(newCfg); !bytes.Equal(storedData, newData) { if newData, _ := json.Marshal(newCfg); !bytes.Equal(storedData, newData) {
log.Info("Writing new chain config")
rawdb.WriteChainConfig(db, ghash, newCfg) rawdb.WriteChainConfig(db, ghash, newCfg)
} }
return newCfg, ghash, nil, nil return newCfg, ghash, nil, nil
@ -419,8 +425,8 @@ func LoadChainConfig(db ethdb.Database, genesis *Genesis) (cfg *params.ChainConf
return genesis.Config, ghash, nil return genesis.Config, ghash, nil
} }
// There is no stored chain config and no new config provided, // There is no stored chain config and no new config provided,
// In this case the default chain config(mainnet) will be used // In this case the default chain config(berachain mainnet) will be used
return params.MainnetChainConfig, params.MainnetGenesisHash, nil return params.BerachainChainConfig, params.BerachainGenesisHash, nil
} }
// chainConfigOrDefault retrieves the attached chain configuration. If the genesis // chainConfigOrDefault retrieves the attached chain configuration. If the genesis
@ -438,6 +444,10 @@ func (g *Genesis) chainConfigOrDefault(ghash common.Hash, stored *params.ChainCo
return params.SepoliaChainConfig return params.SepoliaChainConfig
case ghash == params.HoodiGenesisHash: case ghash == params.HoodiGenesisHash:
return params.HoodiChainConfig return params.HoodiChainConfig
case ghash == params.BerachainGenesisHash:
return params.BerachainChainConfig
case ghash == params.BepoliaGenesisHash:
return params.BepoliaChainConfig
default: default:
return stored return stored
} }
@ -518,6 +528,12 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
if conf.IsPrague(num, g.Timestamp) { if conf.IsPrague(num, g.Timestamp) {
head.RequestsHash = &types.EmptyRequestsHash head.RequestsHash = &types.EmptyRequestsHash
} }
if conf.IsPrague1(num, g.Timestamp) {
// BRIP-0004: The parentProposerPubkey of the genesis block is always
// the zero pubkey. This is because the genesis block does not have a parent
// by definition.
head.ParentProposerPubkey = new(common.Pubkey)
}
} }
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil)) return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil))
} }
@ -644,6 +660,32 @@ func DefaultHoodiGenesisBlock() *Genesis {
} }
} }
// DefaultBerachainGenesisBlock returns the Berachain main net genesis block.
func DefaultBerachainGenesisBlock() *Genesis {
return &Genesis{
Config: params.BerachainChainConfig,
Nonce: 0x1234,
ExtraData: []byte{},
GasLimit: 0x1c9c380,
Difficulty: big.NewInt(0x01),
Timestamp: 1737381600,
Alloc: decodePrealloc(berachainAllocData),
}
}
// DefaultBepoliaGenesisBlock returns the Bepolia network genesis block.
func DefaultBepoliaGenesisBlock() *Genesis {
return &Genesis{
Config: params.BepoliaChainConfig,
Nonce: 0x1234,
ExtraData: []byte{},
GasLimit: 0x1c9c380,
Difficulty: big.NewInt(0x01),
Timestamp: 1739976735,
Alloc: decodePrealloc(bepoliaAllocData),
}
}
// DeveloperGenesisBlock returns the 'geth --dev' genesis block. // DeveloperGenesisBlock returns the 'geth --dev' genesis block.
func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis { func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
// Override the default period to the user requested one // Override the default period to the user requested one

File diff suppressed because one or more lines are too long

View file

@ -72,8 +72,8 @@ func testSetupGenesis(t *testing.T, scheme string) {
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil) return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
}, },
wantHash: params.MainnetGenesisHash, wantHash: params.BerachainGenesisHash,
wantConfig: params.MainnetChainConfig, wantConfig: params.BerachainChainConfig,
}, },
{ {
name: "mainnet block in DB, genesis == nil", name: "mainnet block in DB, genesis == nil",
@ -84,6 +84,15 @@ func testSetupGenesis(t *testing.T, scheme string) {
wantHash: params.MainnetGenesisHash, wantHash: params.MainnetGenesisHash,
wantConfig: params.MainnetChainConfig, wantConfig: params.MainnetChainConfig,
}, },
{
name: "berachain block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
DefaultBerachainGenesisBlock().MustCommit(db, triedb.NewDatabase(db, newDbConfig(scheme)))
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
},
wantHash: params.BerachainGenesisHash,
wantConfig: params.BerachainChainConfig,
},
{ {
name: "custom block in DB, genesis == nil", name: "custom block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
@ -103,6 +112,15 @@ func testSetupGenesis(t *testing.T, scheme string) {
}, },
wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash}, wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash},
}, },
{
name: "custom block in DB, genesis == bepolia",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
customg.Commit(db, tdb)
return SetupGenesisBlock(db, tdb, DefaultBepoliaGenesisBlock())
},
wantErr: &GenesisMismatchError{Stored: customghash, New: params.BepoliaGenesisHash},
},
{ {
name: "custom block in DB, genesis == hoodi", name: "custom block in DB, genesis == hoodi",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
@ -188,6 +206,8 @@ func TestGenesisHashes(t *testing.T) {
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash}, {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
{DefaultHoleskyGenesisBlock(), params.HoleskyGenesisHash}, {DefaultHoleskyGenesisBlock(), params.HoleskyGenesisHash},
{DefaultHoodiGenesisBlock(), params.HoodiGenesisHash}, {DefaultHoodiGenesisBlock(), params.HoodiGenesisHash},
{DefaultBerachainGenesisBlock(), params.BerachainGenesisHash},
{DefaultBepoliaGenesisBlock(), params.BepoliaGenesisHash},
} { } {
// Test via MustCommit // Test via MustCommit
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()

View file

@ -413,6 +413,10 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
nBlobs += len(tx.BlobHashes()) nBlobs += len(tx.BlobHashes())
} }
header.Root = common.BytesToHash(hasher.Sum(nil)) header.Root = common.BytesToHash(hasher.Sum(nil))
if config.IsPrague1(header.Number, header.Time) {
proposerPubkey := common.Pubkey{0x01, 0x02, 0x03}
header.ParentProposerPubkey = &proposerPubkey
}
if config.IsCancun(header.Number, header.Time) { if config.IsCancun(header.Number, header.Time) {
excess := eip4844.CalcExcessBlobGas(config, parent.Header(), header.Time) excess := eip4844.CalcExcessBlobGas(config, parent.Header(), header.Time)
used := uint64(nBlobs * params.BlobTxBlobGasPerBlob) used := uint64(nBlobs * params.BlobTxBlobGasPerBlob)

View file

@ -166,6 +166,9 @@ type Message struct {
// 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.
SkipFromEOACheck bool SkipFromEOACheck bool
// Berachain:IsPoLTx is true if the message is a PoL tx.
IsPoLTx bool
} }
// TransactionToMessage converts a transaction into a Message. // TransactionToMessage converts a transaction into a Message.
@ -185,6 +188,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
SkipFromEOACheck: false, SkipFromEOACheck: false,
BlobHashes: tx.BlobHashes(), BlobHashes: tx.BlobHashes(),
BlobGasFeeCap: tx.BlobGasFeeCap(), BlobGasFeeCap: tx.BlobGasFeeCap(),
IsPoLTx: tx.Type() == types.PoLTxType,
} }
// If baseFee provided, set gasPrice to effectiveGasPrice. // If baseFee provided, set gasPrice to effectiveGasPrice.
if baseFee != nil { if baseFee != nil {
@ -207,9 +211,30 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
// state and would never be accepted within a block. // state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, error) { func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, error) {
evm.SetTxContext(NewEVMTxContext(msg)) evm.SetTxContext(NewEVMTxContext(msg))
// Berachain: ApplyPoLMessage is used to apply the PoL tx to the EVM, skipping the
// normal state transition checks.
if msg.IsPoLTx {
return ApplyPoLMessage(msg, evm), nil
}
return newStateTransition(evm, msg, gp).execute() return newStateTransition(evm, msg, gp).execute()
} }
// Berachain: ApplyPoLMessage applies the BRIP-0004 PoL tx to the EVM. No gas is consumed.
func ApplyPoLMessage(msg *Message, evm *vm.EVM) *ExecutionResult {
evm.StateDB.AddAddressToAccessList(*msg.To)
result := &ExecutionResult{}
ret, leftOverGas, err := evm.Call(msg.From, *msg.To, msg.Data, msg.GasLimit, common.U2560)
if err != nil {
result.Err = fmt.Errorf("PoL tx failed to execute: %v", err)
}
result.ReturnData = ret
result.MaxUsedGas = msg.GasLimit - leftOverGas // To inform how much gas was needed to run the msg.
return result
}
// stateTransition represents a state transition. // stateTransition represents a state transition.
// //
// == The State Transitioning Model // == The State Transitioning Model

View file

@ -327,6 +327,11 @@ func (p *TxPool) Add(txs []*types.Transaction, sync bool) []error {
// Mark this transaction belonging to no-subpool // Mark this transaction belonging to no-subpool
splits[i] = -1 splits[i] = -1
// Berachain: PoL txs are rejected.
if tx.Type() == types.PoLTxType {
continue
}
// Try to find a subpool that accepts the transaction // Try to find a subpool that accepts the transaction
for j, subpool := range p.subpools { for j, subpool := range p.subpools {
if subpool.Filter(tx) { if subpool.Filter(tx) {

View file

@ -106,6 +106,9 @@ type Header struct {
// RequestsHash was added by EIP-7685 and is ignored in legacy headers. // RequestsHash was added by EIP-7685 and is ignored in legacy headers.
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
// ParentProposerPubkey was added by BRIP-0004 and is ignored in legacy headers.
ParentProposerPubkey *common.Pubkey `json:"parentProposerPubkey" rlp:"optional"`
} }
// field type overrides for gencodec // field type overrides for gencodec
@ -329,6 +332,10 @@ func CopyHeader(h *Header) *Header {
cpy.RequestsHash = new(common.Hash) cpy.RequestsHash = new(common.Hash)
*cpy.RequestsHash = *h.RequestsHash *cpy.RequestsHash = *h.RequestsHash
} }
if h.ParentProposerPubkey != nil {
cpy.ParentProposerPubkey = new(common.Pubkey)
*cpy.ParentProposerPubkey = *h.ParentProposerPubkey
}
return &cpy return &cpy
} }
@ -408,8 +415,9 @@ func (b *Block) BaseFee() *big.Int {
return new(big.Int).Set(b.header.BaseFee) return new(big.Int).Set(b.header.BaseFee)
} }
func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot } func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot }
func (b *Block) RequestsHash() *common.Hash { return b.header.RequestsHash } func (b *Block) RequestsHash() *common.Hash { return b.header.RequestsHash }
func (b *Block) ProposerPubkey() *common.Pubkey { return b.header.ParentProposerPubkey }
func (b *Block) ExcessBlobGas() *uint64 { func (b *Block) ExcessBlobGas() *uint64 {
var excessBlobGas *uint64 var excessBlobGas *uint64

View file

@ -16,28 +16,29 @@ var _ = (*headerMarshaling)(nil)
// MarshalJSON marshals as JSON. // MarshalJSON marshals as JSON.
func (h Header) MarshalJSON() ([]byte, error) { func (h Header) MarshalJSON() ([]byte, error) {
type Header struct { type Header struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"` ParentHash common.Hash `json:"parentHash" gencodec:"required"`
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase common.Address `json:"miner"` Coinbase common.Address `json:"miner"`
Root common.Hash `json:"stateRoot" gencodec:"required"` Root common.Hash `json:"stateRoot" gencodec:"required"`
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
Number *hexutil.Big `json:"number" gencodec:"required"` Number *hexutil.Big `json:"number" gencodec:"required"`
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` Time hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Extra hexutil.Bytes `json:"extraData" gencodec:"required"` Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash"` MixDigest common.Hash `json:"mixHash"`
Nonce BlockNonce `json:"nonce"` Nonce BlockNonce `json:"nonce"`
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
Hash common.Hash `json:"hash"` ParentProposerPubkey *common.Pubkey `json:"parentProposerPubkey" rlp:"optional"`
Hash common.Hash `json:"hash"`
} }
var enc Header var enc Header
enc.ParentHash = h.ParentHash enc.ParentHash = h.ParentHash
@ -61,6 +62,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas) enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas)
enc.ParentBeaconRoot = h.ParentBeaconRoot enc.ParentBeaconRoot = h.ParentBeaconRoot
enc.RequestsHash = h.RequestsHash enc.RequestsHash = h.RequestsHash
enc.ParentProposerPubkey = h.ParentProposerPubkey
enc.Hash = h.Hash() enc.Hash = h.Hash()
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -68,27 +70,28 @@ func (h Header) MarshalJSON() ([]byte, error) {
// UnmarshalJSON unmarshals from JSON. // UnmarshalJSON unmarshals from JSON.
func (h *Header) UnmarshalJSON(input []byte) error { func (h *Header) UnmarshalJSON(input []byte) error {
type Header struct { type Header struct {
ParentHash *common.Hash `json:"parentHash" gencodec:"required"` ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase *common.Address `json:"miner"` Coinbase *common.Address `json:"miner"`
Root *common.Hash `json:"stateRoot" gencodec:"required"` Root *common.Hash `json:"stateRoot" gencodec:"required"`
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom *Bloom `json:"logsBloom" gencodec:"required"` Bloom *Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
Number *hexutil.Big `json:"number" gencodec:"required"` Number *hexutil.Big `json:"number" gencodec:"required"`
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest *common.Hash `json:"mixHash"` MixDigest *common.Hash `json:"mixHash"`
Nonce *BlockNonce `json:"nonce"` Nonce *BlockNonce `json:"nonce"`
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
ParentProposerPubkey *common.Pubkey `json:"parentProposerPubkey" rlp:"optional"`
} }
var dec Header var dec Header
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -169,5 +172,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
if dec.RequestsHash != nil { if dec.RequestsHash != nil {
h.RequestsHash = dec.RequestsHash h.RequestsHash = dec.RequestsHash
} }
if dec.ParentProposerPubkey != nil {
h.ParentProposerPubkey = dec.ParentProposerPubkey
}
return nil return nil
} }

View file

@ -43,7 +43,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
_tmp4 := obj.ExcessBlobGas != nil _tmp4 := obj.ExcessBlobGas != nil
_tmp5 := obj.ParentBeaconRoot != nil _tmp5 := obj.ParentBeaconRoot != nil
_tmp6 := obj.RequestsHash != nil _tmp6 := obj.RequestsHash != nil
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 { _tmp7 := obj.ParentProposerPubkey != nil
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 {
if obj.BaseFee == nil { if obj.BaseFee == nil {
w.Write(rlp.EmptyString) w.Write(rlp.EmptyString)
} else { } else {
@ -53,41 +54,48 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
w.WriteBigInt(obj.BaseFee) w.WriteBigInt(obj.BaseFee)
} }
} }
if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 { if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 {
if obj.WithdrawalsHash == nil { if obj.WithdrawalsHash == nil {
w.Write([]byte{0x80}) w.Write([]byte{0x80})
} else { } else {
w.WriteBytes(obj.WithdrawalsHash[:]) w.WriteBytes(obj.WithdrawalsHash[:])
} }
} }
if _tmp3 || _tmp4 || _tmp5 || _tmp6 { if _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 {
if obj.BlobGasUsed == nil { if obj.BlobGasUsed == nil {
w.Write([]byte{0x80}) w.Write([]byte{0x80})
} else { } else {
w.WriteUint64((*obj.BlobGasUsed)) w.WriteUint64((*obj.BlobGasUsed))
} }
} }
if _tmp4 || _tmp5 || _tmp6 { if _tmp4 || _tmp5 || _tmp6 || _tmp7 {
if obj.ExcessBlobGas == nil { if obj.ExcessBlobGas == nil {
w.Write([]byte{0x80}) w.Write([]byte{0x80})
} else { } else {
w.WriteUint64((*obj.ExcessBlobGas)) w.WriteUint64((*obj.ExcessBlobGas))
} }
} }
if _tmp5 || _tmp6 { if _tmp5 || _tmp6 || _tmp7 {
if obj.ParentBeaconRoot == nil { if obj.ParentBeaconRoot == nil {
w.Write([]byte{0x80}) w.Write([]byte{0x80})
} else { } else {
w.WriteBytes(obj.ParentBeaconRoot[:]) w.WriteBytes(obj.ParentBeaconRoot[:])
} }
} }
if _tmp6 { if _tmp6 || _tmp7 {
if obj.RequestsHash == nil { if obj.RequestsHash == nil {
w.Write([]byte{0x80}) w.Write([]byte{0x80})
} else { } else {
w.WriteBytes(obj.RequestsHash[:]) w.WriteBytes(obj.RequestsHash[:])
} }
} }
if _tmp7 {
if obj.ParentProposerPubkey == nil {
w.Write([]byte{0x80})
} else {
w.WriteBytes(obj.ParentProposerPubkey[:])
}
}
w.ListEnd(_tmp0) w.ListEnd(_tmp0)
return w.Flush() return w.Flush()
} }

View file

@ -205,7 +205,7 @@ func (r *Receipt) decodeTyped(b []byte) error {
return errShortTypedReceipt return errShortTypedReceipt
} }
switch b[0] { switch b[0] {
case DynamicFeeTxType, AccessListTxType, BlobTxType, SetCodeTxType: case DynamicFeeTxType, AccessListTxType, BlobTxType, SetCodeTxType, PoLTxType:
var data receiptRLP var data receiptRLP
err := rlp.DecodeBytes(b[1:], &data) err := rlp.DecodeBytes(b[1:], &data)
if err != nil { if err != nil {
@ -368,7 +368,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
} }
w.WriteByte(r.Type) w.WriteByte(r.Type)
switch r.Type { switch r.Type {
case AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType: case AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType, PoLTxType:
rlp.Encode(w, data) rlp.Encode(w, data)
default: default:
// For unsupported types, write nothing. Since this is for // For unsupported types, write nothing. Since this is for

View file

@ -49,6 +49,9 @@ const (
DynamicFeeTxType = 0x02 DynamicFeeTxType = 0x02
BlobTxType = 0x03 BlobTxType = 0x03
SetCodeTxType = 0x04 SetCodeTxType = 0x04
// Berachain specific.
PoLTxType = 0x7E
) )
// Transaction is an Ethereum transaction. // Transaction is an Ethereum transaction.
@ -211,6 +214,8 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
inner = new(BlobTx) inner = new(BlobTx)
case SetCodeTxType: case SetCodeTxType:
inner = new(SetCodeTx) inner = new(SetCodeTx)
case PoLTxType:
inner = new(PoLTx)
default: default:
return nil, ErrTxTypeNotSupported return nil, ErrTxTypeNotSupported
} }

View file

@ -154,6 +154,7 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
enc.Commitments = itx.Sidecar.Commitments enc.Commitments = itx.Sidecar.Commitments
enc.Proofs = itx.Sidecar.Proofs enc.Proofs = itx.Sidecar.Proofs
} }
case *SetCodeTx: case *SetCodeTx:
enc.ChainID = (*hexutil.Big)(itx.ChainID.ToBig()) enc.ChainID = (*hexutil.Big)(itx.ChainID.ToBig())
enc.Nonce = (*hexutil.Uint64)(&itx.Nonce) enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
@ -170,6 +171,22 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
enc.S = (*hexutil.Big)(itx.S.ToBig()) enc.S = (*hexutil.Big)(itx.S.ToBig())
yparity := itx.V.Uint64() yparity := itx.V.Uint64()
enc.YParity = (*hexutil.Uint64)(&yparity) enc.YParity = (*hexutil.Uint64)(&yparity)
case *PoLTx:
enc.ChainID = (*hexutil.Big)(itx.ChainID)
enc.To = tx.To()
enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
enc.Gas = (*hexutil.Uint64)(&itx.GasLimit)
enc.MaxFeePerGas = (*hexutil.Big)(itx.GasPrice)
enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasPrice)
enc.GasPrice = (*hexutil.Big)(itx.GasPrice)
enc.Input = (*hexutil.Bytes)(&itx.Data)
v, r, s := itx.rawSignatureValues()
enc.V = (*hexutil.Big)(v)
enc.R = (*hexutil.Big)(r)
enc.S = (*hexutil.Big)(s)
yparity := v.Uint64()
enc.YParity = (*hexutil.Uint64)(&yparity)
} }
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -507,6 +524,38 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
} }
} }
case PoLTxType:
var itx PoLTx
inner = &itx
if dec.ChainID == nil {
return errors.New("missing required field 'chainId' in transaction")
}
var overflow bool
chainID, overflow := uint256.FromBig(dec.ChainID.ToInt())
if overflow {
return errors.New("'chainId' value overflows uint256")
}
itx.ChainID = chainID.ToBig()
if dec.To == nil {
return errors.New("missing required field 'to' in transaction")
}
itx.To = *dec.To
if dec.Nonce == nil {
return errors.New("missing required field 'nonce' in transaction")
}
itx.Nonce = uint64(*dec.Nonce)
if dec.Gas == nil {
return errors.New("missing required field 'gas' for txdata")
}
itx.GasLimit = uint64(*dec.Gas)
if dec.Value == nil {
return errors.New("missing required field 'value' in transaction")
}
if dec.Input == nil {
return errors.New("missing required field 'input' in transaction")
}
itx.Data = *dec.Input
default: default:
return ErrTxTypeNotSupported return ErrTxTypeNotSupported
} }

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